summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Seitz <keiths@redhat.com>2002-04-15 17:39:27 +0000
committerKeith Seitz <keiths@redhat.com>2002-04-15 17:39:27 +0000
commite18731d328254b7e926369741b282fbffc840ea5 (patch)
tree376cfb4a4d9b3272f5950f1c79a5081cbd89d5e6
parentee7709783c85577eba6af30cd711e17e403befed (diff)
downloadgdb-blt2_4y.tar.gz
import of blt2.4yblt2_4y
-rw-r--r--blt/INSTALL115
-rw-r--r--blt/MANIFEST381
-rw-r--r--blt/Makefile.cyg54
-rw-r--r--blt/Makefile.in75
-rw-r--r--blt/Makefile.vc59
-rw-r--r--blt/NEWS609
-rw-r--r--blt/PROBLEMS138
-rw-r--r--blt/README182
-rw-r--r--blt/acconfig.h39
-rw-r--r--blt/aclocal.m438
-rw-r--r--blt/blt.mak34
-rwxr-xr-xblt/cf/config.guess1121
-rwxr-xr-xblt/cf/config.sub1232
-rwxr-xr-xblt/cf/install-sh251
-rw-r--r--blt/cf/install.sh238
-rw-r--r--blt/cf/ldAix72
-rwxr-xr-xblt/configure4566
-rw-r--r--blt/configure.in1385
-rw-r--r--blt/demos/Makefile.cyg78
-rw-r--r--blt/demos/Makefile.in90
-rw-r--r--blt/demos/Makefile.vc78
-rwxr-xr-xblt/demos/barchart1.tcl185
-rwxr-xr-xblt/demos/barchart2.tcl215
-rwxr-xr-xblt/demos/barchart3.tcl173
-rwxr-xr-xblt/demos/barchart4.tcl135
-rwxr-xr-xblt/demos/barchart5.tcl112
-rwxr-xr-xblt/demos/bgexec1.tcl198
-rwxr-xr-xblt/demos/bgexec2.tcl46
-rwxr-xr-xblt/demos/bgexec3.tcl224
-rwxr-xr-xblt/demos/bgexec4.tcl180
-rwxr-xr-xblt/demos/bgexec5.tcl47
-rwxr-xr-xblt/demos/bitmap.tcl233
-rw-r--r--blt/demos/bitmaps/face.xbm171
-rw-r--r--blt/demos/bitmaps/fish/left.xbm8
-rw-r--r--blt/demos/bitmaps/fish/left1.xbm8
-rw-r--r--blt/demos/bitmaps/fish/left1m.xbm8
-rw-r--r--blt/demos/bitmaps/fish/leftm.xbm8
-rw-r--r--blt/demos/bitmaps/fish/mid.xbm8
-rw-r--r--blt/demos/bitmaps/fish/midm.xbm8
-rw-r--r--blt/demos/bitmaps/fish/right.xbm8
-rw-r--r--blt/demos/bitmaps/fish/right1.xbm8
-rw-r--r--blt/demos/bitmaps/fish/right1m.xbm8
-rw-r--r--blt/demos/bitmaps/fish/rightm.xbm8
-rw-r--r--blt/demos/bitmaps/greenback.xbm885
-rw-r--r--blt/demos/bitmaps/hand/hand01.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand01m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand02.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand02m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand03.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand03m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand04.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand04m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand05.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand05m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand06.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand06m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand07.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand07m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand08.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand08m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand09.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand09m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand10.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand10m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand11.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand11m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand12.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand12m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand13.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand13m.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand14.xbm8
-rw-r--r--blt/demos/bitmaps/hand/hand14m.xbm8
-rw-r--r--blt/demos/bitmaps/hobbes.xbm16
-rw-r--r--blt/demos/bitmaps/hobbes_mask.xbm14
-rw-r--r--blt/demos/bitmaps/sharky.xbm129
-rw-r--r--blt/demos/bitmaps/xbob.xbm47
-rwxr-xr-xblt/demos/busy1.tcl254
-rwxr-xr-xblt/demos/busy2.tcl259
-rwxr-xr-xblt/demos/container.tcl14
-rwxr-xr-xblt/demos/container3.tcl451
-rwxr-xr-xblt/demos/dnd1.tcl212
-rwxr-xr-xblt/demos/dnd2.tcl328
-rwxr-xr-xblt/demos/dragdrop1.tcl131
-rwxr-xr-xblt/demos/dragdrop2.tcl183
-rwxr-xr-xblt/demos/eps.tcl256
-rwxr-xr-xblt/demos/graph1.tcl132
-rwxr-xr-xblt/demos/graph2.tcl142
-rwxr-xr-xblt/demos/graph3.tcl105
-rwxr-xr-xblt/demos/graph4.tcl2292
-rwxr-xr-xblt/demos/graph5.tcl89
-rwxr-xr-xblt/demos/graph6.tcl2343
-rwxr-xr-xblt/demos/graph7.tcl95
-rwxr-xr-xblt/demos/hierbox1.tcl133
-rwxr-xr-xblt/demos/hierbox2.tcl100
-rwxr-xr-xblt/demos/hierbox3.tcl80
-rwxr-xr-xblt/demos/hierbox4.tcl78
-rwxr-xr-xblt/demos/hiertable1.tcl236
-rwxr-xr-xblt/demos/hiertable2.tcl221
-rwxr-xr-xblt/demos/hiertable3.tcl201
-rw-r--r--blt/demos/htext.txt615
-rwxr-xr-xblt/demos/htext1.tcl186
-rw-r--r--blt/demos/images/blt98.gifbin0 -> 36719 bytes
-rw-r--r--blt/demos/images/buckskin.gifbin0 -> 7561 bytes
-rw-r--r--blt/demos/images/chalk.gifbin0 -> 4378 bytes
-rw-r--r--blt/demos/images/close.gifbin0 -> 142 bytes
-rw-r--r--blt/demos/images/close2.gifbin0 -> 142 bytes
-rw-r--r--blt/demos/images/clouds.gifbin0 -> 6414 bytes
-rw-r--r--blt/demos/images/corrugated_metal.gifbin0 -> 7708 bytes
-rw-r--r--blt/demos/images/folder.gifbin0 -> 88 bytes
-rw-r--r--blt/demos/images/jan25_palm3x_L.jpgbin0 -> 4048 bytes
-rw-r--r--blt/demos/images/mini-book1.gifbin0 -> 109 bytes
-rw-r--r--blt/demos/images/mini-book2.gifbin0 -> 93 bytes
-rw-r--r--blt/demos/images/mini-display.gifbin0 -> 109 bytes
-rw-r--r--blt/demos/images/mini-doc.gifbin0 -> 91 bytes
-rw-r--r--blt/demos/images/mini-filemgr.gifbin0 -> 106 bytes
-rw-r--r--blt/demos/images/mini-ofolder.gifbin0 -> 114 bytes
-rw-r--r--blt/demos/images/mini-windows.gifbin0 -> 86 bytes
-rw-r--r--blt/demos/images/ofolder.gifbin0 -> 110 bytes
-rw-r--r--blt/demos/images/open.gifbin0 -> 148 bytes
-rw-r--r--blt/demos/images/open2.gifbin0 -> 148 bytes
-rw-r--r--blt/demos/images/out.ps11662
-rw-r--r--blt/demos/images/qv100.t.gifbin0 -> 2694 bytes
-rw-r--r--blt/demos/images/rain.gifbin0 -> 3785 bytes
-rw-r--r--blt/demos/images/sample.gifbin0 -> 186103 bytes
-rw-r--r--blt/demos/images/smblue_rock.gifbin0 -> 3820 bytes
-rw-r--r--blt/demos/images/stopsign.gifbin0 -> 259 bytes
-rw-r--r--blt/demos/images/tan_paper.gifbin0 -> 18904 bytes
-rw-r--r--blt/demos/images/tan_paper2.gifbin0 -> 18901 bytes
-rw-r--r--blt/demos/images/txtrflag.gifbin0 -> 17135 bytes
-rw-r--r--blt/demos/scripts/barchart2.tcl125
-rw-r--r--blt/demos/scripts/bgtest.tcl35
-rw-r--r--blt/demos/scripts/clone.tcl88
-rw-r--r--blt/demos/scripts/demo.tcl28
-rw-r--r--blt/demos/scripts/globe.tcl509
-rw-r--r--blt/demos/scripts/graph1.tcl72
-rw-r--r--blt/demos/scripts/graph2.tcl138
-rw-r--r--blt/demos/scripts/graph3.tcl78
-rw-r--r--blt/demos/scripts/graph5.tcl65
-rw-r--r--blt/demos/scripts/graph8.tcl85
-rwxr-xr-xblt/demos/scripts/page.tcl131
-rw-r--r--blt/demos/scripts/patterns.tcl16
-rw-r--r--blt/demos/scripts/ps.tcl767
-rw-r--r--blt/demos/scripts/send.tcl115
-rw-r--r--blt/demos/scripts/stipples.tcl153
-rwxr-xr-xblt/demos/scripts/xcolors.tcl271
-rwxr-xr-xblt/demos/spline.tcl84
-rwxr-xr-xblt/demos/stripchart1.tcl405
-rwxr-xr-xblt/demos/tabnotebook1.tcl86
-rwxr-xr-xblt/demos/tabnotebook2.tcl80
-rwxr-xr-xblt/demos/tabnotebook3.tcl193
-rwxr-xr-xblt/demos/tabset1.tcl57
-rwxr-xr-xblt/demos/tabset2.tcl79
-rwxr-xr-xblt/demos/tabset3.tcl199
-rwxr-xr-xblt/demos/tabset4.tcl109
-rwxr-xr-xblt/demos/tour.tcl159
-rwxr-xr-xblt/demos/treeview1.tcl191
-rwxr-xr-xblt/demos/winop1.tcl62
-rwxr-xr-xblt/demos/winop2.tcl57
-rwxr-xr-xblt/examples/calendar.tcl141
-rwxr-xr-xblt/examples/form.tcl1060
-rwxr-xr-xblt/examples/pareto.tcl139
-rw-r--r--blt/html/BLT.html161
-rw-r--r--blt/html/Makefile.cyg32
-rw-r--r--blt/html/Makefile.vc33
-rw-r--r--blt/html/barchart.html2240
-rw-r--r--blt/html/beep.html41
-rw-r--r--blt/html/bgexec.html271
-rw-r--r--blt/html/bitmap.html208
-rw-r--r--blt/html/bltdebug.html34
-rw-r--r--blt/html/busy.html218
-rw-r--r--blt/html/container.html272
-rw-r--r--blt/html/cutbuffer.html57
-rw-r--r--blt/html/dragdrop.html479
-rw-r--r--blt/html/eps.html1231
-rw-r--r--blt/html/graph.html2311
-rw-r--r--blt/html/hierbox.html2331
-rw-r--r--blt/html/hiertable.html2331
-rw-r--r--blt/html/htext.html397
-rw-r--r--blt/html/spline.html160
-rw-r--r--blt/html/stripchart.html2179
-rw-r--r--blt/html/table.html721
-rw-r--r--blt/html/tabset.html936
-rw-r--r--blt/html/tile.html100
-rw-r--r--blt/html/tree.html930
-rw-r--r--blt/html/treeview.html2336
-rw-r--r--blt/html/vector.html1124
-rw-r--r--blt/html/watch.html140
-rw-r--r--blt/html/winop.html124
-rw-r--r--blt/library/Makefile.cyg70
-rw-r--r--blt/library/Makefile.in74
-rw-r--r--blt/library/Makefile.vc71
-rw-r--r--blt/library/ZoomStack.itcl359
-rw-r--r--blt/library/bltCanvEps.pro78
-rw-r--r--blt/library/bltGraph.pro462
-rw-r--r--blt/library/dd_protocols/dd-color.tcl51
-rw-r--r--blt/library/dd_protocols/dd-file.tcl53
-rw-r--r--blt/library/dd_protocols/dd-number.tcl51
-rw-r--r--blt/library/dd_protocols/dd-text.tcl48
-rw-r--r--blt/library/dd_protocols/tclIndex12
-rw-r--r--blt/library/dnd.tcl102
-rw-r--r--blt/library/dragdrop.tcl75
-rw-r--r--blt/library/graph.tcl492
-rw-r--r--blt/library/hierbox.tcl522
-rw-r--r--blt/library/hiertable.tcl943
-rw-r--r--blt/library/pkgIndex.tcl.in29
-rw-r--r--blt/library/tabnotebook.tcl318
-rw-r--r--blt/library/tabset.tcl325
-rw-r--r--blt/library/tclIndex13
-rw-r--r--blt/library/treeview.curbin0 -> 326 bytes
-rw-r--r--blt/library/treeview.tcl972
-rw-r--r--blt/library/treeview.xbm8
-rw-r--r--blt/library/treeview_m.xbm8
-rw-r--r--blt/man/BLT.mann153
-rw-r--r--blt/man/Blt_Tree.man3232
-rw-r--r--blt/man/Blt_TreeCreate.man3100
-rw-r--r--blt/man/Blt_TreeCreateNode.man395
-rw-r--r--blt/man/Blt_TreeDeleteNode.man375
-rw-r--r--blt/man/Blt_TreeExists.man366
-rw-r--r--blt/man/Blt_TreeGetNode.man369
-rw-r--r--blt/man/Blt_TreeGetToken.man388
-rw-r--r--blt/man/Blt_TreeName.man359
-rw-r--r--blt/man/Blt_TreeNodeId.man358
-rw-r--r--blt/man/Blt_TreeReleaseToken.man364
-rw-r--r--blt/man/Makefile.in74
-rw-r--r--blt/man/barchart.mann2236
-rw-r--r--blt/man/beep.mann48
-rw-r--r--blt/man/bgexec.mann309
-rw-r--r--blt/man/bitmap.mann220
-rw-r--r--blt/man/bltdebug.mann38
-rw-r--r--blt/man/busy.mann241
-rw-r--r--blt/man/container.mann303
-rw-r--r--blt/man/cutbuffer.mann54
-rw-r--r--blt/man/dragdrop.mann456
-rw-r--r--blt/man/eps.mann163
-rw-r--r--blt/man/graph.mann2329
-rw-r--r--blt/man/hierbox.mann2261
-rw-r--r--blt/man/hiertable.mann2261
-rw-r--r--blt/man/htext.mann384
-rw-r--r--blt/man/man.macros240
-rw-r--r--blt/man/spline.mann181
-rw-r--r--blt/man/stripchart.mann2168
-rw-r--r--blt/man/table.mann757
-rw-r--r--blt/man/tabset.mann920
-rw-r--r--blt/man/tile.mann108
-rw-r--r--blt/man/tree.mann897
-rw-r--r--blt/man/treeview.mann2264
-rw-r--r--blt/man/vector.mann1104
-rw-r--r--blt/man/watch.mann137
-rw-r--r--blt/man/winop.mann131
-rw-r--r--blt/src/Makefile.cyg236
-rw-r--r--blt/src/Makefile.in248
-rw-r--r--blt/src/Makefile.vc330
-rw-r--r--blt/src/TODO97
-rw-r--r--blt/src/blt.h78
-rw-r--r--blt/src/blt.mak304
-rw-r--r--blt/src/bltAlloc.c98
-rw-r--r--blt/src/bltArrayObj.c244
-rw-r--r--blt/src/bltBeep.c92
-rw-r--r--blt/src/bltBgexec.c2002
-rw-r--r--blt/src/bltBind.c644
-rw-r--r--blt/src/bltBind.h114
-rw-r--r--blt/src/bltBitmap.c1504
-rw-r--r--blt/src/bltBusy.c1196
-rw-r--r--blt/src/bltCanvEps.c1742
-rw-r--r--blt/src/bltChain.c445
-rw-r--r--blt/src/bltChain.h85
-rw-r--r--blt/src/bltColor.c980
-rw-r--r--blt/src/bltConfig.c1370
-rw-r--r--blt/src/bltConfig.h.in137
-rw-r--r--blt/src/bltContainer.c1701
-rw-r--r--blt/src/bltCutbuffer.c265
-rw-r--r--blt/src/bltDebug.c329
-rw-r--r--blt/src/bltDragdrop.c2715
-rw-r--r--blt/src/bltGrAxis.c4549
-rw-r--r--blt/src/bltGrAxis.h301
-rw-r--r--blt/src/bltGrBar.c2270
-rw-r--r--blt/src/bltGrElem.c2221
-rw-r--r--blt/src/bltGrElem.h272
-rw-r--r--blt/src/bltGrGrid.c517
-rw-r--r--blt/src/bltGrHairs.c542
-rw-r--r--blt/src/bltGrLegd.c1488
-rw-r--r--blt/src/bltGrLegd.h56
-rw-r--r--blt/src/bltGrLine.c5091
-rw-r--r--blt/src/bltGrMarker.c4954
-rw-r--r--blt/src/bltGrMisc.c1372
-rw-r--r--blt/src/bltGrPen.c700
-rw-r--r--blt/src/bltGrPs.c1271
-rw-r--r--blt/src/bltGraph.c2362
-rw-r--r--blt/src/bltGraph.h680
-rw-r--r--blt/src/bltHash.c1349
-rw-r--r--blt/src/bltHash.h.in223
-rw-r--r--blt/src/bltHierbox.c8672
-rw-r--r--blt/src/bltHtext.c4496
-rw-r--r--blt/src/bltImage.c2700
-rw-r--r--blt/src/bltImage.h287
-rw-r--r--blt/src/bltInit.c680
-rw-r--r--blt/src/bltInt.h1260
-rw-r--r--blt/src/bltInterp.h376
-rw-r--r--blt/src/bltList.c588
-rw-r--r--blt/src/bltList.h105
-rw-r--r--blt/src/bltNsUtil.c728
-rw-r--r--blt/src/bltNsUtil.h119
-rw-r--r--blt/src/bltObjConfig.c2346
-rw-r--r--blt/src/bltObjConfig.h238
-rw-r--r--blt/src/bltParse.c541
-rw-r--r--blt/src/bltPool.c458
-rw-r--r--blt/src/bltPool.h36
-rw-r--r--blt/src/bltPs.c1491
-rw-r--r--blt/src/bltPs.h154
-rw-r--r--blt/src/bltSpline.c1451
-rw-r--r--blt/src/bltSwitch.c525
-rw-r--r--blt/src/bltSwitch.h79
-rw-r--r--blt/src/bltTable.c4956
-rw-r--r--blt/src/bltTable.h390
-rw-r--r--blt/src/bltTabnotebook.c5712
-rw-r--r--blt/src/bltTabset.c5882
-rw-r--r--blt/src/bltTed.c1868
-rw-r--r--blt/src/bltText.c1500
-rw-r--r--blt/src/bltText.h212
-rw-r--r--blt/src/bltTile.c1274
-rw-r--r--blt/src/bltTile.h63
-rw-r--r--blt/src/bltTkInt.h240
-rw-r--r--blt/src/bltTree.c2511
-rw-r--r--blt/src/bltTree.h435
-rw-r--r--blt/src/bltTreeCmd.c5793
-rw-r--r--blt/src/bltTreeView.c5245
-rw-r--r--blt/src/bltTreeView.h1041
-rw-r--r--blt/src/bltTreeViewCmd.c5206
-rw-r--r--blt/src/bltTreeViewColumn.c1881
-rw-r--r--blt/src/bltTreeViewEdit.c1663
-rw-r--r--blt/src/bltTreeViewStyle.c423
-rw-r--r--blt/src/bltUnixDnd.c5140
-rw-r--r--blt/src/bltUnixImage.c885
-rw-r--r--blt/src/bltUnixMain.c174
-rw-r--r--blt/src/bltUnixPipe.c1071
-rw-r--r--blt/src/bltUtil.c1178
-rw-r--r--blt/src/bltVecCmd.c1978
-rw-r--r--blt/src/bltVecInt.h248
-rw-r--r--blt/src/bltVecMath.c2041
-rw-r--r--blt/src/bltVecObjCmd.c2068
-rw-r--r--blt/src/bltVector.c2361
-rw-r--r--blt/src/bltVector.h125
-rw-r--r--blt/src/bltWait.h242
-rw-r--r--blt/src/bltWatch.c855
-rw-r--r--blt/src/bltWinConfig.h159
-rw-r--r--blt/src/bltWinDde.c1312
-rw-r--r--blt/src/bltWinDraw.c2716
-rw-r--r--blt/src/bltWinImage.c801
-rw-r--r--blt/src/bltWinMain.c402
-rw-r--r--blt/src/bltWinPipe.c2439
-rw-r--r--blt/src/bltWinPrnt.c1548
-rw-r--r--blt/src/bltWinUtil.c77
-rw-r--r--blt/src/bltWindow.c1666
-rw-r--r--blt/src/bltWinop.c1119
-rw-r--r--blt/src/missing.h114
-rw-r--r--blt/src/pure_api.c126
-rw-r--r--blt/src/shared/Makefile.in325
-rw-r--r--blt/src/tkButton.c2143
-rw-r--r--blt/src/tkConsole.c641
-rw-r--r--blt/src/tkFrame.c1094
-rw-r--r--blt/src/tkMenubutton.c1239
-rw-r--r--blt/src/tkScrollbar.c1405
-rw-r--r--blt/win/README122
-rw-r--r--blt/win/install.tcl739
-rw-r--r--blt/win/makedefs52
365 files changed, 248004 insertions, 0 deletions
diff --git a/blt/INSTALL b/blt/INSTALL
new file mode 100644
index 00000000000..a468b343221
--- /dev/null
+++ b/blt/INSTALL
@@ -0,0 +1,115 @@
+This file describes how to compile and install the BLT library for UNIX.
+[See the file ./win/README for details on how to build under Win32.]
+
+1. Uncompress and untar the distribution file.
+
+ zcat BLT2.4.tar.gz | tar -xvf -
+
+ This will create a directory "blt2.4" with the following subdirectories:
+
+ blt2.4
+ ______________|_______________________________
+ | | | | | |
+ demos html library man src win
+ |
+ shared
+
+2. Run ./configure
+
+ Go into the "blt2.4" directory
+
+ cd blt2.4
+
+ and run the auto-configuration script "./configure". Tell where to find
+ the Tcl and Tk header files and libraries with the "--with-tcl" switch.
+
+ ./configure --with-tcl=/util/lang/tcl
+
+ Switches:
+
+ --prefix=path Specifies the path where "bltwish", the BLT
+ header files, libraries, scripts, and manual
+ pages are installed. The default is
+ "/usr/local/blt".
+
+ This switch also indicates where to find the
+ Tcl/Tk header files and libraries. You can use
+ the --with-tcl and --with-tk switches to override
+ this value if the location of the Tcl/Tk files
+ is different.
+
+ --with-tcl=dir Directory where Tcl and/or Tk is installed.
+
+ --with-tk=dir Directory where Tk is installed if different
+ from "--with-tcl".
+
+ --with-cc=program Lets you specify the C compiler, such as
+ "acc" or "gcc".
+
+ The configure script creates a header file "src/bltConfig.h". It will also
+ generate new Makefiles from their respective templates (Makefile.in).
+
+ Makefile.in ==> Makefile
+ src/Makefile.in ==> src/Makefile
+ src/shared/Makefile.in ==> src/shared/Makefile
+ man/Makefile.in ==> man/Makefile
+ library/Makefile.in ==> library/Makefile
+
+3. Compile the libraries and build the demonstration program "bltwish".
+
+ make
+
+ The program "bltwish" will be created in the ./src directory.
+
+4. Test by running the demos.
+
+ Go into the demos directory
+
+ cd demos
+
+ and run the test scripts.
+
+ ./graph1.tcl
+
+ If your system doesn't support "#!" in shell scripts, then it's
+
+ ../src/bltwish ./graph1.tcl
+
+
+5. Installing BLT
+
+ make install
+
+ The following directories will be created when BLT is installed.
+ By default, the top directory is /usr/local/blt.
+
+ ___________|__________
+ | | | |
+ bin include lib man
+ |
+ blt2.4
+ ____|____
+ | |
+ demos dd_protocols
+
+ You can change the top directory by supplying the "--prefix=dir" switch
+ to ./configure.
+
+*6. (Optional) Compiling BLT into your own custom "wish".
+
+ [If your version of "wish" supports dynamic loading of packages
+ you can simply add
+
+ package require BLT
+
+ to the start of your script.]
+
+ Add the following lines to your program's Tcl_AppInit routine in
+ tkAppInit.c
+
+ if (Blt_Init(interp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ then link with libBLT.a. And that's all there's to it.
+
diff --git a/blt/MANIFEST b/blt/MANIFEST
new file mode 100644
index 00000000000..bfa84c96147
--- /dev/null
+++ b/blt/MANIFEST
@@ -0,0 +1,381 @@
+blt2.4y
+blt2.4y/MANIFEST
+blt2.4y/INSTALL
+blt2.4y/cf
+blt2.4y/cf/config.guess
+blt2.4y/cf/config.sub
+blt2.4y/cf/install-sh
+blt2.4y/cf/install.sh
+blt2.4y/cf/ldAix
+blt2.4y/html
+blt2.4y/html/BLT.html
+blt2.4y/html/Makefile.cyg
+blt2.4y/html/Makefile.vc
+blt2.4y/html/barchart.html
+blt2.4y/html/beep.html
+blt2.4y/html/bgexec.html
+blt2.4y/html/bitmap.html
+blt2.4y/html/bltdebug.html
+blt2.4y/html/busy.html
+blt2.4y/html/container.html
+blt2.4y/html/cutbuffer.html
+blt2.4y/html/dragdrop.html
+blt2.4y/html/eps.html
+blt2.4y/html/graph.html
+blt2.4y/html/hierbox.html
+blt2.4y/html/hiertable.html
+blt2.4y/html/htext.html
+blt2.4y/html/spline.html
+blt2.4y/html/stripchart.html
+blt2.4y/html/table.html
+blt2.4y/html/tabset.html
+blt2.4y/html/tile.html
+blt2.4y/html/tree.html
+blt2.4y/html/treeview.html
+blt2.4y/html/vector.html
+blt2.4y/html/watch.html
+blt2.4y/html/winop.html
+blt2.4y/Makefile.cyg
+blt2.4y/Makefile.in
+blt2.4y/Makefile.vc
+blt2.4y/NEWS
+blt2.4y/PROBLEMS
+blt2.4y/README
+blt2.4y/acconfig.h
+blt2.4y/aclocal.m4
+blt2.4y/blt.mak
+blt2.4y/configure
+blt2.4y/configure.in
+blt2.4y/demos
+blt2.4y/demos/bitmaps
+blt2.4y/demos/bitmaps/fish
+blt2.4y/demos/bitmaps/fish/left.xbm
+blt2.4y/demos/bitmaps/fish/left1.xbm
+blt2.4y/demos/bitmaps/fish/left1m.xbm
+blt2.4y/demos/bitmaps/fish/leftm.xbm
+blt2.4y/demos/bitmaps/fish/mid.xbm
+blt2.4y/demos/bitmaps/fish/midm.xbm
+blt2.4y/demos/bitmaps/fish/right.xbm
+blt2.4y/demos/bitmaps/fish/right1.xbm
+blt2.4y/demos/bitmaps/fish/right1m.xbm
+blt2.4y/demos/bitmaps/fish/rightm.xbm
+blt2.4y/demos/bitmaps/hand
+blt2.4y/demos/bitmaps/hand/hand01.xbm
+blt2.4y/demos/bitmaps/hand/hand01m.xbm
+blt2.4y/demos/bitmaps/hand/hand02.xbm
+blt2.4y/demos/bitmaps/hand/hand02m.xbm
+blt2.4y/demos/bitmaps/hand/hand03.xbm
+blt2.4y/demos/bitmaps/hand/hand03m.xbm
+blt2.4y/demos/bitmaps/hand/hand04.xbm
+blt2.4y/demos/bitmaps/hand/hand04m.xbm
+blt2.4y/demos/bitmaps/hand/hand05.xbm
+blt2.4y/demos/bitmaps/hand/hand05m.xbm
+blt2.4y/demos/bitmaps/hand/hand06.xbm
+blt2.4y/demos/bitmaps/hand/hand06m.xbm
+blt2.4y/demos/bitmaps/hand/hand07.xbm
+blt2.4y/demos/bitmaps/hand/hand07m.xbm
+blt2.4y/demos/bitmaps/hand/hand08.xbm
+blt2.4y/demos/bitmaps/hand/hand08m.xbm
+blt2.4y/demos/bitmaps/hand/hand09.xbm
+blt2.4y/demos/bitmaps/hand/hand09m.xbm
+blt2.4y/demos/bitmaps/hand/hand10.xbm
+blt2.4y/demos/bitmaps/hand/hand10m.xbm
+blt2.4y/demos/bitmaps/hand/hand11.xbm
+blt2.4y/demos/bitmaps/hand/hand11m.xbm
+blt2.4y/demos/bitmaps/hand/hand12.xbm
+blt2.4y/demos/bitmaps/hand/hand12m.xbm
+blt2.4y/demos/bitmaps/hand/hand13.xbm
+blt2.4y/demos/bitmaps/hand/hand13m.xbm
+blt2.4y/demos/bitmaps/hand/hand14.xbm
+blt2.4y/demos/bitmaps/hand/hand14m.xbm
+blt2.4y/demos/bitmaps/face.xbm
+blt2.4y/demos/bitmaps/greenback.xbm
+blt2.4y/demos/bitmaps/hobbes.xbm
+blt2.4y/demos/bitmaps/hobbes_mask.xbm
+blt2.4y/demos/bitmaps/sharky.xbm
+blt2.4y/demos/bitmaps/xbob.xbm
+blt2.4y/demos/Makefile.cyg
+blt2.4y/demos/Makefile.in
+blt2.4y/demos/Makefile.vc
+blt2.4y/demos/barchart1.tcl
+blt2.4y/demos/barchart2.tcl
+blt2.4y/demos/barchart3.tcl
+blt2.4y/demos/barchart4.tcl
+blt2.4y/demos/barchart5.tcl
+blt2.4y/demos/bgexec1.tcl
+blt2.4y/demos/bgexec2.tcl
+blt2.4y/demos/bgexec3.tcl
+blt2.4y/demos/bgexec4.tcl
+blt2.4y/demos/bgexec5.tcl
+blt2.4y/demos/bitmap.tcl
+blt2.4y/demos/busy1.tcl
+blt2.4y/demos/busy2.tcl
+blt2.4y/demos/container.tcl
+blt2.4y/demos/container3.tcl
+blt2.4y/demos/dnd1.tcl
+blt2.4y/demos/dnd2.tcl
+blt2.4y/demos/dragdrop1.tcl
+blt2.4y/demos/dragdrop2.tcl
+blt2.4y/demos/eps.tcl
+blt2.4y/demos/graph1.tcl
+blt2.4y/demos/graph2.tcl
+blt2.4y/demos/graph3.tcl
+blt2.4y/demos/graph4.tcl
+blt2.4y/demos/graph5.tcl
+blt2.4y/demos/graph6.tcl
+blt2.4y/demos/graph7.tcl
+blt2.4y/demos/hierbox1.tcl
+blt2.4y/demos/hierbox2.tcl
+blt2.4y/demos/hierbox3.tcl
+blt2.4y/demos/hierbox4.tcl
+blt2.4y/demos/hiertable1.tcl
+blt2.4y/demos/hiertable2.tcl
+blt2.4y/demos/hiertable3.tcl
+blt2.4y/demos/htext.txt
+blt2.4y/demos/htext1.tcl
+blt2.4y/demos/spline.tcl
+blt2.4y/demos/stripchart1.tcl
+blt2.4y/demos/tabnotebook1.tcl
+blt2.4y/demos/tabnotebook2.tcl
+blt2.4y/demos/tabnotebook3.tcl
+blt2.4y/demos/tabset1.tcl
+blt2.4y/demos/tabset2.tcl
+blt2.4y/demos/tabset3.tcl
+blt2.4y/demos/tabset4.tcl
+blt2.4y/demos/tour.tcl
+blt2.4y/demos/treeview1.tcl
+blt2.4y/demos/winop1.tcl
+blt2.4y/demos/winop2.tcl
+blt2.4y/demos/images
+blt2.4y/demos/images/blt98.gif
+blt2.4y/demos/images/buckskin.gif
+blt2.4y/demos/images/chalk.gif
+blt2.4y/demos/images/close.gif
+blt2.4y/demos/images/close2.gif
+blt2.4y/demos/images/clouds.gif
+blt2.4y/demos/images/corrugated_metal.gif
+blt2.4y/demos/images/folder.gif
+blt2.4y/demos/images/jan25_palm3x_L.jpg
+blt2.4y/demos/images/mini-book1.gif
+blt2.4y/demos/images/mini-book2.gif
+blt2.4y/demos/images/mini-display.gif
+blt2.4y/demos/images/mini-doc.gif
+blt2.4y/demos/images/mini-filemgr.gif
+blt2.4y/demos/images/mini-ofolder.gif
+blt2.4y/demos/images/mini-windows.gif
+blt2.4y/demos/images/ofolder.gif
+blt2.4y/demos/images/open.gif
+blt2.4y/demos/images/open2.gif
+blt2.4y/demos/images/out.ps
+blt2.4y/demos/images/qv100.t.gif
+blt2.4y/demos/images/rain.gif
+blt2.4y/demos/images/sample.gif
+blt2.4y/demos/images/smblue_rock.gif
+blt2.4y/demos/images/stopsign.gif
+blt2.4y/demos/images/tan_paper.gif
+blt2.4y/demos/images/tan_paper2.gif
+blt2.4y/demos/images/txtrflag.gif
+blt2.4y/demos/scripts
+blt2.4y/demos/scripts/barchart2.tcl
+blt2.4y/demos/scripts/bgtest.tcl
+blt2.4y/demos/scripts/clone.tcl
+blt2.4y/demos/scripts/demo.tcl
+blt2.4y/demos/scripts/globe.tcl
+blt2.4y/demos/scripts/graph1.tcl
+blt2.4y/demos/scripts/graph2.tcl
+blt2.4y/demos/scripts/graph3.tcl
+blt2.4y/demos/scripts/graph5.tcl
+blt2.4y/demos/scripts/graph8.tcl
+blt2.4y/demos/scripts/page.tcl
+blt2.4y/demos/scripts/patterns.tcl
+blt2.4y/demos/scripts/ps.tcl
+blt2.4y/demos/scripts/send.tcl
+blt2.4y/demos/scripts/stipples.tcl
+blt2.4y/demos/scripts/xcolors.tcl
+blt2.4y/examples
+blt2.4y/examples/calendar.tcl
+blt2.4y/examples/form.tcl
+blt2.4y/examples/pareto.tcl
+blt2.4y/library
+blt2.4y/library/dd_protocols
+blt2.4y/library/dd_protocols/dd-color.tcl
+blt2.4y/library/dd_protocols/dd-file.tcl
+blt2.4y/library/dd_protocols/dd-number.tcl
+blt2.4y/library/dd_protocols/dd-text.tcl
+blt2.4y/library/dd_protocols/tclIndex
+blt2.4y/library/Makefile.cyg
+blt2.4y/library/Makefile.in
+blt2.4y/library/Makefile.vc
+blt2.4y/library/ZoomStack.itcl
+blt2.4y/library/bltCanvEps.pro
+blt2.4y/library/bltGraph.pro
+blt2.4y/library/dnd.tcl
+blt2.4y/library/dragdrop.tcl
+blt2.4y/library/graph.tcl
+blt2.4y/library/hierbox.tcl
+blt2.4y/library/hiertable.tcl
+blt2.4y/library/pkgIndex.tcl.in
+blt2.4y/library/tabnotebook.tcl
+blt2.4y/library/tabset.tcl
+blt2.4y/library/tclIndex
+blt2.4y/library/treeview.cur
+blt2.4y/library/treeview.tcl
+blt2.4y/library/treeview.xbm
+blt2.4y/library/treeview_m.xbm
+blt2.4y/man
+blt2.4y/man/BLT.mann
+blt2.4y/man/Blt_Tree.man3
+blt2.4y/man/Blt_TreeCreate.man3
+blt2.4y/man/Blt_TreeCreateNode.man3
+blt2.4y/man/Blt_TreeDeleteNode.man3
+blt2.4y/man/Blt_TreeExists.man3
+blt2.4y/man/Blt_TreeGetNode.man3
+blt2.4y/man/Blt_TreeGetToken.man3
+blt2.4y/man/Blt_TreeName.man3
+blt2.4y/man/Blt_TreeNodeId.man3
+blt2.4y/man/Blt_TreeReleaseToken.man3
+blt2.4y/man/Makefile.in
+blt2.4y/man/barchart.mann
+blt2.4y/man/beep.mann
+blt2.4y/man/bgexec.mann
+blt2.4y/man/bitmap.mann
+blt2.4y/man/bltdebug.mann
+blt2.4y/man/busy.mann
+blt2.4y/man/container.mann
+blt2.4y/man/cutbuffer.mann
+blt2.4y/man/dragdrop.mann
+blt2.4y/man/eps.mann
+blt2.4y/man/graph.mann
+blt2.4y/man/hierbox.mann
+blt2.4y/man/hiertable.mann
+blt2.4y/man/htext.mann
+blt2.4y/man/man.macros
+blt2.4y/man/spline.mann
+blt2.4y/man/stripchart.mann
+blt2.4y/man/table.mann
+blt2.4y/man/tabset.mann
+blt2.4y/man/tile.mann
+blt2.4y/man/tree.mann
+blt2.4y/man/treeview.mann
+blt2.4y/man/vector.mann
+blt2.4y/man/watch.mann
+blt2.4y/man/winop.mann
+blt2.4y/src
+blt2.4y/src/shared
+blt2.4y/src/shared/Makefile.in
+blt2.4y/src/Makefile.cyg
+blt2.4y/src/Makefile.in
+blt2.4y/src/Makefile.vc
+blt2.4y/src/TODO
+blt2.4y/src/blt.h
+blt2.4y/src/blt.mak
+blt2.4y/src/bltAlloc.c
+blt2.4y/src/bltArrayObj.c
+blt2.4y/src/bltBeep.c
+blt2.4y/src/bltBgexec.c
+blt2.4y/src/bltBind.c
+blt2.4y/src/bltBind.h
+blt2.4y/src/bltBitmap.c
+blt2.4y/src/bltBusy.c
+blt2.4y/src/bltCanvEps.c
+blt2.4y/src/bltChain.c
+blt2.4y/src/bltChain.h
+blt2.4y/src/bltColor.c
+blt2.4y/src/bltConfig.c
+blt2.4y/src/bltConfig.h.in
+blt2.4y/src/bltContainer.c
+blt2.4y/src/bltCutbuffer.c
+blt2.4y/src/bltDebug.c
+blt2.4y/src/bltDragdrop.c
+blt2.4y/src/bltGrAxis.c
+blt2.4y/src/bltGrAxis.h
+blt2.4y/src/bltGrBar.c
+blt2.4y/src/bltGrElem.c
+blt2.4y/src/bltGrElem.h
+blt2.4y/src/bltGrGrid.c
+blt2.4y/src/bltGrHairs.c
+blt2.4y/src/bltGrLegd.c
+blt2.4y/src/bltGrLegd.h
+blt2.4y/src/bltGrLine.c
+blt2.4y/src/bltGrMarker.c
+blt2.4y/src/bltGrMisc.c
+blt2.4y/src/bltGrPen.c
+blt2.4y/src/bltGrPs.c
+blt2.4y/src/bltGraph.c
+blt2.4y/src/bltGraph.h
+blt2.4y/src/bltHash.c
+blt2.4y/src/bltHash.h.in
+blt2.4y/src/bltHierbox.c
+blt2.4y/src/bltHtext.c
+blt2.4y/src/bltImage.c
+blt2.4y/src/bltImage.h
+blt2.4y/src/bltInit.c
+blt2.4y/src/bltInt.h
+blt2.4y/src/bltInterp.h
+blt2.4y/src/bltList.c
+blt2.4y/src/bltList.h
+blt2.4y/src/bltNsUtil.c
+blt2.4y/src/bltNsUtil.h
+blt2.4y/src/bltObjConfig.c
+blt2.4y/src/bltObjConfig.h
+blt2.4y/src/bltParse.c
+blt2.4y/src/bltPool.c
+blt2.4y/src/bltPool.h
+blt2.4y/src/bltPs.c
+blt2.4y/src/bltPs.h
+blt2.4y/src/bltSpline.c
+blt2.4y/src/bltSwitch.c
+blt2.4y/src/bltSwitch.h
+blt2.4y/src/bltTable.c
+blt2.4y/src/bltTable.h
+blt2.4y/src/bltTabnotebook.c
+blt2.4y/src/bltTabset.c
+blt2.4y/src/bltTed.c
+blt2.4y/src/bltText.c
+blt2.4y/src/bltText.h
+blt2.4y/src/bltTile.c
+blt2.4y/src/bltTile.h
+blt2.4y/src/bltTkInt.h
+blt2.4y/src/bltTree.c
+blt2.4y/src/bltTree.h
+blt2.4y/src/bltTreeCmd.c
+blt2.4y/src/bltTreeView.c
+blt2.4y/src/bltTreeView.h
+blt2.4y/src/bltTreeViewCmd.c
+blt2.4y/src/bltTreeViewColumn.c
+blt2.4y/src/bltTreeViewEdit.c
+blt2.4y/src/bltTreeViewStyle.c
+blt2.4y/src/bltUnixDnd.c
+blt2.4y/src/bltUnixImage.c
+blt2.4y/src/bltUnixMain.c
+blt2.4y/src/bltUnixPipe.c
+blt2.4y/src/bltUtil.c
+blt2.4y/src/bltVecCmd.c
+blt2.4y/src/bltVecInt.h
+blt2.4y/src/bltVecMath.c
+blt2.4y/src/bltVecObjCmd.c
+blt2.4y/src/bltVector.c
+blt2.4y/src/bltVector.h
+blt2.4y/src/bltWait.h
+blt2.4y/src/bltWatch.c
+blt2.4y/src/bltWinConfig.h
+blt2.4y/src/bltWinDde.c
+blt2.4y/src/bltWinDraw.c
+blt2.4y/src/bltWinImage.c
+blt2.4y/src/bltWinMain.c
+blt2.4y/src/bltWinPipe.c
+blt2.4y/src/bltWinPrnt.c
+blt2.4y/src/bltWinUtil.c
+blt2.4y/src/bltWindow.c
+blt2.4y/src/bltWinop.c
+blt2.4y/src/missing.h
+blt2.4y/src/pure_api.c
+blt2.4y/src/tkButton.c
+blt2.4y/src/tkConsole.c
+blt2.4y/src/tkFrame.c
+blt2.4y/src/tkMenubutton.c
+blt2.4y/src/tkScrollbar.c
+blt2.4y/win
+blt2.4y/win/install.tcl
+blt2.4y/win/README
+blt2.4y/win/makedefs
diff --git a/blt/Makefile.cyg b/blt/Makefile.cyg
new file mode 100644
index 00000000000..3f25b293b59
--- /dev/null
+++ b/blt/Makefile.cyg
@@ -0,0 +1,54 @@
+# ------------------------------------------------------------------------
+# Makefile for demonstation shell of BLT library
+# ------------------------------------------------------------------------
+
+include ./win/makedefs
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) $(includedir)
+
+# ------------------------------------------------------------------------
+# Don't edit anything beyond this point
+# ------------------------------------------------------------------------
+
+subdirs = src demos library html
+
+all:
+ @for i in $(subdirs) ; do \
+ (cd $$i; $(MAKE) -f Makefile.cyg all) ; \
+ done
+
+install: install-dirs install-all install-readme
+
+install-all:
+ @for i in $(subdirs) ; do \
+ (cd $$i; $(MAKE) -f Makefile.cyg install) ; \
+ done
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test ! -d "$$i" ; then \
+ echo " mkdir $$i" ; \
+ mkdir $$i ; \
+ fi ; \
+ done
+
+install-readme:
+ $(INSTALL_DATA) $(srcdir)/README $(scriptdir)
+ $(INSTALL_DATA) $(srcdir)/PROBLEMS $(scriptdir)
+
+clean:
+ @for i in $(subdirs) ; do \
+ (cd $$i; $(MAKE) -f Makefile.cyg clean) ; \
+ done
+ $(RM) *.bak *\~ "#"* *pure* .pure*
+
+GENERATED_FILES = \
+ config.status config.cache config.log Makefile
+
+distclean: clean
+ $(RM) $(GENERATED_FILES)
diff --git a/blt/Makefile.in b/blt/Makefile.in
new file mode 100644
index 00000000000..8b38d975074
--- /dev/null
+++ b/blt/Makefile.in
@@ -0,0 +1,75 @@
+# ------------------------------------------------------------------------
+# Makefile for BLT distribution
+# ------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+srcdir = @srcdir@
+bindir = @bindir@
+includedir = @includedir@
+libdir = @libdir@
+version = @BLT_VERSION@
+scriptdir = $(prefix)/lib/blt$(version)
+
+instdirs = $(prefix) \
+ $(exec_prefix) \
+ $(bindir) \
+ $(libdir) \
+ $(includedir) \
+ $(scriptdir)
+
+# ------------------------------------------------------------------------
+# Don't edit anything beyond this point
+# ------------------------------------------------------------------------
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+SHELL = /bin/sh
+RM = rm -f
+
+subdirs = src library man demos
+
+all:
+ (cd src; $(MAKE) all)
+ (cd library; $(MAKE) all)
+ (cd man; $(MAKE) all)
+ (cd demos; $(MAKE) all)
+
+install: mkdirs install-all
+
+install-all:
+ (cd src; $(MAKE) install)
+ (cd library; $(MAKE) install)
+ (cd man; $(MAKE) install)
+ (cd demos; $(MAKE) install)
+ $(INSTALL_DATA) $(srcdir)/README $(INSTALL_DIR)$(scriptdir)
+ $(INSTALL_DATA) $(srcdir)/PROBLEMS $(INSTALL_DIR)$(scriptdir)
+ $(INSTALL_DATA) $(srcdir)/NEWS $(INSTALL_DIR)$(scriptdir)
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)$$i ; then \
+ : ; \
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)$$i ; \
+ fi ; \
+ done
+
+clean:
+ (cd src; $(MAKE) clean)
+ (cd library; $(MAKE) clean)
+ (cd man; $(MAKE) clean)
+ (cd demos; $(MAKE) clean)
+ $(RM) *.bak *\~ "#"* *pure* .pure*
+
+GENERATED_FILES = \
+ config.status config.cache config.log Makefile
+
+distclean: clean
+ $(RM) $(GENERATED_FILES)
diff --git a/blt/Makefile.vc b/blt/Makefile.vc
new file mode 100644
index 00000000000..f90ee3a15db
--- /dev/null
+++ b/blt/Makefile.vc
@@ -0,0 +1,59 @@
+
+# ------------------------------------------------------------------------
+# Makefile for demonstation shell of BLT library
+# ------------------------------------------------------------------------
+
+include ./win/makedefs
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) $(includedir)
+
+# ------------------------------------------------------------------------
+# Don't edit anything beyond this point
+# ------------------------------------------------------------------------
+
+subdirs = src demos library html
+
+all:
+ (cd src; $(MAKE) -f Makefile.vc all)
+ (cd demos; $(MAKE) -f Makefile.vc all)
+ (cd library; $(MAKE) -f Makefile.vc all)
+ (cd html; $(MAKE) -f Makefile.vc all)
+
+install: install-dirs install-all install-readme
+
+install-all:
+ (cd src; $(MAKE) -f Makefile.vc install)
+ (cd demos; $(MAKE) -f Makefile.vc install)
+ (cd library; $(MAKE) -f Makefile.vc install)
+ (cd html; $(MAKE) -f Makefile.vc install)
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test ! -d "$$i" ; then \
+ echo " mkdir $$i" ; \
+ mkdir $$i ; \
+ fi ; \
+ done
+
+install-readme:
+ $(INSTALL_DATA) $(srcdir)/README $(scriptdir)
+ $(INSTALL_DATA) $(srcdir)/PROBLEMS $(scriptdir)
+ $(INSTALL_DATA) $(srcdir)/NEWS $(scriptdir)
+
+clean:
+ (cd src; $(MAKE) -f Makefile.vc clean)
+ (cd demos; $(MAKE) -f Makefile.vc clean)
+ (cd library; $(MAKE) -f Makefile.vc clean)
+ (cd html; $(MAKE) -f Makefile.vc clean)
+ $(RM) *.bak *\~ "#"* *pure* .pure*
+
+GENERATED_FILES = \
+ config.status config.cache config.log Makefile
+
+distclean: clean
+ $(RM) $(GENERATED_FILES)
diff --git a/blt/NEWS b/blt/NEWS
new file mode 100644
index 00000000000..1d389c3db8e
--- /dev/null
+++ b/blt/NEWS
@@ -0,0 +1,609 @@
+Changes from 2.4x to 2.4y
+
+FEATURES
+========
+
+tree
+ Added -notags switch to "restore" operation.
+tree
+ Added additional fields to the tree "dump" format. When trees are
+ restored, will try to reuse old node ids (not always possible).
+tree
+ Added -label switch to "copy" operation. This lets you relabel the
+ destination node.
+
+BUG FIXES
+=========
+
+graph/stripchart/barchart
+ PostScript output sometimes includes a spurious box around an axis.
+ Thanks to Harvey.Davies@csiro.au for the bug report and example.
+tabset/tabnotebook
+ On errors tabnotebook grows ad infinitum. Thanks to Terri Fischer
+ <terri@ner.com> for the bug report and example.
+<general>
+ Tcl_Init fails with "can't find usable init.tcl" when running
+ bltsh or bltwish compile with ActiveTcl. Changed Tcl_AppInit to
+ set global "tclDefaultLibrary" variable.
+graph
+ "legend get" operation doesn't account for hidden entries
+ (i.e. -label is configured to ""). Thanks to Karl Voskuil
+ <voskuil@ll.mit.edu> for the bug report and the suggested fix.
+graph
+ NULL pointer referenced (bindingTable) when destroying axis.
+bgexec
+ File redirection broken under Windows.
+graph
+ Area under curve not stippled correctly when bitmap is greater than
+ 8x8 (W95/W98) or device context is a metafile (all?). Created
+ XFillPolygon replacement for Windows.
+tree
+ Node modifiers are incorrectly ignored when first component is a tag.
+treeview
+ -shadow option no longer accepts empty string (no shadow). Many
+ thanks to Todd Copeland <todd.copeland@legerity.com> for the report.
+vector
+ Vector "create" operation slows down using #auto as more vectors
+ are created. Thanks to Todd Copeland <todd.copeland@legerity.com>
+ for the bug report.
+treeview/hiertable
+ Widget doesn't scroll horizontally correctly when -hideleaves is
+ true. Ignores last level when computing world width.
+treeview/hiertable
+ Deleting a node doesn't remove tag references to it. Thanks to
+ Steven Hafer <steven.hafer@legerity.com> for the bug report.
+treeview/hiertable
+ Giving the -path option to the "index" operation always fails. Thanks
+ to Paul Robins <paul.robins@st.com> for the bug report.
+<treeview.mann/hiertable.mann>
+ Documentation for the "get" operation isn't clear that it always returns
+ a list of lists when the -separator option is "" (the default), even
+ when there is only one node specified.
+
+Changes from 2.4w to 2.4x
+
+FEATURES
+========
+
+dragdrop
+ Back ported 8.3 "dde" command for use with 8.0.
+
+BUG FIXES
+=========
+
+treeview/hiertable
+ Can't create column that starts with a minus. Thanks to Todd Copeland
+ <todd.copeland@legerity.com> for the bug report.
+<Makefile.in>
+ pkgIndex.tcl file not getting rebuilt. Thanks to Terri Fischer
+ <terri@ner.com> for the bug report and fix.
+<dragdrop1.tcl,dragdrop2.tcl>
+ Send emulation script isn't needed for Tcl8.0 under Windows. Thanks to
+ Linh H Phan <phan@grover.jpl.nasa.gov> for the bug report.
+graph/stripchart/barchart
+ Using pen styles results in a bus error. Thanks to Julian H J
+ Loaring <jhjl@bigj.demon.co.uk> for the bug report.
+hiertable/treeview/tree
+ List of data values is reversed from 2.4v. Thanks to Jorge Suit
+ Prez Ronda <jsperez@bayesinf.com> for the bug report.
+<bltObjConfig.c>
+ Missing header file for varargs. Thanks to Terri Fischer <terri@ner.com>
+ for the bug report and fix.
+<bltAlloc.c>
+ TclpAlloc and TclpFree not found in Tcl 8.0. Again, thanks to Terri
+ Fischer <terri@ner.com> for the bug report and fix.
+<BLT.mann,barchart.mann,eps.mann,graph.mann,tabset.man>
+ Updated manual pages graciously provided by Terri Fischer <terri@ner.com>.
+
+Changes from 2.4v to 2.4w
+
+FEATURES
+========
+treeview
+ New treeview widget is updated version of hiertable. Uses Tcl_Objs.
+ The "hiertable" and "treeview" are the 100% syntax compatible. The
+ old hiertable is temporariliy available as "hiertable-old" should
+ you find errors. Also use the "treeview" instead of the "hierbox"
+ widget. The "hierbox" isn't as capable and doesn't use tree data
+ objects.
+treeview/hiertable
+ Added tagging operations similar to the "tree" command. Attaching
+ a tree to the treeview/hiertable (the -tree option) now gives you
+ access to the tree's tags too. Don't confuse this with "bindtags".
+ For example, you can tag nodes with the "tree" command and operate
+ on them in the treeview/hiertable widget using that tag. If you
+ don't want to share tags, the -newtags option will prevent this.
+ There's an update "treeview" manual entry to describe this.
+treeview/hiertable
+ The "nearest" operation can report what part of the entry the pointer
+ is over. If a variable name argument is given, the variable will
+ contain either "button", "label", "icon", or "".
+eps/winop
+ Faster image zooming and rotation (fixed-point arithmetic).
+
+BUG FIXES
+=========
+vector/graph/barchart/stripchart
+ Test of real number in a range is broken. Thanks to Paul Robins
+ for the bug report.
+treeview/hiertable
+ "nearest" operation doesn't allow an optional "variable" argument.
+hiertable/hierbox
+ The -selectioncommand command is invoked when closing an entry
+ with no selected descendants. Thanks to Jorge Suit Prez Ronda
+ <jsperez@bayesinf.com> for the bug report.
+hiertable/hierbox
+ In single "mode", the selection anchor is not updated when the
+ selection is moved via the keyboard. Thanks to Jorge Suit Prez Ronda
+ <jsperez@bayesinf.com> for the bug report.
+hiertable
+ Editor overwrites memory (seen best under Windows).
+hiertable
+ The "open" and "close" operations don't check for no arguments.
+hiertable
+ Vertical dotted lines start on wrong y-coordinate when clipped.
+hiertable
+ Active button isn't clipped by column titles.
+hiertable
+ Column titles are still displayed and picked despite -showtitles
+ set to "no".
+hiertable
+ Editor doesn't automatically select acquired text.
+hiertable
+ Moving the cursor in the editor doesn't clear the selection.
+hiertable
+ Typing a "space" doesn't replace the selection with a space.
+tree
+ Traces on the same node loop infinitely. TRACE_ACTIVE flag not
+ set/unset.
+tree
+ The "restore" and "restorefile" operations don't handle newlines
+ in data key/values, node labels, or tags.
+graph/barchart/stripchart
+ Crosshairs left on screen when the mouse is pulled quicky from the
+ widget.
+graph/barchart/stripchart
+ Spurious crosshairs also left on the screen if axes are reconfigured
+ (active axes).
+graph/barchart/stripchart
+ Image marker not updated if image is changed.
+graph/barchart/stripchart
+ PostScript not generated for -showvalues option.
+graph/barchart/stripchart
+ PostScript not generated for errorbars.
+bgexec
+ No check for wrong number of arguments if switch is present.
+<bltAlloc.c>
+ Blt_MallocProcPtr and Blt_FreeProcPtr not declared extern in
+ bltInt.h
+<bltTile.c>
+ Bogus test for mask in Blt_TilePolygon routine.
+<bltImage.c>
+ Counter for transparent pixels wrong in Win32 version of Blt_PolygonMask.
+<bltInit.c>
+ blt_version not set when dynamically loaded into wish83.exe.
+
+Note: Stub support is still missing although patches have been
+ graciously provided for the 2.4q release. This will be added as
+ soon as I can get some free time.
+
+Changes from 2.4u to 2.4v
+
+1. "bgexec"
+
+ o New -linebuffered switch.
+
+ bgexec myVar -linebuffered yes -onoutput ShowLine myProg &
+
+ This option lets you process updated data (-onerror, -onoutput,
+ -error, or -output) on a line by line basis. Normally notifications
+ occur once for entire data block. This switch causes separate
+ notifications to made for each complete line.
+
+ o New -decodeoutput and -decodeerror switches.
+
+ bgexec myVar -decodeoutput unicode -output myOut myProg &
+
+ Translates data from the specified encoding to UTF before passing
+ it to the Tcl interpreter. Normally no translation is made (under
+ Windows CR-LF conversions are made) and the raw, typically ASCII,
+ characters are passed back to the Tcl interpreter.
+
+ Binary data can be collected with the "binary" encoding. For
+ versions using Tcl 8.1 or greater, data is returned as Tcl byte
+ array object, so you can use the "binary" command to convert it
+ as needed.
+
+ set out [bgexec myVar -decodeoutput binary myProg]
+ binary scan $myOut f values
+
+ o Fixed a race condition that caused assertion under Windows to fail.
+ When both stdout and stderr are collected, if the stdout handler
+ finishes first, the memory used by read thread handler could be
+ freed before the stderr pipe was closed.
+
+2. "tree"
+
+ o Added "dumpfile" and "restorefile" operations to "tree" command.
+
+ o Extended -> syntax in tree command to use node names.
+
+ set data [$tree get root->"fred"->"pebbles"]
+
+ o Improved memory handling of large trees. Pool allocators should
+ reduce overall memory consumption.
+
+3. "graph", "stripchart", "barchart"
+
+ o New -buffergraph switch.
+
+ .graph configure -buffergraph no
+
+ o PostScript coordinates are no longer integers (screen resolution).
+
+ o (graph only) New options to fill area under curve of an element.
+
+ .graph element configure line1 -areapattern solid
+
+ .graph element configure line2 -areapattern BLT
+
+ o New -reduce option.
+
+ .graph element configure line1 -abstol 0.5
+
+ Designates error tolerance for line simplificiation. Points that
+ vary less than the given tolerance are merged into a single line
+ segment.
+
+ o Polygon markers now clipped properly.
+
+
+4. "vector"
+
+ o Vectors can't be mapped to local variables. This was broken
+ in the 2.4r release. Thanks to Johannes Zellner
+ <johannes@zellner.org> for the bug report.
+
+ o Tcl command associated with a vector not destroyed when the vector
+ is deleted. Much thanks to Alexander Eisenhuth <stacom@topmail.de>
+ for the bug report and the example script.
+
+5. "drag&drop"
+
+ o "drag&drop" command fails when multiple formats are specified.
+ Seen in the dragdrop2.tcl demo.
+
+6. "spline"
+
+ o spline command incorrectly reporting the spline's x-vector to be
+ non-monotonic. Thanks to Chang Li <changl@neatware.com> for the
+ bug report.
+
+7. Miscellaneous
+
+ o Fixed pkgIndex.tcl.in to figure out whether to load libBLT24.so
+ or libBLTlite24.so when BLT is loaded, not when the package is
+ registered. Thanks to Dr. Dieter Ruppert <ru@swb.siemens.de> for
+ the bug report and fix.
+
+ o Can globally replace memory allocation routines by setting
+ pointers Blt_MallocProcPtr and Blt_FreeProcPtr.
+
+8. "winop"
+
+ o New "rotate" operation lets you rotate photo images.
+
+Changes from 2.4t to 2.4u
+
+ o Fixed my stupid error (missing close brace) in ./configure file.
+
+ o Makefile in src/shared doesn't define BLT_LIBRARY. Thanks
+ to terri@ner.com (Terri L. Fischer) for the bug report and fix.
+
+ o graph doesn't find vector in global namespace when inside of another
+ namespace. Thanks to Julian H J Loaring <bigj@bigj.demon.co.uk> for
+ the bug report.
+
+ o Scratch buffer to small for PostScript prolog. Thanks again to
+ Julian H J Loaring <bigj@bigj.demon.co.uk> for the bug report
+ and fix.
+
+ o graph "bind" would fail on elements without traces (-linewidth 0).
+ Thanks again to terri@ner.com (Terri L. Fischer) for the bug report.
+
+ o Many changes to "dnd" command.
+
+ + -package option is treated as a command prefix (like the
+ scrollbar), not a script. Percent sign substitutions are
+ no longer allowed. Information is passed via key-value
+ parameters like the -onleave, -ondrop procedures. Procedure
+ must return 1 if operation was Ok, and 0 if it failed.
+
+ + The command arguments for both the "setdata" and "getdata"
+ operations have changed from an arbitrary Tcl script with
+ percent sign substitutions, to a command prefix with
+ key-value arguments appended. The general form is
+
+ procName widget args...
+
+ where args is one of more key value pairs.
+
+ x Relative X-coordinate of drop or pickup.
+ y Relative Y-coordinate of drop or pickup.
+ timestamp Timestamp of transaction.
+ format Format desired.
+ value Value transfered (setdata only).
+
+ You can use array set to parse "args". For example:
+
+ proc GetColor { widget args } {
+ array set info $args
+
+ puts "x-coordinate is $info(x)"
+ puts "selected format is $info(format)"
+
+ return [$widget cget -bg]
+ }
+
+ + If an -onmotion procedure is specified for the target, it is
+ automatically invoked on drops before the -dropcmd is run.
+ If it returns 0, the drop is canceled.
+
+ + Added ./demos/dnd2.tcl to show more complicated example.
+ Just like dnd1.tcl, you need to run two of them at the same
+ time to see the drag-and-drop operations.
+
+ + Target property wasn't getting reset when changing -onmotion,
+ -onleave, etc. procedures.
+
+ + Timestamps now displayed as unsigned.
+
+ Many thanks to Tom Lane <tgl@sss.pgh.pa.us> for all his help and
+ suggestions.
+
+Changes from 2.4s to 2.4t
+
+ o Tree command syntax changes. Notify and trace operation now work
+ as advertised and a copy operation added. Many thanks to Matt
+ Newman <matt@sensus.org> for meticulously reviewing the command.
+
+ o graph "snap" operation syntax change. Added support for generating
+ Aldus metafiles and enhanced metafiles under Windows.
+
+ # Normal syntax.
+ set image [image create photo]
+ .graph snap $image
+
+ # New additions.
+ .graph snap -format emf myFile.emf ;# Enhanced metafile
+ .graph snap -format wmf myFile.wmf ;# Aldus placable metafile
+ .graph snap -format emf CLIPBOARD ;# Metafile written into clipboard.
+
+ Old width and height arguments are replaced with -width and -height
+ switches.
+
+ .graph snap $image 500 500 ;# Old
+ .graph snap -width 500 -height 500 $image ;# New
+
+ Thanks to Alain Zuur <a.zuur@marlab.ac.uk> for the enhancement.
+
+ o Tabset/Tabnotebook -selectforeground option for tabs using wrong
+ configuration option type. Both the bug report and fix are from
+ Mark E. Smith <mark@pearl.grand.gdats.com>. Thanks.
+
+ o graph "bind" to use closest point instead of line segment when
+ element contains only 1 point. Thanks to Uwe Klein
+ <uwe-klein@foni.net> for the bug report and script.
+
+ o Hiertable tree view column has been internally renamed to
+
+ "BLT Hiertable widgetName".
+
+ It was formerly the name of the widget. Fortunately, you can
+ refer to the column as "treeView" instead.
+
+ .ht column configure treeView -text "View Label"
+
+ o There's no ".tree" suffix anymore on the default tree created by
+ the hiertable widget. It's now just the widget name.
+
+ o Many hiertable column bug fixes. Thanks to Julian H J Loaring
+ <bigj@bigj.demon.co.uk> for all the tests and reports.
+
+ o Rotated text displayed incorrectly under Windows 95/98 using
+ non-TrueType fonts. A test for typetype fonts has been restored.
+ Thanks to James Pakko <jpakko@ford.com> for the bug report and
+ script.
+
+ Under Windows, Non-TrueType fonts are drawn into a bitmap and
+ the bitmap is rotated. This provides the same quality as using
+ rotated fonts for on-screen display. Unfortunately it's much
+ poorer for higher resolution devices such as printers. The best
+ bet is to simply choose TrueType fonts if you can.
+
+ o Improved Hiertable folder images. Many thanks to Tom Lane
+ <tgl@sss.pgh.pa.us> for the new images.
+
+ o Bgexec segfaults under Windows (NT/95) if file handler is
+ deleted inside of callback. Thanks to Chris Oliver
+ <coliver@mminternet.com> for the bug report.
+
+ o graph segfaults if pen style range min/max are the same.
+ Thanks to Thomas Wu <twu@gene.com> for the bug report and script.
+
+ o tabnotebook and tabset widgets would generate X11 errors if
+ embedded window was resize to zero width/height. Thanks to
+ Ed Ohsone <eosn@rahul.net> for the report and the script to
+ demonstrate the error.
+
+Changes from 2.4r to 2.4s
+
+ o Fixed bug in stripchart (introduced in 2.4r) allowing uninitialized
+ data to be displayed. Thanks to Dick Gooris <gooris@lucent.com> for
+ the bug report.
+
+ o AIX dynamic loading. Actually made it work on a 4.3 AIX box.
+
+ o Fixed -tree option in hiertable. Would segfault if tree was not
+ fully initialized first.
+
+ o Tree insert operation syntax changed from
+
+ tree0 insert $node key1 value1 key2 value2
+ to
+ tree0 insert $node -data { key1 value1 key2 value2 }
+
+ o Fixed tree label operation. Save uid instead of string.
+
+ o Bug in TreeEventProc, should be node != NULL instead of node >= 0
+ Thanks to Julian H J Loaring <bigj@bigj.demon.co.uk> for the
+ bug report.
+
+
+What's new in 2.4?
+
+ 1. "eps" canvas item.
+
+ An encapsulated PostScript canvas item lets you embed an EPS file into
+ the canvas. The "eps" item displays either a EPS preview image found
+ in the file, or a Tk image that you provide.
+
+ 2. "hierbox" and "hiertable" widget.
+
+ Hierarchical listbox widget. Displays a general ordered tree which
+ may be built on-the-fly or all at once.
+
+ 3. "tabset" and "tabnotebook" widget.
+
+ Can be used either as a tab notebook or simple tabset. Tabs can
+ be arranged in a variety of ways: multi-tiered, scrolled, and
+ attached to any of the four sides. Tab labels can contain both
+ images and text (text can be arbitrarily rotated). Notebook pages
+ can be torn-off into separate windows and replaced later.
+
+ 4. Changes to vectors.
+
+ New features:
+
+ o Vector expressions. The vector now has an "expr" operation
+ that lets you perform math (including math library
+ functions) on vectors. There are several new functions
+ (such as "max", "min", "mean" "median", "q1", "q3", "prod",
+ "sum", "adev", "sdev", "skew", ...)
+
+ vector expr { sin(x)^2 + cos(x)^2 }
+ y expr { log(x) * $value }
+
+ o New syntax to create and destroy vectors:
+
+ vector create x
+ vector destroy x
+
+ The old syntax for creating vectors still works.
+
+ vector x
+
+ o Vectors are *not* automatically deleted when their Tcl
+ variable is unset anymore. This means that you can
+ temporarily map vectors to variables and use them as you
+ would an ordinary Tcl array (kind of like "upvar").
+
+ proc AddValue { vecName value } {
+ $vecName variable x
+
+ set x(++end) $value
+ }
+
+ There's an "-watchunset" flag to restore the old
+ behavior if you need it.
+
+ vector create x -watchunset yes
+
+ o Vectors still automatically create Tcl variables by
+ default. I'd like to change this, but it silently
+ breaks lots of code, so it will stay.
+
+ Bug fixes:
+
+ o Vector reallocation failed when shrinking the vector.
+
+ o Vector "destroy" callback made after vector was already freed.
+
+ o Fixed vector/scalar operations.
+
+ o Always store results in temporary, so not to overwrite accidently
+ current vector values.
+
+ 5. Changes to Graph, Barchart, Stripchart widgets.
+
+ New features:
+
+ o Drop shadows for text (titles, markers, etc). Drop
+ shadows improve contrast when displaying text over a
+ background with similar color intensities.
+
+ o Postscript "-preview" option to generate a EPS
+ PostScript preview image that can be read and
+ displayed by the EPS canvas item.
+
+ o New "-topvariable", "-bottomvariable",
+ "-leftvariable", and "-rightvariable" options. They
+ specify variables to contain the current margin
+ sizes. These variables are updated whenever the
+ graph is redrawn.
+
+ o New "-aspect" option. Let's you maintain a particular aspect
+ ratio for the the graph.
+
+ o Image markers can now be stretched and zoomed like
+ bitmap markers.
+
+ o Bind operation for legend entries, markers, and elements.
+
+ Much thanks to Julian Loaring <bigj@bigj.demon.co.uk>
+ for the idea.
+
+ o New "-xor" option for line markers, lets you draw the line
+ by rubberbanded by XOR-ing without requiring the graph to
+ be redrawn. This can be used, for example, to select regions
+ for zooming.
+
+ Thanks to Johannes Zellner (joze@krisal.physik.uni-karlsruhe.de)
+ for the idea.
+
+ o Can attach a scrollbar to an axis.
+
+ .sbar configure -command { .graph axis view y }
+ .graph axis configure y -scrollcommand { .sbar set }
+
+ Bug fixes:
+
+ o Closest line (point) broken when using pens styles.
+
+ o Marker elastic coordinates were wrong.
+
+ o PostScript bounding box included the border of the page.
+
+ o Bad PostScript generated for barchart symbols with stipples.
+
+ o Wrong dimensions computed with postscript " -maxpect" option.
+
+ o Text markers fixed.
+
+ Thanks to De Clarke for the bug report and fix.
+
+
+ o Renamed axis configuration from "-range" to "-autorange" to
+ match the documentation.
+
+ Thanks to Brian Smith for the correction.
+
+ o Fixed polygon marker pick routine.
+
+ o Fixed active tab labels overlapping the selected tab.
+
+ o PostScript graph footer turned off by default. Use -footer option
+ to turn on.
+
+ .graph postscript configure -footer yes
+
diff --git a/blt/PROBLEMS b/blt/PROBLEMS
new file mode 100644
index 00000000000..680bb644e92
--- /dev/null
+++ b/blt/PROBLEMS
@@ -0,0 +1,138 @@
+Specific problems:
+
+1. I've built BLT, but when I run "bltwish", it doesn't know about any
+ of the BLT commands.
+
+ % graph .g
+ invalid command name "graph"
+
+ Starting with Tcl 8.x, the BLT commands are stored in their own
+ namespace called "blt". The idea is to prevent name clashes with Tcl
+ commands and variables from other packages, such as a "table" command
+ in two different packages.
+
+ You can access the BLT commands in a couple of ways.
+
+ Prefix the BLT commands with the namespace qualifier "blt::"
+
+ % blt::graph .g
+ % blt::table . .g -resize both
+
+ or import the BLT commands into the global namespace.
+
+ % namespace import blt::*
+ % graph .g
+ % table . .g -resize both
+
+2. I'm try to compile BLT with ActiveState's Tcl/Tk distribution,
+ but all the demos core dump.
+
+ Look in the "include" directory where you installed ActiveState.
+ Is there an "X11" directory? Remove it and recompile BLT.
+ It contains all the fake X11 headers needed for Windows builds.
+ So it's okay to remove it for Solaris and Linux.
+
+3. Under Windows the "drag&drop" command doesn't work for me.
+
+ The "drag&drop" command uses the "send" command to communicate
+ between Tk applications and under Windows Tk has no built-in
+ "send" command. In ./demos/scripts/send.tcl there is a "send"
+ look-a-like that uses the DDE package. Source this first and
+ make sure you invoke the procedures "SendInit" and "SendVerify"
+ *before* you create and drag-and-drop targets.
+
+4. I'm using Windows 95/98 and the -stipple option doesn't seem to work.
+
+ Under Windows 95/98, your bitmap must be exactly 8x8. If you use
+ a bigger or smaller bitmap, Windows won't stipple the pattern correctly.
+ For bitmaps larger than 8x8, only the upper-left 8x8 corner of the
+ bitmap is used. For smaller bitmaps, the bitmap is extended to 8x8
+ with the new bits 0 (blank). This is a limitation of Windows 95/98,
+ not Tk.
+
+5. I can't run bltwish.exe under Windows with Tcl/Tk version 8.0.
+
+ Did you compile and install Tcl/Tk yourself? Tcl is expecting a
+ registry key to be set. The installer normally does that for you.
+ The key tells Tcl where to find the Tcl library scripts. Setting
+ the TCL_LIBRARY environment variable to the location of the Tcl
+ script directory (where init.tcl is located) will fix things.
+ Dynamic loading (package require BLT) of BLT should also work.
+ This problem is fixed in later versions of Tcl.
+
+6. I'm on a DEC Alpha running the graph widget. I don't see any ticks
+ or lines.
+
+ There's a problem with code generated by the GNU C compiler
+ 2.8.[0-1] for bltGrAxis.c and bltGrLine.c (I think it's just
+ these two files).
+
+ Try compiling with either the native "cc" compiler or compile
+ the two modules with -O0.
+
+7. When I compile BLT on Solaris (maybe others?), I get lots of error
+ messages in the form:
+
+ <unknown> 0xf44 /usr/local/lib/libtcl7.6.a(tclCmdIL.o)
+ <unknown> 0xf3c /usr/local/lib/libtcl7.6.a(tclCmdIL.o)
+ <unknown> 0x628 /usr/local/lib/libtcl7.6.a(tclCmdIL.o)
+
+ This is because Tcl and Tk have been installed only as static libraries,
+ not shared libraries. The ./src/shared/Makefile creates the shared BLT
+ library with a back-link to these libraries. The advantage of this link
+ is that when you dynamically load BLT, the correct Tcl/Tk libraries are
+ automatically searched for any unresolved references.
+
+ You can fix this in one of two ways.
+
+ o Remove the back-link. Edit ./src/shared/Makefile and cut the
+ "-ltcl* -ltk*" references from the SHLIB_LD_LIBS macro.
+
+ o Create shared libraries for Tcl and Tk. Re-configure, compile,
+ and install Tcl/Tk from their sources. Make sure you add the
+ "--enable-shared" switch to "configure".
+
+ ./configure --enable-shared
+
+8. How do I create a shared library of BLT under AIX?
+
+ Check that Tcl and Tk were both configured with the --enable-shared flag.
+ When you compile each of them, a "lib.exp" file is created in their
+ respective "unix" subdirectories. The lib.exp files are removed when
+ you do a "make clean", so you may need to recompile.
+
+ The BLT Makefile uses the TCL_SRC_DIR and TK_SRC_DIR values in the
+ tclConfig.sh and tkConfig.sh files to find these lib.exp files. You
+ may need to edit ./src/Makefile/shared to reflect the real paths of
+ the Tcl and Tk source distributions.
+
+General Problems:
+
+1. I can't compile BLT.
+
+ Send the output of both "./configure" and "make" to me at
+
+ gah@myfirstlink.net
+ gah@silconmetrics.com
+
+ This will make it easier to track down the exact problem.
+
+2. I get a segfault when running BLT in my application.
+
+ The best method is to send a Tcl script that I can run to
+ demonstrate the problem. The hard work you do pruning
+ down the problem into a small script will greatly help solve
+ it. Once I see what the problem is, I can usually fix it right
+ away.
+
+ Make sure you include all the necessary pieces to make it
+ run (e.g. data file). If it's needed, also include directions
+ how to make the problem occur (e.g. "double click on the left
+ mouse button").
+
+3. The manual page lies.
+
+ I appreciate any help in pointing out errors, omissions, or lies
+ in the manuals. If you have ideas how they might be improved,
+ I'd love to hear them.
+
diff --git a/blt/README b/blt/README
new file mode 100644
index 00000000000..1d4be346426
--- /dev/null
+++ b/blt/README
@@ -0,0 +1,182 @@
+
+This is version 2.4 of the BLT library. It's an extension to the
+Tcl/Tk toolkit. You simply compile and link with the Tcl/Tk
+libraries. It does not require the Tcl or Tk source files.
+
+BLT is available from
+
+ www.sourceforge.net/projects/blt/files
+
+This release has been built and tested with the following Tcl/Tk
+versions:
+
+ Tcl 7.5 / Tk 4.1
+ Tcl 7.6 / Tk 4.2
+ Tcl/Tk 8.0.2 thru 8.0.5
+ Tcl/Tk 8.1.0 thru 8.1.1
+ Tcl/Tk 8.2.0 thru 8.2.3
+ Tcl/Tk 8.3.0 thru 8.3.4
+
+Avoid alpha and beta versions of Tcl/Tk. They probably won't work.
+
+What is BLT?
+
+ BLT is an extension to Tcl/Tk. It adds plotting widgets (X-Y graph,
+ barchart, stripchart), a powerful geometry manager, a new canvas
+ item, and several new commands to Tk.
+
+ Plotting widgets:
+
+ graph, barchart, stripchart
+ BLT has X-Y graph, barchart, and stripchart widgets that are
+ both easy to use and customize. All the widgets work with
+ BLT vector data objects, which makes it easy to manage data.
+
+ Tree viewer
+
+ treeview Displays a general ordered tree which may be built
+ on-the-fly or all at once.
+
+ tree Tree data object.
+
+ Tab set:
+
+ tabset Can be used either as a tab notebook or simple tabset.
+ Multi-tiered and/or scrolled tabsets are available.
+ Notebook pages can be torn-off into separate windows and
+ later put back.
+
+ Geometry Manager:
+
+ table A table-based geometry manager. Lets you specify widget
+ layouts by row and column positions in the table. Unlike the
+ packer or grid, you can finely control and constrain window
+ sizes.
+
+ Vector Data Object:
+
+ vector Lets you manage a vector of floating point values in a
+ high-level fashion. Vectors inter-operate seamlessly with
+ the plotting widgets. The graphs will automatically redraw
+ themselves when the vector data changes. Vector's components
+ can be managed through a Tcl array variable, a Tcl command,
+ or the using its own C API.
+
+ Background Program Execution:
+
+ bgexec Like Tcl's "exec ... &", but collects the output, error, and
+ status of the detached UNIX subprocesses. Sets a Tcl variable
+ upon completion.
+
+ Busy Command:
+
+ busy For preventing user-interactions when the application is
+ busy. Manages an invisible "busy" window which prevents
+ further user interactions (keyboard, mouse, button, etc.).
+ Also you can provide a busy cursor that temporarily
+ overrides those of the Tk widgets.
+
+ New Canvas Item:
+
+ eps An new item is added to the Tk canvas for handling
+ encapsulated PostScript. It lets you embed an EPS file into
+ the canvas displaying either an EPS preview image found in
+ the file, or a Tk image that you provide. When you print
+ the canvas the EPS item will automatically include the EPS
+ file, translating and scaling the PostScript. For example,
+ you could use "eps" items to tile several PostScript pages
+ into single page.
+
+ The "eps" item can also be used as a replacement for "image"
+ canvas items. Unlike "image" canvas items, the image of an
+ eps item can be printed and scaled arbitrarily.
+
+ Drag & Drop Facility:
+
+ drag&drop Adds drag-n-drop capabilities to Tk. It uses "send"-style
+ communication between drag-drop sources and targets. The
+ result is a much more powerful drag-and-drop mechanism than
+ is available with OpenLook or Motif.
+
+ Bitmap Command:
+
+ bitmap Lets you read and write bitmaps from Tcl. You can define
+ bitmaps from ordinary text strings. Bitmaps can also be
+ scaled and rotated. For example, you can create a button
+ with rotated text by defining a bitmap from a text string
+ and rotating it. You can then use the bitmap in the button
+ widget.
+
+ Miscellaneous Commands:
+
+ winop Basic window operations. You can raise, lower, map, or,
+ unmap windows. Other operations let you move the pointer
+ or take photo image snapshots of Tk widgets.
+
+ bltdebug Lets you trace the execution of Tcl commands and procedures.
+ Prints out each Tcl command before it's executed.
+
+ watch Lets you specify Tcl procedures to be run before and/or
+ after every Tcl command. May be used for logging, tracing,
+ profiling, or debugging or Tcl code.
+
+ spline Computes a spline fitting a set of data points (x and y
+ vectors) and produces a vector of the interpolated images
+ (y-coordinates) at a given set of x-coordinates.
+
+ htext A simple hypertext widget. Allows text and Tk widgets to
+ be combined in a scroll-able text window. Any Tk widget
+ can be embedded and used to form hyper-links. Other
+ options allow for selections and text searches.
+
+
+How to compile and test BLT?
+
+ See the file "INSTALL" for instructions.
+
+
+Does BLT work under Windows?
+
+ Yes. Windows 95/98/ME/NT/2000/XP. I've compiled it with both
+ MS VC++ 5.0/6.0p4 and EGCS 1.1.1. Self-installing pre-compiled versions
+ are available.
+
+What are the differences between the Windows and Unix releases?
+
+ All commands work: graphs, bgexec, busy, drag&drop etc. except
+ the "container", and "cutbuffer" widgets.
+
+ The "drag&drop" command still needs to use "send" to transfer
+ information between Tk applications. You can use
+
+ ./demos/scripts/send.tcl
+
+ to imitate "send" using DDE. Just source the script and execute
+
+ SendInit
+ SendVerify
+
+ to set up the new send command.
+
+When will...?
+
+ In general, I can't answer the "When will" questions, mostly out of
+ embarrassment. My estimates of when new features and releases will
+ occur usually turn out to be way way off.
+
+What does BLT stand for?
+
+ Whatever you want it to.
+
+Where to send bugs reports, suggestions, etc. ?
+
+ gah@siliconmetrics.com
+
+ -and-
+
+ ghowlett@grandecom.net
+
+ (best to send to both addresses) Make sure you include BLT
+ and the version number in the subject line.
+
+--gah
diff --git a/blt/acconfig.h b/blt/acconfig.h
new file mode 100644
index 00000000000..2c1d006d9d8
--- /dev/null
+++ b/blt/acconfig.h
@@ -0,0 +1,39 @@
+/* acconfig.h
+ This file is in the public domain.
+
+ Descriptive text for the C preprocessor macros that
+ the distributed Autoconf macros can define.
+ No software package will use all of them; autoheader copies the ones
+ your configure.in uses into your configuration header file templates.
+
+ The entries are in sort -df order: alphabetical, case insensitive,
+ ignoring punctuation (such as underscores). Although this order
+ can split up related entries, it makes it easier to check whether
+ a given entry is in the file.
+
+ Leave the following blank line there!! Autoheader needs it. */
+
+
+#define BLT_CONFIG_H 1
+
+/* Define if DBL_EPSILON is not defined in float.h */
+#undef BLT_DBL_EPSILON
+
+/* Define if drand48 isn't declared in math.h. */
+#undef NEED_DECL_DRAND48
+
+/* Define if srand48 isn't declared in math.h. */
+#undef NEED_DECL_SRAND48
+
+/* Define if strdup isn't declared in a standard header file. */
+#undef NEED_DECL_STRDUP
+
+/* Define if j1 isn't declared in a standard header file. */
+#undef NEED_DECL_J1
+
+/* Define if union wait type is defined incorrectly. */
+#undef HAVE_UNION_WAIT
+
+/* Define if isfinite is found in libm. */
+#undef HAVE_ISFINITE
+
diff --git a/blt/aclocal.m4 b/blt/aclocal.m4
new file mode 100644
index 00000000000..7441d3d50d8
--- /dev/null
+++ b/blt/aclocal.m4
@@ -0,0 +1,38 @@
+dnl AC_TRY_RUN_WITH_OUTPUT(VARIABLE, PROGRAM,)
+AC_DEFUN(AC_TRY_RUN_WITH_OUTPUT,
+[AC_REQUIRE([AC_PROG_CC])dnl
+if test "$cross_compiling" = yes; then
+ ifelse([$3], ,
+ [errprint(__file__:__line__: warning: [AC_TRY_RUN_WITH_OUTPUT] called without default to
+ allow cross compiling
+)dnl
+ AC_MSG_ERROR(can not run test program while cross compiling)],
+ [$3])
+else
+cat > conftest.$ac_ext <<EOF
+[#]line __oline__ "configure"
+#include "confdefs.h"
+ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus
+extern "C" void exit(int);
+#endif
+])dnl
+[$2]
+EOF
+eval $ac_link
+if test -s conftest && (./conftest > ./conftest.stdout; exit) 2>/dev/null; then
+ $1=`cat ./conftest.stdout`
+else
+ $1=""
+fi
+fi
+rm -fr conftest*])
+
+dnl AC_GREP_SYMBOL(VARIABLE, SYMBOL, FILE)
+AC_DEFUN(AC_GREP_SYMBOL,
+[AC_REQUIRE([AC_PROG_AWK])dnl
+cat > conftest.awk <<EOF
+[/^# *define *]$2[[ \t]]/ { print [\$][3] }
+EOF
+$1=`${AWK} -f conftest.awk $3`
+rm -rf conftest*])
+
diff --git a/blt/blt.mak b/blt/blt.mak
new file mode 100644
index 00000000000..a208592d562
--- /dev/null
+++ b/blt/blt.mak
@@ -0,0 +1,34 @@
+# ------------------------------------------------------------------------
+# Makefile for demonstation shell of BLT library
+# ------------------------------------------------------------------------
+
+!INCLUDE ./win/makedefs
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+
+# ------------------------------------------------------------------------
+# Don't edit anything beyond this point
+# ------------------------------------------------------------------------
+
+all:
+ cd $(MAKEDIR)\src
+ $(MAKE) -f blt.mak all
+ cd $(MAKEDIR)
+
+install: install-all
+
+install-all:
+ wish$(v2)d.exe win/install.tcl $(v1) $(srcdir)
+
+clean:
+ cd $(MAKEDIR)\src
+ $(MAKE) -f blt.mak clean
+ cd $(MAKEDIR)
+ $(RM) *.bak *\~ "#"* *pure* .pure*
+
+distclean: clean
+
diff --git a/blt/cf/config.guess b/blt/cf/config.guess
new file mode 100755
index 00000000000..e1b58717080
--- /dev/null
+++ b/blt/cf/config.guess
@@ -0,0 +1,1121 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
+# Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+# Please send patches to <autoconf-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# Use $HOST_CC if defined. $CC may point to a cross-compiler
+if test x"$CC_FOR_BUILD" = x; then
+ if test x"$HOST_CC" != x; then
+ CC_FOR_BUILD="$HOST_CC"
+ else
+ if test x"$CC" != x; then
+ CC_FOR_BUILD="$CC"
+ else
+ CC_FOR_BUILD=cc
+ fi
+ fi
+fi
+
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >$dummy.s
+ .globl main
+ .ent main
+main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ arm32:NetBSD:*:*)
+ echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ SR2?01:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:NetBSD:*:*)
+ echo powerpc-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ sed 's/^ //' << EOF >$dummy.c
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ rm -f $dummy.c $dummy
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i?86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ hppa*:OpenBSD:*:*)
+ echo hppa-unknown-openbsd
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alpha-cray-unicosmk${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F300:UNIX_System_V:*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ F301:UNIX_System_V:*:*)
+ echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ if test -x /usr/bin/objformat; then
+ if test "elf" = "`/usr/bin/objformat`"; then
+ echo ${UNAME_MACHINE}-unknown-freebsdelf`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+ exit 0
+ fi
+ fi
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i386-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ ld_help_string=`cd /; ld --help 2>&1`
+ ld_supported_emulations=`echo $ld_help_string \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ *ia64)
+ echo "${UNAME_MACHINE}-unknown-linux"
+ exit 0
+ ;;
+ i?86linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0
+ ;;
+ i?86coff)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0
+ ;;
+ sparclinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ armlinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ elf32arm*)
+ echo "${UNAME_MACHINE}-unknown-linux-gnu"
+ exit 0
+ ;;
+ armelf_linux*)
+ echo "${UNAME_MACHINE}-unknown-linux-gnu"
+ exit 0
+ ;;
+ m68klinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ elf32ppc)
+ # Determine Lib Version
+ cat >$dummy.c <<EOF
+#include <features.h>
+#if defined(__GLIBC__)
+extern char __libc_version[];
+extern char __libc_release[];
+#endif
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#if defined(__GLIBC__)
+ printf("%s %s\n", __libc_version, __libc_release);
+#else
+ printf("unkown\n");
+#endif
+ return 0;
+}
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy | grep 1\.99 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.c $dummy
+ echo powerpc-unknown-linux-gnu${LIBC}
+ exit 0
+ ;;
+ esac
+
+ if test "${UNAME_MACHINE}" = "alpha" ; then
+ sed 's/^ //' <<EOF >$dummy.s
+ .globl main
+ .ent main
+ main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+
+ objdump --private-headers $dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+ elif test "${UNAME_MACHINE}" = "mips" ; then
+ cat >$dummy.c <<EOF
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ else
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+ test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+ case "${UNAME_MACHINE}" in
+ i?86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i?86:*:5:7*)
+ # Fixed at (any) Pentium or better
+ UNAME_MACHINE=i586
+ if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then
+ echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:*:6*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-qnx-qnx${UNAME_VERSION}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/blt/cf/config.sub b/blt/cf/config.sub
new file mode 100755
index 00000000000..28426bb8fa0
--- /dev/null
+++ b/blt/cf/config.sub
@@ -0,0 +1,1232 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1991, 92-97, 1998, 1999 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+ | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 \
+ | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+ | alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \
+ | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+ | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \
+ | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+ | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+ | mips64vr5000 | miprs64vr5000el | mcore \
+ | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \
+ | thumb | d10v | fr30)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[34567]86)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ # FIXME: clean up the formatting here.
+ vax-* | tahoe-* | i[34567]86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+ | xmp-* | ymp-* \
+ | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \
+ | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67]-* \
+ | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+ | clipper-* | orion-* \
+ | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+ | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+ | mipstx39-* | mipstx39el-* | mcore-* \
+ | f301-* | armv*-* | t3e-* \
+ | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+ | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* )
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-cbm
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[34567]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i[34567]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i[34567]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i[34567]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ i386-go32 | go32)
+ basic_machine=i386-unknown
+ os=-go32
+ ;;
+ i386-mingw32 | mingw32)
+ basic_machine=i386-unknown
+ os=-mingw32
+ ;;
+ i386-qnx | qnx)
+ basic_machine=i386-qnx
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-unknown
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc | sparcv9)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -opened* | -openstep* | -oskit*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -qnx)
+ os=-qnx4
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -*MiNT)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f301-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -*MiNT)
+ vendor=atari
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
diff --git a/blt/cf/install-sh b/blt/cf/install-sh
new file mode 100755
index 00000000000..e9de23842dc
--- /dev/null
+++ b/blt/cf/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/blt/cf/install.sh b/blt/cf/install.sh
new file mode 100644
index 00000000000..ed46b48283f
--- /dev/null
+++ b/blt/cf/install.sh
@@ -0,0 +1,238 @@
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/blt/cf/ldAix b/blt/cf/ldAix
new file mode 100644
index 00000000000..199c1257d1c
--- /dev/null
+++ b/blt/cf/ldAix
@@ -0,0 +1,72 @@
+
+#
+# ldAix ldCmd ldArg ldArg ...
+#
+# This shell script provides a wrapper for ld under AIX in order to
+# create the .exp file required for linking. Its arguments consist
+# of the name and arguments that would normally be provided to the
+# ld command. This script extracts the names of the object files
+# from the argument list, creates a .exp file describing all of the
+# symbols exported by those files, and then invokes "ldCmd" to
+# perform the real link.
+#
+# SCCS: @(#) ldAix 1.8 97/02/21 14:50:27
+
+# Extract from the arguments the names of all of the object files.
+
+args=$*
+ofiles=""
+for i do
+ x=`echo $i | grep '[^.].o$'`
+ if test "$x" != ""; then
+ ofiles="$ofiles $i"
+ fi
+done
+
+# Create the export file from all of the object files, using nm followed
+# by sed editing. Here are some tricky aspects of this:
+#
+# 1. Nm produces different output under AIX 4.1 than under AIX 3.2.5;
+# the following statements handle both versions.
+# 2. Use the -g switch to nm instead of -e under 4.1 (this shows just
+# externals, not statics; -g isn't available under 3.2.5, though).
+# 3. Eliminate lines that end in ":": these are the names of object
+# files (relevant in 4.1 only).
+# 4. Eliminate entries with the "U" key letter; these are undefined
+# symbols (relevant in 4.1 only).
+# 5. Eliminate lines that contain the string "0|extern" preceded by space;
+# in 3.2.5, these are undefined symbols (address 0).
+# 6. Eliminate lines containing the "unamex" symbol. In 3.2.5, these
+# are also undefined symbols.
+# 7. If a line starts with ".", delete the leading ".", since this will
+# just cause confusion later.
+# 8. Eliminate everything after the first field in a line, so that we're
+# left with just the symbol name.
+
+nmopts="-g -C"
+osver=`uname -v`
+if test $osver -eq 3; then
+ nmopts="-e"
+fi
+rm -f lib.exp
+echo "#! " >lib.exp
+/usr/ccs/bin/nm $nmopts -h $ofiles | sed -e '/:$/d' -e '/ U /d' -e '/[ ]0|extern/d' -e '/unamex/d' -e 's/^\.//' -e 's/[ |].*//' | sort | uniq >>lib.exp
+
+# Extract the name of the object file that we're linking. If it's a .a
+# file, then link all the objects together into a single file "shr.o"
+# and then put that into the archive. Otherwise link the object files
+# directly into the .a file.
+
+outputFile=`echo $args | sed -e 's/.*-o \([^ ]*\).*/\1/'`
+noDotA=`echo $outputFile | sed -e '/\.a$/d'`
+echo "noDotA=\"$noDotA\""
+if test "$noDotA" = "" ; then
+ linkArgs=`echo $args | sed -e 's/-o .*\.a /-o shr.o /'`
+ echo $linkArgs
+ eval $linkArgs
+ echo ar cr $outputFile shr.o
+ ar cr $outputFile shr.o
+ rm -f shr.o
+else
+ eval $args
+fi
diff --git a/blt/configure b/blt/configure
new file mode 100755
index 00000000000..59bcda56ed6
--- /dev/null
+++ b/blt/configure
@@ -0,0 +1,4566 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-tcl=DIR Find tclConfig.sh in DIR"
+ac_help="$ac_help
+ --with-tk=DIR Find tkConfig.sh in DIR"
+ac_help="$ac_help
+ --with-tclincls=DIR Find tcl.h in DIR"
+ac_help="$ac_help
+ --with-tkincls=DIR Find tk.h in DIR"
+ac_help="$ac_help
+ --with-tcllibs=DIR Find Tcl library in DIR"
+ac_help="$ac_help
+ --with-tklibs=DIR Find Tk library in DIR"
+ac_help="$ac_help
+ --enable-jpeg=DIR Find JPEG headers and libraries in DIR"
+ac_help="$ac_help
+ --with-cc=CC Set C compiler to CC"
+ac_help="$ac_help
+ --with-cflags=FLAGS Set compiler flags to FLAGS"
+ac_help="$ac_help
+ --with-gnu-ld Use GNU linker"
+ac_help="$ac_help
+ --with-x use the X Window System"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=src/bltInit.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in cf $srcdir/cf; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in cf $srcdir/cf" 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+
+# -----------------------------------------------------------------------
+#
+# Handle command line options
+#
+# --with-tcl=DIR
+# --with-tk=DIR
+# --with-cc=CC
+# --with-cflags=flags This is probably for me only
+# --with-gnu-ld
+#
+# -----------------------------------------------------------------------
+
+INC_SPECS=""
+LIB_SPECS=""
+TCL_ONLY_LIB_SPECS=""
+loader_run_path=""
+DEFINES=""
+
+blt_with_tcl=""
+blt_with_tk=""
+blt_enable_jpeg="no"
+blt_with_cc=""
+blt_with_cflags="$CFLAGS"
+blt_with_gnu_ld="no"
+blt_with_tcl_includes=""
+blt_with_tk_includes=""
+blt_with_tcl_libraries=""
+blt_with_tk_libraries=""
+
+# Check whether --with-tcl or --without-tcl was given.
+if test "${with_tcl+set}" = set; then
+ withval="$with_tcl"
+ blt_with_tcl=$withval
+fi
+
+# Check whether --with-tk or --without-tk was given.
+if test "${with_tk+set}" = set; then
+ withval="$with_tk"
+ blt_with_tk=$withval
+fi
+
+# Check whether --with-tclincls or --without-tclincls was given.
+if test "${with_tclincls+set}" = set; then
+ withval="$with_tclincls"
+ blt_with_tcl_includes=$withval
+fi
+
+# Check whether --with-tkincls or --without-tkincls was given.
+if test "${with_tkincls+set}" = set; then
+ withval="$with_tkincls"
+ blt_with_tk_includes=$withval
+fi
+
+# Check whether --with-tcllibs or --without-tcllibs was given.
+if test "${with_tcllibs+set}" = set; then
+ withval="$with_tcllibs"
+ blt_with_tcl_libraries=$withval
+fi
+
+# Check whether --with-tklibs or --without-tklibs was given.
+if test "${with_tklibs+set}" = set; then
+ withval="$with_tklibs"
+ blt_with_tk_libraries=$withval
+fi
+
+# Check whether --enable-jpeg or --disable-jpeg was given.
+if test "${enable_jpeg+set}" = set; then
+ enableval="$enable_jpeg"
+
+ unset ac_cv_header_jpeglib_h
+ unset ac_cv_lib_jpeg ac_cv_lib_jpeg_jpeg_read_header
+ blt_enable_jpeg=$enableval
+fi
+
+# Check whether --with-cc or --without-cc was given.
+if test "${with_cc+set}" = set; then
+ withval="$with_cc"
+
+ blt_with_cc=$with_cc
+ unset ac_cv_prog_CC
+ unset ac_cv_prog_CPP
+fi
+
+# Check whether --with-cflags or --without-cflags was given.
+if test "${with_cflags+set}" = set; then
+ withval="$with_cflags"
+ blt_with_cflags="$with_cflags"
+fi
+
+# Check whether --with-gnu_ld or --without-gnu_ld was given.
+if test "${with_gnu_ld+set}" = set; then
+ withval="$with_gnu_ld"
+ blt_with_gnu_ld="yes"
+fi
+
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:686: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:707: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:725: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+if test "x$prefix" = xNONE; then
+echo $ac_n "checking for prefix by $ac_c" 1>&6
+# Extract the first word of "bltwish", so it can be a program name with args.
+set dummy bltwish; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:752: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_BLTWISH'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$BLTWISH" in
+ /*)
+ ac_cv_path_BLTWISH="$BLTWISH" # Let the user override the test with a path.
+ ;;
+ ?:/*)
+ ac_cv_path_BLTWISH="$BLTWISH" # Let the user override the test with a dos path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_BLTWISH="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+BLTWISH="$ac_cv_path_BLTWISH"
+if test -n "$BLTWISH"; then
+ echo "$ac_t""$BLTWISH" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -n "$ac_cv_path_BLTWISH"; then
+ prefix=`echo $ac_cv_path_BLTWISH|sed 's%/[^/][^/]*//*[^/][^/]*$%%'`
+ fi
+fi
+
+
+# -----------------------------------------------------------------------
+#
+# Set a variable containing current working directory if /bin/sh
+# doesn't do it already.
+#
+# -----------------------------------------------------------------------
+
+PWD=`pwd`
+
+# -----------------------------------------------------------------------
+#
+# C compiler and debugging flags
+#
+# -----------------------------------------------------------------------
+
+BLT_ENV_CC=$CC
+
+#
+# CC search order
+#
+# 1. command line (--with-cc)
+# 2. environment variable ($CC)
+# 3. cached variable ($blt_cv_prog_cc)
+# 4. check for program (AC_PROG_CC)
+# 4. default to cc
+#
+
+echo $ac_n "checking which C compiler""... $ac_c" 1>&6
+echo "configure:818: checking which C compiler" >&5
+if test "x${blt_with_cc}" != "x" ; then
+ CC=${blt_with_cc}
+ unset ac_cv_prog_CPP
+ unset ac_cv_prog_CC
+elif test "x${BLT_ENV_CC}" != "x" ; then
+ unset ac_cv_prog_CPP
+ unset ac_cv_prog_CC
+elif test "x${blt_cv_prog_cc}" != "x" ; then
+ CC=${blt_cv_prog_cc}
+ unset ac_cv_prog_CC
+else
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:833: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:863: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:914: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:946: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 957 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:962: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:988: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:993: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1002: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:1021: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+fi
+if test "x${CC}" = "x" ; then
+ CC=cc
+fi
+echo "$ac_t""$CC" 1>&6
+
+unset blt_cv_prog_cc
+if eval "test \"`echo '$''{'blt_cv_prog_cc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ blt_cv_prog_cc=$CC
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1067: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1082 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1088: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1099 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1105: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1116 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1122: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+if test "x${GCC}" != "x" ; then
+ blt_have_gcc="yes"
+else
+ echo $ac_n "checking if C compiler is really gcc""... $ac_c" 1>&6
+echo "configure:1150: checking if C compiler is really gcc" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 1152 "configure"
+#include "confdefs.h"
+
+#ifdef __GNUC__
+ _cc_is_gcc_
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "_cc_is_gcc_" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_have_gcc=yes
+else
+ rm -rf conftest*
+ blt_have_gcc=no
+fi
+rm -f conftest*
+
+ echo "$ac_t""$blt_have_gcc" 1>&6
+fi
+
+#
+# CFLAGS search order
+#
+# 1. command line (--with-cflags)
+# 2. cached variable ($blt_cv_prog_cflags)
+# 3. set to "-O6" if using gcc ($blt_have_gcc)
+# 4. otherwise, default to "-O"
+#
+echo $ac_n "checking default compiler flags""... $ac_c" 1>&6
+echo "configure:1182: checking default compiler flags" >&5
+if test "x${blt_with_cflags}" != "x" ; then
+ CFLAGS=${blt_with_cflags}
+elif test "x${blt_cv_prog_cflags}" != "x" ; then
+ CFLAGS=${blt_cv_prog_cflags}
+elif test "${blt_have_gcc}" = "yes" ; then
+ CFLAGS=-O6
+else
+ CFLAGS=-O
+fi
+
+echo "$ac_t""$CFLAGS" 1>&6
+unset blt_cv_prog_cflags
+if eval "test \"`echo '$''{'blt_cv_prog_cflags'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ blt_cv_prog_cflags=$CFLAGS
+fi
+
+
+
+GCCFLAGS=""
+if test "${blt_have_gcc}" = "yes" ; then
+ GCCFLAGS="-Wall"
+ if test "${CFLAGS}" = "-g" ; then
+ GCCFLAGS="-Wshadow -Winline -Wpointer-arith ${GCCFLAGS}"
+ fi
+fi
+
+
+
+# -----------------------------------------------------------------------
+#
+# Programs: Check for existence of ranlib and install programs
+#
+# -----------------------------------------------------------------------
+for ac_prog in mawk gawk nawk awk
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1223: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AWK="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AWK="$ac_cv_prog_AWK"
+if test -n "$AWK"; then
+ echo "$ac_t""$AWK" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AWK" && break
+done
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:1264: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1319: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
+echo "configure:1347: checking whether ln -s works" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+ rm -f conftestdata
+ ac_cv_prog_LN_S="ln -s"
+else
+ ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+ echo "$ac_t""yes" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+# -----------------------------------------------------------------------
+#
+# Libraries: Check for libraries used
+#
+# -----------------------------------------------------------------------
+echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6
+echo "configure:1374: checking for main in -lsocket" >&5
+ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1382 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1389: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lsocket $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6
+echo "configure:1417: checking for main in -lnsl" >&5
+ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1425 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1432: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lnsl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -lm""... $ac_c" 1>&6
+echo "configure:1460: checking for main in -lm" >&5
+ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lm $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1468 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1475: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo m | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lm $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# -----------------------------------------------------------------------
+#
+# Headers: Check for header files used
+#
+# -----------------------------------------------------------------------
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1509: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1514 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1522: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1539 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1557 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1578 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1589: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:1613: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1618 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:1634: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1655: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1660 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1669: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_time=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+ cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+
+for ac_hdr in inttypes.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1694: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1699 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1704: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+if test "${ac_cv_header_inttypes_h}" = "yes" ; then
+ HAVE_INTTYPES_H=1
+else
+ HAVE_INTTYPES_H=0
+fi
+
+
+for ac_hdr in limits.h sys/param.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1741: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1746 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1751: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in string.h ctype.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1781: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1786 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1791: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in errno.h float.h math.h ieeefp.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1821: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1826 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1831: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/time.h waitflags.h sys/wait.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1861: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1866 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1871: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in malloc.h memory.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1901: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1906 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1911: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in setjmp.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1941: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1946 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1951: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+if test "${blt_enable_jpeg}" != "no" ; then
+ jpeg_save_CPPFLAGS=${CPPFLAGS}
+ CPPFLAGS=""
+ if test "${blt_enable_jpeg}" != "yes" ; then
+ CPPFLAGS="-I${blt_enable_jpeg}/include"
+ fi
+ for ac_hdr in jpeglib.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1988: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1993 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1998: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ JPEG_INC_SPEC="${CPPFLAGS}"
+else
+ echo "$ac_t""no" 1>&6
+JPEG_INC_SPEC=""
+fi
+done
+
+ CPPFLAGS=${jpeg_save_CPPFLAGS}
+fi
+
+# Run this check after jpeglib.h because jpeglib.h sets HAVE_STDLIB_H
+for ac_hdr in stdlib.h unistd.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2033: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2038 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2043: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+# -----------------------------------------------------------------------
+#
+# Types: Check for existence of types of size_t and pid_t
+#
+# -----------------------------------------------------------------------
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:2076: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2081 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:2109: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2114 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+
+echo $ac_n "checking whether union wait is defined correctly""... $ac_c" 1>&6
+echo "configure:2143: checking whether union wait is defined correctly" >&5
+if eval "test \"`echo '$''{'blt_cv_struct_wait_works'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2148 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+int main() {
+
+ /*
+ * Check whether <sys/wait.h> defines the type "union wait"
+ * correctly. It's needed because of weirdness in HP-UX where
+ * "union wait" is defined in both the BSD and SYS-V environments.
+ * Checking the usability of WIFEXITED seems to do the trick.
+ */
+ union wait x;
+ WIFEXITED(x); /* Generates compiler error if WIFEXITED
+ * uses an int. */
+
+; return 0; }
+EOF
+if { (eval echo configure:2166: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ blt_cv_struct_wait_works="yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ blt_cv_struct_wait_works="no"
+fi
+rm -f conftest*
+fi
+
+
+if test "${blt_cv_struct_wait_works}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_UNION_WAIT 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_struct_wait_works" 1>&6
+
+# -----------------------------------------------------------------------
+#
+# Compiler characteristics:
+# Check for existence of types of size_t and pid_t
+#
+# -----------------------------------------------------------------------
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:2195: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 2202 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:2213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 2217 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:2228: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_bigendian=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2248 "configure"
+#include "confdefs.h"
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:2261: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_c_bigendian=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+ cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:2285: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2293 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(int));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2304: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_int=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:2324: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2332 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2343: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+echo $ac_n "checking size of long long""... $ac_c" 1>&6
+echo "configure:2363: checking size of long long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2371 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2382: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+EOF
+
+
+echo $ac_n "checking size of void *""... $ac_c" 1>&6
+echo "configure:2402: checking size of void *" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_void_p'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2410 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(void *));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_void_p=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_void_p=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_void_p" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_VOID_P $ac_cv_sizeof_void_p
+EOF
+
+
+
+SIZEOF_LONG="${ac_cv_sizeof_long}"
+SIZEOF_LONG_LONG="${ac_cv_sizeof_long_long}"
+SIZEOF_VOID_P="${ac_cv_sizeof_void_p}"
+SIZEOF_INT="${ac_cv_sizeof_int}"
+
+
+
+
+
+# -----------------------------------------------------------------------
+#
+# Library Functions: Check for strdup, drand48, and srand48.
+#
+# -----------------------------------------------------------------------
+
+for ac_func in strdup strcasecmp strncasecmp drand48 srand48 finite
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2459: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2464 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2487: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+# For HPUX it's a little more complicated to search for isfinite
+echo $ac_n "checking for isfinite""... $ac_c" 1>&6
+echo "configure:2514: checking for isfinite" >&5
+if eval "test \"`echo '$''{'blt_cv_have_isfinite'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2519 "configure"
+#include "confdefs.h"
+#include <math.h>
+int main() {
+
+double x = 1.0;
+if (isfinite(x)) {
+ return 0;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:2531: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ blt_cv_have_isfinite="yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ blt_cv_have_isfinite="no"
+fi
+rm -f conftest*
+fi
+
+
+if test "${blt_cv_have_isfinite}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ISFINITE 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_have_isfinite" 1>&6
+
+# -----------------------------------------------------------------------
+#
+# Check the smallest value such that 1.0 + x != 1.0.
+# For ANSI compilers this is DBL_EPSILON in float.h
+#
+#--------------------------------------------------------------------
+
+echo $ac_n "checking whether DBL_EPSILON is defined in float.h""... $ac_c" 1>&6
+echo "configure:2560: checking whether DBL_EPSILON is defined in float.h" >&5
+if eval "test \"`echo '$''{'blt_cv_found_dbl_epsilon'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2565 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef DBL_EPSILON
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_cv_found_dbl_epsilon=yes
+else
+ rm -rf conftest*
+ blt_cv_found_dbl_epsilon=no
+fi
+rm -f conftest*
+
+
+fi
+
+echo "$ac_t""${blt_cv_found_dbl_epsilon}" 1>&6
+
+if test "${blt_cv_found_dbl_epsilon}" = "no" ; then
+ if eval "test \"`echo '$''{'blt_cv_dbl_epsilon'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ old_flags="$CFLAGS"
+ CFLAGS="-g -lm"
+ echo $ac_n "checking whether DBL_EPSILON can be computed""... $ac_c" 1>&6
+echo "configure:2598: checking whether DBL_EPSILON can be computed" >&5
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 2603 "configure"
+#include "confdefs.h"
+
+main () {
+ double e, u;
+ /*
+ * Check the smallest value such that 1.0 + x != 1.0.
+ * For ANSI compilers this is DBL_EPSILON in float.h
+ */
+ u = 1.0;
+ for(;;) {
+ u *= 0.5;
+ if ((1.0 + u) == 1.0) {
+ break;
+ }
+ }
+ e = u * 2.0;
+ printf("%.17e\n", e);
+ exit(0);
+}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest > ./conftest.stdout; exit) 2>/dev/null; then
+ blt_cv_dbl_epsilon=`cat ./conftest.stdout`
+else
+ blt_cv_dbl_epsilon=""
+fi
+fi
+rm -fr conftest*
+ CFLAGS="$old_flags"
+ cat >> confdefs.h <<EOF
+#define BLT_DBL_EPSILON ${blt_cv_dbl_epsilon}
+EOF
+
+ echo "$ac_t""${blt_cv_dbl_epsilon}" 1>&6
+
+fi
+
+fi
+
+
+echo $ac_n "checking whether declaration is needed for strdup""... $ac_c" 1>&6
+echo "configure:2645: checking whether declaration is needed for strdup" >&5
+if eval "test \"`echo '$''{'blt_cv_nedd_decl_strdup'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2650 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "strdup" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_cv_need_decl_strdup=no
+else
+ rm -rf conftest*
+ blt_cv_need_decl_strdup=yes
+fi
+rm -f conftest*
+
+fi
+
+
+if test "${blt_cv_need_decl_strdup}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define NEED_DECL_STRDUP 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_need_decl_strdup" 1>&6
+
+echo $ac_n "checking whether declaration is needed for drand48""... $ac_c" 1>&6
+echo "configure:2686: checking whether declaration is needed for drand48" >&5
+if eval "test \"`echo '$''{'blt_cv_need_decl_drand48'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2691 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "drand48" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_cv_need_decl_drand48=no
+else
+ rm -rf conftest*
+ blt_cv_need_decl_drand48=yes
+fi
+rm -f conftest*
+
+fi
+
+
+if test "${blt_cv_need_decl_drand48}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define NEED_DECL_DRAND48 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_need_decl_drand48" 1>&6
+
+echo $ac_n "checking whether declaration is needed for srand48""... $ac_c" 1>&6
+echo "configure:2727: checking whether declaration is needed for srand48" >&5
+if eval "test \"`echo '$''{'blt_cv_need_decl_srand48'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2732 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "srand48" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_cv_need_decl_srand48=no
+else
+ rm -rf conftest*
+ blt_cv_need_decl_srand48=yes
+fi
+rm -f conftest*
+
+fi
+
+
+if test "${blt_cv_need_decl_srand48}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define NEED_DECL_SRAND48 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_need_decl_srand48" 1>&6
+
+echo $ac_n "checking whether declaration is needed for j1""... $ac_c" 1>&6
+echo "configure:2768: checking whether declaration is needed for j1" >&5
+if eval "test \"`echo '$''{'blt_cv_need_decl_j1'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2773 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "j1" >/dev/null 2>&1; then
+ rm -rf conftest*
+ blt_cv_need_decl_j1=no
+else
+ rm -rf conftest*
+ blt_cv_need_decl_j1=yes
+fi
+rm -f conftest*
+
+fi
+
+
+if test "${blt_cv_need_decl_j1}" = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define NEED_DECL_J1 1
+EOF
+
+fi
+echo "$ac_t""$blt_cv_need_decl_j1" 1>&6
+
+# -----------------------------------------------------------------------
+#
+# System services: X, Tcl, Tk
+#
+# -----------------------------------------------------------------------
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+echo "configure:2818: checking for X" >&5
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+ :
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+ # The user explicitly disabled X.
+ have_x=disabled
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=NO ac_x_libraries=NO
+rm -fr conftestdir
+if mkdir conftestdir; then
+ cd conftestdir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat > Imakefile <<'EOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case "$ac_im_incroot" in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+ esac
+ case "$ac_im_usrlibdir" in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftestdir
+fi
+
+if test "$ac_x_includes" = NO; then
+ # Guess where to find include files, by looking for this one X11 .h file.
+ test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+
+ # First, try using that file with no special directory specified.
+cat > conftest.$ac_ext <<EOF
+#line 2880 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2885: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ # Look for the header file in a standard set of common directories.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ for ac_dir in \
+ /usr/X11/include \
+ /usr/X11R6/include \
+ /usr/X11R5/include \
+ /usr/X11R4/include \
+ \
+ /usr/include/X11 \
+ /usr/include/X11R6 \
+ /usr/include/X11R5 \
+ /usr/include/X11R4 \
+ \
+ /usr/local/X11/include \
+ /usr/local/X11R6/include \
+ /usr/local/X11R5/include \
+ /usr/local/X11R4/include \
+ \
+ /usr/local/include/X11 \
+ /usr/local/include/X11R6 \
+ /usr/local/include/X11R5 \
+ /usr/local/include/X11R4 \
+ \
+ /usr/X386/include \
+ /usr/x386/include \
+ /usr/XFree86/include/X11 \
+ \
+ /usr/include \
+ /usr/local/include \
+ /usr/unsupported/include \
+ /usr/athena/include \
+ /usr/local/x11r5/include \
+ /usr/lpp/Xamples/include \
+ \
+ /usr/openwin/include \
+ /usr/openwin/share/include \
+ ; \
+ do
+ if test -r "$ac_dir/$x_direct_test_include"; then
+ ac_x_includes=$ac_dir
+ break
+ fi
+ done
+fi
+rm -f conftest*
+fi # $ac_x_includes = NO
+
+if test "$ac_x_libraries" = NO; then
+ # Check for the libraries.
+
+ test -z "$x_direct_test_library" && x_direct_test_library=Xt
+ test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+
+ # See if we find them without any special options.
+ # Don't add to $LIBS permanently.
+ ac_save_LIBS="$LIBS"
+ LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2954 "configure"
+#include "confdefs.h"
+
+int main() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if { (eval echo configure:2961: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+ /usr/X11/lib \
+ /usr/X11R6/lib \
+ /usr/X11R5/lib \
+ /usr/X11R4/lib \
+ \
+ /usr/lib/X11 \
+ /usr/lib/X11R6 \
+ /usr/lib/X11R5 \
+ /usr/lib/X11R4 \
+ \
+ /usr/local/X11/lib \
+ /usr/local/X11R6/lib \
+ /usr/local/X11R5/lib \
+ /usr/local/X11R4/lib \
+ \
+ /usr/local/lib/X11 \
+ /usr/local/lib/X11R6 \
+ /usr/local/lib/X11R5 \
+ /usr/local/lib/X11R4 \
+ \
+ /usr/X386/lib \
+ /usr/x386/lib \
+ /usr/XFree86/lib/X11 \
+ \
+ /usr/lib \
+ /usr/local/lib \
+ /usr/unsupported/lib \
+ /usr/athena/lib \
+ /usr/local/x11r5/lib \
+ /usr/lpp/Xamples/lib \
+ /lib/usr/lib/X11 \
+ \
+ /usr/openwin/lib \
+ /usr/openwin/share/lib \
+ ; \
+do
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest*
+fi # $ac_x_libraries = NO
+
+if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # Record where we found X for the cache.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$ac_t""$have_x" 1>&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+
+# -----------------------------------------------------------------------
+#
+# Find the Tcl build configuration file "tclConfig.sh"
+#
+# -----------------------------------------------------------------------
+
+echo $ac_n "checking for tclConfig.sh""... $ac_c" 1>&6
+echo "configure:3055: checking for tclConfig.sh" >&5
+tcl_config_sh=""
+if test "x$blt_with_tcl" != "x" ; then
+ #
+ # Verify that a tclConfig.sh file exists in the directory specified
+ # by --with-tcl.
+ #
+ for dir in \
+ $blt_with_tcl
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ elif test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+else
+ #
+ # Otherwise, search for Tcl configuration file.
+ #
+
+
+ # 1. Search previously named locations.
+
+ for dir in \
+ $prefix \
+ $exec_prefix \
+ $blt_cv_tcl_lib
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ elif test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+
+ # 2. Search source directories.
+
+ if test "x$tcl_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr ../tcl[7-9].[0-9]* 2>/dev/null` \
+ ../tcl \
+ `ls -dr ../../tcl[7-9].[0-9]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../../tcl[7-9].[0-9]* 2>/dev/null` \
+ ../../../tcl
+ do
+ if test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+ fi
+
+ # 3. Search standard locations.
+
+ if test "x$tcl_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr /usr/local/tcl/tcl[7-9].[0-9]* 2>/dev/null` \
+ /usr/local/tcl \
+ /usr/local \
+ /usr
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ fi
+ done
+ fi
+fi
+
+echo "$ac_t""${tcl_config_sh}" 1>&6
+
+if test "x$tcl_config_sh" = "x" ; then
+ echo "can't find Tcl configuration script \"tclConfig.sh\""
+ exit 1
+fi
+
+# -----------------------------------------------------------------------
+#
+# Find the Tk build configuration file "tkConfig.sh"
+#
+# -----------------------------------------------------------------------
+
+echo $ac_n "checking for tkConfig.sh""... $ac_c" 1>&6
+echo "configure:3153: checking for tkConfig.sh" >&5
+tk_config_sh=""
+if test "x$blt_with_tk" != "x" -o "x$blt_with_tcl" != "x"; then
+ #
+ # Verify that a tkConfig.sh file exists in the directory specified
+ # by --with-tcl or --with-tk.
+ #
+ for dir in \
+ $blt_with_tk \
+ $blt_with_tcl
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ elif test -r "$dir/unix/tkConfig.sh" ; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+else
+ #
+ # Search for Tk configuration file.
+ #
+
+ #
+ # 1. Search previously named locations.
+ #
+ for dir in \
+ $prefix \
+ $exec_prefix \
+ $blt_cv_tk_lib \
+ $blt_cv_tcl_lib
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ elif test -r "$dir/unix/tkConfig.sh" ; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+ #
+ # 2. Search source directories.
+ #
+ if test "x$tk_config_sh" = "x" ; then
+ for dir in \
+ ../tcl \
+ `ls -dr ../tk[4-9].[0-9]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tk[4-9].[0-9]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tk[4-9].[0-9]* 2>/dev/null`
+ do
+ if test -r "$dir/unix/tkConfig.sh"; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+ fi
+ #
+ # 3. Search standard locations.
+ #
+ if test "x$tk_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr /usr/local/tcl/tcl[7-9].[0-9]* 2>/dev/null` \
+ /usr/local/tcl \
+ /usr/local \
+ ${x_libraries} \
+ /usr
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ fi
+ done
+ fi
+fi
+echo "$ac_t""${tk_config_sh}" 1>&6
+
+if test "x$tk_config_sh" = "x" ; then
+ echo "can't find Tk configuration script \"tkConfig.sh\""
+ exit 1
+fi
+
+# -----------------------------------------------------------------------
+#
+# Source in the Tcl/Tk configuration scripts.
+#
+# -----------------------------------------------------------------------
+
+. $tcl_config_sh
+. $tk_config_sh
+
+TCL_INC_DIR=""
+TK_INC_DIR=""
+
+if test "x${blt_with_tcl_includes}" != "x" ; then
+ if test -r "${blt_with_tcl_includes}/tcl.h" ; then
+ TCL_INC_DIR=${blt_with_tcl_includes}
+ else
+ echo "Can't find tcl.h in \"${blt_with_tcl_includes}\""
+ exit 1
+ fi
+else
+ for dir in \
+ ${TCL_PREFIX}/include/tcl${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION} \
+ ${TCL_PREFIX}/include \
+ ${TCL_SRC_DIR}/generic
+ do
+ if test -r "$dir/tcl.h" ; then
+ TCL_INC_DIR=$dir
+ break
+ fi
+ done
+ if test "x${TCL_INC_DIR}" = "x" ; then
+ echo "Can't find tcl.h header file"
+ exit 1
+ fi
+fi
+
+if test "x${blt_with_tk_includes}" != "x" ; then
+ if test -r "${blt_with_tk_includes}/tk.h" ; then
+ TK_INC_DIR=${blt_with_tk_includes}
+ else
+ echo "Can't find tk.h in \"${blt_with_tk_includes}\""
+ exit 1
+ fi
+else
+ for dir in \
+ ${TK_PREFIX}/include/tk${TK_MAJOR_VERSION}.${TK_MINOR_VERSION} \
+ ${TK_PREFIX}/include \
+ ${TK_SRC_DIR}/generic \
+ ${TCL_INC_DIR}
+ do
+ if test -r "$dir/tk.h" ; then
+ TK_INC_DIR=$dir
+ break
+ fi
+ done
+ if test "x${TK_INC_DIR}" = "x" ; then
+ echo "Can't find tk.h header file"
+ exit 1
+ fi
+fi
+
+case $target in
+ *-sunos4*|*-*-netbsd|NetBSD-*|FreeBSD-*|OpenBSD-*)
+ TCL_LIB_NAME="tcl${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}"
+ TK_LIB_NAME="tk${TK_MAJOR_VERSION}${TK_MINOR_VERSION}"
+ ;;
+ *)
+ TCL_LIB_NAME="tcl${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}"
+ TK_LIB_NAME="tk${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}"
+ ;;
+esac
+
+TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
+TK_LIB_SPEC="-l${TK_LIB_NAME}"
+
+case $target in
+ *-hpux*)
+ SHLIB_SUFFIX="sl"
+ ;;
+ *)
+ SHLIB_SUFFIX="so"
+ ;;
+esac
+
+TCL_LIB_DIR="${TCL_SRC_DIR}/unix"
+TK_LIB_DIR="${TK_SRC_DIR}/unix"
+
+if test "x${blt_with_tcl_libraries}" != "x" ; then
+ for libname in \
+ "${blt_with_tcl_libraries}/lib${TCL_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${blt_with_tcl_libraries}/lib${TCL_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TCL_LIB_DIR="${blt_with_tcl_libraries}"
+ break
+ fi
+ done
+ if test "x${TCL_LIB_DIR}" = "x" ; then
+ echo "Can't find tcl library in \"${blt_with_tcl_libraries}\""
+ exit 1
+ fi
+else
+ for libname in \
+ "${TCL_EXEC_PREFIX}/lib/lib${TCL_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${TCL_EXEC_PREFIX}/lib/lib${TCL_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TCL_LIB_DIR="${TCL_EXEC_PREFIX}/lib"
+ break
+ fi
+ done
+ if test "x${TCL_LIB_DIR}" = "x" ; then
+ echo "Can't find tcl library"
+ exit 1
+ fi
+fi
+
+if test "x${blt_with_tk_libraries}" != "x" ; then
+ for libname in \
+ "${blt_with_tk_libraries}/lib${TK_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${blt_with_tk_libraries}/lib${TK_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TK_LIB_DIR="${blt_with_tk_libraries}"
+ break
+ fi
+ done
+ if test "x${TK_LIB_DIR}" = "x" ; then
+ echo "Can't find tk library in \"${blt_with_tk_libraries}\""
+ exit 1
+ fi
+else
+ for libname in \
+ "${TK_EXEC_PREFIX}/lib/lib${TK_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${TK_EXEC_PREFIX}/lib/lib${TK_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TK_LIB_DIR="${TK_EXEC_PREFIX}/lib"
+ break
+ fi
+ done
+ if test "x${TK_LIB_DIR}" = "x" ; then
+ echo "Can't find tk library"
+ exit 1
+ fi
+fi
+
+# -----------------------------------------------------------------------
+#
+# Include files
+#
+# Append to INC_SPECS the various include files specifications
+# (built fromt the include directory information).
+#
+# -----------------------------------------------------------------------
+
+# JPEG include files
+if test "${blt_enable_jpeg}" != "no" ; then
+ if test "x${JPEG_INC_SPEC}" != "x" ; then
+ INC_SPECS="${INC_SPECS} ${JPEG_INC_SPEC}"
+ fi
+fi
+
+# X11 include files
+if test "x${x_includes}" != "x" -a \
+ "${x_includes}" != "NONE" -a \
+ "${x_includes}" != "/usr/include" -a \
+ "${x_includes}" != "${TK_INC_DIR}" -a \
+ "${x_includes}" != "${TCL_INC_DIR}" ; then
+ INC_SPECS="${INC_SPECS} -I${x_includes}"
+fi
+
+# Tk include files
+if test "${TK_INC_DIR}" != "/usr/include" ; then
+ INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
+fi
+
+# Tcl include files
+#
+# Add the include directory specification only if the Tcl
+# headers reside in a different directory from Tk's.
+if test "${TCL_INC_DIR}" != "/usr/include" -a \
+ "${TCL_INC_DIR}" != "${TK_INC_DIR}" ; then
+ INC_SPECS="${INC_SPECS} -I${TCL_INC_DIR}"
+fi
+
+# -----------------------------------------------------------------------
+#
+# Libraries
+#
+# Append to LIB the various library specifications
+# (built from the library directory information).
+#
+# -----------------------------------------------------------------------
+
+# Tk libraries
+if test "${TK_LIB_DIR}" = "/usr/lib" ; then
+ LIB_SPECS="${LIB_SPECS} ${TK_LIB_SPEC}"
+else
+ LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_LIB_SPEC}"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${TK_LIB_DIR}"
+ else
+ loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
+ fi
+fi
+
+# Tcl libraries
+if test "${TCL_LIB_DIR}" = "/usr/lib" -o \
+ "${TCL_LIB_DIR}" = "${TK_LIB_DIR}" ; then
+ LIB_SPECS="${LIB_SPECS} ${TCL_LIB_SPEC}"
+else
+ LIB_SPECS="${LIB_SPECS} -L${TCL_LIB_DIR} ${TCL_LIB_SPEC}"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${TCL_LIB_DIR}"
+ else
+ loader_run_path="${TCL_LIB_DIR}:${loader_run_path}"
+ fi
+fi
+
+if test "${TCL_LIB_DIR}" = "/usr/lib" ; then
+ TCL_ONLY_LIB_SPECS="${TCL_LIB_SPEC}"
+else
+ TCL_ONLY_LIB_SPECS="-L${TCL_LIB_DIR} ${TCL_LIB_SPEC}"
+fi
+
+
+# Collect the libraries for AIX that aren't using stubs.
+aix_lib_specs=""
+
+# X11 library
+if test "x${x_libraries}" = "x" -o \
+ "x${x_libraries}" = "NONE" -o \
+ "${x_libraries}" = "/usr/lib" -o \
+ "${x_libraries}" = "${TK_LIB_DIR}" -o \
+ "${x_libraries}" = "${TCL_LIB_DIR}" ; then
+ LIB_SPECS="${LIB_SPECS} -lX11"
+ aix_lib_specs="${aix_lib_specs} -lX11"
+else
+ LIB_SPECS="${LIB_SPECS} -L${x_libraries} -lX11"
+ aix_lib_specs="${aix_lib_specs} -L${x_libraries} -lX11"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${x_libraries}"
+ else
+ loader_run_path="${loader_run_path}:${x_libraries}"
+ fi
+fi
+
+# JPEG library
+if test "${blt_enable_jpeg}" != "no" ; then
+ jpeg_save_LDFlags="${LDFLAGS}"
+ JPEG_LIB_SPEC="-ljpeg"
+ JPEG_LIB_DIR=""
+ if test "${blt_enable_jpeg}" != "yes" ; then
+ JPEG_LIB_DIR="${blt_enable_jpeg}/lib"
+ JPEG_LIB_SPEC="-L${JPEG_LIB_DIR} ${JPEG_LIB_SPEC}"
+ LDFLAGS="-L${JPEG_LIB_DIR} ${LDFLAGS}"
+ fi
+ echo $ac_n "checking for jpeg_read_header in -ljpeg""... $ac_c" 1>&6
+echo "configure:3505: checking for jpeg_read_header in -ljpeg" >&5
+ac_lib_var=`echo jpeg'_'jpeg_read_header | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ljpeg $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3513 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char jpeg_read_header();
+
+int main() {
+jpeg_read_header()
+; return 0; }
+EOF
+if { (eval echo configure:3524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+found=no
+fi
+
+ if test "${found}" = "yes" ; then
+ LIB_SPECS="${LIB_SPECS} ${JPEG_LIB_SPEC}"
+ aix_lib_specs="${aix_lib_specs} ${JPEG_LIB_SPEC}"
+ if test "x${JPEG_LIB_DIR}" != "x" ; then
+ loader_run_path="${loader_run_path}:${JPEG_LIB_DIR}"
+ fi
+ fi
+ LDFLAGS=${jpeg_save_LDFlags}
+fi
+
+save_libs=$LIBS
+LIBS="$LIB_SPECS $LIBS"
+for ac_func in XExtendedMaxRequestSize
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:3560: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3565 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3588: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+LIBS=$save_libs
+
+# -----------------------------------------------------------------------
+#
+# Set up a new default prefix to installation path. The ways
+# the prefix can be set and their precedence are as follows:
+#
+# 1. --prefix option given to ./configure. (prefix != NONE)
+# 2. use previously configured Tk prefix
+#
+# -----------------------------------------------------------------------
+
+if test "$prefix" = "NONE" ; then
+ prefix=${TCL_PREFIX}
+fi
+
+if test "$exec_prefix" = "NONE" ; then
+ exec_prefix=${TCL_EXEC_PREFIX}
+fi
+
+# -------------------------------------------------------------------------
+#
+# Extract the BLT version number for the blt.h header
+#
+# -------------------------------------------------------------------------
+echo $ac_n "checking BLT_MAJOR_VERSION""... $ac_c" 1>&6
+echo "configure:3638: checking BLT_MAJOR_VERSION" >&5
+if eval "test \"`echo '$''{'blt_cv_major_version'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.awk <<EOF
+/^# *define *BLT_MAJOR_VERSION[ \t]/ { print \$3 }
+EOF
+blt_cv_major_version=`${AWK} -f conftest.awk ${srcdir}/src/blt.h`
+rm -rf conftest*
+
+fi
+
+BLT_MAJOR_VERSION=${blt_cv_major_version}
+echo "$ac_t""$blt_cv_major_version" 1>&6
+
+echo $ac_n "checking BLT_MINOR_VERSION""... $ac_c" 1>&6
+echo "configure:3654: checking BLT_MINOR_VERSION" >&5
+if eval "test \"`echo '$''{'blt_cv_minor_version'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.awk <<EOF
+/^# *define *BLT_MINOR_VERSION[ \t]/ { print \$3 }
+EOF
+blt_cv_minor_version=`${AWK} -f conftest.awk ${srcdir}/src/blt.h`
+rm -rf conftest*
+
+fi
+
+echo "$ac_t""$blt_cv_minor_version" 1>&6
+BLT_MINOR_VERSION=${blt_cv_minor_version}
+
+BLT_VERSION=${BLT_MAJOR_VERSION}.${BLT_MINOR_VERSION}
+
+# Add BLT to the run path
+libdir=${exec_prefix}/lib
+
+if test "x${libdir}" != "x" -a \
+ "${libdir}" != "/usr/lib" -a \
+ "${libdir}" != "${x_libraries}" -a \
+ "${libdir}" != "${TK_LIB_DIR}" -a \
+ "${libdir}" != "${TCL_LIB_DIR}" ; then
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${libdir}"
+ else
+ loader_run_path="${libdir}:${loader_run_path}"
+ fi
+fi
+
+aix_lib_specs="${aix_lib_specs} ${LIBS}"
+LIB_SPECS="${LIB_SPECS} ${LIBS}"
+TCL_ONLY_LIB_SPECS="${TCL_ONLY_LIB_SPECS} ${LIBS}"
+
+# -------------------------------------------------------------------------
+#
+# Extract the Tcl version number for the tcl.h header
+#
+# -------------------------------------------------------------------------
+echo $ac_n "checking TCL_VERSION in tcl.h""... $ac_c" 1>&6
+echo "configure:3696: checking TCL_VERSION in tcl.h" >&5
+if eval "test \"`echo '$''{'blt_cv_tcl_h_version'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.awk <<EOF
+/^# *define *TCL_VERSION[ \t]/ { print \$3 }
+EOF
+blt_cv_tcl_h_version=`${AWK} -f conftest.awk ${TCL_INC_DIR}/tcl.h`
+rm -rf conftest*
+
+fi
+
+eval TCL_H_VERSION=${blt_cv_tcl_h_version}
+echo "$ac_t""$TCL_H_VERSION" 1>&6
+if test "${TCL_H_VERSION}" != "${TCL_VERSION}" ; then
+ echo "Error: Tcl version mismatch. "
+ echo " ${TCL_VERSION} ${tcl_config_sh}"
+ echo " ${TCL_H_VERSION} ${TCL_INC_DIR}/tcl.h"
+ exit 1
+fi
+echo $ac_n "checking TK_VERSION in tk.h""... $ac_c" 1>&6
+echo "configure:3717: checking TK_VERSION in tk.h" >&5
+if eval "test \"`echo '$''{'blt_cv_tk_h_version'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.awk <<EOF
+/^# *define *TK_VERSION[ \t]/ { print \$3 }
+EOF
+blt_cv_tk_h_version=`${AWK} -f conftest.awk ${TK_INC_DIR}/tk.h`
+rm -rf conftest*
+
+fi
+
+eval TK_H_VERSION=${blt_cv_tk_h_version}
+echo "$ac_t""$TK_H_VERSION" 1>&6
+if test "${TK_H_VERSION}" != "${TK_VERSION}" ; then
+ echo "Error: Tk version mismatch."
+ echo " ${TK_VERSION} ${tk_config_sh}"
+ echo " ${TK_H_VERSION} ${TK_INC_DIR}/tk.h"
+ exit 1
+fi
+
+if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then
+ :
+elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
+ :
+elif test "$TCL_VERSION" = "$TK_VERSION" ; then
+ :
+else
+ echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
+ exit 1
+fi
+
+#--------------------------------------------------------------------
+#
+# Check if we can generate shared libraries on this system. Set flags
+# to generate shared libraries for systems that we know about. Start
+# with the values found in tclConfig.sh, make changes as we know about
+# the different systems.
+#
+#--------------------------------------------------------------------
+
+LIB_BASE_NAME=libBLT
+
+# Initialize shared library build variables
+
+SHLIB_CFLAGS="$TCL_SHLIB_CFLAGS"
+SHLIB_LD="$TCL_SHLIB_LD"
+SHLIB_LD_FLAGS="$TCL_LD_FLAGS"
+SHLIB_RUNPATH="$TCL_LD_SEARCH_FLAGS"
+
+SHLIB_SUFFIX=".so"
+SHLIB_TARGET=""
+SHLIB_CFLAGS=""
+SHLIB_LIB_SPECS="${JPEG_LIB_SPEC}"
+SHLIB_TCL_ONLY_LIB_SPECS="${TCL_ONLY_LIB_SPECS}"
+SHLIB_TCL_ONLY_LIB_SPECS=""
+LDFLAGS=""
+LD_RUN_PATH=""
+EXTRA_LIB_SPECS=""
+
+build_shared="yes"
+library_name=libBLT${BLT_MAJOR_VERSION}${BLT_MINOR_VERSION}
+
+case $target in
+ *-aix4.[2-9]*)
+ # No Position-Independent flags needed
+ SHLIB_CFLAGS=""
+
+ # Use the installed export file or the one found in the source directory.
+
+ if test -r "${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp" ; then
+ tcl_exp="${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp"
+ else
+ tcl_exp="${TCL_SRC_DIR}/unix/lib.exp"
+ fi
+ if test -r "${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp" ; then
+ tk_exp="${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp"
+ else
+ tk_exp="${TK_SRC_DIR}/unix/lib.exp"
+ fi
+
+ full_src_path=`cd ${srcdir}; pwd`
+
+ # Use shell-script to link shared library
+ SHLIB_LD="${full_src_path}/cf/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry -bI:${tk_exp} -bI:${tcl_exp}"
+
+ SHLIB_LIB_SPECS="${aix_lib_specs} -lc"
+
+ LDFLAGS="-L${loader_run_path}"
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-aix*)
+ # No Position-Independent flags needed
+ SHLIB_CFLAGS=""
+
+ # Use the installed export file or the one found in the source directory.
+
+ if test -r "${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp" ; then
+ tcl_exp="${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp"
+ else
+ tcl_exp="${TCL_SRC_DIR}/unix/lib.exp"
+ fi
+ if test -r "${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp" ; then
+ tk_exp="${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp"
+ else
+ tk_exp="${TK_SRC_DIR}/unix/lib.exp"
+ fi
+
+ full_src_path=`cd ${srcdir}/cf; pwd`
+
+ # Use shell-script to link shared library
+
+ SHLIB_LD="${full_src_path}/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry -bI:${tk_exp} -bI:${tcl_exp}"
+
+ SHLIB_LIB_SPECS="${aix_lib_specs} -lc"
+
+ LDFLAGS="-L${loader_run_path}"
+ EXTRA_LIB_SPECS="-lld"
+ ;;
+
+ *-bsdi2*|*-bsdi3*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="shlicc"
+ SHLIB_LD_FLAGS="-r"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-bsdi4*)
+ SHLIB_CFLAGS="-export-dynamic -fPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS='-shared -Wl,-E -Wl,-soname,$@'
+ ;;
+
+ *-dgux*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-hpux*)
+ if test "$blt_have_gcc" = "no" ; then
+ DEFINES="$DEFINES -D_HPUX_SOURCE"
+ fi
+ echo $ac_n "checking for shl_load in -ldld""... $ac_c" 1>&6
+echo "configure:3865: checking for shl_load in -ldld" >&5
+ac_lib_var=`echo dld'_'shl_load | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldld $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3873 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shl_load();
+
+int main() {
+shl_load()
+; return 0; }
+EOF
+if { (eval echo configure:3884: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+found=no
+fi
+
+ if test "${found}" = "yes" ; then
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-b -E -n +s +b,${loader_run_path}:."
+ SHLIB_SUFFIX=".sl"
+
+ # The run path is included in both LDFLAGS and SHLIB_LD_FLAGS
+ # because SHLIB_LD is ld and LD is cc/gcc.
+
+ LDFLAGS="-Wl,-E -Wl,+s,+b,${loader_run_path}:."
+ EXTRA_LIB_SPECS="-ldld"
+ fi
+ ;;
+
+ *-irix64-6.5*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-32 -shared -rdata_shared"
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ ;;
+
+ *-irix-56.*|*-irix64-*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-shared -rdata_shared"
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ LDFLAGS=""
+ if test "$blt_have_gcc" = "yes" ; then
+ SHLIB_CFLAGS="-mabi=n32 $SHLIB_CFLAGS"
+ SHLIB_LD_FLAGS="-mabi=n32 $SHLIB_LD_FLAGS"
+ LDFLAGS="-mabi=n32 $LDFLAGS"
+ else
+ CFLAGS="-n32 $CFLAGS"
+ LDFLAGS="-n32 $LDFLAGS"
+ fi
+ ;;
+
+ *-linux*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS='-rdynamic -shared -Wl,-E -Wl,-soname,$@'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+
+ LDFLAGS=""
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-mp-ras-02*)
+ SHLIB_CFLAGS="-G -K PIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS=""
+ ;;
+
+ *-mp-ras-*)
+ SHLIB_CFLAGS="-G -K PIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS="-Wl,-Bexport"
+ ;;
+
+ *-ncr-sysv4-*2*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-ncr-sysv4*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G -Wl,-Bexport"
+
+ LDFLAGS="-Wl,-Bexport"
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-netbsd*|*-freebsd*|*-openbsd*)
+ # Not available on all versions: check for include file.
+ ac_safe=`echo "dlfcn.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for dlfcn.h""... $ac_c" 1>&6
+echo "configure:3985: checking for dlfcn.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3990 "configure"
+#include "confdefs.h"
+#include <dlfcn.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:3995: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ test_ok=yes
+else
+ echo "$ac_t""no" 1>&6
+test_ok=no
+fi
+
+ if test "$test_ok" = yes; then
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-Bshareable -x"
+ fi
+ ;;
+
+ *-nextstep*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-nostdlib -r"
+ ;;
+
+ *-osf1-1.012*)
+ # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1
+
+ SHLIB_CFLAGS=""
+
+ # Warning: Ugly Makefile Hack
+ # Make package name same as library name
+
+ SHLIB_LD='ld -R -export $@:'
+ ;;
+
+ *-osf1-1.*)
+ # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2
+
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="ld -shared"
+ ;;
+
+ *-osf1V*)
+ # Digital OSF/1
+
+ SHLIB_CFLAGS=""
+ SHLIB_LD='ld'
+ SHLIB_LD_FLAGS='-shared -expect_unresolved "*"'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ LDFLAGS=""
+ ;;
+
+ *-sco*)
+ # Note, dlopen is available only on SCO 3.2.5 and greater. However,
+ # this test works, since "uname -s" was non-standard in 3.2.4 and
+ # below.
+
+ SHLIB_CFLAGS="-Kpic -belf"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-G"
+ LDFLAGS="-belf -Wl,-Bexport"
+ ;;
+
+ *-sni-sysv*)
+
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-sunos4*)
+
+ SHLIB_CFLAGS="-PIC"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-assert pure-text"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-solaris2*)
+
+ SHLIB_CFLAGS="-KPIC"
+ if test "${blt_with_gnu_ld}" = "yes" -a "$blt_have_gcc" = "yes" ; then
+ SHLIB_LD="gcc"
+ SHLIB_LD_FLAGS='-rdynamic -shared -Wl,-E -Wl,-soname,$@'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ else
+ SHLIB_LD="/usr/ccs/bin/ld"
+ SHLIB_LD_FLAGS="-G -z text"
+ LD_RUN_PATH="-R ${loader_run_path}"
+ fi
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-mips-dde-sysv*)
+
+ SHLIB_CFLAGS="-KPIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-pc-sysv4* | *-unixware-5*)
+ SHLIB_CFLAGS="-G -KPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS=" -Wl,-Bexport"
+ ;;
+
+ *)
+ build_shared="no"
+ ;;
+
+esac
+
+# If we're running gcc, then set SHLIB_CFLAGS flags for compiling
+# shared libraries for gcc, instead of those of the vendor's
+# compiler.
+
+
+if test "$blt_have_gcc" = "yes" ; then
+ SHLIB_CFLAGS="-fPIC"
+fi
+
+# We can't back link against static versions of Tcl/Tk.
+# If # ${TCL_SHARED_BUILD} can't be found or isn't "1", assume that
+# shared libraies weren't built.
+
+if test "${TCL_SHARED_BUILD}" != "1" ; then
+ SHLIB_LIB_SPECS=""
+fi
+
+if test "${build_shared}" = "yes" ; then
+ SHLIB_TARGET="build_shared"
+
+
+
+
+
+
+
+fi
+
+
+
+
+LIBS=${LIB_SPECS}
+
+
+
+
+INCLUDES=${INC_SPECS}
+
+
+
+
+
+
+
+
+
+#--------------------------------------------------------------------
+# Propagate prefix argument as installation directory.
+#--------------------------------------------------------------------
+
+BLT_LIBRARY="${prefix}/lib/blt${BLT_VERSION}"
+
+
+#--------------------------------------------------------------------
+# Print out some of the more important settings
+#--------------------------------------------------------------------
+echo ""
+echo "Configuration results:"
+echo ""
+echo " tcl.h found in $TCL_INC_DIR"
+echo " tk.h found in $TK_INC_DIR"
+echo " X11/Xlib.h found in $x_includes"
+echo " lib${TCL_LIB_NAME} found in $TCL_LIB_DIR"
+echo " lib${TK_LIB_NAME} found in $TK_LIB_DIR"
+echo " libX11 found in $x_libraries"
+echo ""
+echo "Directories where BLT is to be installed:"
+echo ""
+echo " \"\$prefix\" is $prefix"
+echo " \"\$exec_prefix\" is $exec_prefix"
+echo ""
+echo " bltwish to be installed in $bindir"
+echo " libBLT.a to be installed in $libdir"
+echo " scripts to be installed in $BLT_LIBRARY"
+echo " manual pages to be installed in $mandir"
+echo ""
+
+#--------------------------------------------------------------------
+#
+# Generate the following Makefiles
+#
+# ./Makefile
+# ./src/Makefile
+# ./src/shared/Makefile
+# ./man/Makefile
+# ./library/Makefile
+# ./demos/Makefile
+#
+#--------------------------------------------------------------------
+trap '' 1 2 15
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile src/Makefile src/bltHash.h src/shared/Makefile man/Makefile library/Makefile demos/Makefile src/bltConfig.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@BLTWISH@%$BLTWISH%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@GCCFLAGS@%$GCCFLAGS%g
+s%@AWK@%$AWK%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@LN_S@%$LN_S%g
+s%@HAVE_INTTYPES_H@%$HAVE_INTTYPES_H%g
+s%@SIZEOF_INT@%$SIZEOF_INT%g
+s%@SIZEOF_LONG@%$SIZEOF_LONG%g
+s%@SIZEOF_LONG_LONG@%$SIZEOF_LONG_LONG%g
+s%@SIZEOF_VOID_P@%$SIZEOF_VOID_P%g
+s%@SHLIB_CFLAGS@%$SHLIB_CFLAGS%g
+s%@SHLIB_TARGET@%$SHLIB_TARGET%g
+s%@SHLIB_LD@%$SHLIB_LD%g
+s%@SHLIB_LD_FLAGS@%$SHLIB_LD_FLAGS%g
+s%@SHLIB_LIB_SPECS@%$SHLIB_LIB_SPECS%g
+s%@SHLIB_TCL_ONLY_LIB_SPECS@%$SHLIB_TCL_ONLY_LIB_SPECS%g
+s%@SHLIB_SUFFIX@%$SHLIB_SUFFIX%g
+s%@LD_RUN_PATH@%$LD_RUN_PATH%g
+s%@LIB_SPECS@%$LIB_SPECS%g
+s%@TCL_ONLY_LIB_SPECS@%$TCL_ONLY_LIB_SPECS%g
+s%@EXTRA_LIB_SPECS@%$EXTRA_LIB_SPECS%g
+s%@INCLUDES@%$INCLUDES%g
+s%@DEFINES@%$DEFINES%g
+s%@BLT_MAJOR_VERSION@%$BLT_MAJOR_VERSION%g
+s%@BLT_MINOR_VERSION@%$BLT_MINOR_VERSION%g
+s%@BLT_VERSION@%$BLT_VERSION%g
+s%@AUX_LIBS@%$AUX_LIBS%g
+s%@TCL_LIB_DIR@%$TCL_LIB_DIR%g
+s%@TCL_VERSION@%$TCL_VERSION%g
+s%@BLT_LIBRARY@%$BLT_LIBRARY%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile src/Makefile src/bltHash.h src/shared/Makefile man/Makefile library/Makefile demos/Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="src/bltConfig.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/blt/configure.in b/blt/configure.in
new file mode 100644
index 00000000000..ae52720ff7c
--- /dev/null
+++ b/blt/configure.in
@@ -0,0 +1,1385 @@
+define([AC_CACHE_LOAD], )dnl
+define([AC_CACHE_SAVE], )dnl
+AC_INIT(src/bltInit.c)
+AC_CONFIG_HEADER(src/bltConfig.h)
+AC_CONFIG_AUX_DIR(cf)
+AC_PREREQ(2.0)
+
+# -----------------------------------------------------------------------
+#
+# Handle command line options
+#
+# --with-tcl=DIR
+# --with-tk=DIR
+# --with-cc=CC
+# --with-cflags=flags This is probably for me only
+# --with-gnu-ld
+#
+# -----------------------------------------------------------------------
+
+INC_SPECS=""
+LIB_SPECS=""
+TCL_ONLY_LIB_SPECS=""
+loader_run_path=""
+DEFINES=""
+
+blt_with_tcl=""
+blt_with_tk=""
+blt_enable_jpeg="no"
+blt_with_cc=""
+blt_with_cflags="$CFLAGS"
+blt_with_gnu_ld="no"
+blt_with_tcl_includes=""
+blt_with_tk_includes=""
+blt_with_tcl_libraries=""
+blt_with_tk_libraries=""
+
+AC_ARG_WITH(tcl, [ --with-tcl=DIR Find tclConfig.sh in DIR],
+ blt_with_tcl=$withval)
+AC_ARG_WITH(tk, [ --with-tk=DIR Find tkConfig.sh in DIR],
+ blt_with_tk=$withval)
+AC_ARG_WITH(tclincls, [ --with-tclincls=DIR Find tcl.h in DIR],
+ blt_with_tcl_includes=$withval)
+AC_ARG_WITH(tkincls, [ --with-tkincls=DIR Find tk.h in DIR],
+ blt_with_tk_includes=$withval)
+AC_ARG_WITH(tcllibs, [ --with-tcllibs=DIR Find Tcl library in DIR],
+ blt_with_tcl_libraries=$withval)
+AC_ARG_WITH(tklibs, [ --with-tklibs=DIR Find Tk library in DIR],
+ blt_with_tk_libraries=$withval)
+AC_ARG_ENABLE(jpeg, [ --enable-jpeg=DIR Find JPEG headers and libraries in DIR], [
+ unset ac_cv_header_jpeglib_h
+ unset ac_cv_lib_jpeg ac_cv_lib_jpeg_jpeg_read_header
+ blt_enable_jpeg=$enableval ])
+AC_ARG_WITH(cc, [ --with-cc=CC Set C compiler to CC], [
+ blt_with_cc=$with_cc
+ unset ac_cv_prog_CC
+ unset ac_cv_prog_CPP ])
+AC_ARG_WITH(cflags, [ --with-cflags=FLAGS Set compiler flags to FLAGS],
+ blt_with_cflags="$with_cflags")
+AC_ARG_WITH(gnu_ld, [ --with-gnu-ld Use GNU linker],
+ blt_with_gnu_ld="yes")
+
+AC_CANONICAL_SYSTEM
+AC_PREFIX_PROGRAM(bltwish)
+
+# -----------------------------------------------------------------------
+#
+# Set a variable containing current working directory if /bin/sh
+# doesn't do it already.
+#
+# -----------------------------------------------------------------------
+
+PWD=`pwd`
+
+# -----------------------------------------------------------------------
+#
+# C compiler and debugging flags
+#
+# -----------------------------------------------------------------------
+
+BLT_ENV_CC=$CC
+
+#
+# CC search order
+#
+# 1. command line (--with-cc)
+# 2. environment variable ($CC)
+# 3. cached variable ($blt_cv_prog_cc)
+# 4. check for program (AC_PROG_CC)
+# 4. default to cc
+#
+
+AC_MSG_CHECKING([which C compiler])
+if test "x${blt_with_cc}" != "x" ; then
+ CC=${blt_with_cc}
+ unset ac_cv_prog_CPP
+ unset ac_cv_prog_CC
+elif test "x${BLT_ENV_CC}" != "x" ; then
+ unset ac_cv_prog_CPP
+ unset ac_cv_prog_CC
+elif test "x${blt_cv_prog_cc}" != "x" ; then
+ CC=${blt_cv_prog_cc}
+ unset ac_cv_prog_CC
+else
+ AC_PROG_CC
+fi
+if test "x${CC}" = "x" ; then
+ CC=cc
+fi
+AC_MSG_RESULT([$CC])
+
+unset blt_cv_prog_cc
+AC_CACHE_VAL(blt_cv_prog_cc, blt_cv_prog_cc=$CC)
+AC_SUBST(CC)
+AC_PROG_CPP
+if test "x${GCC}" != "x" ; then
+ blt_have_gcc="yes"
+else
+ AC_MSG_CHECKING([if C compiler is really gcc])
+ AC_EGREP_CPP(_cc_is_gcc_, [
+#ifdef __GNUC__
+ _cc_is_gcc_
+#endif
+], [blt_have_gcc=yes], [blt_have_gcc=no])
+ AC_MSG_RESULT([$blt_have_gcc])
+fi
+
+#
+# CFLAGS search order
+#
+# 1. command line (--with-cflags)
+# 2. cached variable ($blt_cv_prog_cflags)
+# 3. set to "-O6" if using gcc ($blt_have_gcc)
+# 4. otherwise, default to "-O"
+#
+AC_MSG_CHECKING([default compiler flags])
+if test "x${blt_with_cflags}" != "x" ; then
+ CFLAGS=${blt_with_cflags}
+elif test "x${blt_cv_prog_cflags}" != "x" ; then
+ CFLAGS=${blt_cv_prog_cflags}
+elif test "${blt_have_gcc}" = "yes" ; then
+ CFLAGS=-O6
+else
+ CFLAGS=-O
+fi
+
+AC_MSG_RESULT([$CFLAGS])
+unset blt_cv_prog_cflags
+AC_CACHE_VAL(blt_cv_prog_cflags, blt_cv_prog_cflags=$CFLAGS)
+AC_SUBST(CFLAGS)
+
+GCCFLAGS=""
+if test "${blt_have_gcc}" = "yes" ; then
+ GCCFLAGS="-Wall"
+ if test "${CFLAGS}" = "-g" ; then
+ GCCFLAGS="-Wshadow -Winline -Wpointer-arith ${GCCFLAGS}"
+ fi
+fi
+AC_SUBST(GCCFLAGS)
+
+
+# -----------------------------------------------------------------------
+#
+# Programs: Check for existence of ranlib and install programs
+#
+# -----------------------------------------------------------------------
+AC_PROG_AWK
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PROG_LN_S
+
+# -----------------------------------------------------------------------
+#
+# Libraries: Check for libraries used
+#
+# -----------------------------------------------------------------------
+AC_CHECK_LIB(socket, main)
+AC_CHECK_LIB(nsl, main)
+AC_CHECK_LIB(m, main)
+# -----------------------------------------------------------------------
+#
+# Headers: Check for header files used
+#
+# -----------------------------------------------------------------------
+
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIME
+
+AC_CHECK_HEADERS(inttypes.h)
+if test "${ac_cv_header_inttypes_h}" = "yes" ; then
+ HAVE_INTTYPES_H=1
+else
+ HAVE_INTTYPES_H=0
+fi
+AC_SUBST(HAVE_INTTYPES_H)
+
+AC_CHECK_HEADERS(limits.h sys/param.h)
+AC_CHECK_HEADERS(string.h ctype.h)
+AC_CHECK_HEADERS(errno.h float.h math.h ieeefp.h)
+AC_CHECK_HEADERS(sys/time.h waitflags.h sys/wait.h)
+AC_CHECK_HEADERS(malloc.h memory.h)
+AC_CHECK_HEADERS(setjmp.h)
+
+if test "${blt_enable_jpeg}" != "no" ; then
+ jpeg_save_CPPFLAGS=${CPPFLAGS}
+ CPPFLAGS=""
+ if test "${blt_enable_jpeg}" != "yes" ; then
+ CPPFLAGS="-I${blt_enable_jpeg}/include"
+ fi
+ AC_CHECK_HEADERS(jpeglib.h, [JPEG_INC_SPEC="${CPPFLAGS}"], [JPEG_INC_SPEC=""])
+ CPPFLAGS=${jpeg_save_CPPFLAGS}
+fi
+
+# Run this check after jpeglib.h because jpeglib.h sets HAVE_STDLIB_H
+AC_CHECK_HEADERS(stdlib.h unistd.h)
+
+# -----------------------------------------------------------------------
+#
+# Types: Check for existence of types of size_t and pid_t
+#
+# -----------------------------------------------------------------------
+AC_TYPE_SIZE_T
+AC_TYPE_PID_T
+
+AC_MSG_CHECKING([whether union wait is defined correctly])
+AC_CACHE_VAL(blt_cv_struct_wait_works,
+ AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/wait.h>], [
+ /*
+ * Check whether <sys/wait.h> defines the type "union wait"
+ * correctly. It's needed because of weirdness in HP-UX where
+ * "union wait" is defined in both the BSD and SYS-V environments.
+ * Checking the usability of WIFEXITED seems to do the trick.
+ */
+ union wait x;
+ WIFEXITED(x); /* Generates compiler error if WIFEXITED
+ * uses an int. */
+],
+ [blt_cv_struct_wait_works="yes"],
+ [blt_cv_struct_wait_works="no"]))
+
+if test "${blt_cv_struct_wait_works}" = "yes"; then
+ AC_DEFINE(HAVE_UNION_WAIT)
+fi
+AC_MSG_RESULT([$blt_cv_struct_wait_works])
+
+# -----------------------------------------------------------------------
+#
+# Compiler characteristics:
+# Check for existence of types of size_t and pid_t
+#
+# -----------------------------------------------------------------------
+
+AC_C_BIGENDIAN
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(void *)
+
+SIZEOF_LONG="${ac_cv_sizeof_long}"
+SIZEOF_LONG_LONG="${ac_cv_sizeof_long_long}"
+SIZEOF_VOID_P="${ac_cv_sizeof_void_p}"
+SIZEOF_INT="${ac_cv_sizeof_int}"
+AC_SUBST(SIZEOF_INT)
+AC_SUBST(SIZEOF_LONG)
+AC_SUBST(SIZEOF_LONG_LONG)
+AC_SUBST(SIZEOF_VOID_P)
+
+# -----------------------------------------------------------------------
+#
+# Library Functions: Check for strdup, drand48, and srand48.
+#
+# -----------------------------------------------------------------------
+
+AC_HAVE_FUNCS(strdup strcasecmp strncasecmp drand48 srand48 finite)
+
+# For HPUX it's a little more complicated to search for isfinite
+AC_MSG_CHECKING([for isfinite])
+AC_CACHE_VAL(blt_cv_have_isfinite,
+ AC_TRY_LINK([#include <math.h>], [
+double x = 1.0;
+if (isfinite(x)) {
+ return 0;
+}
+], [blt_cv_have_isfinite="yes"], [blt_cv_have_isfinite="no"]))
+
+if test "${blt_cv_have_isfinite}" = "yes"; then
+ AC_DEFINE(HAVE_ISFINITE)
+fi
+AC_MSG_RESULT([$blt_cv_have_isfinite])
+
+# -----------------------------------------------------------------------
+#
+# Check the smallest value such that 1.0 + x != 1.0.
+# For ANSI compilers this is DBL_EPSILON in float.h
+#
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([whether DBL_EPSILON is defined in float.h])
+AC_CACHE_VAL(blt_cv_found_dbl_epsilon,
+ AC_EGREP_CPP(yes,
+ [
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef DBL_EPSILON
+ yes
+#endif
+], blt_cv_found_dbl_epsilon=yes, blt_cv_found_dbl_epsilon=no)
+)
+AC_MSG_RESULT([${blt_cv_found_dbl_epsilon}])
+
+if test "${blt_cv_found_dbl_epsilon}" = "no" ; then
+ AC_CACHE_VAL(blt_cv_dbl_epsilon,
+ old_flags="$CFLAGS"
+ CFLAGS="-g -lm"
+ AC_MSG_CHECKING([whether DBL_EPSILON can be computed])
+ AC_TRY_RUN_WITH_OUTPUT(blt_cv_dbl_epsilon, [
+main () {
+ double e, u;
+ /*
+ * Check the smallest value such that 1.0 + x != 1.0.
+ * For ANSI compilers this is DBL_EPSILON in float.h
+ */
+ u = 1.0;
+ for(;;) {
+ u *= 0.5;
+ if ((1.0 + u) == 1.0) {
+ break;
+ }
+ }
+ e = u * 2.0;
+ printf("%.17e\n", e);
+ exit(0);
+}])
+ CFLAGS="$old_flags"
+ AC_DEFINE_UNQUOTED(BLT_DBL_EPSILON, ${blt_cv_dbl_epsilon})
+ AC_MSG_RESULT([${blt_cv_dbl_epsilon}])
+)
+fi
+
+
+AC_MSG_CHECKING([whether declaration is needed for strdup])
+AC_CACHE_VAL(blt_cv_nedd_decl_strdup,
+ AC_EGREP_CPP(strdup, [
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+], [blt_cv_need_decl_strdup=no], [blt_cv_need_decl_strdup=yes]))
+
+if test "${blt_cv_need_decl_strdup}" = "yes"; then
+ AC_DEFINE(NEED_DECL_STRDUP)
+fi
+AC_MSG_RESULT([$blt_cv_need_decl_strdup])
+
+AC_MSG_CHECKING([whether declaration is needed for drand48])
+AC_CACHE_VAL(blt_cv_need_decl_drand48,
+ AC_EGREP_CPP(drand48, [
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+], [blt_cv_need_decl_drand48=no], [blt_cv_need_decl_drand48=yes]))
+
+if test "${blt_cv_need_decl_drand48}" = "yes"; then
+ AC_DEFINE(NEED_DECL_DRAND48)
+fi
+AC_MSG_RESULT([$blt_cv_need_decl_drand48])
+
+AC_MSG_CHECKING([whether declaration is needed for srand48])
+AC_CACHE_VAL(blt_cv_need_decl_srand48,
+ AC_EGREP_CPP(srand48, [
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+], [blt_cv_need_decl_srand48=no], [blt_cv_need_decl_srand48=yes]))
+
+if test "${blt_cv_need_decl_srand48}" = "yes"; then
+ AC_DEFINE(NEED_DECL_SRAND48)
+fi
+AC_MSG_RESULT([$blt_cv_need_decl_srand48])
+
+AC_MSG_CHECKING([whether declaration is needed for j1])
+AC_CACHE_VAL(blt_cv_need_decl_j1,
+ AC_EGREP_CPP(j1, [
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+], [blt_cv_need_decl_j1=no], [blt_cv_need_decl_j1=yes]))
+
+if test "${blt_cv_need_decl_j1}" = "yes"; then
+ AC_DEFINE(NEED_DECL_J1)
+fi
+AC_MSG_RESULT([$blt_cv_need_decl_j1])
+
+# -----------------------------------------------------------------------
+#
+# System services: X, Tcl, Tk
+#
+# -----------------------------------------------------------------------
+AC_PATH_X
+
+# -----------------------------------------------------------------------
+#
+# Find the Tcl build configuration file "tclConfig.sh"
+#
+# -----------------------------------------------------------------------
+
+AC_MSG_CHECKING([for tclConfig.sh])
+tcl_config_sh=""
+if test "x$blt_with_tcl" != "x" ; then
+ #
+ # Verify that a tclConfig.sh file exists in the directory specified
+ # by --with-tcl.
+ #
+ for dir in \
+ $blt_with_tcl
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ elif test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+else
+ #
+ # Otherwise, search for Tcl configuration file.
+ #
+
+
+ # 1. Search previously named locations.
+
+ for dir in \
+ $prefix \
+ $exec_prefix \
+ $blt_cv_tcl_lib
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ elif test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+
+ # 2. Search source directories.
+
+ if test "x$tcl_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr ../tcl[[7-9]].[[0-9]]* 2>/dev/null` \
+ ../tcl \
+ `ls -dr ../../tcl[[7-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../../tcl[[7-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl
+ do
+ if test -r "$dir/unix/tclConfig.sh" ; then
+ tcl_config_sh="$dir/unix/tclConfig.sh"
+ break
+ fi
+ done
+ fi
+
+ # 3. Search standard locations.
+
+ if test "x$tcl_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr /usr/local/tcl/tcl[[7-9]].[[0-9]]* 2>/dev/null` \
+ /usr/local/tcl \
+ /usr/local \
+ /usr
+ do
+ if test -r "$dir/tclConfig.sh" ; then
+ tcl_config_sh="$dir/tclConfig.sh"
+ break
+ elif test -r "$dir/lib/tclConfig.sh" ; then
+ tcl_config_sh="$dir/lib/tclConfig.sh"
+ break
+ fi
+ done
+ fi
+fi
+
+AC_MSG_RESULT([${tcl_config_sh}])
+
+if test "x$tcl_config_sh" = "x" ; then
+ echo "can't find Tcl configuration script \"tclConfig.sh\""
+ exit 1
+fi
+
+# -----------------------------------------------------------------------
+#
+# Find the Tk build configuration file "tkConfig.sh"
+#
+# -----------------------------------------------------------------------
+
+AC_MSG_CHECKING([for tkConfig.sh])
+tk_config_sh=""
+if test "x$blt_with_tk" != "x" -o "x$blt_with_tcl" != "x"; then
+ #
+ # Verify that a tkConfig.sh file exists in the directory specified
+ # by --with-tcl or --with-tk.
+ #
+ for dir in \
+ $blt_with_tk \
+ $blt_with_tcl
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ elif test -r "$dir/unix/tkConfig.sh" ; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+else
+ #
+ # Search for Tk configuration file.
+ #
+
+ #
+ # 1. Search previously named locations.
+ #
+ for dir in \
+ $prefix \
+ $exec_prefix \
+ $blt_cv_tk_lib \
+ $blt_cv_tcl_lib
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ elif test -r "$dir/unix/tkConfig.sh" ; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+ #
+ # 2. Search source directories.
+ #
+ if test "x$tk_config_sh" = "x" ; then
+ for dir in \
+ ../tcl \
+ `ls -dr ../tk[[4-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tk[[4-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tk[[4-9]].[[0-9]]* 2>/dev/null`
+ do
+ if test -r "$dir/unix/tkConfig.sh"; then
+ tk_config_sh="$dir/unix/tkConfig.sh"
+ break
+ fi
+ done
+ fi
+ #
+ # 3. Search standard locations.
+ #
+ if test "x$tk_config_sh" = "x" ; then
+ for dir in \
+ `ls -dr /usr/local/tcl/tcl[[7-9]].[[0-9]]* 2>/dev/null` \
+ /usr/local/tcl \
+ /usr/local \
+ ${x_libraries} \
+ /usr
+ do
+ if test -r "$dir/tkConfig.sh" ; then
+ tk_config_sh="$dir/tkConfig.sh"
+ break
+ elif test -r "$dir/lib/tkConfig.sh" ; then
+ tk_config_sh="$dir/lib/tkConfig.sh"
+ break
+ fi
+ done
+ fi
+fi
+AC_MSG_RESULT([${tk_config_sh}])
+
+if test "x$tk_config_sh" = "x" ; then
+ echo "can't find Tk configuration script \"tkConfig.sh\""
+ exit 1
+fi
+
+# -----------------------------------------------------------------------
+#
+# Source in the Tcl/Tk configuration scripts.
+#
+# -----------------------------------------------------------------------
+
+. $tcl_config_sh
+. $tk_config_sh
+
+TCL_INC_DIR=""
+TK_INC_DIR=""
+
+if test "x${blt_with_tcl_includes}" != "x" ; then
+ if test -r "${blt_with_tcl_includes}/tcl.h" ; then
+ TCL_INC_DIR=${blt_with_tcl_includes}
+ else
+ echo "Can't find tcl.h in \"${blt_with_tcl_includes}\""
+ exit 1
+ fi
+else
+ for dir in \
+ ${TCL_PREFIX}/include/tcl${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION} \
+ ${TCL_PREFIX}/include \
+ ${TCL_SRC_DIR}/generic
+ do
+ if test -r "$dir/tcl.h" ; then
+ TCL_INC_DIR=$dir
+ break
+ fi
+ done
+ if test "x${TCL_INC_DIR}" = "x" ; then
+ echo "Can't find tcl.h header file"
+ exit 1
+ fi
+fi
+
+if test "x${blt_with_tk_includes}" != "x" ; then
+ if test -r "${blt_with_tk_includes}/tk.h" ; then
+ TK_INC_DIR=${blt_with_tk_includes}
+ else
+ echo "Can't find tk.h in \"${blt_with_tk_includes}\""
+ exit 1
+ fi
+else
+ for dir in \
+ ${TK_PREFIX}/include/tk${TK_MAJOR_VERSION}.${TK_MINOR_VERSION} \
+ ${TK_PREFIX}/include \
+ ${TK_SRC_DIR}/generic \
+ ${TCL_INC_DIR}
+ do
+ if test -r "$dir/tk.h" ; then
+ TK_INC_DIR=$dir
+ break
+ fi
+ done
+ if test "x${TK_INC_DIR}" = "x" ; then
+ echo "Can't find tk.h header file"
+ exit 1
+ fi
+fi
+
+case $target in
+ *-sunos4*|*-*-netbsd|NetBSD-*|FreeBSD-*|OpenBSD-*)
+ TCL_LIB_NAME="tcl${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}"
+ TK_LIB_NAME="tk${TK_MAJOR_VERSION}${TK_MINOR_VERSION}"
+ ;;
+ *)
+ TCL_LIB_NAME="tcl${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}"
+ TK_LIB_NAME="tk${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}"
+ ;;
+esac
+
+TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
+TK_LIB_SPEC="-l${TK_LIB_NAME}"
+
+case $target in
+ *-hpux*)
+ SHLIB_SUFFIX="sl"
+ ;;
+ *)
+ SHLIB_SUFFIX="so"
+ ;;
+esac
+
+TCL_LIB_DIR="${TCL_SRC_DIR}/unix"
+TK_LIB_DIR="${TK_SRC_DIR}/unix"
+
+if test "x${blt_with_tcl_libraries}" != "x" ; then
+ for libname in \
+ "${blt_with_tcl_libraries}/lib${TCL_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${blt_with_tcl_libraries}/lib${TCL_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TCL_LIB_DIR="${blt_with_tcl_libraries}"
+ break
+ fi
+ done
+ if test "x${TCL_LIB_DIR}" = "x" ; then
+ echo "Can't find tcl library in \"${blt_with_tcl_libraries}\""
+ exit 1
+ fi
+else
+ for libname in \
+ "${TCL_EXEC_PREFIX}/lib/lib${TCL_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${TCL_EXEC_PREFIX}/lib/lib${TCL_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TCL_LIB_DIR="${TCL_EXEC_PREFIX}/lib"
+ break
+ fi
+ done
+ if test "x${TCL_LIB_DIR}" = "x" ; then
+ echo "Can't find tcl library"
+ exit 1
+ fi
+fi
+
+if test "x${blt_with_tk_libraries}" != "x" ; then
+ for libname in \
+ "${blt_with_tk_libraries}/lib${TK_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${blt_with_tk_libraries}/lib${TK_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TK_LIB_DIR="${blt_with_tk_libraries}"
+ break
+ fi
+ done
+ if test "x${TK_LIB_DIR}" = "x" ; then
+ echo "Can't find tk library in \"${blt_with_tk_libraries}\""
+ exit 1
+ fi
+else
+ for libname in \
+ "${TK_EXEC_PREFIX}/lib/lib${TK_LIB_NAME}.${SHLIB_SUFFIX}" \
+ "${TK_EXEC_PREFIX}/lib/lib${TK_LIB_NAME}.a"
+ do
+ if test -r "$libname" ; then
+ TK_LIB_DIR="${TK_EXEC_PREFIX}/lib"
+ break
+ fi
+ done
+ if test "x${TK_LIB_DIR}" = "x" ; then
+ echo "Can't find tk library"
+ exit 1
+ fi
+fi
+
+# -----------------------------------------------------------------------
+#
+# Include files
+#
+# Append to INC_SPECS the various include files specifications
+# (built fromt the include directory information).
+#
+# -----------------------------------------------------------------------
+
+# JPEG include files
+if test "${blt_enable_jpeg}" != "no" ; then
+ if test "x${JPEG_INC_SPEC}" != "x" ; then
+ INC_SPECS="${INC_SPECS} ${JPEG_INC_SPEC}"
+ fi
+fi
+
+# X11 include files
+if test "x${x_includes}" != "x" -a \
+ "${x_includes}" != "NONE" -a \
+ "${x_includes}" != "/usr/include" -a \
+ "${x_includes}" != "${TK_INC_DIR}" -a \
+ "${x_includes}" != "${TCL_INC_DIR}" ; then
+ INC_SPECS="${INC_SPECS} -I${x_includes}"
+fi
+
+# Tk include files
+if test "${TK_INC_DIR}" != "/usr/include" ; then
+ INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
+fi
+
+# Tcl include files
+#
+# Add the include directory specification only if the Tcl
+# headers reside in a different directory from Tk's.
+if test "${TCL_INC_DIR}" != "/usr/include" -a \
+ "${TCL_INC_DIR}" != "${TK_INC_DIR}" ; then
+ INC_SPECS="${INC_SPECS} -I${TCL_INC_DIR}"
+fi
+
+# -----------------------------------------------------------------------
+#
+# Libraries
+#
+# Append to LIB the various library specifications
+# (built from the library directory information).
+#
+# -----------------------------------------------------------------------
+
+# Tk libraries
+if test "${TK_LIB_DIR}" = "/usr/lib" ; then
+ LIB_SPECS="${LIB_SPECS} ${TK_LIB_SPEC}"
+else
+ LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_LIB_SPEC}"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${TK_LIB_DIR}"
+ else
+ loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
+ fi
+fi
+
+# Tcl libraries
+if test "${TCL_LIB_DIR}" = "/usr/lib" -o \
+ "${TCL_LIB_DIR}" = "${TK_LIB_DIR}" ; then
+ LIB_SPECS="${LIB_SPECS} ${TCL_LIB_SPEC}"
+else
+ LIB_SPECS="${LIB_SPECS} -L${TCL_LIB_DIR} ${TCL_LIB_SPEC}"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${TCL_LIB_DIR}"
+ else
+ loader_run_path="${TCL_LIB_DIR}:${loader_run_path}"
+ fi
+fi
+
+if test "${TCL_LIB_DIR}" = "/usr/lib" ; then
+ TCL_ONLY_LIB_SPECS="${TCL_LIB_SPEC}"
+else
+ TCL_ONLY_LIB_SPECS="-L${TCL_LIB_DIR} ${TCL_LIB_SPEC}"
+fi
+
+
+# Collect the libraries for AIX that aren't using stubs.
+aix_lib_specs=""
+
+# X11 library
+if test "x${x_libraries}" = "x" -o \
+ "x${x_libraries}" = "NONE" -o \
+ "${x_libraries}" = "/usr/lib" -o \
+ "${x_libraries}" = "${TK_LIB_DIR}" -o \
+ "${x_libraries}" = "${TCL_LIB_DIR}" ; then
+ LIB_SPECS="${LIB_SPECS} -lX11"
+ aix_lib_specs="${aix_lib_specs} -lX11"
+else
+ LIB_SPECS="${LIB_SPECS} -L${x_libraries} -lX11"
+ aix_lib_specs="${aix_lib_specs} -L${x_libraries} -lX11"
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${x_libraries}"
+ else
+ loader_run_path="${loader_run_path}:${x_libraries}"
+ fi
+fi
+
+# JPEG library
+if test "${blt_enable_jpeg}" != "no" ; then
+ jpeg_save_LDFlags="${LDFLAGS}"
+ JPEG_LIB_SPEC="-ljpeg"
+ JPEG_LIB_DIR=""
+ if test "${blt_enable_jpeg}" != "yes" ; then
+ JPEG_LIB_DIR="${blt_enable_jpeg}/lib"
+ JPEG_LIB_SPEC="-L${JPEG_LIB_DIR} ${JPEG_LIB_SPEC}"
+ LDFLAGS="-L${JPEG_LIB_DIR} ${LDFLAGS}"
+ fi
+ AC_CHECK_LIB(jpeg, jpeg_read_header, [found=yes], [found=no], )
+ if test "${found}" = "yes" ; then
+ LIB_SPECS="${LIB_SPECS} ${JPEG_LIB_SPEC}"
+ aix_lib_specs="${aix_lib_specs} ${JPEG_LIB_SPEC}"
+ if test "x${JPEG_LIB_DIR}" != "x" ; then
+ loader_run_path="${loader_run_path}:${JPEG_LIB_DIR}"
+ fi
+ fi
+ LDFLAGS=${jpeg_save_LDFlags}
+fi
+
+save_libs=$LIBS
+LIBS="$LIB_SPECS $LIBS"
+AC_CHECK_FUNCS(XExtendedMaxRequestSize)
+LIBS=$save_libs
+
+# -----------------------------------------------------------------------
+#
+# Set up a new default prefix to installation path. The ways
+# the prefix can be set and their precedence are as follows:
+#
+# 1. --prefix option given to ./configure. (prefix != NONE)
+# 2. use previously configured Tk prefix
+#
+# -----------------------------------------------------------------------
+
+if test "$prefix" = "NONE" ; then
+ prefix=${TCL_PREFIX}
+fi
+
+if test "$exec_prefix" = "NONE" ; then
+ exec_prefix=${TCL_EXEC_PREFIX}
+fi
+
+# -------------------------------------------------------------------------
+#
+# Extract the BLT version number for the blt.h header
+#
+# -------------------------------------------------------------------------
+AC_MSG_CHECKING([BLT_MAJOR_VERSION])
+AC_CACHE_VAL(blt_cv_major_version,
+AC_GREP_SYMBOL(blt_cv_major_version, BLT_MAJOR_VERSION, ${srcdir}/src/blt.h)
+)
+BLT_MAJOR_VERSION=${blt_cv_major_version}
+AC_MSG_RESULT([$blt_cv_major_version])
+
+AC_MSG_CHECKING([BLT_MINOR_VERSION])
+AC_CACHE_VAL(blt_cv_minor_version,
+AC_GREP_SYMBOL(blt_cv_minor_version, BLT_MINOR_VERSION, ${srcdir}/src/blt.h)
+)
+AC_MSG_RESULT([$blt_cv_minor_version])
+BLT_MINOR_VERSION=${blt_cv_minor_version}
+
+BLT_VERSION=${BLT_MAJOR_VERSION}.${BLT_MINOR_VERSION}
+
+# Add BLT to the run path
+libdir=${exec_prefix}/lib
+
+if test "x${libdir}" != "x" -a \
+ "${libdir}" != "/usr/lib" -a \
+ "${libdir}" != "${x_libraries}" -a \
+ "${libdir}" != "${TK_LIB_DIR}" -a \
+ "${libdir}" != "${TCL_LIB_DIR}" ; then
+ if test "x${loader_run_path}" = "x" ; then
+ loader_run_path="${libdir}"
+ else
+ loader_run_path="${libdir}:${loader_run_path}"
+ fi
+fi
+
+aix_lib_specs="${aix_lib_specs} ${LIBS}"
+LIB_SPECS="${LIB_SPECS} ${LIBS}"
+TCL_ONLY_LIB_SPECS="${TCL_ONLY_LIB_SPECS} ${LIBS}"
+
+# -------------------------------------------------------------------------
+#
+# Extract the Tcl version number for the tcl.h header
+#
+# -------------------------------------------------------------------------
+AC_MSG_CHECKING([TCL_VERSION in tcl.h])
+AC_CACHE_VAL(blt_cv_tcl_h_version,
+AC_GREP_SYMBOL(blt_cv_tcl_h_version, [TCL_VERSION], ${TCL_INC_DIR}/tcl.h)
+)
+eval TCL_H_VERSION=${blt_cv_tcl_h_version}
+AC_MSG_RESULT([$TCL_H_VERSION])
+if test "${TCL_H_VERSION}" != "${TCL_VERSION}" ; then
+ echo "Error: Tcl version mismatch. "
+ echo " ${TCL_VERSION} ${tcl_config_sh}"
+ echo " ${TCL_H_VERSION} ${TCL_INC_DIR}/tcl.h"
+ exit 1
+fi
+AC_MSG_CHECKING([TK_VERSION in tk.h])
+AC_CACHE_VAL(blt_cv_tk_h_version,
+AC_GREP_SYMBOL(blt_cv_tk_h_version, [TK_VERSION], ${TK_INC_DIR}/tk.h)
+)
+eval TK_H_VERSION=${blt_cv_tk_h_version}
+AC_MSG_RESULT([$TK_H_VERSION])
+if test "${TK_H_VERSION}" != "${TK_VERSION}" ; then
+ echo "Error: Tk version mismatch."
+ echo " ${TK_VERSION} ${tk_config_sh}"
+ echo " ${TK_H_VERSION} ${TK_INC_DIR}/tk.h"
+ exit 1
+fi
+
+if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then
+ :
+elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
+ :
+elif test "$TCL_VERSION" = "$TK_VERSION" ; then
+ :
+else
+ echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
+ exit 1
+fi
+
+#--------------------------------------------------------------------
+#
+# Check if we can generate shared libraries on this system. Set flags
+# to generate shared libraries for systems that we know about. Start
+# with the values found in tclConfig.sh, make changes as we know about
+# the different systems.
+#
+#--------------------------------------------------------------------
+
+LIB_BASE_NAME=libBLT
+
+# Initialize shared library build variables
+
+SHLIB_CFLAGS="$TCL_SHLIB_CFLAGS"
+SHLIB_LD="$TCL_SHLIB_LD"
+SHLIB_LD_FLAGS="$TCL_LD_FLAGS"
+SHLIB_RUNPATH="$TCL_LD_SEARCH_FLAGS"
+
+SHLIB_SUFFIX=".so"
+SHLIB_TARGET=""
+SHLIB_CFLAGS=""
+SHLIB_LIB_SPECS="${JPEG_LIB_SPEC}"
+SHLIB_TCL_ONLY_LIB_SPECS="${TCL_ONLY_LIB_SPECS}"
+SHLIB_TCL_ONLY_LIB_SPECS=""
+LDFLAGS=""
+LD_RUN_PATH=""
+EXTRA_LIB_SPECS=""
+
+build_shared="yes"
+library_name=libBLT${BLT_MAJOR_VERSION}${BLT_MINOR_VERSION}
+
+case $target in
+ *-aix4.[[2-9]]*)
+ # No Position-Independent flags needed
+ SHLIB_CFLAGS=""
+
+ # Use the installed export file or the one found in the source directory.
+
+ if test -r "${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp" ; then
+ tcl_exp="${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp"
+ else
+ tcl_exp="${TCL_SRC_DIR}/unix/lib.exp"
+ fi
+ if test -r "${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp" ; then
+ tk_exp="${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp"
+ else
+ tk_exp="${TK_SRC_DIR}/unix/lib.exp"
+ fi
+
+ full_src_path=`cd ${srcdir}; pwd`
+
+ # Use shell-script to link shared library
+ SHLIB_LD="${full_src_path}/cf/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry -bI:${tk_exp} -bI:${tcl_exp}"
+
+ SHLIB_LIB_SPECS="${aix_lib_specs} -lc"
+
+ LDFLAGS="-L${loader_run_path}"
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-aix*)
+ # No Position-Independent flags needed
+ SHLIB_CFLAGS=""
+
+ # Use the installed export file or the one found in the source directory.
+
+ if test -r "${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp" ; then
+ tcl_exp="${TCL_LIB_DIR}/lib${TCL_LIB_NAME}.exp"
+ else
+ tcl_exp="${TCL_SRC_DIR}/unix/lib.exp"
+ fi
+ if test -r "${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp" ; then
+ tk_exp="${TK_LIB_DIR}/lib${TK_LIB_NAME}.exp"
+ else
+ tk_exp="${TK_SRC_DIR}/unix/lib.exp"
+ fi
+
+ full_src_path=`cd ${srcdir}/cf; pwd`
+
+ # Use shell-script to link shared library
+
+ SHLIB_LD="${full_src_path}/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry -bI:${tk_exp} -bI:${tcl_exp}"
+
+ SHLIB_LIB_SPECS="${aix_lib_specs} -lc"
+
+ LDFLAGS="-L${loader_run_path}"
+ EXTRA_LIB_SPECS="-lld"
+ ;;
+
+ *-bsdi2*|*-bsdi3*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="shlicc"
+ SHLIB_LD_FLAGS="-r"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-bsdi4*)
+ SHLIB_CFLAGS="-export-dynamic -fPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS='-shared -Wl,-E -Wl,-soname,$@'
+ ;;
+
+ *-dgux*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-hpux*)
+ if test "$blt_have_gcc" = "no" ; then
+ DEFINES="$DEFINES -D_HPUX_SOURCE"
+ fi
+ AC_CHECK_LIB(dld, shl_load, [found=yes], [found=no])
+ if test "${found}" = "yes" ; then
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-b -E -n +s +b,${loader_run_path}:."
+ SHLIB_SUFFIX=".sl"
+
+ # The run path is included in both LDFLAGS and SHLIB_LD_FLAGS
+ # because SHLIB_LD is ld and LD is cc/gcc.
+
+ LDFLAGS="-Wl,-E -Wl,+s,+b,${loader_run_path}:."
+ EXTRA_LIB_SPECS="-ldld"
+ fi
+ ;;
+
+ *-irix64-6.5*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-32 -shared -rdata_shared"
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ ;;
+
+ *-irix-[56].*|*-irix64-*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-shared -rdata_shared"
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ LDFLAGS=""
+ if test "$blt_have_gcc" = "yes" ; then
+ SHLIB_CFLAGS="-mabi=n32 $SHLIB_CFLAGS"
+ SHLIB_LD_FLAGS="-mabi=n32 $SHLIB_LD_FLAGS"
+ LDFLAGS="-mabi=n32 $LDFLAGS"
+ else
+ CFLAGS="-n32 $CFLAGS"
+ LDFLAGS="-n32 $LDFLAGS"
+ fi
+ ;;
+
+ *-linux*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS='-rdynamic -shared -Wl,-E -Wl,-soname,$@'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+
+ LDFLAGS=""
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-mp-ras-02*)
+ SHLIB_CFLAGS="-G -K PIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS=""
+ ;;
+
+ *-mp-ras-*)
+ SHLIB_CFLAGS="-G -K PIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS="-Wl,-Bexport"
+ ;;
+
+ *-ncr-sysv4-*2*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-ncr-sysv4*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G -Wl,-Bexport"
+
+ LDFLAGS="-Wl,-Bexport"
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-netbsd*|*-freebsd*|*-openbsd*)
+ # Not available on all versions: check for include file.
+ AC_CHECK_HEADER(dlfcn.h, test_ok=yes, test_ok=no)
+ if test "$test_ok" = yes; then
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-Bshareable -x"
+ fi
+ ;;
+
+ *-nextstep*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-nostdlib -r"
+ ;;
+
+ *-osf1-1.[012]*)
+ # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1
+
+ SHLIB_CFLAGS=""
+
+ # Warning: Ugly Makefile Hack
+ # Make package name same as library name
+
+ SHLIB_LD='ld -R -export $@:'
+ ;;
+
+ *-osf1-1.*)
+ # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2
+
+ SHLIB_CFLAGS="-fpic"
+ SHLIB_LD="ld -shared"
+ ;;
+
+ *-osf1V*)
+ # Digital OSF/1
+
+ SHLIB_CFLAGS=""
+ SHLIB_LD='ld'
+ SHLIB_LD_FLAGS='-shared -expect_unresolved "*"'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ LDFLAGS=""
+ ;;
+
+ *-sco*)
+ # Note, dlopen is available only on SCO 3.2.5 and greater. However,
+ # this test works, since "uname -s" was non-standard in 3.2.4 and
+ # below.
+
+ SHLIB_CFLAGS="-Kpic -belf"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-G"
+ LDFLAGS="-belf -Wl,-Bexport"
+ ;;
+
+ *-sni-sysv*)
+
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-sunos4*)
+
+ SHLIB_CFLAGS="-PIC"
+ SHLIB_LD="ld"
+ SHLIB_LD_FLAGS="-assert pure-text"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-solaris2*)
+
+ SHLIB_CFLAGS="-KPIC"
+ if test "${blt_with_gnu_ld}" = "yes" -a "$blt_have_gcc" = "yes" ; then
+ SHLIB_LD="gcc"
+ SHLIB_LD_FLAGS='-rdynamic -shared -Wl,-E -Wl,-soname,$@'
+ LD_RUN_PATH="-Wl,-rpath,${loader_run_path}"
+ else
+ SHLIB_LD="/usr/ccs/bin/ld"
+ SHLIB_LD_FLAGS="-G -z text"
+ LD_RUN_PATH="-R ${loader_run_path}"
+ fi
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-mips-dde-sysv*)
+
+ SHLIB_CFLAGS="-KPIC"
+ SHLIB_LD="cc"
+ SHLIB_LD_FLAGS="-G"
+
+ EXTRA_LIB_SPECS="-ldl"
+ ;;
+
+ *-pc-sysv4* | *-unixware-5*)
+ SHLIB_CFLAGS="-G -KPIC"
+ SHLIB_LD="${CC}"
+ SHLIB_LD_FLAGS=" -Wl,-Bexport"
+ ;;
+
+ *)
+ build_shared="no"
+ ;;
+
+esac
+
+# If we're running gcc, then set SHLIB_CFLAGS flags for compiling
+# shared libraries for gcc, instead of those of the vendor's
+# compiler.
+
+
+if test "$blt_have_gcc" = "yes" ; then
+ SHLIB_CFLAGS="-fPIC"
+fi
+
+# We can't back link against static versions of Tcl/Tk.
+# If # ${TCL_SHARED_BUILD} can't be found or isn't "1", assume that
+# shared libraies weren't built.
+
+if test "${TCL_SHARED_BUILD}" != "1" ; then
+ SHLIB_LIB_SPECS=""
+fi
+
+if test "${build_shared}" = "yes" ; then
+ SHLIB_TARGET="build_shared"
+ AC_SUBST(SHLIB_CFLAGS)
+ AC_SUBST(SHLIB_TARGET)
+ AC_SUBST(SHLIB_LD)
+ AC_SUBST(SHLIB_LD_FLAGS)
+ AC_SUBST(SHLIB_LIB_SPECS)
+ AC_SUBST(SHLIB_TCL_ONLY_LIB_SPECS)
+ AC_SUBST(SHLIB_SUFFIX)
+fi
+
+AC_SUBST(LDFLAGS)
+AC_SUBST(LD_RUN_PATH)
+
+LIBS=${LIB_SPECS}
+AC_SUBST(LIB_SPECS)
+AC_SUBST(TCL_ONLY_LIB_SPECS)
+AC_SUBST(EXTRA_LIB_SPECS)
+
+INCLUDES=${INC_SPECS}
+AC_SUBST(INCLUDES)
+AC_SUBST(DEFINES)
+AC_SUBST(BLT_MAJOR_VERSION)
+AC_SUBST(BLT_MINOR_VERSION)
+AC_SUBST(BLT_VERSION)
+AC_SUBST(AUX_LIBS)
+AC_SUBST(TCL_LIB_DIR)
+AC_SUBST(TCL_VERSION)
+
+#--------------------------------------------------------------------
+# Propagate prefix argument as installation directory.
+#--------------------------------------------------------------------
+
+BLT_LIBRARY="${prefix}/lib/blt${BLT_VERSION}"
+AC_SUBST(BLT_LIBRARY)
+
+#--------------------------------------------------------------------
+# Print out some of the more important settings
+#--------------------------------------------------------------------
+echo ""
+echo "Configuration results:"
+echo ""
+echo " tcl.h found in $TCL_INC_DIR"
+echo " tk.h found in $TK_INC_DIR"
+echo " X11/Xlib.h found in $x_includes"
+echo " lib${TCL_LIB_NAME} found in $TCL_LIB_DIR"
+echo " lib${TK_LIB_NAME} found in $TK_LIB_DIR"
+echo " libX11 found in $x_libraries"
+echo ""
+echo "Directories where BLT is to be installed:"
+echo ""
+echo " \"\$prefix\" is $prefix"
+echo " \"\$exec_prefix\" is $exec_prefix"
+echo ""
+echo " bltwish to be installed in $bindir"
+echo " libBLT.a to be installed in $libdir"
+echo " scripts to be installed in $BLT_LIBRARY"
+echo " manual pages to be installed in $mandir"
+echo ""
+
+#--------------------------------------------------------------------
+#
+# Generate the following Makefiles
+#
+# ./Makefile
+# ./src/Makefile
+# ./src/shared/Makefile
+# ./man/Makefile
+# ./library/Makefile
+# ./demos/Makefile
+#
+#--------------------------------------------------------------------
+AC_OUTPUT(Makefile src/Makefile src/bltHash.h src/shared/Makefile man/Makefile library/Makefile demos/Makefile)
diff --git a/blt/demos/Makefile.cyg b/blt/demos/Makefile.cyg
new file mode 100644
index 00000000000..d9dbe399365
--- /dev/null
+++ b/blt/demos/Makefile.cyg
@@ -0,0 +1,78 @@
+# ------------------------------------------------------------------------
+# Makefile for demos
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+destdir = $(scriptdir)/demos
+srcdir = .
+
+SHELL = bash
+RM = rm -rf
+INSTALL = install -m 0755
+INSTALL_DATA = install -m 0644
+
+instdirs = $(prefix) $(exec_prefix) $(libdir) $(scriptdir) \
+ $(destdir) \
+ $(destdir)/bitmaps \
+ $(destdir)/bitmaps/hand \
+ $(destdir)/bitmaps/fish \
+ $(destdir)/images \
+ $(destdir)/scripts
+
+demos = barchart1.tcl barchart2.tcl barchart3.tcl barchart4.tcl \
+ barchart5.tcl \
+ bgexec1.tcl bgexec2.tcl bgexec3.tcl bgexec4.tcl \
+ bitmap.tcl \
+ busy1.tcl \
+ dragdrop1.tcl dragdrop2.tcl \
+ eps.tcl \
+ graph1.tcl graph2.tcl graph3.tcl graph4.tcl graph5.tcl \
+ graph6.tcl graph7.tcl \
+ hierbox1.tcl hierbox2.tcl hierbox3.tcl hierbox4.tcl \
+ hiertable1.tcl hiertable2.tcl \
+ htext1.tcl htext.txt \
+ spline.tcl stripchart1.tcl \
+ tabset1.tcl tabset2.tcl tabset3.tcl tabset4.tcl \
+ tabnotebook1.tcl tabnotebook2.tcl tabnotebook3.tcl \
+ treeview1.tcl \
+ winop1.tcl winop2.tcl
+
+all:
+
+install: inst-dirs inst-bitmaps inst-images inst-scripts
+
+inst-scripts:
+ for i in $(srcdir)/scripts/*.tcl ; do \
+ $(INSTALL) $$i $(destdir)/scripts ; \
+ done
+ for i in $(demos) ; do \
+ $(INSTALL) $(srcdir)/$$i $(destdir)/$$i ; \
+ done
+
+inst-bitmaps:
+ for i in $(srcdir)/bitmaps/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps ; \
+ done
+ for i in $(srcdir)/bitmaps/hand/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps/hand ; \
+ done
+ for i in $(srcdir)/bitmaps/fish/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps/fish ; \
+ done
+
+inst-images:
+ for i in $(srcdir)/images/*.gif $(srcdir)/images/*.ps ; do \
+ $(INSTALL_DATA) $$i $(destdir)/images ; \
+ done
+
+inst-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else mkdir "$$i" ; fi ; \
+ done
+
+clean:
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) *.ps Makefile
diff --git a/blt/demos/Makefile.in b/blt/demos/Makefile.in
new file mode 100644
index 00000000000..75b3f16b901
--- /dev/null
+++ b/blt/demos/Makefile.in
@@ -0,0 +1,90 @@
+# ------------------------------------------------------------------------
+# Makefile for demos
+# ------------------------------------------------------------------------
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+version = @BLT_VERSION@
+libdir = @libdir@
+scriptdir = $(prefix)/lib/blt$(version)
+destdir = $(scriptdir)/demos
+srcdir = @srcdir@
+
+SHELL = /bin/sh
+RM = rm -rf
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+
+instdirs = $(prefix) \
+ $(exec_prefix) \
+ $(libdir) \
+ $(scriptdir) \
+ $(destdir) \
+ $(destdir)/bitmaps \
+ $(destdir)/bitmaps/hand \
+ $(destdir)/bitmaps/fish \
+ $(destdir)/images \
+ $(destdir)/scripts
+
+demos = barchart1.tcl barchart2.tcl barchart3.tcl barchart4.tcl \
+ barchart5.tcl \
+ bgexec1.tcl bgexec2.tcl bgexec3.tcl bgexec4.tcl \
+ bitmap.tcl \
+ busy1.tcl busy2.tcl \
+ dnd1.tcl dnd2.tcl dragdrop1.tcl dragdrop2.tcl \
+ eps.tcl \
+ graph1.tcl graph2.tcl graph3.tcl graph4.tcl graph5.tcl \
+ graph6.tcl graph7.tcl \
+ hierbox1.tcl hierbox2.tcl hierbox3.tcl hierbox4.tcl \
+ hiertable1.tcl hiertable2.tcl \
+ htext1.tcl htext.txt \
+ spline.tcl stripchart1.tcl \
+ tabset1.tcl tabset2.tcl tabset3.tcl tabset4.tcl \
+ tabnotebook1.tcl tabnotebook2.tcl tabnotebook3.tcl \
+ treeview1.tcl \
+ winop1.tcl winop2.tcl
+
+all:
+
+install: mkdirs install-bitmaps install-images install-scripts
+
+install-scripts:
+ for i in $(srcdir)/scripts/*.tcl ; do \
+ $(INSTALL) $$i $(INSTALL_ROOT)$(destdir)/scripts ; \
+ done
+ for i in $(demos) ; do \
+ $(INSTALL) $(srcdir)/$$i $(INSTALL_ROOT)$(destdir)/$$i ; \
+ done
+
+install-bitmaps:
+ for i in $(srcdir)/bitmaps/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(destdir)/bitmaps ; \
+ done
+ for i in $(srcdir)/bitmaps/hand/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(destdir)/bitmaps/hand ; \
+ done
+ for i in $(srcdir)/bitmaps/fish/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(destdir)/bitmaps/fish ; \
+ done
+
+install-images:
+ for i in $(srcdir)/images/*.gif $(srcdir)/images/*.ps ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(destdir)/images ; \
+ done
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)"$$i" ; then \
+ : ; \
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)"$$i" ; \
+ fi ; \
+ done
+
+clean:
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) *.ps Makefile
diff --git a/blt/demos/Makefile.vc b/blt/demos/Makefile.vc
new file mode 100644
index 00000000000..3b6f63a7267
--- /dev/null
+++ b/blt/demos/Makefile.vc
@@ -0,0 +1,78 @@
+# ------------------------------------------------------------------------
+# Makefile for demos
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+destdir = $(scriptdir)/demos
+srcdir = .
+
+SHELL = bash
+RM = rm -rf
+INSTALL = install -m 0755
+INSTALL_DATA = install -m 0644
+
+instdirs = $(prefix) $(exec_prefix) $(libdir) $(scriptdir) \
+ $(destdir) \
+ $(destdir)/bitmaps \
+ $(destdir)/bitmaps/hand \
+ $(destdir)/bitmaps/fish \
+ $(destdir)/images \
+ $(destdir)/scripts
+
+demos = barchart1.tcl barchart2.tcl barchart3.tcl barchart4.tcl \
+ barchart5.tcl \
+ bgexec1.tcl bgexec2.tcl bgexec3.tcl bgexec4.tcl \
+ bitmap.tcl \
+ busy1.tcl \
+ dragdrop1.tcl dragdrop2.tcl \
+ eps.tcl \
+ graph1.tcl graph2.tcl graph3.tcl graph4.tcl graph5.tcl \
+ graph6.tcl graph7.tcl \
+ hierbox1.tcl hierbox2.tcl hierbox3.tcl hierbox4.tcl \
+ hiertable1.tcl hiertable2.tcl hiertable3.tcl \
+ htext1.tcl htext.txt \
+ spline.tcl stripchart1.tcl \
+ tabset1.tcl tabset2.tcl tabset3.tcl tabset4.tcl \
+ tabnotebook1.tcl tabnotebook2.tcl tabnotebook3.tcl \
+ treeview1.tcl \
+ winop1.tcl winop2.tcl
+
+all:
+
+install: inst-dirs inst-bitmaps inst-images inst-scripts
+
+inst-scripts:
+ for i in $(srcdir)/scripts/*.tcl ; do \
+ $(INSTALL) $$i $(destdir)/scripts ; \
+ done
+ for i in $(demos) ; do \
+ $(INSTALL) $(srcdir)/$$i $(destdir)/$$i ; \
+ done
+
+inst-bitmaps:
+ for i in $(srcdir)/bitmaps/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps ; \
+ done
+ for i in $(srcdir)/bitmaps/hand/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps/hand ; \
+ done
+ for i in $(srcdir)/bitmaps/fish/*.xbm ; do \
+ $(INSTALL_DATA) $$i $(destdir)/bitmaps/fish ; \
+ done
+
+inst-images:
+ for i in $(srcdir)/images/*.gif $(srcdir)/images/*.ps ; do \
+ $(INSTALL_DATA) $$i $(destdir)/images ; \
+ done
+
+inst-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else mkdir "$$i" ; fi ; \
+ done
+
+clean:
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) *.ps Makefile
diff --git a/blt/demos/barchart1.tcl b/blt/demos/barchart1.tcl
new file mode 100755
index 00000000000..5e3393c82c0
--- /dev/null
+++ b/blt/demos/barchart1.tcl
@@ -0,0 +1,185 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+set graph .bc
+
+proc random {{max 1.0} {min 0.0}} {
+ global randomSeed
+
+ set randomSeed [expr (7141*$randomSeed+54773) % 259200]
+ set num [expr $randomSeed/259200.0*($max-$min)+$min]
+ return $num
+}
+set randomSeed 148230
+
+proc FormatLabel { w value } {
+
+ # Determine the element name from the value
+
+ set names [$w element show]
+ set index [expr round($value)]
+ if { $index != $value } {
+ return $value
+ }
+ global elemLabels
+ if { [info exists elemLabels($index)] } {
+ return $elemLabels($index)
+ }
+ return $value
+}
+
+source scripts/stipples.tcl
+
+image create photo bgTexture -file ./images/rain.gif
+
+option add *tile bgTexture
+
+option add *Button.tile ""
+
+option add *Htext.tileOffset no
+option add *Htext.font { Times 12 }
+
+option add *Barchart.title "A Simple Barchart"
+
+option add *Axis.tickFont { Courier 10 }
+option add *Axis.titleFont { Helvetica 12 bold }
+
+option add *x.Title "X Axis Label"
+option add *x.Rotate 90
+option add *x.Command FormatLabel
+option add *y.Title "Y Axis Label"
+
+option add *Element.Background white
+option add *Element.Relief solid
+option add *Element.BorderWidth 1
+
+option add *Legend.hide yes
+
+option add *Grid.hide no
+option add *Grid.dashes { 2 4 }
+option add *Grid.mapX ""
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *print.background yellow
+ option add *quit.background red
+ option add *graph.background palegreen
+}
+
+htext .header -text {
+ The barchart has several components: coordinate axes, data
+ elements, legend, crosshairs, grid, postscript, and markers.
+ They each control various aspects of the barchart. For example,
+ the postscript component lets you generate PostScript output.
+ Pressing the %%
+
+ set w $htext(widget)
+ button $w.print -text {Print} -command {
+ .bc postscript output bar.ps
+ }
+ $w append $w.print
+
+%% button will create a file "bar.ps"
+}
+
+htext .footer -text {
+ Hit the %%
+
+ set w $htext(widget)
+ button $w.quit -text quit -command exit
+ $w append $w.quit
+
+%% button when you've seen enough.%%
+
+ label $w.logo -bitmap BLT
+ $w append $w.logo -padx 20
+
+%% }
+
+barchart .bc
+
+#
+# Element attributes:
+#
+# Label Foreground Background Stipple Pattern
+
+source scripts/stipples.tcl
+
+set bitmaps {
+ bdiagonal1 bdiagonal2 checker2 checker3 cross1 cross2 cross3 crossdiag
+ dot1 dot2 dot3 dot4 fdiagonal1 fdiagonal2 hline1 hline2 lbottom ltop
+ rbottom rtop vline1 vline2
+}
+set count 1
+foreach stipple $bitmaps {
+ set label [file tail $stipple]
+ set label [file root $label]
+ set y [random -2 10]
+ set yhigh [expr $y + 0.5]
+ set ylow [expr $y - 0.5]
+ .bc element create $label -y $y -x $count \
+ -fg brown -bg orange -stipple $stipple -yhigh $yhigh -ylow $ylow
+ set elemLabels($count) $label
+ incr count
+}
+
+table . \
+ 0,0 .header -fill x \
+ 1,0 .bc -fill both \
+ 2,0 .footer -fill x
+
+table configure . r0 r2 -resize none
+
+Blt_ZoomStack .bc
+Blt_Crosshairs .bc
+Blt_ActiveLegend .bc
+Blt_ClosestPoint .bc
+
+if 0 {
+set printer [printer open [lindex [printer names] 0]]
+printer getattr $printer attrs
+set attrs(Orientation) Portrait
+printer setattr $printer attrs
+after 2000 {
+ $graph print2 $printer
+ printer close $printer
+}
+}
+
+.bc axis bind x <Enter> {
+ set axis [%W axis get current]
+ %W axis configure $axis -color blue3 -titlecolor blue3
+}
+.bc axis bind x <Leave> {
+ set axis [%W axis get current]
+ %W axis configure $axis -color black -titlecolor black
+}
+
+
diff --git a/blt/demos/barchart2.tcl b/blt/demos/barchart2.tcl
new file mode 100755
index 00000000000..6d329ab1632
--- /dev/null
+++ b/blt/demos/barchart2.tcl
@@ -0,0 +1,215 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+proc FormatXTicks { w value } {
+
+ # Determine the element name from the value
+
+ set index [expr round($value)]
+ if { $index != $value } {
+ return $value
+ }
+ incr index -1
+
+ set name [lindex { A1 B1 A2 B2 C1 D1 C2 A3 E1 } $index]
+ return $name
+}
+
+source scripts/stipples.tcl
+
+#image create photo bgTexture -file ./images/chalk.gif
+image create photo bgTexture -file ./images/rain.gif
+
+option add *Button.padX 5
+
+option add *tile bgTexture
+
+option add *Radiobutton.font -*-courier*-medium-r-*-*-14-*-*
+option add *Radiobutton.relief flat
+option add *Radiobutton.borderWidth 2
+option add *Radiobutton.highlightThickness 0
+
+option add *Htext.font -*-times*-bold-r-*-*-14-*-*
+option add *Htext.tileOffset no
+option add *header.font -*-times*-medium-r-*-*-14-*-*
+
+option add *Barchart.font -*-helvetica-bold-r-*-*-14-*-*
+option add *Barchart.title "Comparison of Simulators"
+
+option add *Axis.tickFont -*-helvetica-medium-r-*-*-12-*-*
+option add *Axis.titleFont -*-helvetica-bold-r-*-*-12-*-*
+option add *x.Command FormatXTicks
+option add *x.Title "Simulator"
+option add *y.Title "Time (hrs)"
+
+option add *activeBar.Foreground pink
+option add *activeBar.stipple dot3
+option add *Element.Background red
+option add *Element.Relief solid
+
+option add *Grid.dashes { 2 4 }
+option add *Grid.hide no
+option add *Grid.mapX ""
+
+option add *Legend.Font "-*-helvetica*-bold-r-*-*-12-*-*"
+option add *Legend.activeBorderWidth 2
+option add *Legend.activeRelief raised
+option add *Legend.anchor ne
+option add *Legend.borderWidth 0
+option add *Legend.position right
+
+option add *TextMarker.Font *Helvetica-Bold-R*14*
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *print.background yellow
+ option add *quit.background red
+ option add *quit.activeBackground red2
+}
+
+htext .title -text {
+ Data points with like x-coordinates, can have their bar segments displayed
+ in one of the following modes (using the -barmode option):
+}
+htext .header -text {
+ %%
+ radiobutton .header.stacked -text stacked -variable barMode \
+ -anchor w -value "stacked" -selectcolor red -command {
+ .graph configure -barmode $barMode
+ }
+ .header append .header.stacked -width 1.5i -anchor w
+ %% Bars are stacked on top of each other. The overall height is the
+ sum of the y-coordinates.
+ %%
+ radiobutton .header.aligned -text aligned -variable barMode \
+ -anchor w -value "aligned" -selectcolor yellow -command {
+ .graph configure -barmode $barMode
+ }
+ .header append .header.aligned -width 1.5i -fill x
+ %% Bars are drawn side-by-side at a fraction of their normal width.
+ %%
+ radiobutton .header.overlap -text "overlap" -variable barMode \
+ -anchor w -value "overlap" -selectcolor green -command {
+ .graph configure -barmode $barMode
+ }
+ .header append .header.overlap -width 1.5i -fill x
+ %% Bars overlap slightly.
+ %%
+ radiobutton .header.normal -text "normal" -variable barMode \
+ -anchor w -value "normal" -selectcolor blue -command {
+ .graph configure -barmode $barMode
+ }
+ .header append .header.normal -width 1.5i -fill x
+ %% Bars are overlayed one on top of the next.
+}
+
+htext .footer -text { Hit the %%
+ set im [image create photo -file ./images/stopsign.gif]
+ button $htext(widget).quit -image $im -command { exit }
+ $htext(widget) append $htext(widget).quit -pady 2
+%% button when you've seen enough. %%
+ label $htext(widget).logo -bitmap BLT
+ $htext(widget) append $htext(widget).logo
+%%}
+
+barchart .graph -tile bgTexture
+
+vector X Y0 Y1 Y2 Y3 Y4
+
+X set { 1 2 3 4 5 6 7 8 9 }
+Y0 set {
+ 0.729111111 0.002250000 0.09108333 0.006416667 0.026509167
+ 0.007027778 0.1628611 0.06405278 0.08786667
+}
+Y1 set {
+ 0.003120278 0.004638889 0.01113889 0.048888889 0.001814722
+ 0.291388889 0.0503500 0.13876389 0.04513333
+}
+Y2 set {
+ 11.534444444 3.879722222 4.54444444 4.460277778 2.334055556
+ 1.262194444 1.8009444 4.12194444 3.24527778
+}
+Y3 set {
+ 1.015750000 0.462888889 0.49394444 0.429166667 1.053694444
+ 0.466111111 1.4152500 2.17538889 2.55294444
+}
+Y4 set {
+ 0.022018611 0.516333333 0.54772222 0.177638889 0.021703889
+ 0.134305556 0.5189278 0.07957222 0.41155556
+}
+
+
+#
+# Element attributes:
+#
+# Label yData Color Stipple Pattern
+set attributes {
+ "Setup" Y1 lightyellow fdiagonal1
+ "Read In" Y0 lightgoldenrod bdiagonal1
+ "Other" Y4 lightpink fdiagonal1
+ "Solve" Y3 cyan bdiagonal1
+ "Load" Y2 lightblue fdiagonal1
+}
+
+foreach {label yData color stipple} $attributes {
+ .graph element create $yData -label $label -bd 1 \
+ -y $yData -x X -fg ${color}3 -bg ${color}1 -stipple $stipple
+}
+
+.header.stacked invoke
+
+table . \
+ 0,0 .title -fill x \
+ 1,0 .header -fill x \
+ 2,0 .graph -fill both \
+ 3,0 .footer -fill x
+
+table configure . r0 r1 r3 -resize none
+
+Blt_ZoomStack .graph
+Blt_Crosshairs .graph
+Blt_ActiveLegend .graph
+Blt_ClosestPoint .graph
+
+.graph marker bind all <B2-Motion> {
+ set coords [%W invtransform %x %y]
+ catch { %W marker configure [%W marker get current] -coords $coords }
+}
+
+.graph marker bind all <Enter> {
+ set marker [%W marker get current]
+ catch { %W marker configure $marker -bg green}
+}
+
+.graph marker bind all <Leave> {
+ set marker [%W marker get current]
+ catch { %W marker configure $marker -bg ""}
+}
+
diff --git a/blt/demos/barchart3.tcl b/blt/demos/barchart3.tcl
new file mode 100755
index 00000000000..6c6b2c568f6
--- /dev/null
+++ b/blt/demos/barchart3.tcl
@@ -0,0 +1,173 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+source scripts/stipples.tcl
+source scripts/patterns.tcl
+
+
+option add *graph.xTitle "X Axis Label"
+option add *graph.yTitle "Y Axis Label"
+option add *graph.title "A Simple Barchart"
+option add *graph.xFont *Times-Medium-R*12*
+option add *graph.elemBackground white
+option add *graph.elemRelief raised
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *print.background yellow
+ option add *quit.background red
+}
+
+htext .header -text {
+This is an example of the barchart widget. To create a postscript
+file "bar.ps", press the %%
+button $htext(widget).print -text {Print} -command {
+ $graph postscript output bar.ps -maxpect 1
+}
+$htext(widget) append $htext(widget).print
+%% button.}
+
+set graph [barchart .b]
+$graph configure \
+ -invert true \
+ -baseline 1.2
+$graph xaxis configure \
+ -command FormatLabel \
+ -descending true
+$graph legend configure \
+ -hide yes
+
+htext .footer -text {Hit the %%
+button $htext(widget).quit -text quit -command exit
+$htext(widget) append $htext(widget).quit
+%% button when you've seen enough.%%
+label $htext(widget).logo -bitmap BLT
+$htext(widget) append $htext(widget).logo -padx 20
+%%}
+
+set names { One Two Three Four Five Six Seven Eight }
+if { $visual == "staticgray" || $visual == "grayscale" } {
+ set fgcolors { white white white white white white white white }
+ set bgcolors { black black black black black black black black }
+} else {
+ set fgcolors { red green blue purple orange brown cyan navy }
+ set bgcolors { green blue purple orange brown cyan navy red }
+}
+set bitmaps {
+ bdiagonal1 bdiagonal2 checker2 checker3 cross1 cross2 cross3 crossdiag
+ dot1 dot2 dot3 dot4 fdiagonal1 fdiagonal2 hline1 hline2 lbottom ltop
+ rbottom rtop vline1 vline2
+}
+set numColors [llength $names]
+
+for { set i 0} { $i < $numColors } { incr i } {
+ $graph element create [lindex $names $i] \
+ -data { $i+1 $i+1 } \
+ -fg [lindex $fgcolors $i] \
+ -bg [lindex $bgcolors $i] \
+ -stipple [lindex $bitmaps $i] \
+ -relief raised \
+ -bd 2
+}
+
+$graph element create Nine \
+ -data { 9 -1.0 } \
+ -fg red \
+ -relief sunken
+$graph element create Ten \
+ -data { 10 2 } \
+ -fg seagreen \
+ -stipple hobbes \
+ -background palegreen
+$graph element create Eleven \
+ -data { 11 3.3 } \
+ -fg blue
+
+# -coords { -Inf Inf }
+
+$graph marker create bitmap \
+ -coords { 11 3.3 } -anchor center \
+ -bitmap @bitmaps/sharky.xbm \
+ -name bitmap \
+ -fill ""
+
+$graph marker create polygon \
+ -coords { 5 0 7 2 10 10 10 9 } \
+ -name poly -linewidth 2 -fill ""
+
+
+table . \
+
+table . \
+ .header 0,0 -padx .25i \
+ .graph 1,0 -fill both \
+ .footer 2,0 -padx .25i
+
+table configure . r0 r2 -resize none
+
+wm min . 0 0
+
+proc FormatLabel { w value } {
+ # Determine the element name from the value
+ set displaylist [$w element show]
+ set index [expr round($value)-1]
+ set name [lindex $displaylist $index]
+ if { $name == "" } {
+ return $name
+ }
+ # Return the element label
+ set info [$w element configure $name -label]
+ return [lindex $info 4]
+}
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+
+
+.graph marker bind all <B3-Motion> {
+ set coords [%W invtransform %x %y]
+ catch { %W marker configure [%W marker get current] -coords $coords }
+}
+
+.graph marker bind all <Enter> {
+ set marker [%W marker get current]
+ catch { %W marker configure $marker -fill green -outline black}
+}
+
+.graph marker bind all <Leave> {
+ set marker [%W marker get current]
+ catch {
+ set default [lindex [%W marker configure $marker -fill] 3]
+ %W marker configure $marker -fill "$default"
+ }
+}
+
diff --git a/blt/demos/barchart4.tcl b/blt/demos/barchart4.tcl
new file mode 100755
index 00000000000..c4ab70cbb62
--- /dev/null
+++ b/blt/demos/barchart4.tcl
@@ -0,0 +1,135 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+proc random {{max 1.0} {min 0.0}} {
+ global randomSeed
+
+ set randomSeed [expr (7141*$randomSeed+54773) % 259200]
+ set num [expr $randomSeed/259200.0*($max-$min)+$min]
+ return $num
+}
+set randomSeed 14823
+
+
+set graph .graph
+
+source scripts/stipples.tcl
+source scripts/patterns.tcl
+
+option add *Barchart.title "A Simple Barchart"
+option add *Barchart.relief raised
+option add *Barchart.borderWidth 2
+option add *Barchart.plotBackground white
+option add *Barchart.baseline 57.299
+
+option add *Element.borderWidth 2
+option add *Element.Background white
+option add *Element.Relief raised
+
+option add *x.Title "X Axis"
+option add *x.Font *Times-Medium-R*10*
+option add *y.Title "Y Axis"
+option add *LineMarker.Foreground yellow
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *print.background yellow
+ option add *quit.background red
+ option add *graph.background palegreen
+}
+
+htext .header -text \
+{ This is an example of the barchart widget. The barchart has
+ many components; x and y axis, legend, crosshairs, elements, etc.
+ To create a postscript file "bar.ps", press the %%
+ set w $htext(widget)
+ button $w.print -text {Print} -command {
+ $graph postscript output bar.ps
+ }
+ $w append $w.print
+
+%% button.
+}
+barchart $graph
+$graph xaxis configure -rotate 90 -stepsize 0
+
+htext .footer -text { Hit the %%
+ set im [image create photo -file ./images/stopsign.gif]
+ button $htext(widget).quit -image $im -command { exit }
+ $htext(widget) append $htext(widget).quit -pady 2
+%% button when you've seen enough. %%
+ label $htext(widget).logo -bitmap BLT
+ $htext(widget) append $htext(widget).logo
+%%}
+
+set attributes {
+ red bdiagonal1
+ orange bdiagonal2
+ yellow fdiagonal1
+ green fdiagonal2
+ blue hline1
+ cyan hline2
+ magenta vline1
+ violetred vline2
+ purple crossdiag
+ lightblue hobbes
+}
+
+set count 0
+foreach { color stipple } $attributes {
+ $graph pen create pen$count -fg ${color}1 -bg ${color}4 -stipple $stipple
+ lappend styles [list pen$count $count $count]
+ incr count
+}
+
+vector x y w
+
+x seq 0 1000
+y expr random(x)*90.0
+w expr round(y/10.0)%$count
+y expr y+10.0
+
+$graph element create data -label {} \
+ -x x -y y -weight w -styles $styles
+
+table . \
+ 0,0 .header -fill x \
+ 1,0 .graph -fill both \
+ 2,0 .footer -fill x
+
+table configure . r0 r2 -resize none
+
+wm min . 0 0
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+
diff --git a/blt/demos/barchart5.tcl b/blt/demos/barchart5.tcl
new file mode 100755
index 00000000000..e6f679b6b0c
--- /dev/null
+++ b/blt/demos/barchart5.tcl
@@ -0,0 +1,112 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+
+source scripts/stipples.tcl
+
+option add *graph.x.Title "X Axis Label"
+option add *graph.y.Title "Y Axis Label"
+option add *graph.title "A Simple Barchart"
+option add *graph.x.Font { Times 10 }
+option add *graph.Element.Relief raised
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *graph.LineMarker.color yellow
+ option add *graph.Element.Background white
+ option add *graph.Legend.activeForeground pink
+ option add *print.background yellow
+ option add *quit.background red
+ option add *graph.background palegreen
+ option add *graph.plotBackground lightblue
+}
+
+htext .htext -text \
+{ This is an example of the barchart widget. The barchart has
+ many components; x and y axis, legend, crosshairs, elements, etc.
+ To create a postscript file "bar.ps", press the %%
+ set w $htext(widget)
+ button $w.print -text {Print} -command {
+ $graph postscript output bar.ps
+ }
+ $w append $w.print
+
+%% button.
+%%
+
+ set graph [barchart .htext.graph]
+ $graph configure \
+ -relief raised \
+ -bd 2
+ $graph xaxis configure \
+ -rotate 90 \
+ -stepsize 0
+ $w append $graph -fill both -padx 4
+
+%%
+ Hit the %%
+
+ button $w.quit -text quit -command exit
+ $w append $w.quit
+
+%% button when you've seen enough.%%
+
+ label $w.logo -bitmap BLT
+ $w append $w.logo -padx 20
+
+%% }
+
+set names { One Two Three Four Five Six Seven Eight }
+if { $visual == "staticgray" || $visual == "grayscale" } {
+ set fgcolors { white white white white white white white white }
+ set bgcolors { black black black black black black black black }
+} else {
+ set fgcolors { yellow orange red magenta purple blue cyan green }
+ set bgcolors { yellow4 orange4 red4 magenta4 purple4 blue4 cyan4 green4 }
+}
+
+set numColors [llength $names]
+
+set tcl_precision 15
+vector create x
+vector create y
+x seq -5.0 5.0 0.2
+y expr sin(x)
+set barWidth 0.19
+
+$graph element create sin -relief raised -bd 1 -x x -y y -barwidth $barWidth
+table . .htext -fill both
+
+wm min . 0 0
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
diff --git a/blt/demos/bgexec1.tcl b/blt/demos/bgexec1.tcl
new file mode 100755
index 00000000000..11cdbfda7f8
--- /dev/null
+++ b/blt/demos/bgexec1.tcl
@@ -0,0 +1,198 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+bitmap define blt.0 {{40 40} {
+ 00 00 00 00 00 00 fc 07 00 00 00 04 08 00 00 00 04 04 00 00 00 e4 03 00
+ 00 00 64 fe 07 00 00 64 02 04 00 00 e4 03 04 00 00 64 7e 02 00 00 64 1a
+ 02 00 00 e4 1b 01 00 00 04 1a 01 00 00 04 1a 01 00 00 fc 1b 02 00 00 0c
+ 1a 02 00 00 0c 02 04 00 00 0c 02 f4 03 80 ed fe 07 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.1 {{40 40} {
+ 00 00 00 00 00 00 fc 07 00 00 00 04 08 00 00 00 04 04 00 00 00 e4 ff 0f
+ 00 00 64 06 08 00 00 64 06 08 00 00 e4 ff 04 00 00 64 36 04 00 00 64 36
+ 02 00 00 e4 37 02 00 00 04 34 02 00 00 04 34 04 00 00 fc 35 04 00 00 0c
+ 04 08 00 00 0c 04 08 00 00 0c fc ef 03 80 ed 01 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.2 {{40 40} {
+ 00 00 00 00 00 00 fc 0f 00 00 00 04 10 00 00 00 04 10 00 00 00 e4 fb 3f
+ 00 00 64 0e 20 00 00 64 0e 20 00 00 e4 fb 13 00 00 64 ce 10 00 00 64 ce
+ 08 00 00 e4 cb 08 00 00 04 c8 08 00 00 04 c8 10 00 00 fc cf 10 00 00 0c
+ 08 20 00 00 0c 08 20 00 00 0c f8 bf 03 80 ed 03 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.3 {{40 40} {
+ 00 00 00 00 00 00 fc 0f 00 00 00 04 f0 ff 00 00 04 00 80 00 00 e4 03 80
+ 00 00 64 d6 4f 00 00 64 16 43 00 00 e4 13 23 00 00 64 16 23 00 00 64 16
+ 23 00 00 e4 13 43 00 00 04 70 43 00 00 04 00 80 00 00 fc 0f 80 00 00 0c
+ f0 ff 00 00 0c 00 00 00 00 0c f8 ff 03 80 ed 07 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.4 {{40 40} {
+ 00 00 00 00 00 00 fc ff ff 03 00 04 00 00 02 00 04 00 00 02 00 e4 33 3f
+ 01 00 64 36 0c 01 00 64 36 8c 00 00 e4 33 8c 00 00 64 36 8c 00 00 64 36
+ 0c 01 00 e4 f3 0d 01 00 04 00 00 02 00 04 00 00 02 00 fc ff ff 03 00 0c
+ 00 00 00 00 0c 00 00 00 00 0c f8 ff 03 80 ed 07 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+
+
+#set animate(colors) { #ff8813 #ffaa13 #ffcc13 #ffff13 #ffcc13 #ffaa13 #ff8813 }
+bitmap define blt.5 [bitmap data blt.3]
+bitmap define blt.6 [bitmap data blt.2]
+bitmap define blt.7 [bitmap data blt.1]
+
+
+set interval 200
+
+proc AnimateBitmap { index } {
+ global interval afterId
+ if { ![winfo exists .logo] } {
+ return
+ }
+ if { $index >= 0 } {
+ .logo configure -bitmap blt.$index
+ incr index
+ if { $index >= 7 } {
+ set index 0
+ }
+ set afterId [after $interval "AnimateBitmap $index"]
+ }
+}
+
+set length 80
+
+option add *text.yScrollCommand { .vscroll set }
+option add *text.relief sunken
+option add *text.width $length
+option add *text.height 10
+option add *text.borderWidth 2
+option add *vscroll.command { .text yview }
+option add *vscroll.minSlider 4p
+option add *quit.command { exit }
+option add *quit.text { quit }
+option add *stop.command { set bgStatus {} }
+option add *stop.text { stop }
+option add *logo.relief sunken
+option add *logo.padX 4
+option add *title.text "Virtual Memory Statistics"
+option add *title.font -*-Helvetica-Bold-R-*-*-14-*-*-*-*-*-*-*
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *text.background lightblue
+ option add *text.foreground blue
+ option add *quit.background red
+ option add *quit.foreground white
+ option add *stop.background yellow
+ option add *stop.foreground navyblue
+ option add *logo.background beige
+ option add *logo.foreground brown
+}
+
+# Create widgets
+text .text
+scrollbar .vscroll
+button .quit
+button .stop
+label .logo
+label .title
+
+# Layout widgets in table
+table . \
+ .title 0,0 -columnspan 4 \
+ .text 1,0 -columnspan 3 \
+ .vscroll 1,3 -fill y \
+ .logo 2,0 -anchor w -padx 10 -reqheight .6i -pady 4 \
+ .stop 2,1 \
+ .quit 2,2
+
+set buttonWidth 1i
+table configure . c1 c2 -width 1i
+table configure . c3 -resize none
+table configure . .stop .quit -reqwidth $buttonWidth -anchor e
+table configure . .title .text -fill both
+
+wm min . 0 0
+
+proc DisplayStats { data } {
+ .text insert end "$data\n"
+ set textlen [expr int([.text index end])]
+ scan [.vscroll get] "%s %s %s %s" total window first last
+ if { $textlen > $total } {
+ .text yview [expr $textlen-$window]
+ }
+ update idletasks
+}
+
+set bgStatus {}
+
+AnimateBitmap 0
+
+#
+# Pick a command that
+# 1) periodically writes output and
+# 2) flushes output each time.
+#
+set command { vmstat 1 }
+#set command { netstat -c }
+
+catch { eval "bgexec bgStatus -onoutput DisplayStats $command" }
+
+# Turn off animation by canceling any pending after task.
+if { [info exists afterId] } {
+ after cancel $afterId
+}
diff --git a/blt/demos/bgexec2.tcl b/blt/demos/bgexec2.tcl
new file mode 100755
index 00000000000..c64673d6330
--- /dev/null
+++ b/blt/demos/bgexec2.tcl
@@ -0,0 +1,46 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+proc ShowResult { name1 name2 how } {
+ global var
+ .l$name2 configure -text "$var($name2)"
+ after 2000 "table forget .l$name2"
+}
+
+for { set i 1 } { $i <= 20 } { incr i } {
+ label .l$i
+ table . .l$i $i,0
+ set pid [bgexec var($i) du /usr/lib &]
+ .l$i configure -text "Starting #$i pid=$pid"
+ trace variable var($i) w ShowResult
+ update
+ after 500
+}
+
diff --git a/blt/demos/bgexec3.tcl b/blt/demos/bgexec3.tcl
new file mode 100755
index 00000000000..f9100c61f2e
--- /dev/null
+++ b/blt/demos/bgexec3.tcl
@@ -0,0 +1,224 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+bitmap define blt.0 {{40 40} {
+ 00 00 00 00 00 00 fc 07 00 00 00 04 08 00 00 00 04 04 00 00 00 e4 03 00
+ 00 00 64 fe 07 00 00 64 02 04 00 00 e4 03 04 00 00 64 7e 02 00 00 64 1a
+ 02 00 00 e4 1b 01 00 00 04 1a 01 00 00 04 1a 01 00 00 fc 1b 02 00 00 0c
+ 1a 02 00 00 0c 02 04 00 00 0c 02 f4 03 80 ed fe 07 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.1 {{40 40} {
+ 00 00 00 00 00 00 fc 07 00 00 00 04 08 00 00 00 04 04 00 00 00 e4 ff 0f
+ 00 00 64 06 08 00 00 64 06 08 00 00 e4 ff 04 00 00 64 36 04 00 00 64 36
+ 02 00 00 e4 37 02 00 00 04 34 02 00 00 04 34 04 00 00 fc 35 04 00 00 0c
+ 04 08 00 00 0c 04 08 00 00 0c fc ef 03 80 ed 01 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.2 {{40 40} {
+ 00 00 00 00 00 00 fc 0f 00 00 00 04 10 00 00 00 04 10 00 00 00 e4 fb 3f
+ 00 00 64 0e 20 00 00 64 0e 20 00 00 e4 fb 13 00 00 64 ce 10 00 00 64 ce
+ 08 00 00 e4 cb 08 00 00 04 c8 08 00 00 04 c8 10 00 00 fc cf 10 00 00 0c
+ 08 20 00 00 0c 08 20 00 00 0c f8 bf 03 80 ed 03 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.3 {{40 40} {
+ 00 00 00 00 00 00 fc 0f 00 00 00 04 f0 ff 00 00 04 00 80 00 00 e4 03 80
+ 00 00 64 d6 4f 00 00 64 16 43 00 00 e4 13 23 00 00 64 16 23 00 00 64 16
+ 23 00 00 e4 13 43 00 00 04 70 43 00 00 04 00 80 00 00 fc 0f 80 00 00 0c
+ f0 ff 00 00 0c 00 00 00 00 0c f8 ff 03 80 ed 07 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+bitmap define blt.4 {{40 40} {
+ 00 00 00 00 00 00 fc ff ff 03 00 04 00 00 02 00 04 00 00 02 00 e4 33 3f
+ 01 00 64 36 0c 01 00 64 36 8c 00 00 e4 33 8c 00 00 64 36 8c 00 00 64 36
+ 0c 01 00 e4 f3 0d 01 00 04 00 00 02 00 04 00 00 02 00 fc ff ff 03 00 0c
+ 00 00 00 00 0c 00 00 00 00 0c f8 ff 03 80 ed 07 00 04 e0 0c 00 20 09 10
+ 0c 00 00 12 10 0c 00 00 10 30 00 00 00 19 d0 03 00 00 14 b0 fe ff ff 1b
+ 50 55 55 55 0d e8 aa aa aa 16 e4 ff ff ff 2f f4 ff ff ff 27 d8 ae aa bd
+ 2d 6c 5f d5 67 1b bc f3 7f d0 36 f8 01 10 cc 1f e0 45 8e 92 0f b0 32 41
+ 43 0b d0 cf 3c 7c 0d b0 aa c2 ab 0a 60 55 55 55 05 c0 ff ab aa 03 00 00
+ fe ff 00 00 00 00 00 00}
+}
+
+set program ../src/bltwish
+if { [info exists tcl_platform ] } {
+ puts stderr $tcl_platform(platform)
+ if { $tcl_platform(platform) == "windows" } {
+ set shells [glob C:/Program\ Files/Tcl/bin/tclsh8*.exe ]
+ set program [lindex $shells 0]
+ }
+}
+if { ![file executable $program] } {
+ error "Can't execute $program"
+}
+set command [list $program scripts/bgtest.tcl]
+set animate(index) -1
+set animate(interval) 200
+#set animate(colors) { #ff8813 #ffaa13 #ffcc13 #ffff13 #ffcc13 #ffaa13 #ff8813 }
+bitmap define blt.5 [bitmap data blt.3]
+bitmap define blt.6 [bitmap data blt.2]
+bitmap define blt.7 [bitmap data blt.1]
+
+proc Animate {} {
+ global animate
+ if { [info commands .logo] != ".logo" } {
+ set animate(index) 0
+ return
+ }
+ if { $animate(index) >= 0 } {
+ .logo configure -bitmap blt.$animate(index)
+ incr animate(index)
+ if { $animate(index) >= 7 } {
+ set animate(index) 0
+ }
+ after $animate(interval) Animate
+ }
+}
+
+
+proc InsertText { string tag } {
+ .text insert end "$tag: " "" $string $tag
+ set textlen [expr int([.text index end])]
+ scan [.vscroll get] "%s %s %s %s" total window first last
+ if { $textlen > $total } {
+ .text yview [expr $textlen-$window]
+ }
+ update idletasks
+ update
+}
+
+proc DisplayOutput { data } {
+ InsertText "$data\n" stdout
+}
+
+proc DisplayErrors { data } {
+ InsertText "$data\n" stderr
+}
+
+set length 80
+
+option add *text.yScrollCommand { .vscroll set }
+option add *text.relief sunken
+option add *text.width 20
+option add *text.height 10
+option add *text.height 10
+option add *text.borderWidth 2
+option add *vscroll.command { .text yview }
+option add *vscroll.minSlider 4p
+option add *stop.command { set results {} }
+option add *stop.text { stop }
+option add *logo.relief sunken
+option add *logo.padX 4
+option add *title.text "Catching stdout and stderr"
+option add *title.font -*-Helvetica-Bold-R-*-*-14-*-*-*-*-*-*-*
+
+set visual [winfo screenvisual .]
+if { [string match *color $visual] } {
+ option add *text.background white
+ option add *text.foreground blue
+ option add *stop.background yellow
+ option add *stop.activeBackground yellow2
+ option add *stop.foreground navyblue
+ option add *start.activeBackground green2
+ option add *start.background green
+ option add *start.foreground navyblue
+ option add *logo.background beige
+ option add *logo.foreground brown
+}
+
+proc Start { command } {
+ global results animate
+ .text delete 1.0 end
+ if { $animate(index) < 0 } {
+ set results {}
+ set animate(index) 0
+ eval "bgexec results -error barney -output fred -killsignal SIGINT \
+ -onoutput DisplayOutput -onerror DisplayErrors -linebuffered no \
+ $command &"
+ Animate
+ }
+}
+
+proc Stop { } {
+ global results animate
+ set results {}
+ set animate(index) -1
+}
+
+# Create widgets
+text .text
+.text tag configure stdout -font { Courier-Bold 14 } -foreground green2
+.text tag configure stderr -font { Courier 14 } -foreground red2
+
+scrollbar .vscroll
+button .start -text "Start" -command [list Start $command]
+button .stop -text "Stop" -command Stop
+label .logo -bitmap blt.0
+label .title
+
+# Layout widgets in table
+table . \
+ .title 0,0 -columnspan 4 \
+ .text 1,0 -columnspan 3 \
+ .vscroll 1,3 -fill y \
+ .logo 2,0 -anchor w -padx 10 -reqheight .6i -pady 4 \
+ .start 2,1 \
+ .stop 2,2
+
+set buttonWidth 1i
+table configure . c1 c2 -width 1i
+table configure . c3 r0 r2 -resize none
+table configure . .start .stop -reqwidth $buttonWidth -anchor e
+table configure . .title .text -fill both
+
+wm min . 0 0
+
+
diff --git a/blt/demos/bgexec4.tcl b/blt/demos/bgexec4.tcl
new file mode 100755
index 00000000000..95edde3ffdd
--- /dev/null
+++ b/blt/demos/bgexec4.tcl
@@ -0,0 +1,180 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+source scripts/globe.tcl
+
+option add *HighlightThickness 0
+
+set program ../src/bltwish
+if { [info exists tcl_platform ] } {
+ puts stderr $tcl_platform(platform)
+ if { $tcl_platform(platform) == "windows" } {
+ set shells [glob C:/Program\ Files/Tcl/bin/tclsh8*.exe ]
+ set program [lindex $shells 0]
+ }
+}
+if { ![file executable $program] } {
+ error "Can't execute $program"
+}
+
+set command [list $program scripts/bgtest.tcl]
+
+array set animate {
+ index -1
+ interval 200
+ colors "#ff8813 #ffaa13 #ffcc13 #ffff13 #ffcc13 #ffaa13 #ff8813"
+ numBitmaps 30
+ prefix globe
+}
+
+proc Animate {} {
+ global animate
+ if { [info commands .logo] != ".logo" } {
+ set animate(index) 0
+ return
+ }
+ if { $animate(index) >= 0 } {
+ .logo configure -bitmap $animate(prefix).$animate(index)
+ incr animate(index)
+ if { $animate(index) >= $animate(numBitmaps) } {
+ set animate(index) 0
+ }
+ after $animate(interval) Animate
+ }
+}
+
+proc InsertText { string tag } {
+ .text insert end "$tag: " "" $string $tag
+ set textlen [expr int([.text index end])]
+ scan [.vscroll get] "%s %s %s %s" total window first last
+ if { $textlen > $total } {
+ .text yview [expr $textlen-$window]
+ }
+ update idletasks
+}
+
+proc DisplayOutput { name1 name2 how } {
+ upvar #0 $name1 arr
+
+ InsertText "$arr($name2)\n" stdout
+ set arr($name2) {}
+}
+
+proc DisplayErrors { name1 name2 how } {
+ upvar #0 $name1 arr
+
+ InsertText "$arr($name2)\n" stderr
+ set arr($name2) {}
+}
+
+
+option add *text.yScrollCommand { .vscroll set }
+option add *text.relief sunken
+option add *text.width 20
+option add *text.height 10
+option add *text.height 10
+option add *text.borderWidth 2
+option add *vscroll.command { .text yview }
+option add *vscroll.minSlider 4p
+option add *stop.command { set results {} }
+option add *stop.text { stop }
+option add *logo.padX 4
+option add *title.text "Catching stdout and stderr"
+option add *title.font -*-Helvetica-Bold-R-*-*-14-*-*-*-*-*-*-*
+
+set visual [winfo screenvisual .]
+if { [string match *color $visual] } {
+ option add *text.background white
+ option add *text.foreground blue
+ option add *stop.background yellow
+ option add *stop.activeBackground yellow2
+ option add *stop.foreground navyblue
+ option add *start.activeBackground green2
+ option add *start.background green
+ option add *start.foreground navyblue
+ option add *logo.background beige
+ option add *logo.foreground brown
+ option add *logo.foreground green4
+ option add *title.background lightblue
+ option add *logo.background lightblue
+}
+. configure -bg lightblue
+
+trace variable results(stdout) w DisplayOutput
+trace variable results(stderr) w DisplayErrors
+
+proc Start { command } {
+ global results animate
+ .text delete 1.0 end
+ if { $animate(index) < 0 } {
+ set results(status) {}
+ eval "bgexec results(status) -lasterror results(stderr) \
+ -lastoutput results(stdout) $command &"
+ set animate(index) 0
+ Animate
+ }
+}
+
+proc Stop { } {
+ global results animate
+ set results(status) {}
+ set animate(index) -1
+}
+
+# Create widgets
+text .text
+.text tag configure stdout -font -*-Helvetica-Bold-R-*-*-18-*-*-*-*-*-*-* \
+ -foreground green2
+.text tag configure stderr -font -*-Helvetica-Medium-O-*-*-18-*-*-*-*-*-*-* \
+ -foreground red2
+
+scrollbar .vscroll
+button .start -text "Start" -command [list Start $command]
+button .stop -text "Stop" -command Stop
+label .logo -bitmap globe.0
+label .title
+
+# Layout widgets in table
+table . \
+ .title 0,0 -columnspan 4 \
+ .text 1,0 -columnspan 3 \
+ .vscroll 1,3 -fill y \
+ .logo 2,0 -anchor w -padx 10 -reqheight .6i -pady 4 \
+ .start 2,1 \
+ .stop 2,2
+
+set buttonWidth 1i
+table configure . c1 c2 -width 1i
+table configure . c3 r0 r2 -resize none
+table configure . .start .stop -reqwidth $buttonWidth -anchor e
+table configure . .title .text -fill both
+
+wm min . 0 0
+
+
diff --git a/blt/demos/bgexec5.tcl b/blt/demos/bgexec5.tcl
new file mode 100755
index 00000000000..44eb665e947
--- /dev/null
+++ b/blt/demos/bgexec5.tcl
@@ -0,0 +1,47 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+set shell bltwish
+if { [info exists tcl_platform] && $tcl_platform(platform) == "windows" } {
+ set shell "$shell.exe"
+}
+if { [file executable "../src/$shell"] } {
+ set shell "../src/$shell"
+}
+
+set count 0
+foreach demo [glob barchart?.tcl] {
+ bgexec var $shell $demo &
+}
+
+button .kill -text "Kill All" -command { set var 0 }
+table . .kill -fill both
+
+
diff --git a/blt/demos/bitmap.tcl b/blt/demos/bitmap.tcl
new file mode 100755
index 00000000000..0a52ceb5087
--- /dev/null
+++ b/blt/demos/bitmap.tcl
@@ -0,0 +1,233 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ #namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+source scripts/stipples.tcl
+source scripts/patterns.tcl
+
+bitmap define wide_weave {
+#define wide_weave_width 16
+#define wide_weave_height 16
+static char wide_weave_bits[] = {
+ 0x11, 0x11, 0xb8, 0xb8, 0x7c, 0x7c, 0x3a, 0x3a, 0x11, 0x11, 0xa3, 0xa3,
+ 0xc7, 0xc7, 0x8b, 0x8b, 0x11, 0x11, 0xb8, 0xb8, 0x7c, 0x7c, 0x3a, 0x3a,
+ 0x11, 0x11, 0xa3, 0xa3, 0xc7, 0xc7, 0x8b, 0x8b};
+}
+
+bitmap define hobbes3 {
+#define hobbes_width 25
+#define hobbes_height 25
+static char hobbes_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x78, 0xe0, 0x07, 0x00, 0xfc, 0xf8, 0x07, 0x00, 0xcc, 0x07, 0x04, 0x00,
+ 0x0c, 0xf0, 0x0b, 0x00, 0x7c, 0x1c, 0x06, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0xe0, 0x03, 0x10, 0x00, 0xe0, 0x41, 0x11, 0x00, 0x20, 0x40, 0x11, 0x00,
+ 0xe0, 0x07, 0x10, 0x00, 0xe0, 0xc1, 0x17, 0x00, 0x10, 0xe0, 0x2f, 0x00,
+ 0x20, 0xe0, 0x6f, 0x00, 0x18, 0xe0, 0x2f, 0x00, 0x20, 0xc6, 0x67, 0x00,
+ 0x18, 0x84, 0x2b, 0x00, 0x20, 0x08, 0x64, 0x00, 0x70, 0xf0, 0x13, 0x00,
+ 0x80, 0x01, 0x08, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+} -scale 3.0
+
+
+bitmap define gort {
+#define gort_width 64
+#define gort_height 64
+static char gort_bits[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xf0,
+ 0x3f, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xff, 0xff, 0x07, 0x00, 0x80,
+ 0x01, 0x00, 0xc0, 0xdf, 0x0f, 0x1e, 0x00, 0x80, 0x01, 0x00, 0x60, 0xdf,
+ 0x7f, 0x38, 0x00, 0x80, 0x01, 0x00, 0x30, 0x84, 0xfd, 0x67, 0x00, 0x80,
+ 0x01, 0x00, 0x18, 0x04, 0xf6, 0xef, 0x00, 0x80, 0x01, 0x00, 0x0c, 0x86,
+ 0xe1, 0xc7, 0x01, 0x80, 0x01, 0x00, 0x06, 0x06, 0xc0, 0x96, 0x01, 0x80,
+ 0x01, 0x00, 0x06, 0x06, 0x00, 0x97, 0x03, 0x80, 0x01, 0x00, 0x06, 0x06,
+ 0x00, 0x3c, 0x03, 0x80, 0x01, 0x00, 0x03, 0x06, 0x00, 0x5c, 0x07, 0x80,
+ 0x01, 0x00, 0x03, 0x02, 0x00, 0xd8, 0x06, 0x80, 0x01, 0x00, 0x03, 0x02,
+ 0x00, 0xd8, 0x06, 0x80, 0x01, 0x00, 0x43, 0x02, 0x00, 0xb0, 0x0c, 0x80,
+ 0x01, 0x80, 0x31, 0x03, 0x00, 0xe0, 0x0d, 0x80, 0x01, 0x80, 0x61, 0x03,
+ 0x00, 0xe0, 0x0d, 0x80, 0x01, 0x80, 0x1b, 0x03, 0x00, 0xf0, 0x0c, 0x80,
+ 0x01, 0x80, 0xb3, 0x03, 0xff, 0xff, 0x1d, 0x80, 0x01, 0xc0, 0xeb, 0xfb,
+ 0xff, 0xff, 0x1d, 0x80, 0x01, 0xe0, 0xc5, 0x7f, 0xfe, 0x7f, 0x3f, 0x01,
+ 0xe0, 0xeb, 0xe3, 0xff, 0xff, 0x6e, 0x80, 0x01, 0xd0, 0x3b, 0xfe, 0x01,
+ 0x80, 0x40, 0xcf, 0x80, 0x01, 0xf0, 0xf7, 0x07, 0x00, 0x30, 0x8e, 0x80,
+ 0x01, 0xf0, 0xf4, 0x00, 0x00, 0x1b, 0x98, 0x80, 0x01, 0x70, 0x14, 0x00,
+ 0x00, 0x1f, 0xdc, 0x80, 0x01, 0x30, 0xfe, 0xff, 0x1f, 0xc8, 0xff, 0x80,
+ 0x01, 0x20, 0xee, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0x20, 0xf7, 0xff,
+ 0x7f, 0xfe, 0x7f, 0x80, 0x01, 0xc0, 0xe1, 0xff, 0xff, 0xfe, 0x3f, 0x80,
+ 0x01, 0x80, 0xed, 0xff, 0xff, 0xff, 0x19, 0x80, 0x01, 0x80, 0x99, 0xff,
+ 0xff, 0xff, 0x18, 0x80, 0x01, 0x00, 0x63, 0x83, 0xff, 0x7f, 0x08, 0x80,
+ 0x01, 0x00, 0xc3, 0x06, 0x00, 0x00, 0x0c, 0x80, 0x01, 0x00, 0x9b, 0x07,
+ 0x00, 0x00, 0x0c, 0x80, 0x01, 0x00, 0xb6, 0x07, 0x00, 0x10, 0x0c, 0x80,
+ 0x01, 0x00, 0xc6, 0x07, 0x00, 0x10, 0x04, 0x80, 0x01, 0x00, 0x36, 0x06,
+ 0x00, 0x18, 0x06, 0x80, 0x01, 0x00, 0x66, 0x06, 0x00, 0x18, 0x06, 0x80,
+ 0x01, 0x00, 0x8c, 0x0d, 0x00, 0x18, 0x02, 0x80, 0x01, 0x00, 0x18, 0x0e,
+ 0x00, 0x18, 0x03, 0x80, 0x01, 0x00, 0xf0, 0x0c, 0x00, 0x18, 0x03, 0x80,
+ 0x01, 0x00, 0x30, 0x0f, 0x00, 0x98, 0x01, 0x80, 0x01, 0x00, 0xb0, 0x1f,
+ 0x01, 0x98, 0x01, 0x80, 0x01, 0x00, 0x60, 0x1f, 0x03, 0xdc, 0x00, 0x80,
+ 0x01, 0x00, 0xe0, 0x3f, 0x03, 0xdc, 0x00, 0x80, 0x01, 0x00, 0xe0, 0x3b,
+ 0x07, 0xee, 0x00, 0x80, 0x01, 0x00, 0x70, 0xf8, 0xff, 0xff, 0x01, 0x80,
+ 0x01, 0x00, 0xf0, 0x80, 0xff, 0x37, 0x03, 0x80, 0x01, 0x00, 0xfc, 0x00,
+ 0x08, 0xd8, 0x03, 0x80, 0x01, 0x00, 0xfe, 0x03, 0xf8, 0x7f, 0x07, 0x80,
+ 0x01, 0xc0, 0x87, 0x07, 0xe0, 0x3f, 0x1c, 0x80, 0x01, 0xf0, 0x03, 0x00,
+ 0x00, 0x00, 0xf8, 0x8f, 0x81, 0xff, 0x00, 0x38, 0x00, 0xf6, 0xf9, 0xff,
+ 0xfd, 0x3f, 0x00, 0xe0, 0x00, 0x83, 0x8f, 0xff, 0xff, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0xfc, 0xc0, 0x07, 0x0e, 0x00, 0x38, 0xe0, 0x00, 0xe0, 0x9f,
+ 0xf9, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0xff, 0x7f, 0x80, 0x01, 0x00,
+ 0xc0, 0x00, 0x00, 0xf0, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x0f, 0xf0, 0x00, 0x38, 0x00, 0x00, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00,
+ 0x38, 0xc0, 0xc0, 0x80, 0x01, 0x1c, 0xe0, 0x00, 0x0c, 0xc0, 0x83, 0x81,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+} -rotate 180
+
+bitmap define xbob {
+#define bob_x_hot 30
+#define bob_y_hot 37
+#define bob_width 61
+#define bob_height 75
+static char bob_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff,
+ 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x1f, 0x00, 0x00,
+ 0x00, 0x80, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xcf,
+ 0x9f, 0xd1, 0x03, 0x00, 0x00, 0xf0, 0x7f, 0x8c, 0x33, 0x91, 0x07, 0x00,
+ 0x00, 0xf8, 0xa7, 0x18, 0x27, 0xb1, 0x06, 0x00, 0x00, 0xfc, 0x47, 0x31,
+ 0x4e, 0xa6, 0x0e, 0x00, 0x00, 0xfe, 0x4f, 0x21, 0x4c, 0xae, 0x3d, 0x00,
+ 0x00, 0xff, 0xdf, 0x23, 0x8d, 0xbe, 0x7d, 0x00, 0x80, 0xff, 0xff, 0x67,
+ 0xbd, 0xfe, 0xff, 0x01, 0x80, 0xff, 0xff, 0x7f, 0xbf, 0xff, 0xff, 0x03,
+ 0xc0, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xf8, 0x07, 0xc0, 0xff, 0xff, 0xff,
+ 0xbf, 0x3f, 0xf8, 0x07, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0x0f,
+ 0xc0, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xf8, 0x0f, 0xe0, 0x7f, 0x00, 0xf8,
+ 0x07, 0x00, 0xf0, 0x0f, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07,
+ 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xe0, 0x3f, 0x00, 0x00,
+ 0x00, 0x00, 0xf4, 0x07, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x07,
+ 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x07, 0xe0, 0x3f, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x07, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x07,
+ 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x07, 0xe0, 0x3f, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x07, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x07,
+ 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x78, 0xf6, 0x07, 0xa0, 0xbf, 0xff, 0x00,
+ 0x00, 0xff, 0xf7, 0x07, 0x70, 0x9f, 0xff, 0x01, 0x80, 0xff, 0xef, 0x07,
+ 0xf0, 0x1c, 0x80, 0x03, 0xe0, 0x01, 0xef, 0x07, 0xf0, 0x1f, 0xbe, 0x07,
+ 0xf0, 0x3f, 0xee, 0x07, 0xe0, 0x9d, 0x83, 0x1f, 0xf8, 0xe1, 0xdc, 0x07,
+ 0xe0, 0xc1, 0x7f, 0x1f, 0xfc, 0xff, 0xc8, 0x07, 0xe0, 0xc1, 0x69, 0x1e,
+ 0x7e, 0xca, 0xc0, 0x03, 0xe0, 0x81, 0xb8, 0x1f, 0xc0, 0x0e, 0xc0, 0x03,
+ 0xe0, 0x01, 0xc0, 0x1b, 0xc0, 0xcf, 0xc1, 0x03, 0xc0, 0x03, 0xf7, 0x11,
+ 0x00, 0x7f, 0xc0, 0x03, 0xc0, 0x03, 0x7c, 0x18, 0x00, 0x1c, 0xc0, 0x02,
+ 0xc0, 0x02, 0x30, 0x08, 0x00, 0x00, 0x40, 0x03, 0x40, 0x03, 0x00, 0x08,
+ 0x00, 0x00, 0x40, 0x02, 0x40, 0x13, 0x00, 0x0c, 0x00, 0x00, 0x60, 0x02,
+ 0x40, 0x12, 0x00, 0x0e, 0x00, 0x00, 0xc0, 0x03, 0x80, 0x33, 0x80, 0x0e,
+ 0x00, 0x00, 0xa8, 0x01, 0x00, 0x33, 0x40, 0x0f, 0xa0, 0x03, 0x2c, 0x00,
+ 0x00, 0x74, 0x30, 0x0f, 0x38, 0x07, 0x2e, 0x00, 0x00, 0x74, 0x98, 0x1f,
+ 0x1e, 0x1e, 0x2f, 0x00, 0x00, 0xfc, 0x8f, 0xff, 0x0f, 0xfc, 0x2f, 0x00,
+ 0x00, 0xf8, 0xe3, 0xff, 0x03, 0xf8, 0x2f, 0x00, 0x00, 0xf8, 0xfd, 0xff,
+ 0x81, 0xff, 0x3f, 0x00, 0x00, 0xb8, 0xf9, 0x1f, 0xf8, 0x0f, 0x1e, 0x00,
+ 0x00, 0x30, 0xf1, 0xf0, 0x0f, 0x03, 0x0e, 0x00, 0x00, 0x30, 0xf1, 0x01,
+ 0x80, 0x01, 0x0f, 0x00, 0x00, 0x20, 0xf1, 0xf7, 0xff, 0x00, 0x07, 0x00,
+ 0x00, 0x60, 0xe3, 0x01, 0x60, 0x80, 0x07, 0x00, 0x00, 0x60, 0xc3, 0xef,
+ 0x3f, 0x80, 0x03, 0x00, 0x00, 0x40, 0xc2, 0xff, 0x0f, 0xc0, 0x03, 0x00,
+ 0x00, 0xc0, 0xe6, 0x1f, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0xf4, 0xfe,
+ 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x79, 0xfe, 0x1f, 0xe0, 0x00, 0x00,
+ 0xc0, 0x01, 0x3d, 0x3e, 0x00, 0x70, 0x00, 0x00, 0x30, 0x06, 0x3e, 0x0f,
+ 0x00, 0x38, 0x00, 0x00, 0xc8, 0x8c, 0x1f, 0x07, 0x00, 0x38, 0x00, 0x00,
+ 0xf4, 0xcc, 0x8f, 0x07, 0x00, 0x1c, 0x00, 0x00, 0x72, 0xee, 0xf7, 0x07,
+ 0x00, 0x0e, 0x00, 0x00, 0x02, 0xff, 0xe3, 0x07, 0x00, 0x07, 0x00, 0x00,
+ 0x32, 0xfe, 0xc1, 0xff, 0x8f, 0x03, 0x00, 0x00, 0x3e, 0xfe, 0x80, 0xff,
+ 0xff, 0x01, 0x00, 0x00, 0x7e, 0x7c, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x7c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+} -rotate -123
+
+bitmap compose top "Top\nScaled 2x" -pady 5 -padx 10 -rotate 0 -scale 2.0
+bitmap compose left "Left\na\nb\nc" -rotate 90
+bitmap compose right {Right} -rotate 270
+bitmap compose center {Center} -rotate 45
+bitmap compose bottom {Bottom} -rotate 180
+
+
+
+
+#
+# Test bitmap
+#
+# 1. Test of rotated text bitmap
+# 2. Define bitmap from output of "data" command
+# 3. Define bitmap from X11 bitmap file
+# 4. Define bitmap from X10 bitmap file
+# 5. Define bitmap from internal Tcl list
+# 6. Use predefined internal bitmap
+#
+proc ChangeBitmap { w } {
+ global count bitmapList
+ if { [incr count] >= [llength $bitmapList] } {
+ exit
+ }
+ $w configure -bitmap [lindex $bitmapList $count]
+}
+set count -1
+set bitmapList {
+ sharky
+ hobbes3
+ xbob
+ gort
+ question
+ large_question
+ questhead
+ large_questhead
+ hobbes
+ BLT
+}
+
+option add *center*padX 8
+option add *center*padY 4
+
+button .left -bitmap left -command {
+ .center configure -bitmap sharky ; set count -1
+}
+button .top -bitmap top -command {
+ .center configure -bitmap hobbes3 ; set count 0
+}
+button .right -bitmap right -command {
+ .center configure -bitmap xbob ; set count 1
+}
+button .bottom -bitmap bottom -command {
+ .center configure -bitmap gort ; set count 2
+}
+button .center -bitmap center -command "ChangeBitmap .center"
+
+set bitmapFile @bitmaps/sharky.xbm
+bitmap define sharky [bitmap data $bitmapFile] -rotate 45 -scale 0.75
+bitmap define large_question [bitmap data question] -scale 2.0
+bitmap define large_questhead [bitmap data questhead] -scale 2.0
+
+table . \
+ .top 0,1 -fill x \
+ .left 1,0 -fill y \
+ .center 1,1 -fill both \
+ .right 1,2 -fill y \
+ .bottom 2,1 -fill x
+
diff --git a/blt/demos/bitmaps/face.xbm b/blt/demos/bitmaps/face.xbm
new file mode 100644
index 00000000000..8e09419969c
--- /dev/null
+++ b/blt/demos/bitmaps/face.xbm
@@ -0,0 +1,171 @@
+#define face_width 108
+#define face_height 144
+static char face_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09,
+ 0x20, 0x80, 0x24, 0x05, 0x00, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x88,
+ 0x24, 0x20, 0x80, 0x24, 0x00, 0x00, 0x00, 0x10, 0x80, 0x04, 0x00, 0x01,
+ 0x00, 0x01, 0x40, 0x0a, 0x09, 0x00, 0x92, 0x04, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x40, 0x12, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x84,
+ 0x24, 0x40, 0x22, 0xa8, 0x02, 0x14, 0x84, 0x92, 0x40, 0x42, 0x12, 0x04,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x52, 0x11, 0x00, 0x12, 0x00,
+ 0x40, 0x02, 0x00, 0x20, 0x00, 0x08, 0x00, 0xaa, 0x02, 0x54, 0x85, 0x24,
+ 0x00, 0x10, 0x12, 0x00, 0x00, 0x81, 0x44, 0x00, 0x90, 0x5a, 0x00, 0xea,
+ 0x1b, 0x00, 0x80, 0x40, 0x40, 0x02, 0x00, 0x08, 0x00, 0x20, 0xa2, 0x05,
+ 0x8a, 0xb4, 0x6e, 0x45, 0x12, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10, 0x02,
+ 0xa8, 0x92, 0x00, 0xda, 0x5f, 0x10, 0x00, 0x10, 0xa1, 0x04, 0x20, 0x41,
+ 0x02, 0x00, 0x5a, 0x25, 0xa0, 0xff, 0xfb, 0x05, 0x41, 0x02, 0x04, 0x00,
+ 0x00, 0x08, 0x40, 0x80, 0xec, 0x9b, 0xec, 0xfe, 0x7f, 0x01, 0x04, 0x20,
+ 0x90, 0x02, 0x04, 0x00, 0x08, 0x20, 0xfb, 0x2e, 0xf5, 0xff, 0xff, 0x57,
+ 0x00, 0x04, 0x02, 0x00, 0x00, 0x20, 0x01, 0xc1, 0x6e, 0xab, 0xfa, 0xff,
+ 0xff, 0x05, 0x90, 0x20, 0x48, 0x02, 0x00, 0x04, 0x20, 0xa8, 0xdf, 0xb5,
+ 0xfe, 0xff, 0xff, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x04, 0xe0,
+ 0xbb, 0xef, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x04, 0x48, 0x02, 0x00, 0x20,
+ 0x80, 0xf4, 0x6f, 0xfb, 0xff, 0xff, 0xff, 0x20, 0x90, 0x40, 0x02, 0x00,
+ 0x00, 0x04, 0x08, 0xb8, 0xf6, 0xff, 0xff, 0xdf, 0xbe, 0x12, 0x45, 0x10,
+ 0x90, 0x04, 0x90, 0x00, 0x22, 0xfa, 0xff, 0xff, 0xff, 0xbb, 0xd7, 0xe9,
+ 0x3a, 0x02, 0x02, 0x00, 0x04, 0x90, 0x80, 0xfe, 0xdf, 0xf6, 0xb7, 0xef,
+ 0xbe, 0x56, 0x57, 0x40, 0x48, 0x09, 0x00, 0x04, 0x00, 0xfa, 0xf5, 0xdf,
+ 0xed, 0x5a, 0xd5, 0xea, 0xbd, 0x09, 0x00, 0x00, 0x40, 0x00, 0x92, 0xfe,
+ 0xbf, 0x7d, 0xb7, 0x6a, 0x55, 0xbf, 0xf7, 0x02, 0x11, 0x01, 0x00, 0x91,
+ 0x00, 0xff, 0xff, 0xaf, 0x55, 0x55, 0x5b, 0xeb, 0xef, 0x22, 0x04, 0x04,
+ 0x04, 0x00, 0xa4, 0xff, 0xf7, 0xad, 0xaa, 0xaa, 0xaa, 0xbe, 0xfe, 0x03,
+ 0x20, 0x00, 0x10, 0x44, 0x80, 0xff, 0x7f, 0x55, 0x12, 0x91, 0x2a, 0xeb,
+ 0xbf, 0x0b, 0x82, 0x02, 0x00, 0x00, 0xd1, 0x7f, 0xdf, 0xa2, 0xa4, 0x54,
+ 0x55, 0xfd, 0xfd, 0x47, 0x08, 0x08, 0x00, 0x21, 0xe4, 0xff, 0x37, 0x11,
+ 0x09, 0xa5, 0xaa, 0xb6, 0xff, 0x0d, 0x80, 0x00, 0x00, 0x04, 0xd0, 0xff,
+ 0x4f, 0x44, 0x20, 0x48, 0x55, 0xfb, 0xff, 0x27, 0x11, 0x02, 0x40, 0x40,
+ 0xe2, 0xfb, 0x15, 0x11, 0x4a, 0x55, 0x4a, 0x7d, 0xf7, 0x0f, 0x00, 0x00,
+ 0x04, 0x08, 0xf8, 0xdf, 0x52, 0x44, 0x01, 0x52, 0xb5, 0xfa, 0xff, 0x0f,
+ 0x49, 0x02, 0x00, 0x02, 0xe9, 0xf6, 0x0a, 0x11, 0xa4, 0x88, 0x4a, 0x6d,
+ 0xff, 0x5f, 0x00, 0x00, 0x10, 0x20, 0xf0, 0x2f, 0x21, 0x44, 0x10, 0x52,
+ 0xb5, 0xfa, 0xff, 0x0f, 0x44, 0x04, 0x80, 0x08, 0xf8, 0xab, 0x8a, 0x00,
+ 0x81, 0xa4, 0xd4, 0xd6, 0xfe, 0x2f, 0x00, 0x00, 0x04, 0x40, 0xb5, 0x2d,
+ 0x21, 0x08, 0x04, 0x90, 0xaa, 0xfa, 0xff, 0x1f, 0x11, 0x01, 0x00, 0x04,
+ 0xf0, 0x57, 0x0a, 0x22, 0x40, 0x4a, 0xda, 0x5e, 0xfb, 0x1f, 0x40, 0x00,
+ 0x40, 0x20, 0xba, 0x95, 0x90, 0x00, 0x01, 0xa0, 0xaa, 0xea, 0xff, 0x5f,
+ 0x02, 0x02, 0x00, 0x01, 0xe8, 0x57, 0x05, 0x00, 0x00, 0x12, 0xd5, 0xfe,
+ 0xfd, 0x1f, 0x48, 0x00, 0x04, 0x48, 0x7a, 0x95, 0x08, 0x02, 0x10, 0x40,
+ 0xaa, 0x55, 0xf7, 0x1f, 0x00, 0x09, 0x20, 0x00, 0xf8, 0x57, 0x22, 0x10,
+ 0x00, 0x28, 0xa9, 0xfa, 0xff, 0x5f, 0x02, 0x00, 0x00, 0x49, 0xdd, 0x29,
+ 0x01, 0x00, 0x80, 0x80, 0xaa, 0xd7, 0xff, 0x0f, 0x10, 0x00, 0x08, 0x00,
+ 0xf8, 0x96, 0x08, 0x00, 0x00, 0x20, 0x54, 0xfa, 0xee, 0x3f, 0x81, 0x04,
+ 0x40, 0x24, 0xfe, 0x55, 0x82, 0x00, 0x00, 0x82, 0xd2, 0xad, 0xff, 0x0f,
+ 0x08, 0x00, 0x04, 0x80, 0x6c, 0x97, 0x00, 0x00, 0x02, 0x20, 0xa9, 0xf6,
+ 0xdf, 0x5f, 0x00, 0x02, 0x20, 0x09, 0xfa, 0x49, 0x12, 0x00, 0x20, 0x84,
+ 0x54, 0xdb, 0xfe, 0x1f, 0x91, 0x00, 0x00, 0x00, 0xf8, 0x2b, 0x00, 0x20,
+ 0x00, 0x40, 0xa4, 0xf6, 0xbb, 0x1f, 0x04, 0x00, 0x44, 0x92, 0x7e, 0x95,
+ 0x02, 0x00, 0x00, 0x89, 0xaa, 0xdd, 0xff, 0x1f, 0x20, 0x09, 0x10, 0x00,
+ 0xf4, 0x57, 0x20, 0x01, 0x08, 0x20, 0xa9, 0x76, 0xff, 0x5f, 0x02, 0x00,
+ 0x00, 0x21, 0xfc, 0x4a, 0x05, 0x00, 0x01, 0x80, 0x54, 0xdb, 0xff, 0x1e,
+ 0x08, 0x02, 0x04, 0x08, 0xf9, 0x2b, 0x00, 0x00, 0x40, 0x28, 0xd2, 0xf6,
+ 0xff, 0xbf, 0x80, 0x00, 0x90, 0x00, 0xbc, 0x92, 0x08, 0x10, 0x00, 0x82,
+ 0x54, 0xdb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x44, 0xf9, 0x55, 0x02, 0x01,
+ 0x00, 0x20, 0xaa, 0xbd, 0xfd, 0x3f, 0x08, 0x04, 0x04, 0x10, 0xf4, 0x2a,
+ 0x01, 0x00, 0x22, 0x80, 0xd4, 0xf6, 0xff, 0x5f, 0x82, 0x00, 0x40, 0x02,
+ 0xf8, 0x55, 0x20, 0x00, 0x00, 0x50, 0x6a, 0xdf, 0xfe, 0x3f, 0x00, 0x00,
+ 0x00, 0x48, 0xe9, 0x4a, 0x05, 0x08, 0x00, 0xa5, 0xd5, 0xf5, 0xff, 0x3f,
+ 0x10, 0x01, 0x10, 0x01, 0xb0, 0xab, 0x92, 0x02, 0x40, 0xf8, 0xbf, 0xde,
+ 0xfe, 0x5f, 0x02, 0x04, 0x04, 0x48, 0xfa, 0xd4, 0x6f, 0x20, 0x84, 0xef,
+ 0xff, 0xfb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x00, 0xe0, 0xed, 0xbf, 0x0b,
+ 0xa1, 0x7e, 0xff, 0xbf, 0xfd, 0x5f, 0x04, 0x01, 0x20, 0x49, 0xd2, 0xfb,
+ 0xfe, 0x55, 0xd4, 0xff, 0xff, 0xf6, 0xff, 0x07, 0x00, 0x04, 0x00, 0x00,
+ 0xc0, 0xaa, 0xfb, 0x2b, 0xa2, 0xfe, 0xff, 0xdf, 0xee, 0x1f, 0x91, 0x00,
+ 0x82, 0xa4, 0xa4, 0xf5, 0xff, 0x57, 0xd5, 0xff, 0xbf, 0xfd, 0xff, 0x4d,
+ 0x00, 0x00, 0x20, 0x00, 0x88, 0x5b, 0xff, 0x2f, 0x69, 0xff, 0xff, 0xdb,
+ 0xfe, 0x1f, 0x24, 0x02, 0x00, 0x49, 0xa2, 0xd6, 0xff, 0x5f, 0xea, 0xff,
+ 0x7f, 0x7f, 0x7f, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x40, 0xab, 0xf7, 0xbb,
+ 0xf0, 0xdf, 0xff, 0xd5, 0xff, 0xbf, 0x82, 0x04, 0x42, 0x24, 0x91, 0xd5,
+ 0xaa, 0xae, 0xd4, 0xaa, 0x52, 0x7b, 0xff, 0x15, 0x08, 0x00, 0x00, 0x01,
+ 0x04, 0x55, 0xd5, 0x55, 0x70, 0x5b, 0x75, 0xdd, 0xdf, 0x1f, 0x40, 0x00,
+ 0x08, 0x48, 0xa0, 0x4a, 0xa9, 0x56, 0xea, 0x56, 0xad, 0x6a, 0x7d, 0x9b,
+ 0x04, 0x01, 0x00, 0x02, 0x42, 0x2a, 0xd5, 0xaa, 0xa8, 0xaa, 0xaa, 0xfa,
+ 0xdf, 0x2f, 0x10, 0x04, 0x22, 0x48, 0x08, 0x45, 0x2a, 0x15, 0x68, 0x55,
+ 0x55, 0xd7, 0x76, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x40, 0x2a, 0x80, 0xa0,
+ 0xb2, 0x09, 0x48, 0xb9, 0xdf, 0x17, 0x22, 0x01, 0x00, 0x24, 0x45, 0x8a,
+ 0x24, 0x4a, 0x54, 0x51, 0x91, 0xf6, 0x6e, 0x4b, 0x00, 0x04, 0x90, 0x00,
+ 0x80, 0x52, 0x00, 0x20, 0x69, 0x05, 0xa4, 0xaa, 0xff, 0x1e, 0x48, 0x00,
+ 0x02, 0x92, 0x08, 0x05, 0x81, 0x94, 0xd4, 0x92, 0x40, 0xfd, 0xb6, 0x8b,
+ 0x00, 0x01, 0x40, 0x00, 0x82, 0x54, 0x00, 0x48, 0x68, 0x05, 0x90, 0xa4,
+ 0xef, 0x06, 0x24, 0x00, 0x08, 0x12, 0x10, 0x05, 0x00, 0x10, 0xb5, 0x01,
+ 0x42, 0xfb, 0xbf, 0x43, 0x00, 0x09, 0x00, 0x40, 0x81, 0xa8, 0x08, 0x4a,
+ 0xaa, 0x96, 0x90, 0xac, 0x6d, 0x15, 0x22, 0x00, 0x20, 0x09, 0x04, 0x15,
+ 0x80, 0x28, 0xdc, 0x01, 0x24, 0xfb, 0xbf, 0x01, 0x80, 0x04, 0x09, 0x00,
+ 0x40, 0x48, 0x02, 0x45, 0xb2, 0x2e, 0x41, 0x6d, 0xef, 0x05, 0x11, 0x00,
+ 0x40, 0x52, 0x02, 0x15, 0x29, 0x2a, 0xac, 0x42, 0x54, 0xfb, 0x3b, 0x51,
+ 0x84, 0x00, 0x08, 0x00, 0x20, 0x54, 0x80, 0x05, 0xb5, 0x3d, 0xa2, 0xb6,
+ 0xdf, 0x00, 0x20, 0x04, 0x20, 0x49, 0x89, 0xa8, 0x6a, 0x29, 0xac, 0xd6,
+ 0x54, 0xff, 0x3f, 0x84, 0x00, 0x01, 0x04, 0x10, 0x00, 0x94, 0xa8, 0x56,
+ 0xda, 0x5f, 0xab, 0xd5, 0x1e, 0x10, 0x48, 0x00, 0x90, 0x82, 0x48, 0xa8,
+ 0xb2, 0xac, 0xfd, 0x55, 0xd5, 0xfe, 0x9f, 0x80, 0x00, 0x0a, 0x02, 0x08,
+ 0x02, 0x55, 0x5a, 0x75, 0xff, 0xaf, 0xb6, 0xf7, 0x2d, 0x12, 0x92, 0x00,
+ 0x10, 0x20, 0x10, 0xa8, 0x54, 0xd5, 0xbf, 0x5d, 0xad, 0xdd, 0x0f, 0x00,
+ 0x00, 0x04, 0x40, 0x09, 0x84, 0xa8, 0xaa, 0x5a, 0xed, 0xeb, 0x6a, 0xff,
+ 0x9f, 0xa4, 0x24, 0x01, 0x02, 0xa0, 0x20, 0x50, 0x55, 0xd5, 0xbe, 0xae,
+ 0xad, 0xfd, 0x16, 0x00, 0x10, 0x04, 0x20, 0x0a, 0x08, 0xb4, 0xaa, 0x95,
+ 0xaa, 0x7b, 0xb7, 0xdb, 0x5f, 0x92, 0x04, 0x01, 0x84, 0x20, 0x21, 0x51,
+ 0xd5, 0x2a, 0xa9, 0xee, 0xd5, 0xfe, 0x0d, 0x00, 0x20, 0x04, 0x10, 0x00,
+ 0x08, 0x50, 0xe9, 0xd7, 0xd4, 0xfb, 0xb5, 0xff, 0x9f, 0x24, 0x09, 0x01,
+ 0x42, 0x4a, 0xa2, 0x64, 0xd5, 0x55, 0x7b, 0x7f, 0xda, 0x7d, 0x4f, 0x00,
+ 0x20, 0x04, 0x00, 0x80, 0x00, 0xa0, 0x2a, 0x13, 0x84, 0x6a, 0x55, 0xff,
+ 0x1d, 0x48, 0x8a, 0x00, 0x94, 0x24, 0x8a, 0xc8, 0xaa, 0x42, 0x20, 0x5d,
+ 0xf5, 0xff, 0x5f, 0x01, 0x00, 0x02, 0x01, 0x00, 0x20, 0xa2, 0x4a, 0x1a,
+ 0x82, 0x56, 0xda, 0xbd, 0x3f, 0x92, 0x92, 0x00, 0x90, 0x92, 0x00, 0x40,
+ 0x95, 0x6a, 0xf4, 0x55, 0x6d, 0xff, 0xd6, 0x00, 0x00, 0x0a, 0x04, 0x20,
+ 0x14, 0x49, 0x4b, 0xaa, 0xaa, 0x56, 0xf5, 0xff, 0xbf, 0xab, 0xa4, 0x00,
+ 0x20, 0x89, 0x40, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xde, 0xbf, 0xeb, 0x03,
+ 0x00, 0x02, 0x04, 0x02, 0x0a, 0x10, 0x2b, 0x2a, 0x55, 0x5b, 0xf5, 0xff,
+ 0xd7, 0x2f, 0x92, 0x00, 0x10, 0x28, 0x21, 0x01, 0x56, 0x95, 0xa0, 0x56,
+ 0xdf, 0xef, 0xea, 0x87, 0x40, 0x0a, 0x42, 0x41, 0x00, 0x90, 0xaa, 0x52,
+ 0xb6, 0xad, 0xfa, 0xff, 0xd5, 0x2f, 0x14, 0x00, 0x00, 0x04, 0x95, 0x04,
+ 0xaa, 0xac, 0x55, 0x6b, 0xff, 0xb7, 0xea, 0x9f, 0x40, 0x02, 0x28, 0x51,
+ 0x00, 0x40, 0x58, 0xd5, 0xda, 0xd6, 0x6e, 0x7f, 0xf9, 0x3f, 0x12, 0x04,
+ 0x02, 0x04, 0x49, 0x25, 0x55, 0xaa, 0x77, 0xab, 0xff, 0x2b, 0xfd, 0x3f,
+ 0x48, 0x01, 0x20, 0x41, 0x00, 0x00, 0x58, 0xa9, 0xda, 0xea, 0xfd, 0xaf,
+ 0xfa, 0xff, 0x02, 0x04, 0x08, 0x14, 0x29, 0x49, 0x52, 0x55, 0x55, 0x55,
+ 0xff, 0x8d, 0xfe, 0x3f, 0xa8, 0x00, 0x02, 0x41, 0x00, 0x02, 0xa0, 0xa2,
+ 0xaa, 0xea, 0xff, 0x53, 0xfd, 0xff, 0x02, 0x04, 0x50, 0x04, 0x25, 0xa8,
+ 0x54, 0x49, 0x52, 0xb5, 0xbf, 0x8a, 0xfe, 0xff, 0xa9, 0x08, 0x04, 0x50,
+ 0x80, 0x02, 0xa1, 0x2a, 0x95, 0xea, 0xff, 0xa1, 0xff, 0xff, 0x03, 0x02,
+ 0x90, 0x02, 0x09, 0x08, 0x44, 0x49, 0x52, 0xbd, 0x7f, 0xca, 0xff, 0xff,
+ 0x2b, 0x09, 0x04, 0x48, 0x40, 0x82, 0x90, 0x56, 0xa9, 0xf6, 0xbf, 0xd0,
+ 0xff, 0xff, 0x47, 0x00, 0x50, 0x02, 0x15, 0x11, 0x40, 0x95, 0xaa, 0xfd,
+ 0x2f, 0xe9, 0xff, 0xff, 0x8f, 0x0a, 0x84, 0x50, 0x40, 0x84, 0x14, 0xaa,
+ 0x6a, 0xff, 0x5f, 0xf2, 0xff, 0xff, 0x7f, 0x00, 0x10, 0x02, 0x09, 0x10,
+ 0x40, 0x7d, 0xf7, 0xff, 0x0b, 0xfc, 0xff, 0xff, 0xaf, 0x02, 0x84, 0x50,
+ 0x42, 0x85, 0x12, 0xd0, 0xdd, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0xff, 0x04,
+ 0x00, 0x0a, 0x08, 0x10, 0x48, 0xf8, 0xff, 0xff, 0x0a, 0xfe, 0xff, 0xff,
+ 0x7f, 0x03, 0xa4, 0x80, 0xa2, 0x8a, 0x02, 0x68, 0xff, 0xff, 0x52, 0xfd,
+ 0xff, 0xff, 0xff, 0x07, 0x00, 0x2a, 0x08, 0x20, 0x28, 0xdc, 0xff, 0x5f,
+ 0x05, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x92, 0x40, 0x22, 0x09, 0x02, 0xea,
+ 0xfb, 0xaf, 0x48, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x12, 0x81, 0xa0,
+ 0x48, 0x9c, 0x6e, 0x93, 0xa2, 0xff, 0xff, 0xff, 0xff, 0x07, 0xa8, 0x40,
+ 0x28, 0x0a, 0x02, 0x74, 0xb5, 0x45, 0x81, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x02, 0x0a, 0x81, 0x20, 0x08, 0xae, 0xaa, 0x90, 0xe8, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x90, 0x40, 0x28, 0x88, 0x12, 0x58, 0x15, 0x50, 0xd0, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x44, 0x0a, 0x41, 0x21, 0x08, 0xae, 0x04, 0x14,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40, 0x14, 0x88, 0x04, 0xba,
+ 0x02, 0x28, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x42, 0x15, 0x41, 0x21,
+ 0x05, 0xad, 0x00, 0x05, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40,
+ 0x24, 0x8a, 0x0e, 0x36, 0x00, 0x0a, 0xf4, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x42, 0x25, 0x90, 0xd0, 0x8b, 0xc2, 0x41, 0x05, 0xfc, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x10, 0x08, 0x05, 0xe8, 0x8e, 0x58, 0x80, 0x02, 0xfa, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x4a, 0x20, 0xa8, 0xba, 0x0b, 0x2b, 0x51, 0x01,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x8a, 0x02, 0xe8, 0xaf, 0x84,
+ 0x90, 0x04, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x52, 0x21, 0x54, 0xbf,
+ 0x1f, 0x15, 0xa5, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x08,
+ 0x01, 0xfa, 0xb6, 0xa4, 0x52, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x4a, 0xa2, 0x54, 0xef, 0x5f, 0x4b, 0xa4, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x80, 0x10, 0x82, 0xfe, 0xbf, 0x92, 0x52, 0x42, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x12, 0x42, 0xa8, 0xbf, 0x1f, 0x24, 0x80, 0xa0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28, 0x8a, 0xf7, 0x37, 0x80,
+ 0x52, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x82, 0xe0, 0xff,
+ 0x1f, 0x00, 0x20, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28,
+ 0xca, 0xff, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x10, 0x42, 0xf0, 0xfd, 0x1b, 0x00, 0x50, 0xf0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0xa4, 0x10, 0xc5, 0xff, 0x1f, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0x22, 0xf8, 0xff, 0x0e, 0x00, 0x00, 0xf0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xaa, 0x88, 0xe2, 0xff, 0x0f, 0x10,
+ 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x25, 0xfa, 0xff,
+ 0x0f, 0x01, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xfb,
+ 0xfb, 0xff, 0x7f, 0x5d, 0xd5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f};
diff --git a/blt/demos/bitmaps/fish/left.xbm b/blt/demos/bitmaps/fish/left.xbm
new file mode 100644
index 00000000000..84d3c67d2ae
--- /dev/null
+++ b/blt/demos/bitmaps/fish/left.xbm
@@ -0,0 +1,8 @@
+#define fc_left_width 16
+#define fc_left_height 16
+#define fc_left_x_hot 8
+#define fc_left_y_hot 8
+static char fc_left_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x04, 0x70, 0x82, 0x8c, 0xe2,
+ 0x22, 0x9c, 0x2d, 0x90, 0x2d, 0x80, 0x21, 0x90, 0x22, 0x9c, 0x1c, 0xe3,
+ 0x70, 0x82, 0x80, 0x02, 0x00, 0x07, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/left1.xbm b/blt/demos/bitmaps/fish/left1.xbm
new file mode 100644
index 00000000000..077c5c9f260
--- /dev/null
+++ b/blt/demos/bitmaps/fish/left1.xbm
@@ -0,0 +1,8 @@
+#define fc_left1_width 16
+#define fc_left1_height 16
+#define fc_left1_x_hot 8
+#define fc_left1_y_hot 8
+static char fc_left1_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x02, 0xe0, 0x14, 0x10, 0x1f,
+ 0xd8, 0x14, 0xd8, 0x14, 0x08, 0x10, 0x08, 0x14, 0xd0, 0x14, 0x70, 0x1e,
+ 0x60, 0x13, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/left1m.xbm b/blt/demos/bitmaps/fish/left1m.xbm
new file mode 100644
index 00000000000..c05c9b09518
--- /dev/null
+++ b/blt/demos/bitmaps/fish/left1m.xbm
@@ -0,0 +1,8 @@
+#define fc_left1m_width 16
+#define fc_left1m_height 16
+#define fc_left1m_x_hot 8
+#define fc_left1m_y_hot 8
+static char fc_left1m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xe0, 0x17, 0xf0, 0x1f,
+ 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xf0, 0x1f,
+ 0xe0, 0x13, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/leftm.xbm b/blt/demos/bitmaps/fish/leftm.xbm
new file mode 100644
index 00000000000..5b587364c3c
--- /dev/null
+++ b/blt/demos/bitmaps/fish/leftm.xbm
@@ -0,0 +1,8 @@
+#define fc_leftm_width 16
+#define fc_leftm_height 16
+#define fc_leftm_x_hot 8
+#define fc_leftm_y_hot 8
+static char fc_leftm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x07, 0xf0, 0x83, 0xfc, 0xe3,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xe3,
+ 0xf0, 0x83, 0x80, 0x03, 0x00, 0x07, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/mid.xbm b/blt/demos/bitmaps/fish/mid.xbm
new file mode 100644
index 00000000000..e7a3c6ea1d7
--- /dev/null
+++ b/blt/demos/bitmaps/fish/mid.xbm
@@ -0,0 +1,8 @@
+#define fc_mid_width 16
+#define fc_mid_height 16
+#define fc_mid_x_hot 8
+#define fc_mid_y_hot 8
+static char fc_mid_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x03, 0x40, 0x04, 0x20, 0x08,
+ 0xe0, 0x0e, 0xe0, 0x0e, 0x20, 0x08, 0xa0, 0x0b, 0xe0, 0x0f, 0xa0, 0x0b,
+ 0x40, 0x04, 0x80, 0x03, 0x80, 0x03, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/midm.xbm b/blt/demos/bitmaps/fish/midm.xbm
new file mode 100644
index 00000000000..227afe27a23
--- /dev/null
+++ b/blt/demos/bitmaps/fish/midm.xbm
@@ -0,0 +1,8 @@
+#define fc_midm_width 16
+#define fc_midm_height 16
+#define fc_midm_x_hot 8
+#define fc_midm_y_hot 8
+static char fc_midm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f,
+ 0xe0, 0x0f, 0xe0, 0x0f, 0xe0, 0x0f, 0xe0, 0x0f, 0xe0, 0x0f, 0xe0, 0x0f,
+ 0xc0, 0x07, 0x80, 0x03, 0x80, 0x03, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/right.xbm b/blt/demos/bitmaps/fish/right.xbm
new file mode 100644
index 00000000000..4fb6b80f863
--- /dev/null
+++ b/blt/demos/bitmaps/fish/right.xbm
@@ -0,0 +1,8 @@
+#define fc_right_width 16
+#define fc_right_height 16
+#define fc_right_x_hot 8
+#define fc_right_y_hot 8
+static char fc_right_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x20, 0x03, 0x41, 0x0e, 0x47, 0x30,
+ 0x39, 0x44, 0x09, 0xb4, 0x01, 0xb4, 0x09, 0x84, 0x39, 0x44, 0xc7, 0x38,
+ 0x41, 0x0e, 0x40, 0x01, 0xe0, 0x00, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/right1.xbm b/blt/demos/bitmaps/fish/right1.xbm
new file mode 100644
index 00000000000..7858fa52426
--- /dev/null
+++ b/blt/demos/bitmaps/fish/right1.xbm
@@ -0,0 +1,8 @@
+#define fc_right1_width 16
+#define fc_right1_height 16
+#define fc_right1_x_hot 8
+#define fc_right1_y_hot 8
+static char fc_right1_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0x03, 0x28, 0x07, 0xf8, 0x08,
+ 0x28, 0x1b, 0x28, 0x1b, 0x08, 0x10, 0x28, 0x10, 0x28, 0x0b, 0x38, 0x0e,
+ 0xc8, 0x06, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/right1m.xbm b/blt/demos/bitmaps/fish/right1m.xbm
new file mode 100644
index 00000000000..75cda362c5d
--- /dev/null
+++ b/blt/demos/bitmaps/fish/right1m.xbm
@@ -0,0 +1,8 @@
+#define fc_right1m_width 16
+#define fc_right1m_height 16
+#define fc_right1m_x_hot 8
+#define fc_right1m_y_hot 8
+static char fc_right1m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x03, 0xe8, 0x07, 0xf8, 0x0f,
+ 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x0f, 0xf8, 0x0f,
+ 0xc8, 0x07, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/fish/rightm.xbm b/blt/demos/bitmaps/fish/rightm.xbm
new file mode 100644
index 00000000000..4f8ee37e76b
--- /dev/null
+++ b/blt/demos/bitmaps/fish/rightm.xbm
@@ -0,0 +1,8 @@
+#define fc_rightm_width 16
+#define fc_rightm_height 16
+#define fc_rightm_x_hot 8
+#define fc_rightm_y_hot 8
+static char fc_rightm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xe0, 0x03, 0xc1, 0x0f, 0xc7, 0x3f,
+ 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc7, 0x3f,
+ 0xc1, 0x0f, 0xc0, 0x01, 0xe0, 0x00, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/greenback.xbm b/blt/demos/bitmaps/greenback.xbm
new file mode 100644
index 00000000000..f54211c6584
--- /dev/null
+++ b/blt/demos/bitmaps/greenback.xbm
@@ -0,0 +1,885 @@
+#define greenback_width 499
+#define greenback_height 210
+static char greenback_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x44,
+ 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x09,0x10,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
+ 0x20,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x40,0x00,
+ 0x08,0x20,0x20,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,
+ 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x11,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x48,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x20,0x02,0x04,
+ 0x48,0x10,0x02,0x01,0x01,0x09,0x00,0x00,0x80,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x22,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x04,0x04,0x48,0x10,0x01,
+ 0x41,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x10,0x40,0x00,0x00,0x00,
+ 0x40,0x00,0x02,0x08,0x48,0x00,0x02,0x80,0x20,0x00,0x00,0x80,0x04,0x00,0x00,
+ 0x01,0x40,0x00,0x00,0x04,0x10,0x00,0x44,0x00,0x00,0x00,0x10,0x14,0x22,0x54,
+ 0xa4,0xb6,0xf7,0xfe,0x6f,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0x9f,0xaa,0xd9,
+ 0x95,0xef,0xfe,0xba,0xda,0xd7,0x56,0x77,0xaf,0x56,0x7d,0x81,0x50,0x45,0x15,
+ 0xc9,0x00,0x00,0x00,0x6d,0xb5,0xd3,0x95,0xab,0xaa,0x35,0xad,0xaa,0x4a,0x29,
+ 0x40,0x92,0x40,0x80,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x20,0x11,0x22,0x12,0x00,0x21,0x25,0x55,0xa8,0xa4,0x54,0xa9,0xaa,0x92,0x7e,
+ 0x6f,0x7b,0xed,0xbe,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x88,0x00,0x42,0xa4,0x00,0x08,
+ 0x41,0x00,0x00,0x01,0x00,0x81,0x80,0xa4,0x54,0x5b,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xbf,0x08,0x00,0x10,0xfd,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0xbb,0xf7,0x80,0x84,
+ 0x28,0x22,0x00,0x06,0x14,0x0c,0x68,0x04,0x08,0x80,0xf7,0xf7,0xfd,0xff,0xbf,
+ 0xfb,0xff,0xf3,0xff,0xff,0xbb,0xea,0xef,0xbb,0xbf,0x00,0x00,0x00,0xdd,0xbf,
+ 0xfe,0xbf,0xfe,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xef,0xe7,0x8a,0x5a,
+ 0x82,0x92,0xb4,0x40,0x48,0x40,0x7f,0x7b,0x54,0x3e,0x02,0x06,0x00,0x07,0x00,
+ 0x00,0x0c,0x06,0x03,0x23,0x08,0x1e,0x30,0x8e,0xe1,0x84,0x08,0x0e,0xe7,0xbf,
+ 0xbe,0xef,0x7b,0xf5,0xda,0xb5,0xbd,0x5f,0xbf,0xff,0xee,0xae,0xbf,0x00,0x00,
+ 0x00,0xf5,0x5d,0x9f,0xdf,0xdf,0xff,0xff,0xef,0xbf,0x63,0xeb,0xfe,0xbf,0xd6,
+ 0xe5,0xc0,0x18,0x1c,0x83,0xc1,0xe0,0x38,0x1a,0x3c,0xf0,0x81,0x3f,0x18,0x18,
+ 0x1c,0x83,0x7f,0xf0,0x1c,0x06,0x02,0x77,0x78,0x3e,0x61,0x83,0x43,0x82,0x19,
+ 0x2c,0x67,0xd5,0x6f,0x53,0xb7,0xdd,0xff,0xe5,0x67,0xed,0xee,0xae,0xe7,0x7d,
+ 0xab,0x08,0x00,0x00,0xbd,0xdf,0xaf,0x7f,0xbb,0xfd,0xee,0xcf,0xfb,0xdf,0xbf,
+ 0xff,0xfd,0xfb,0xe7,0xc1,0x19,0x2c,0x87,0xc1,0x61,0x3b,0x1c,0x1c,0xf0,0xc1,
+ 0x7f,0x38,0x38,0x6c,0x01,0xf0,0xa0,0x1d,0x06,0x06,0x7f,0xc8,0x7f,0xc0,0x83,
+ 0xc3,0x96,0x9b,0xf0,0xe7,0xfe,0xdd,0xdd,0xfd,0xdf,0xae,0xa4,0xbb,0xba,0xbf,
+ 0xdd,0x77,0xff,0xbb,0x00,0x00,0x00,0xf5,0x6e,0xaf,0x6f,0xfe,0x5f,0xfb,0xd7,
+ 0xbf,0x7b,0xff,0x4e,0x77,0xef,0xe5,0x01,0x1e,0xe2,0x87,0xc3,0x21,0x3f,0x80,
+ 0xdf,0xe0,0xe1,0xff,0x00,0x3e,0xe4,0x03,0x60,0x10,0x3f,0x82,0x0f,0x7a,0xd0,
+ 0xfe,0x01,0x83,0xc3,0x87,0x1f,0x6c,0xe7,0x5b,0x7f,0xbf,0xb7,0xad,0x7b,0xe6,
+ 0xad,0x6a,0xd7,0xfb,0xcd,0xbe,0xbd,0x20,0x00,0x24,0x5d,0xbf,0xb6,0x5f,0xdd,
+ 0x57,0xfd,0x95,0xff,0xf7,0xdb,0xbd,0xbf,0xde,0xe7,0xc1,0x9e,0x6c,0x87,0xc1,
+ 0x61,0x3b,0x08,0x2e,0xe0,0xe1,0x7f,0x18,0x3c,0x6c,0x17,0x60,0xa2,0x1d,0x06,
+ 0x0f,0x3c,0x78,0x7e,0x03,0x83,0xc3,0x03,0x1f,0x3c,0xf7,0xfe,0xdb,0xfb,0xee,
+ 0xff,0x7f,0xe6,0xdf,0xf7,0xf6,0x76,0xcb,0xbe,0xbd,0x02,0x00,0x00,0xf5,0x7b,
+ 0x33,0xb5,0x5e,0xeb,0x57,0x97,0xdf,0xb5,0x7f,0xff,0xb5,0x5f,0xe3,0xc0,0x1f,
+ 0x1c,0x83,0xc1,0xe0,0x38,0x18,0xe6,0xc0,0x61,0x3c,0x10,0x18,0x0c,0xf3,0x70,
+ 0x30,0x0c,0x24,0x1e,0x1e,0x08,0x1e,0x46,0x8f,0xf1,0x03,0x86,0x04,0xa7,0xdd,
+ 0xb6,0xfe,0xba,0xaf,0x7f,0x67,0xf7,0xdf,0xbf,0xdb,0xdb,0x7f,0xbe,0x00,0x00,
+ 0x00,0xdd,0xfc,0xaa,0x2e,0xff,0xfa,0xff,0x27,0x7f,0x7b,0xb3,0x77,0x7b,0xdb,
+ 0x67,0x10,0x06,0x00,0x49,0xba,0x2a,0x08,0x25,0x80,0x00,0x84,0x2c,0x64,0x00,
+ 0x50,0x57,0xbf,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xe7,0xfe,0xea,0xff,0xb3,0xff,0x6f,0xd7,0xfa,0xb5,0xf6,0xeb,0x9a,0x9f,
+ 0xb6,0x20,0x00,0x00,0xbd,0x79,0x3b,0xb6,0xde,0xde,0xdb,0x35,0xef,0xf6,0x5d,
+ 0xf7,0xdb,0x7e,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0xff,0xff,0xbf,0x47,0x6d,0xff,0xba,
+ 0xff,0xdb,0x97,0xbe,0x08,0x00,0x00,0xcd,0xf6,0xb6,0xae,0xad,0xf7,0x76,0x35,
+ 0xfe,0xff,0xff,0xff,0xff,0xff,0xa7,0x56,0x7b,0x55,0x92,0x52,0x40,0x22,0x01,
+ 0x08,0xa4,0x54,0x4a,0x01,0x12,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xe0,0xe7,
+ 0xd6,0xef,0xe7,0x91,0x35,0x57,0xbe,0x00,0x00,0x00,0xfd,0xe6,0x3c,0xf7,0x5a,
+ 0xfc,0xcf,0x32,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x01,
+ 0x00,0x00,0x42,0x04,0xb5,0xd2,0xaa,0xfa,0xef,0xff,0xf7,0xf7,0xbd,0x55,0xdd,
+ 0xb6,0xad,0xc7,0xf3,0xfe,0xfe,0xe3,0x9d,0x6b,0xbe,0x00,0x00,0x40,0xdd,0xde,
+ 0x77,0x8e,0xc5,0x7f,0xaf,0x5f,0xf2,0xff,0xff,0xff,0xff,0xbf,0xed,0x75,0xab,
+ 0xd6,0x5a,0x55,0x8b,0x54,0x55,0xa9,0xab,0xaa,0xaa,0xaa,0x00,0x02,0x08,0x80,
+ 0x10,0x10,0x00,0x84,0x20,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xa2,0xd3,0xfd,0x4b,0x49,0x40,0x77,0x7f,0xbe,0x02,0x00,
+ 0x00,0xfd,0xd6,0xbd,0x81,0xb6,0xfd,0xaf,0x3b,0x02,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x04,0x20,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0xe7,0x74,0x01,0x00,0x04,0x00,0x20,
+ 0xbe,0x00,0x00,0x08,0xed,0xaa,0x7d,0x48,0x1d,0xee,0x6f,0x7e,0x36,0x40,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x04,0x00,0x00,0x00,0x41,0x20,0x84,0x00,0x02,0x00,0x00,0x08,0x10,0x00,0x20,
+ 0x42,0x02,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x55,0xf7,0xa7,0xad,
+ 0xfe,0x6d,0x79,0xbf,0x40,0x00,0x00,0xfd,0x96,0x3d,0x82,0xeb,0xba,0x7f,0x2b,
+ 0x15,0x02,0x21,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x24,0x22,0x00,
+ 0x00,0x00,0x00,0x28,0x00,0x42,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,
+ 0x00,0x44,0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x21,0xb0,0x67,
+ 0xa7,0xe7,0xff,0xf7,0xff,0x1b,0xbf,0x00,0x00,0x00,0xed,0xb4,0x0d,0xfc,0x14,
+ 0xf4,0xed,0x7c,0x82,0x10,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80,0x80,0x24,
+ 0x00,0x00,0x04,0x01,0x00,0x20,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x40,0x00,
+ 0x00,0x02,0x00,0x08,0x00,0x02,0x80,0x10,0x80,0x40,0x04,0x11,0x20,0x40,0x10,
+ 0x08,0xa9,0xad,0xe5,0xb7,0xff,0x7b,0xff,0x5a,0xb3,0x08,0x00,0x00,0xdd,0x2a,
+ 0x01,0x7f,0xff,0xe9,0xef,0xb8,0x0a,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x08,
+ 0x20,0x08,0x80,0x08,0x00,0x40,0x00,0x40,0x00,0x00,0x09,0x01,0x20,0x00,0x00,
+ 0x10,0x00,0x00,0xc0,0x40,0x42,0x02,0x0a,0xa8,0x60,0xeb,0x7f,0x5a,0x1b,0xd5,
+ 0xa1,0x0a,0x0c,0x00,0xa5,0xaf,0x53,0xb3,0xde,0xfa,0x57,0xdb,0xbf,0x00,0x00,
+ 0x20,0xfd,0x4a,0x93,0x6f,0xf7,0xc9,0xaf,0x78,0x03,0x40,0x24,0x8a,0x02,0x40,
+ 0x01,0x25,0x21,0x15,0xa5,0x02,0x52,0x49,0x05,0xad,0x00,0x80,0x11,0x52,0x42,
+ 0x41,0xb5,0xfd,0x17,0xbd,0x07,0x10,0x45,0x95,0x00,0x10,0x00,0x49,0x55,0xd1,
+ 0xea,0x4a,0xfd,0xc8,0x16,0x28,0x00,0xda,0x7d,0xe5,0x52,0xff,0xb3,0x7f,0xd2,
+ 0xbb,0x00,0x00,0x08,0xad,0x1a,0xc8,0xdb,0xef,0x87,0xaf,0xb8,0x2e,0xf8,0xff,
+ 0x3f,0x3f,0x3f,0x17,0xf0,0x79,0x5e,0xcf,0xf7,0xff,0xbc,0x9e,0xdf,0x04,0x68,
+ 0xfe,0xff,0x87,0xe1,0xef,0x78,0x5c,0x8c,0x0b,0x34,0x16,0x8e,0x0b,0x5c,0xf6,
+ 0xf2,0xc0,0xe1,0xf2,0x78,0x79,0x74,0x5c,0x5c,0x00,0xe5,0xff,0xe2,0x79,0xdf,
+ 0xcc,0xb7,0xe7,0xbf,0x00,0x00,0x00,0xfd,0x65,0xe4,0x7f,0xfe,0x1e,0xaf,0x7d,
+ 0x0b,0x78,0x67,0x3c,0x1e,0x3e,0x07,0xf8,0x60,0xbe,0x86,0xf7,0xef,0x38,0x5c,
+ 0x1e,0x0b,0x70,0x9c,0xbb,0xc7,0xe5,0xde,0x71,0x9c,0x07,0x03,0x3d,0xae,0x8e,
+ 0x43,0x98,0xf8,0xe5,0xd9,0xe9,0xf4,0x78,0x7a,0x7a,0x4a,0x1c,0x00,0x6d,0x3f,
+ 0x13,0x38,0xa9,0xf9,0xad,0x85,0xb6,0x09,0x00,0x00,0x5d,0x69,0x75,0xb3,0xd4,
+ 0x9b,0x0e,0xbb,0x01,0xb8,0xef,0xbc,0x9e,0x9c,0x6e,0xf0,0x29,0x3d,0xa1,0x77,
+ 0xce,0x38,0x1b,0x1e,0x2f,0x3c,0xb1,0x79,0xd3,0x73,0xce,0x70,0x3e,0xe7,0x36,
+ 0x9c,0x5e,0x4f,0x13,0x3e,0xe2,0xe9,0xf4,0xe9,0xf4,0xfe,0x7a,0x3a,0x95,0xbe,
+ 0x41,0xb9,0x5d,0x30,0xdf,0x76,0xb7,0xd7,0x1f,0xf6,0x00,0x00,0x04,0xad,0x12,
+ 0xb2,0x7e,0xbe,0x39,0xae,0x71,0x08,0x10,0xe7,0x3e,0x9e,0x9c,0xa6,0xf0,0x34,
+ 0x7d,0x94,0x33,0xcf,0x38,0x5c,0x9f,0x4f,0x78,0xbd,0x39,0xd6,0x63,0x9e,0x79,
+ 0x18,0x4f,0x27,0x1d,0x9f,0x8e,0x12,0x3e,0xf1,0xe9,0xec,0xd9,0xfc,0x7c,0x3a,
+ 0x3e,0xcb,0x3d,0x02,0x7a,0x5b,0xa5,0xff,0xfc,0x08,0x4e,0x7f,0xbf,0x00,0x00,
+ 0x00,0xbd,0x37,0xb9,0xda,0x5e,0xea,0x4c,0x73,0x05,0x99,0x67,0xbc,0x9f,0x9e,
+ 0x26,0xf0,0x8c,0xfa,0xb0,0x37,0xcf,0x3c,0x8b,0x1e,0xcf,0xfc,0x85,0x78,0xd7,
+ 0x73,0x4f,0x71,0x0a,0xff,0x10,0x9e,0x3f,0x4e,0x33,0x39,0xd3,0xe3,0xc5,0x59,
+ 0xf8,0x7a,0x7e,0x3d,0x41,0x78,0x02,0xe6,0xbe,0xe2,0xb7,0x70,0xa5,0xe6,0xbb,
+ 0xb7,0x00,0x00,0x00,0xdd,0x2d,0x3d,0xb7,0xfa,0x79,0xcd,0xb3,0x28,0x54,0x6f,
+ 0x3e,0x9e,0xdc,0x6a,0xf0,0x2e,0xf1,0xd0,0x27,0xcf,0x3c,0x4b,0x9f,0x8f,0xf8,
+ 0x9f,0x3c,0xa5,0x27,0xdf,0x78,0x83,0xff,0x33,0x3e,0x9f,0x7e,0x10,0x78,0xd2,
+ 0xeb,0xec,0x1f,0xfb,0x3e,0x79,0x7c,0xd3,0x78,0x06,0x6a,0x5d,0x62,0xfa,0x6c,
+ 0x00,0xb6,0x97,0xbf,0x00,0x00,0x00,0xbd,0x9b,0x5c,0x7a,0x5f,0xf6,0x5a,0x33,
+ 0x0d,0x40,0x47,0xbc,0x9e,0xdc,0x24,0xf0,0x84,0xe2,0x93,0x93,0x9e,0xb8,0x53,
+ 0x1f,0x5f,0xf0,0xff,0x7a,0x05,0xa7,0x8e,0x74,0xd3,0xfe,0x17,0x9f,0x3e,0x6f,
+ 0xae,0xf1,0x8d,0xe7,0xc4,0xd9,0xf4,0x9c,0x7d,0x3d,0xb5,0x7c,0x04,0xec,0x59,
+ 0x73,0x9f,0x39,0x00,0xd7,0x9d,0xbd,0x10,0x00,0x08,0xad,0x4e,0xbe,0x1d,0x50,
+ 0xdd,0xb1,0x5b,0x06,0xa0,0x2f,0x3f,0x9e,0x9c,0x28,0xf0,0x24,0xdb,0xb3,0x77,
+ 0x4f,0x3a,0x21,0x9e,0xcf,0x88,0x3f,0x3a,0x89,0xaf,0xce,0x75,0x3a,0xa0,0x07,
+ 0x9e,0x9e,0x4e,0x81,0xfa,0x94,0xf7,0xec,0x89,0xf0,0x78,0x38,0x3e,0x19,0xf0,
+ 0x04,0x54,0x57,0x33,0xfd,0x06,0x00,0xeb,0xaf,0xbb,0x04,0x00,0x00,0xfd,0x4f,
+ 0xce,0xd5,0xcb,0xfa,0x35,0x13,0x0d,0x81,0x47,0x3c,0x9f,0x3c,0x0c,0xf2,0x24,
+ 0xe1,0xd3,0xa7,0xce,0x3c,0x14,0x9e,0x8e,0x04,0x3c,0x79,0x52,0x2f,0x0f,0x79,
+ 0x00,0x82,0x27,0x3d,0x9f,0x4e,0x86,0xf0,0xcc,0xe7,0xcd,0xc9,0xf2,0x7a,0x7b,
+ 0x39,0x91,0xe1,0x09,0x95,0xa7,0xb6,0xfb,0x11,0x10,0x6e,0x47,0xbf,0x00,0x00,
+ 0x00,0x5d,0xa3,0xdf,0x09,0xc0,0xad,0x77,0xab,0x06,0x88,0x67,0xbe,0x9e,0x9e,
+ 0x4e,0xf0,0x8d,0x92,0xab,0x17,0x4f,0x38,0x39,0x1f,0x4f,0xd8,0x3a,0x3d,0x0d,
+ 0x5e,0xde,0x70,0xba,0x79,0x66,0x9e,0x9e,0xce,0xc3,0xe4,0x99,0xf3,0xc4,0xfb,
+ 0xf4,0x7a,0x7e,0x7a,0xc6,0xfc,0x09,0xd8,0x6a,0xbd,0x5e,0x0f,0x01,0xdf,0x65,
+ 0xbf,0x00,0x00,0x00,0xfd,0x27,0xd7,0x06,0xd0,0xf7,0xeb,0x0a,0x2a,0x85,0x27,
+ 0xbc,0x1e,0xbd,0x4e,0xf0,0x24,0xb3,0xd3,0x13,0xcf,0x3c,0x9f,0x9e,0xcf,0x98,
+ 0x32,0x7b,0xca,0x9e,0x9e,0x75,0x1e,0x53,0x47,0x3c,0x8f,0x8e,0xa2,0xee,0x91,
+ 0xea,0xd5,0xe9,0xf8,0x7c,0x79,0x76,0xca,0xf0,0x09,0x30,0x75,0x9b,0xfa,0x1e,
+ 0x80,0xb6,0x57,0xbb,0x00,0x00,0x00,0xbd,0x65,0xfb,0x09,0xd0,0x5d,0xf7,0x62,
+ 0x07,0xa0,0x6f,0x3d,0x9f,0x1e,0x4f,0xe0,0x09,0x47,0xd3,0xb7,0x9f,0xba,0x3c,
+ 0x1e,0xa3,0x38,0x3b,0x7d,0x1a,0x9d,0x1f,0x79,0xbe,0xcf,0x31,0x34,0xe7,0x7f,
+ 0xf8,0xf5,0x7b,0xf9,0xfa,0xab,0x74,0xf5,0xac,0xec,0xb1,0x96,0x32,0x60,0xaf,
+ 0xed,0x54,0xbf,0x00,0xdf,0xd2,0xbf,0x12,0x00,0x00,0xed,0x96,0x6b,0x82,0xc0,
+ 0xb7,0x4e,0xa2,0x85,0xa0,0x1f,0xff,0xbf,0xbf,0x8f,0xd0,0x8b,0x1f,0xe6,0xaf,
+ 0xbf,0x7e,0x9f,0xdf,0x51,0xfc,0x8c,0xfd,0x5e,0x7f,0x7f,0xfd,0x1a,0xbf,0x28,
+ 0xa4,0x21,0x84,0x00,0x09,0x80,0x01,0x00,0x00,0x00,0x04,0x00,0x12,0x00,0x00,
+ 0x10,0x61,0x9a,0x7d,0xb6,0x2d,0x10,0x77,0x99,0xbb,0x00,0x00,0x08,0xad,0x93,
+ 0xd6,0x24,0xd8,0xfd,0xf5,0xd5,0x25,0x20,0x40,0x00,0x00,0x00,0x40,0x00,0x60,
+ 0x40,0x10,0x00,0x00,0x02,0x80,0x00,0x2c,0x00,0x20,0x02,0x80,0x00,0x00,0x02,
+ 0x00,0x01,0x14,0x10,0xd0,0x28,0x61,0x50,0x55,0xbe,0xaf,0xba,0xae,0x95,0xb6,
+ 0x4d,0x7e,0x6b,0x37,0x40,0x6a,0x9f,0xed,0xbd,0x00,0xbf,0xeb,0xb7,0x00,0x00,
+ 0x00,0xfd,0x92,0xfd,0x01,0xe0,0x1f,0x6d,0x97,0x05,0x44,0xa5,0x2a,0xa0,0xaa,
+ 0x52,0xc0,0xa0,0x52,0xb4,0x52,0x4a,0x69,0x55,0x15,0x29,0x20,0x68,0x00,0x84,
+ 0x24,0xa9,0x55,0xf7,0x4a,0x0b,0x90,0x2c,0x57,0x81,0x47,0x55,0x61,0x55,0xeb,
+ 0x52,0xeb,0xda,0xa6,0x45,0xad,0xea,0x80,0x59,0x8c,0x6a,0x37,0x00,0xfb,0x7b,
+ 0xbe,0x10,0x00,0x00,0xfd,0xa9,0xb3,0x0f,0xd0,0xab,0xdd,0xe9,0x0a,0x00,0x5b,
+ 0xd5,0xaf,0x5a,0xad,0x00,0x5d,0xac,0x28,0x6d,0xb4,0x52,0x55,0x55,0x0b,0xc0,
+ 0x17,0xf8,0xb5,0xea,0x6a,0x53,0x4d,0xb9,0x02,0x40,0x05,0x54,0x02,0x95,0xaa,
+ 0x4a,0xad,0x2a,0x45,0x15,0xa5,0x0a,0x02,0x83,0x04,0x08,0xff,0x4d,0xb1,0x9f,
+ 0x00,0x5e,0x41,0xbd,0x00,0x00,0x00,0xad,0xd9,0xcf,0x06,0xf2,0x53,0xee,0x6d,
+ 0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xa8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,
+ 0x4e,0x6a,0x3d,0x88,0xab,0x35,0xb6,0x00,0x00,0x00,0x7d,0xa9,0xbd,0x13,0xc0,
+ 0x09,0xd3,0x7a,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x10,0x00,
+ 0x10,0x05,0x78,0x35,0x6d,0xb7,0x00,0x1e,0x2d,0xb7,0x08,0x00,0x00,0x9d,0xa9,
+ 0x3f,0x06,0xd0,0x7c,0xa2,0x6b,0x10,0x90,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x20,
+ 0x08,0x00,0x00,0x00,0x80,0x42,0x00,0x00,0x00,0x20,0x00,0x00,0x80,0x00,0x00,
+ 0x00,0x00,0x20,0x00,0x18,0x62,0xfd,0xf2,0xb0,0x80,0xe2,0xc9,0xbb,0x00,0x00,
+ 0x20,0xdd,0xa8,0xb6,0x15,0xf2,0x1d,0xf2,0xbe,0x82,0x00,0x20,0x00,0x00,0x00,
+ 0x00,0x80,0x00,0x00,0x02,0x00,0x04,0x00,0x40,0x40,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x10,0x00,0x02,0x02,0x80,0x10,0x08,0x00,0x00,0x11,0x00,0x04,0x41,0x00,
+ 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x90,0xc8,0xec,0xf5,0x35,0x20,0xf7,0xe9,
+ 0xbd,0x00,0x00,0x04,0xbd,0xa9,0xaf,0x85,0xc0,0x35,0xd4,0x54,0x28,0x80,0x00,
+ 0x10,0x11,0x01,0x04,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x09,0x00,0x00,0x00,
+ 0x00,0x04,0x00,0x00,0x00,0x10,0x40,0x20,0x22,0x80,0x00,0x00,0x40,0x00,0x21,
+ 0x01,0x00,0x00,0x08,0x20,0x04,0x88,0x08,0x02,0x00,0xbc,0xe5,0x1f,0xf1,0xbd,
+ 0x08,0x6b,0xe9,0xbf,0x22,0x00,0x00,0xdc,0x98,0xb7,0x17,0xd8,0x7d,0xae,0xad,
+ 0x02,0x20,0x00,0x04,0x00,0x20,0x01,0x10,0x00,0x00,0x00,0x24,0x00,0x49,0x00,
+ 0x00,0x00,0x22,0x04,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x40,
+ 0x10,0x00,0x00,0x00,0x20,0x84,0x80,0x00,0x80,0x00,0x00,0x20,0x04,0x64,0x92,
+ 0x1d,0x65,0x2d,0x00,0xde,0x65,0xb5,0x00,0x00,0x00,0xdd,0x28,0xfb,0x07,0xc0,
+ 0xfb,0x6c,0x23,0x5a,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x20,0x01,0x00,0x00,
+ 0x10,0x00,0x00,0x10,0x20,0x00,0x20,0x02,0x10,0x00,0x40,0x80,0x08,0x00,0x00,
+ 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0xb8,0xa7,0xdb,0xf3,0xb6,0x80,0x66,0xf1,0xbb,0x08,0x00,0x00,0x7d,0x4c,
+ 0x5f,0x17,0xd0,0x5b,0xe4,0x00,0x20,0x01,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x20,0x02,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x10,0x00,
+ 0x00,0x44,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x40,0x10,
+ 0x00,0x00,0x00,0x00,0x68,0x55,0xda,0xe3,0x1e,0x00,0xdf,0xf5,0xbd,0x00,0x00,
+ 0x00,0x9d,0x58,0xf7,0x82,0xd0,0x9f,0x2d,0x9f,0x97,0x06,0x00,0x00,0x38,0x15,
+ 0x62,0x1f,0x9f,0xfc,0x8a,0xcb,0xbe,0x87,0x20,0x00,0x00,0x00,0x00,0x40,0x00,
+ 0x00,0x00,0x10,0x90,0x00,0x22,0x0a,0x09,0x92,0x10,0x84,0x20,0x00,0x80,0x10,
+ 0x00,0x04,0x00,0x20,0x82,0x08,0x00,0xc0,0x75,0xeb,0x62,0xbd,0x00,0x5a,0x75,
+ 0xb7,0x02,0x00,0x00,0xed,0xc8,0x94,0x15,0xe4,0x55,0xc9,0x3c,0x10,0x8c,0x00,
+ 0x00,0x90,0x35,0x0e,0x09,0x8d,0xd4,0x0b,0xe9,0xaa,0x23,0x00,0xa0,0x2a,0xad,
+ 0x57,0x09,0x8a,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
+ 0x00,0x24,0x04,0x44,0x00,0x00,0x08,0x20,0x00,0x02,0x00,0xde,0xae,0xe7,0x3a,
+ 0x88,0xd7,0x9b,0xbf,0x10,0x00,0x00,0x7d,0xd8,0x2c,0x04,0xd0,0x58,0xab,0x77,
+ 0xa0,0x0c,0x00,0x00,0x41,0x00,0x26,0x2b,0x89,0x71,0x0d,0x81,0x9a,0x13,0x00,
+ 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x48,0xa5,0xa2,0x4a,0x45,0x00,
+ 0x84,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x20,0x70,
+ 0xcd,0x62,0xad,0x00,0x4b,0xb5,0xbb,0x0a,0x00,0x00,0x9d,0xa8,0xb1,0x07,0xe0,
+ 0xbb,0xca,0x99,0xd2,0x11,0x04,0x10,0x00,0x14,0x00,0x10,0x00,0x00,0x40,0x00,
+ 0x00,0x00,0x01,0x40,0xc7,0x9f,0x7c,0xef,0x87,0xff,0xff,0xe3,0x97,0xbd,0xfc,
+ 0xe5,0x02,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x40,
+ 0x00,0x42,0xa0,0xff,0xe3,0x1d,0x00,0xb6,0x5b,0xbd,0x00,0x00,0x00,0x6c,0x1c,
+ 0xdb,0x15,0xd1,0xbb,0xd1,0xf6,0x85,0x3b,0x00,0x01,0x00,0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x50,0x00,0x00,0x33,0x1f,0x34,0xeb,0xf1,0xff,0xff,0x8f,
+ 0x77,0x5d,0xfd,0xd0,0x05,0x00,0x00,0x00,0x80,0x40,0x00,0x00,0x00,0x01,0x80,
+ 0x00,0x00,0x02,0x40,0x80,0x6a,0xbb,0x73,0xb6,0x90,0xae,0xf3,0xbf,0x00,0x00,
+ 0x00,0xfd,0x88,0xea,0x07,0xd0,0xfb,0xe9,0xa9,0xa2,0x6e,0x00,0x10,0x11,0x01,
+ 0x08,0x25,0x00,0x02,0x42,0x10,0x2a,0x09,0x0c,0x40,0xfd,0xe0,0xb7,0x7f,0xfe,
+ 0xff,0xff,0x7f,0xfc,0xcb,0x27,0xbf,0x04,0x80,0x10,0x04,0x20,0x00,0x08,0x00,
+ 0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0xd6,0xee,0xf3,0x3d,0x00,0xab,0x93,
+ 0xbf,0x00,0x00,0x00,0x6d,0x58,0xd8,0x05,0xc4,0xdd,0xab,0xe2,0x4f,0x7f,0x02,
+ 0xb7,0xa5,0xc4,0xed,0x37,0x3e,0xd6,0x67,0x72,0x6c,0xab,0x0f,0x49,0xeb,0xe5,
+ 0xd7,0x1b,0xff,0xff,0xff,0xff,0xd1,0xeb,0xcf,0xbf,0x05,0x00,0x00,0x00,0x04,
+ 0x00,0x80,0x10,0x10,0x00,0x00,0x08,0x08,0x00,0x00,0x00,0xb8,0x5d,0x73,0x97,
+ 0x00,0xde,0x73,0xb5,0x02,0x00,0x40,0x6d,0x32,0x7f,0x97,0xd0,0x7b,0xeb,0xcd,
+ 0xf2,0x5a,0x00,0x8e,0x61,0xc4,0xed,0x32,0x9e,0x52,0xa1,0x56,0x7c,0x2d,0x0d,
+ 0x00,0x3f,0xeb,0xf9,0xe7,0xff,0xff,0xff,0xff,0x47,0xdf,0xaa,0xee,0x02,0x00,
+ 0x44,0x80,0x00,0x40,0x20,0x00,0x02,0x20,0x00,0x80,0x00,0x20,0x00,0x00,0x42,
+ 0xb7,0xf9,0xbe,0x90,0xee,0xf3,0xbf,0x20,0x00,0x10,0x7c,0x30,0xbe,0x07,0xd2,
+ 0xff,0xd3,0xc2,0xc7,0xf5,0x20,0xab,0x71,0x44,0xa5,0x02,0x24,0x96,0x65,0x54,
+ 0x42,0x65,0x05,0x40,0xc3,0xee,0xf7,0xe2,0xff,0x7f,0xf5,0xff,0x9f,0xb5,0xab,
+ 0x83,0x01,0x09,0x00,0x20,0x00,0x08,0x04,0x10,0x40,0x02,0x00,0x00,0x40,0x08,
+ 0x00,0x10,0xa0,0xfb,0xd9,0x2a,0x04,0xf7,0xa2,0xbd,0x08,0x00,0x00,0xfd,0x5a,
+ 0xbc,0x04,0xf0,0xda,0xeb,0x94,0x0d,0x68,0x00,0x22,0x08,0xca,0x64,0xb2,0x66,
+ 0x52,0x82,0x14,0x40,0x14,0x04,0x00,0x3c,0x6b,0xbd,0xfc,0xff,0x02,0x80,0xff,
+ 0x3f,0xbe,0xcd,0x3c,0x02,0x80,0x40,0x08,0x00,0x02,0x00,0x00,0x00,0x80,0x88,
+ 0x00,0x08,0x00,0x08,0x00,0xb4,0xee,0xf9,0xb6,0xa0,0xea,0xe7,0xb7,0x02,0x00,
+ 0x00,0xcd,0x30,0xbd,0x17,0xd1,0x2c,0xaa,0xe5,0x27,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x43,0x77,0x3d,0xff,0x0b,
+ 0x0a,0x21,0xfc,0xff,0x68,0xef,0x80,0x00,0x00,0x10,0x00,0x80,0x80,0x00,0x08,
+ 0x42,0x08,0x00,0x08,0x00,0x01,0x00,0x01,0x51,0xfb,0xf9,0xb1,0x00,0xd6,0xe7,
+ 0xbe,0x00,0x00,0x00,0xfd,0x68,0xcc,0x06,0xd0,0xfd,0xeb,0x8c,0x0d,0x00,0x08,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x40,0x80,0xb6,
+ 0x16,0xff,0xf5,0x00,0x00,0xf1,0xff,0xf1,0x7a,0x02,0x20,0x00,0x00,0x08,0x20,
+ 0x02,0x00,0x20,0x10,0x00,0x01,0x05,0x00,0x28,0x40,0x10,0x8c,0xb5,0x59,0x39,
+ 0x90,0x66,0xe5,0xbb,0x00,0x00,0x00,0xad,0xd8,0xbe,0x13,0xd0,0xe7,0xdb,0xe4,
+ 0x06,0x12,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
+ 0x88,0x01,0x7a,0xc7,0x7f,0x83,0x90,0x44,0x82,0xff,0xcf,0x6f,0xc3,0x03,0x00,
+ 0x00,0xfa,0x80,0x8f,0x3e,0x3e,0xe4,0xd1,0xc7,0x9d,0x3e,0x78,0xc1,0x10,0x60,
+ 0xbd,0x6c,0x36,0x82,0xbe,0xcf,0xb5,0x40,0x00,0x04,0xed,0xe8,0xf6,0x07,0xe2,
+ 0x4b,0xd9,0xb4,0x87,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x00,
+ 0x11,0x00,0x10,0x40,0x85,0xd6,0xe3,0xbf,0x14,0x00,0x42,0xc0,0xff,0xcf,0x7c,
+ 0x23,0x06,0x20,0x10,0xa4,0x80,0x19,0x3a,0x20,0x30,0x43,0x84,0x00,0x22,0xca,
+ 0xc0,0x10,0xc5,0xda,0xbc,0x5d,0x00,0x6a,0xdb,0xbb,0x00,0x00,0x20,0x99,0x31,
+ 0x57,0x07,0xd0,0xbb,0xee,0xaa,0x0b,0x00,0x08,0x00,0x00,0x00,0x08,0x40,0x20,
+ 0x42,0x00,0x10,0x00,0x04,0x00,0x60,0x09,0xfa,0xf8,0xbf,0x89,0x42,0x00,0x01,
+ 0xff,0xbf,0x6b,0xd3,0x0a,0x02,0x00,0x09,0x81,0x10,0x03,0x10,0x10,0x22,0x84,
+ 0x00,0x60,0x84,0x40,0x10,0x22,0x2c,0x56,0x47,0xad,0xf8,0x9c,0xbd,0x20,0x00,
+ 0x00,0xfd,0x32,0xff,0x91,0xd0,0x7f,0x56,0xd8,0x0b,0x00,0x00,0x81,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x01,0x44,0x02,0xde,0xfd,0x9f,0x02,
+ 0x04,0x48,0x24,0xff,0x3f,0x7f,0x51,0x8d,0x00,0x00,0x88,0x00,0x18,0x23,0xa0,
+ 0x00,0x40,0x86,0x00,0x20,0x48,0xc0,0x18,0x62,0x4a,0xf6,0x03,0x00,0xd0,0x9b,
+ 0xbf,0x04,0x00,0x00,0xdc,0xf1,0x75,0x0f,0xd0,0x9f,0x7a,0xb5,0x05,0x10,0x41,
+ 0x00,0x40,0x10,0x01,0x08,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x90,0x08,0x7a,
+ 0xfe,0x77,0x5c,0x11,0x02,0x01,0xf8,0x7f,0x4e,0x23,0x0d,0x00,0x08,0xd4,0x10,
+ 0x0e,0x3c,0x30,0x00,0xc3,0x91,0x1f,0x1c,0x7c,0x40,0x4f,0x08,0x6d,0xab,0xfe,
+ 0xff,0xff,0x2a,0xb7,0x00,0x00,0x00,0xfd,0xf1,0xf6,0x03,0xe4,0x66,0x55,0xa0,
+ 0x07,0x00,0x00,0x00,0x02,0x00,0x40,0x00,0x00,0x10,0x08,0x00,0x00,0x00,0x80,
+ 0xa0,0x88,0x6a,0xfe,0x6b,0x05,0x00,0x40,0x00,0xf4,0x7f,0x5c,0x23,0x09,0x00,
+ 0x00,0xf8,0x04,0x16,0x36,0x24,0xc4,0x91,0x86,0x10,0x38,0xd9,0xc0,0x10,0x12,
+ 0x15,0xeb,0xff,0xff,0xbf,0x35,0xbf,0x40,0x00,0x00,0xfd,0xa5,0xaf,0x44,0x01,
+ 0xef,0x39,0x42,0x05,0x02,0x10,0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x02,
+ 0x81,0x00,0x20,0xc0,0x98,0x9e,0xff,0x5a,0x05,0x90,0x10,0x90,0xf0,0xff,0xf9,
+ 0x12,0x06,0x00,0x01,0x84,0x80,0x18,0x23,0x20,0x70,0x60,0x04,0x10,0x60,0x84,
+ 0xc2,0x10,0x81,0xab,0x5f,0xff,0xff,0xff,0x5e,0xbe,0x00,0x00,0x40,0xac,0xe1,
+ 0x6e,0x10,0x80,0xfa,0x3e,0x80,0x03,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x04,
+ 0x02,0x00,0x40,0x00,0x10,0x00,0x00,0x88,0xda,0xbf,0x55,0x50,0x00,0x04,0x22,
+ 0xc4,0xff,0xb3,0x32,0x00,0x00,0x00,0x08,0x01,0x10,0x41,0x20,0x10,0x64,0x0c,
+ 0x08,0x00,0x84,0x40,0x10,0xd3,0x90,0x7b,0x45,0xa1,0xdd,0x77,0xbe,0x40,0x00,
+ 0x08,0x7d,0xe9,0xdf,0xff,0xff,0xff,0x36,0x80,0x01,0x80,0x00,0x00,0x10,0x00,
+ 0x00,0x04,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x48,0xcf,0x7f,0xc6,0x0a,
+ 0x14,0x41,0x08,0xc1,0xff,0xe7,0x10,0x00,0x00,0x80,0x88,0x00,0x18,0x23,0x30,
+ 0x12,0x20,0x04,0x18,0x61,0xcc,0xc0,0x10,0x75,0x34,0xdf,0x7e,0x55,0xdd,0x3f,
+ 0xbe,0x04,0x00,0x02,0xdd,0xe2,0xd7,0xff,0xff,0x6f,0x9d,0x10,0x00,0x20,0x00,
+ 0x20,0x00,0x40,0x10,0x40,0x20,0x00,0x24,0x00,0x00,0x00,0x00,0x10,0x48,0xe7,
+ 0xaf,0x35,0x05,0x10,0x88,0x41,0x94,0xff,0xcf,0x34,0x80,0x10,0x00,0x7c,0x80,
+ 0x0f,0x3e,0xae,0xf0,0xc3,0x83,0x07,0x1e,0x38,0xc0,0x10,0x0b,0x40,0xd4,0xf5,
+ 0xfb,0xf7,0x8c,0xbf,0x10,0x00,0x00,0xfa,0xcb,0x3d,0xad,0x7f,0x79,0x0d,0x00,
+ 0x00,0x01,0x00,0x00,0x02,0x00,0x02,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
+ 0x04,0x0c,0xf3,0x9f,0x0a,0x01,0x40,0x20,0x04,0x01,0xff,0xcf,0x14,0x00,0x00,
+ 0x08,0x68,0x80,0x02,0x0c,0xfc,0xd8,0x02,0x41,0x05,0x14,0x40,0x00,0x00,0x05,
+ 0x80,0xc9,0xea,0xde,0xda,0xc0,0xb7,0x00,0x00,0x00,0x9d,0xca,0xaf,0x7e,0xeb,
+ 0xba,0x0f,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xa4,0xf9,0x55,0xa1,0x08,0x0d,0x00,0x00,0x24,0xdf,0x9f,
+ 0x2d,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,
+ 0x10,0x40,0x02,0x40,0xa2,0x7a,0xf3,0x67,0xf2,0xbf,0x00,0x00,0x00,0xfc,0x97,
+ 0x37,0x6b,0x6f,0xbd,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x42,
+ 0x00,0x00,0x82,0x00,0x04,0x00,0x00,0xa8,0xf9,0x37,0x54,0x10,0x90,0x20,0xa8,
+ 0x00,0xfc,0x1f,0x2b,0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x08,0x10,
+ 0x20,0x00,0x00,0x00,0x10,0x90,0x00,0x96,0xea,0xfd,0x57,0x7b,0xbf,0x44,0x00,
+ 0x00,0xad,0x57,0xcf,0x7d,0xfd,0x4c,0x83,0x00,0x42,0x00,0x02,0x40,0x20,0x08,
+ 0x00,0x92,0x00,0x04,0x00,0x20,0x20,0x80,0x00,0x01,0xd6,0xfc,0x6b,0xa9,0x84,
+ 0x04,0x14,0x48,0xa0,0xfc,0x7f,0x4b,0x00,0x00,0x81,0x00,0x00,0x24,0x40,0x00,
+ 0x80,0x40,0x04,0x04,0x04,0x00,0x00,0x00,0x04,0x08,0x88,0xdd,0xbb,0x6d,0xde,
+ 0xb9,0x00,0x00,0x00,0xf9,0x06,0x5b,0x3f,0xef,0xd9,0x0b,0x00,0x00,0x20,0x00,
+ 0x04,0x08,0x02,0x80,0x00,0x00,0x00,0x80,0x00,0x08,0x00,0x20,0x00,0xc2,0xfe,
+ 0xa7,0x5c,0x0e,0xaa,0x00,0x15,0x09,0xfc,0x7f,0x53,0x00,0x04,0x20,0x00,0x00,
+ 0x00,0x01,0x80,0x24,0x00,0x00,0x01,0x41,0x02,0x00,0x00,0x00,0x80,0x94,0xf4,
+ 0xf3,0xa7,0x7c,0xbb,0x00,0x00,0x00,0x7c,0x4b,0xcf,0x6d,0xba,0xa9,0x01,0x00,
+ 0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x80,0x08,0x00,0x00,0x00,0x08,
+ 0x80,0x62,0xfe,0x8d,0xb2,0x20,0x0a,0xa1,0x00,0x50,0xf9,0xff,0xf4,0x08,0x01,
+ 0x00,0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x40,0x00,0x80,0x08,0x11,0x00,
+ 0x00,0x92,0xf5,0x6a,0xef,0xfe,0xb7,0x08,0x00,0x40,0xad,0x2f,0xf4,0x7e,0x6f,
+ 0xf5,0x05,0x40,0x00,0x08,0x24,0x00,0x00,0x00,0x40,0x00,0x08,0x10,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x2b,0xff,0xb7,0xaa,0x14,0x56,0x48,0x55,0x00,0xf2,0xff,
+ 0xac,0x00,0x00,0x00,0x00,0x04,0xdb,0x10,0x00,0x00,0x04,0x40,0xa8,0xc2,0x12,
+ 0x00,0x00,0x02,0x40,0x88,0xdb,0xed,0xb5,0xfe,0xbf,0x00,0x00,0x00,0xdd,0x0d,
+ 0xff,0x5b,0xfb,0xdb,0x00,0x11,0x00,0x02,0x00,0x20,0x40,0x00,0x08,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x04,0x01,0x10,0x35,0xff,0x2d,0x65,0x81,0x08,0x11,0x00,
+ 0x95,0xf9,0xfb,0x2d,0x00,0x00,0x04,0x00,0x41,0x5a,0x40,0xa9,0x55,0x55,0x4c,
+ 0x47,0x44,0x04,0x00,0x00,0x10,0x09,0x8a,0x7f,0x5b,0x7f,0xb6,0xbb,0x40,0x00,
+ 0x00,0xae,0x7a,0xb2,0xff,0xde,0x6d,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x00,
+ 0x02,0x80,0x80,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0xd0,0xff,0xd5,0x9a,0xa7,
+ 0x52,0x02,0x0a,0x28,0x62,0xff,0x4b,0x01,0x80,0x80,0x40,0x00,0x16,0x33,0x24,
+ 0x8c,0x50,0xb3,0x41,0xc4,0x00,0x40,0x80,0x00,0x00,0x30,0xdf,0xea,0x2f,0x6f,
+ 0xae,0x08,0x00,0x08,0xfd,0xdf,0xfa,0x7e,0xff,0x7e,0x08,0x00,0x08,0x00,0x82,
+ 0x04,0x00,0x90,0x00,0x08,0x00,0x00,0x52,0x08,0x91,0x00,0x00,0x84,0xd8,0xff,
+ 0x9a,0xed,0x2a,0x25,0x24,0xd1,0x44,0xf1,0xff,0x53,0x81,0x00,0x00,0x10,0x00,
+ 0x36,0xf4,0xbd,0xb1,0x40,0xb2,0x45,0x56,0x84,0x10,0x08,0x00,0x80,0x74,0xff,
+ 0xfb,0x37,0xfb,0xbf,0x00,0x00,0x00,0xda,0xdf,0xd4,0x5f,0x3f,0x1f,0x02,0x00,
+ 0x00,0x40,0x00,0x00,0x12,0x02,0x60,0x06,0x24,0x40,0x00,0x00,0x00,0x00,0x10,
+ 0x80,0xca,0xff,0xf5,0xab,0x4b,0x4d,0x45,0x00,0x00,0xc2,0xff,0xb3,0x00,0x08,
+ 0x20,0x00,0x00,0x24,0x94,0x06,0xa5,0x66,0xb2,0x9a,0xd4,0x18,0x04,0x20,0x48,
+ 0x12,0x80,0x7c,0xfb,0x9e,0x97,0xbe,0x00,0x00,0x00,0xbd,0xad,0x94,0x7b,0xf5,
+ 0x8d,0x00,0x00,0x00,0x04,0x08,0x00,0x80,0x00,0x61,0x0c,0x01,0x12,0x00,0x00,
+ 0x00,0x00,0x01,0x00,0xca,0x7f,0x7d,0x55,0xbd,0x96,0x10,0x49,0x49,0xd1,0xff,
+ 0xb7,0x20,0x00,0x04,0x04,0x00,0xa4,0xbd,0xb4,0x2c,0x65,0xda,0xde,0x92,0x0a,
+ 0x00,0x00,0x02,0x00,0xd2,0xfc,0xb3,0xcf,0xdf,0xbe,0x44,0x00,0x00,0xfd,0xb7,
+ 0xb3,0x7c,0xbd,0x03,0x00,0x44,0x90,0x00,0x00,0x80,0x00,0x50,0xff,0xff,0x00,
+ 0x00,0x00,0x02,0x00,0x10,0x40,0x42,0xe5,0xff,0x6b,0xbb,0x53,0x2b,0xaa,0x80,
+ 0x90,0xca,0xbb,0x2f,0x03,0x40,0x00,0x01,0x20,0x10,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xa0,0xf3,0xf5,0xf3,0x7f,0xbe,0x00,0x00,
+ 0x10,0xaa,0x2a,0x63,0x43,0xde,0x09,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,
+ 0xdf,0xf2,0x01,0x00,0x84,0x00,0x00,0x00,0x00,0x50,0xf4,0x7f,0xd7,0xea,0x96,
+ 0xcc,0x3e,0x12,0x04,0xc2,0xff,0x0f,0x01,0x00,0x40,0x00,0x00,0x04,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x42,0x00,0x00,0x14,0xc3,0xca,0xfc,0xac,
+ 0xae,0x00,0x00,0x00,0x6d,0x57,0x8f,0xb8,0xf3,0x02,0x40,0x00,0x00,0x88,0x80,
+ 0x00,0x20,0xf8,0xbe,0xff,0x9e,0x42,0x00,0x80,0x44,0x00,0x40,0x80,0xf2,0xff,
+ 0xbd,0xbe,0xaa,0xb3,0xf4,0xa7,0xf9,0x92,0xff,0x4f,0x05,0x08,0x02,0x80,0x00,
+ 0x01,0x00,0x02,0x42,0x82,0x00,0x90,0x80,0x00,0x40,0x00,0x00,0x41,0xd0,0x1e,
+ 0x3f,0xe4,0x77,0xbc,0x24,0x00,0x00,0xbc,0x77,0x14,0x0b,0x5d,0x00,0x04,0x00,
+ 0x00,0x00,0x10,0x00,0x08,0xdc,0x99,0x93,0x1b,0x00,0x00,0x00,0x00,0x80,0x00,
+ 0xa0,0xfb,0x7f,0x6d,0xa9,0x4a,0xaf,0xe6,0x05,0x7e,0xa5,0xff,0xdf,0x02,0x02,
+ 0x10,0x00,0x40,0x00,0x05,0x80,0x00,0x20,0x40,0x00,0x10,0x00,0x01,0x00,0x00,
+ 0x10,0xa0,0xef,0x8c,0xcb,0x5a,0xbe,0x00,0x00,0x00,0xfd,0xea,0xba,0x74,0x9f,
+ 0x88,0x00,0x10,0x49,0x00,0x00,0x42,0x80,0xec,0x9b,0xe3,0x2e,0x10,0x20,0x00,
+ 0x00,0x08,0x10,0x08,0xfb,0xdf,0xde,0x5a,0xe3,0xfa,0xcd,0xab,0xe7,0x87,0xfb,
+ 0x9f,0x82,0x80,0x00,0x02,0x10,0x40,0x88,0xb0,0x1a,0x50,0xbf,0xbf,0x6f,0x1f,
+ 0x00,0x00,0x00,0x00,0xe8,0xbf,0xc0,0x8b,0xbb,0xbc,0x00,0x00,0x10,0xfa,0xad,
+ 0x7e,0xa4,0x30,0x80,0x04,0x04,0x00,0x42,0x00,0x10,0xc0,0xfb,0xff,0xdf,0xda,
+ 0x03,0x02,0x00,0x00,0x00,0x00,0x42,0xfd,0x77,0x69,0x69,0x88,0x55,0x21,0x8f,
+ 0x0b,0xa5,0xff,0x3f,0x06,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x22,0x00,0x22,
+ 0x40,0x80,0x04,0x00,0x80,0x20,0x02,0xf2,0x7f,0xeb,0x1b,0x2f,0xbc,0x04,0x00,
+ 0x00,0xbe,0xf4,0xae,0x0a,0xd2,0x40,0x80,0x00,0x00,0x00,0x44,0x00,0x82,0x6f,
+ 0xbf,0x7a,0xff,0x01,0x00,0x08,0x08,0x00,0x40,0x40,0xfd,0x6f,0x5f,0xcb,0x65,
+ 0xff,0xbf,0x57,0xb7,0x25,0xfe,0x3f,0x05,0x30,0x80,0x20,0x02,0x10,0x20,0x80,
+ 0x04,0x01,0x15,0x34,0x81,0x02,0x00,0x08,0x00,0x00,0x40,0x7d,0xf6,0x95,0x5f,
+ 0xbd,0x40,0x00,0x00,0xf8,0xfe,0xf7,0x43,0xe0,0xc8,0x30,0x00,0x80,0x08,0x00,
+ 0x00,0x90,0x9f,0xc7,0xf4,0xe5,0x21,0x08,0x02,0x00,0x00,0x01,0xc0,0xfc,0xd5,
+ 0xd0,0x28,0xb2,0xcd,0xc7,0x96,0xfd,0x02,0xff,0x3e,0x05,0x00,0x00,0x02,0x00,
+ 0x20,0x80,0x10,0x18,0x90,0x0a,0x50,0x00,0x04,0x08,0x00,0x00,0x00,0x81,0xf2,
+ 0xf9,0x18,0x1b,0xb6,0x00,0x00,0x00,0x3f,0xff,0xb6,0x0e,0xb0,0xc0,0x40,0x00,
+ 0x22,0x00,0x00,0x04,0x80,0xcd,0x9e,0x32,0xaf,0x02,0x00,0x00,0x01,0x02,0x00,
+ 0xa0,0xfc,0x7b,0xd5,0x82,0x45,0x57,0xcd,0x2e,0xbe,0x2b,0xfc,0x7f,0x05,0x00,
+ 0x10,0x08,0x80,0x14,0x04,0x01,0x25,0x00,0x12,0x60,0x01,0x15,0x02,0x00,0x00,
+ 0x51,0x00,0xc5,0x70,0x15,0x9f,0xbe,0x00,0x00,0x00,0xd8,0x7f,0xb3,0xfd,0xda,
+ 0x80,0x31,0x00,0xc0,0x03,0x00,0x00,0xf8,0xf6,0xfc,0x3f,0xdd,0x9d,0x80,0x00,
+ 0x00,0x00,0x00,0x82,0xde,0xa5,0x16,0xd0,0x78,0xe0,0x11,0x9d,0x40,0x42,0xfa,
+ 0x7f,0x08,0x00,0x00,0x00,0x22,0x02,0x80,0x00,0x48,0x66,0x0a,0x90,0x40,0x04,
+ 0x00,0x01,0x78,0x04,0x08,0xd7,0x67,0x59,0x1f,0xbe,0x00,0x00,0x40,0xfa,0x75,
+ 0x72,0xb7,0xbb,0x00,0x01,0x10,0xf0,0x83,0x10,0x00,0xb1,0xfc,0xff,0x7b,0x7d,
+ 0x0f,0x00,0x00,0x48,0x20,0x24,0xa0,0xfe,0xdb,0xf5,0x93,0x83,0x95,0x26,0x1b,
+ 0x69,0xdd,0xfc,0xf7,0x0a,0x10,0x80,0x80,0x00,0x00,0x00,0x22,0x6a,0x56,0x83,
+ 0x60,0x10,0x0a,0x00,0x80,0xf8,0x00,0x40,0x09,0xe9,0x15,0x5f,0xb6,0x10,0x00,
+ 0x00,0xda,0x3f,0xf3,0x7a,0xf8,0x00,0x00,0x04,0x39,0x0e,0x00,0x22,0xf0,0x6f,
+ 0x0b,0xb0,0x27,0x0d,0x04,0x00,0x02,0x00,0x00,0x40,0xff,0x6d,0x05,0xa4,0x75,
+ 0xa2,0x00,0x2a,0x21,0x22,0xba,0xfd,0x1a,0x00,0x20,0x24,0x00,0x0c,0x80,0x04,
+ 0xd4,0xff,0x3f,0x90,0x00,0x00,0x00,0x00,0x8e,0x03,0x04,0x16,0x86,0x3b,0x0e,
+ 0xb6,0x00,0x00,0x08,0x5e,0x27,0x73,0x4d,0xa8,0x08,0x48,0x00,0x00,0x2c,0x80,
+ 0x00,0x38,0xf3,0x01,0x80,0xf3,0x0f,0x01,0x00,0x00,0x08,0x00,0x50,0xff,0xf7,
+ 0x3e,0x79,0xb9,0x23,0x52,0x15,0x44,0xac,0xf4,0xef,0x14,0x01,0x01,0x00,0x00,
+ 0x01,0x40,0x01,0xfc,0x68,0xf9,0x29,0x01,0x84,0x00,0x00,0x02,0x03,0x10,0x04,
+ 0xba,0x9d,0x5e,0xbf,0x00,0x00,0x00,0xf9,0x3d,0x6b,0x10,0xf9,0x00,0x00,0x00,
+ 0x10,0x0c,0x00,0x00,0xfe,0x67,0x00,0x00,0xaf,0x3a,0x40,0x00,0x00,0x00,0x01,
+ 0x70,0xff,0xff,0xf5,0x6e,0xe5,0x54,0x44,0xb5,0xa4,0x2a,0xeb,0x7b,0x15,0x10,
+ 0x00,0x08,0x80,0x0a,0x42,0x80,0x3c,0xff,0xcf,0x61,0x01,0x24,0x20,0x08,0x00,
+ 0x03,0x80,0x94,0xda,0x35,0x16,0xbb,0x44,0x00,0x00,0x1a,0x3b,0xe2,0x5a,0x2e,
+ 0x00,0x00,0x00,0x82,0x07,0x22,0x80,0x9e,0x7f,0x10,0x10,0xfe,0x69,0x00,0x20,
+ 0x01,0x00,0x00,0x68,0x7f,0xff,0x5a,0xbb,0xf5,0x09,0x21,0x54,0x02,0x94,0x77,
+ 0xdf,0x05,0x00,0x00,0x01,0x10,0x04,0x80,0x83,0xf7,0xbd,0x24,0x5f,0x20,0x00,
+ 0x00,0x02,0xc0,0x81,0x00,0x08,0x34,0xbd,0x8e,0xbf,0x00,0x00,0x00,0x7d,0x39,
+ 0xeb,0xb1,0x3d,0x80,0x44,0x90,0xc0,0x27,0x00,0x08,0x9e,0x3f,0x00,0x01,0xfc,
+ 0x7d,0x00,0x02,0x08,0x12,0x10,0xa9,0xdf,0xff,0xef,0x75,0x75,0x57,0x44,0x25,
+ 0xd4,0x5a,0xca,0xf7,0x35,0x00,0x08,0x00,0x80,0x02,0x40,0x8a,0xbb,0xfb,0xfa,
+ 0x5e,0x01,0x08,0x00,0x00,0xf0,0x00,0x48,0x32,0x68,0x3f,0xae,0xbb,0x20,0x00,
+ 0x00,0xfa,0xa0,0x73,0xde,0x0f,0x10,0x00,0x00,0xf0,0x00,0x00,0x01,0xfc,0x1f,
+ 0x7f,0x9e,0xf8,0x17,0x08,0x00,0x00,0x00,0x04,0xa8,0xff,0xfb,0xff,0xdd,0xf5,
+ 0x57,0x84,0x58,0x2a,0xda,0xf6,0xff,0x21,0x00,0x02,0x10,0x01,0x4c,0x44,0xf5,
+ 0xec,0xd4,0xfb,0x5a,0x80,0x08,0x00,0x40,0x3c,0x00,0x00,0xd0,0xa0,0xba,0xd2,
+ 0xbd,0x00,0x00,0x10,0xfd,0x29,0x63,0xad,0x8f,0x00,0x10,0x04,0x30,0x00,0x41,
+ 0x40,0xee,0x1f,0x7c,0x3c,0xe8,0xbb,0x40,0x00,0x00,0x00,0x00,0xa8,0xfd,0xdf,
+ 0xfd,0xfb,0xaa,0xad,0x18,0x95,0x88,0x7a,0xe9,0xfd,0x29,0x88,0x00,0x04,0x80,
+ 0x13,0x40,0x72,0xbf,0x2f,0xdf,0xf6,0x11,0x08,0x08,0x10,0x0c,0x20,0x40,0x70,
+ 0x65,0x3e,0x96,0xb7,0x14,0x00,0x00,0xba,0xed,0x75,0xda,0x00,0x04,0x82,0x00,
+ 0x18,0x20,0x00,0x80,0xbb,0x07,0x38,0x3c,0xf0,0xef,0x00,0x00,0x00,0x00,0x00,
+ 0xa0,0xef,0xef,0xff,0xaf,0x55,0x2b,0x51,0x5a,0xa4,0xfa,0xee,0xff,0x69,0x00,
+ 0x40,0x00,0x10,0x04,0x80,0xb4,0xeb,0xfe,0x75,0x59,0x00,0x00,0x00,0x00,0x06,
+ 0x08,0x08,0x82,0x83,0x35,0xd7,0xbe,0x00,0x00,0x00,0xed,0xd1,0x73,0x7e,0x01,
+ 0x00,0x00,0x00,0x19,0x00,0x00,0x88,0xe7,0x0e,0xbc,0x3c,0x72,0xe6,0x01,0x20,
+ 0x01,0x80,0x80,0x84,0xff,0xfe,0x7e,0xa6,0xf6,0x5b,0x26,0x25,0x88,0x7a,0xe9,
+ 0xf7,0x29,0x20,0x00,0x00,0x41,0x05,0x40,0x9f,0xb6,0x7b,0xc7,0xf5,0x01,0x00,
+ 0x00,0x04,0x06,0x00,0x40,0xa0,0x0e,0xad,0xea,0xb5,0x04,0x00,0x00,0xda,0x8c,
+ 0xf9,0x3c,0x04,0x01,0x04,0x40,0xf8,0x0f,0x04,0x02,0x5d,0x0b,0x78,0xbc,0xc0,
+ 0xb3,0x00,0x04,0x00,0x10,0x10,0x90,0xff,0xff,0xdf,0x9f,0xab,0x5e,0x14,0x5b,
+ 0x52,0xdb,0x7b,0xbf,0x69,0x00,0x00,0x00,0x40,0x11,0x44,0xfd,0xee,0xef,0x3f,
+ 0xbb,0x93,0x00,0x80,0x00,0xfe,0x03,0x01,0x04,0x71,0xbe,0xed,0xb6,0x00,0x00,
+ 0x00,0xbf,0x39,0xb3,0xdf,0x41,0x90,0x40,0x08,0x00,0x89,0x40,0x00,0x76,0x47,
+ 0x3c,0x3c,0xe0,0xbe,0x08,0x01,0x20,0x04,0x04,0x94,0xef,0xad,0xff,0xbf,0xf5,
+ 0x59,0x83,0xff,0xaa,0x7a,0xfb,0xff,0x5b,0x04,0x00,0x08,0x40,0x06,0x41,0x27,
+ 0xfb,0xde,0xfe,0x66,0x02,0x08,0x11,0x00,0x20,0x04,0x08,0x48,0xe0,0x1d,0xfb,
+ 0xbf,0x00,0x00,0x00,0xea,0x28,0xb3,0x6f,0x08,0x00,0x00,0x00,0x40,0x00,0x00,
+ 0x00,0x6e,0x07,0x3c,0x3c,0xe0,0xde,0x40,0x00,0x00,0x00,0x01,0x94,0x7b,0xff,
+ 0xf7,0xaf,0xf7,0xb7,0xa4,0xdd,0x46,0xdd,0xff,0xff,0x5b,0x00,0x42,0x00,0x49,
+ 0x11,0x40,0xf5,0xbf,0x9f,0xdf,0xb7,0x43,0x00,0x00,0x00,0x01,0x00,0x02,0x41,
+ 0xe1,0xbb,0xda,0xbd,0x20,0x00,0x20,0xfa,0x9c,0xeb,0xe3,0x00,0x00,0x00,0x02,
+ 0x02,0x20,0x08,0x80,0xef,0x07,0xf8,0x07,0xa0,0xa2,0x00,0x00,0x00,0x00,0x00,
+ 0xdc,0xff,0xb7,0xfe,0xff,0x6c,0x6d,0x91,0xfe,0xa9,0xba,0xff,0xfd,0x4b,0x10,
+ 0x00,0x40,0x80,0x16,0x40,0xaf,0x0c,0x44,0x84,0xf9,0x8d,0x00,0x00,0x40,0x10,
+ 0x11,0x40,0x10,0xf4,0xba,0x7d,0xbb,0x00,0x00,0x00,0x7e,0x7a,0xff,0x65,0x00,
+ 0x80,0x00,0x40,0x00,0x00,0x00,0xe0,0x67,0x47,0xf8,0x07,0xe0,0xde,0x03,0x01,
+ 0x00,0x22,0x00,0x94,0xdf,0xfd,0xfb,0xff,0xef,0xab,0x26,0xbd,0x89,0xdd,0x7f,
+ 0xdf,0x57,0x81,0x00,0x04,0x40,0x05,0x10,0xcb,0x46,0x37,0x22,0xef,0x0d,0x00,
+ 0x00,0x02,0x00,0x04,0x01,0x83,0x10,0xf7,0xfe,0xb7,0x08,0x00,0x00,0xd9,0x54,
+ 0xd5,0xb0,0x20,0x02,0x10,0x00,0x00,0x04,0x82,0x04,0x7d,0x0f,0x3a,0xbc,0xa8,
+ 0x96,0x10,0x00,0x84,0x00,0x20,0xdd,0x77,0xdf,0xae,0xef,0xd5,0xb5,0x11,0x01,
+ 0x52,0xfe,0xdf,0xff,0x53,0x20,0x10,0x01,0xa0,0x92,0xc0,0x3d,0x47,0xfa,0x05,
+ 0xb6,0xa6,0x90,0x40,0x00,0x00,0x01,0x08,0x1f,0xed,0xb5,0xff,0xbe,0x00,0x00,
+ 0x00,0xfa,0x32,0xbb,0x92,0x04,0x00,0x00,0x02,0x40,0x00,0x20,0x00,0xfd,0x07,
+ 0x38,0x3c,0xe0,0xbe,0x00,0x00,0x01,0x00,0x04,0xdc,0xfd,0xff,0xf7,0xdd,0x55,
+ 0x6f,0x64,0x52,0x88,0xd5,0xfe,0xdb,0x57,0x10,0x00,0x80,0x88,0x14,0xc0,0xcf,
+ 0x93,0xff,0x4f,0xee,0x1e,0x00,0x00,0x00,0x40,0x00,0x00,0x2d,0xa9,0xd7,0x6b,
+ 0xbb,0x00,0x00,0x40,0xfe,0xd4,0x6a,0x19,0x00,0x00,0x02,0x00,0x10,0x80,0x00,
+ 0x00,0x7e,0x07,0x78,0x78,0xe0,0xbe,0x88,0x00,0x00,0x04,0x00,0xea,0xff,0xff,
+ 0xdf,0xbe,0xef,0x5f,0x8a,0x04,0x56,0xfd,0xf7,0xff,0x17,0x00,0x00,0x00,0x20,
+ 0x05,0x80,0xaf,0xc7,0x62,0x30,0xfa,0xab,0x00,0x08,0x00,0x00,0x40,0x00,0xff,
+ 0x70,0x2f,0x8f,0xb5,0x00,0x00,0x04,0xda,0xaa,0x7e,0x9a,0x00,0x00,0x00,0x41,
+ 0x04,0x00,0x00,0x00,0xfc,0x0f,0x3a,0x78,0xc0,0xbf,0x00,0x10,0x00,0x40,0x08,
+ 0xda,0xff,0xb7,0xfd,0xd7,0xfb,0xd4,0x24,0x89,0x28,0xbe,0x7f,0xbd,0xd7,0x00,
+ 0x00,0x04,0x80,0x12,0xc0,0xaa,0x47,0x6e,0x12,0xde,0x1d,0x00,0x00,0x00,0x08,
+ 0x08,0x84,0xac,0x81,0x7b,0x87,0xbf,0x20,0x00,0x00,0xfb,0x6d,0xab,0x7c,0x00,
+ 0x20,0x00,0x10,0x00,0x44,0x08,0x84,0xef,0x0b,0x78,0x78,0xf2,0xf7,0x01,0x00,
+ 0x80,0x00,0x00,0xea,0xbf,0xff,0xef,0xfd,0x56,0x57,0xed,0xff,0x5f,0xfa,0xff,
+ 0xf7,0x06,0x20,0x02,0x81,0x20,0x15,0xc0,0xaa,0xa5,0x35,0xbc,0xde,0xb2,0x88,
+ 0x00,0x20,0x00,0x00,0x00,0x7b,0xf5,0xbf,0xf7,0xbf,0x08,0x00,0x00,0xba,0xb1,
+ 0x3d,0x8d,0x80,0x08,0x00,0x00,0x00,0x00,0x40,0x00,0xfa,0x4f,0x3c,0x7c,0xf0,
+ 0xf7,0x08,0x02,0x00,0x10,0x80,0xea,0xef,0xff,0x7b,0xaf,0xff,0xaf,0x09,0x40,
+ 0x02,0xff,0xdd,0xfe,0x97,0x00,0x40,0x00,0x80,0x14,0x68,0xdf,0x67,0x6d,0x4a,
+ 0x36,0x77,0x00,0x40,0x00,0x02,0x00,0x01,0x71,0xe3,0xea,0x8b,0xb5,0x00,0x00,
+ 0x00,0xfa,0x7b,0x35,0x5e,0x04,0x00,0x40,0x00,0x00,0x00,0x00,0x01,0xde,0x1f,
+ 0x38,0x3c,0xe8,0x73,0x80,0x80,0x00,0x02,0x00,0xea,0xff,0xfd,0xff,0xdf,0xfb,
+ 0x5b,0x13,0x08,0x28,0xdd,0xff,0xd7,0x87,0x10,0x10,0x00,0x90,0x05,0xc2,0xdd,
+ 0x56,0x7d,0x6e,0xfe,0x35,0x00,0x00,0x00,0x40,0x42,0x00,0xe6,0x66,0xf7,0x7e,
+ 0xb7,0x00,0x00,0x00,0xda,0xa7,0xad,0x9f,0x00,0x00,0x04,0x04,0x41,0x00,0x08,
+ 0x00,0xfe,0x39,0x7f,0x0f,0xf0,0x1b,0x00,0x00,0x80,0x00,0x22,0xea,0x7e,0xdf,
+ 0xee,0xb6,0x5e,0xbf,0xd4,0x22,0x49,0xff,0xf7,0xfd,0xa6,0x00,0x02,0x80,0xa4,
+ 0x12,0x40,0xf7,0x6b,0xd9,0x59,0xbe,0x57,0x10,0x04,0x00,0x00,0x10,0x10,0x00,
+ 0x97,0xbd,0xdf,0xbb,0x00,0x00,0x00,0xfa,0x37,0x4b,0xcf,0x40,0x00,0x01,0x00,
+ 0x04,0x48,0x42,0x10,0xbe,0x2e,0xff,0x03,0x3e,0x1f,0x08,0x00,0x04,0x00,0x00,
+ 0xea,0xf7,0xff,0xbb,0xdf,0xf6,0x6d,0x25,0x4b,0x12,0xed,0xbd,0xff,0x87,0x00,
+ 0x00,0x00,0x80,0x14,0x40,0xdf,0xc7,0xbe,0x35,0x36,0xb5,0x04,0x00,0x80,0x08,
+ 0x00,0x00,0x95,0x8b,0x7f,0xff,0xbf,0x20,0x00,0x40,0xfa,0x4e,0x8b,0x5f,0x00,
+ 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0x7b,0x00,0x40,0xee,0x6d,0x00,0x00,
+ 0x00,0x00,0x00,0xea,0xff,0xdf,0xff,0xaf,0xbe,0x5f,0xd5,0xd4,0x44,0xff,0xff,
+ 0xb7,0xa7,0x80,0x00,0x10,0x20,0x93,0x70,0x77,0x03,0x77,0x27,0xfe,0x76,0x80,
+ 0x40,0x20,0x00,0x00,0x02,0x4c,0xbd,0xfe,0x7b,0xbd,0x00,0x00,0x10,0xfe,0xef,
+ 0xcd,0x5b,0x10,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0xfe,0xfb,0x00,0x00,0xc5,
+ 0x7f,0x00,0x20,0x00,0x04,0x04,0xaa,0xbf,0xf7,0x5a,0xbd,0xed,0xfb,0x2a,0xa9,
+ 0x55,0xff,0xee,0xfe,0x87,0x08,0x00,0x00,0x80,0x14,0xc0,0xdb,0x47,0xfb,0x0e,
+ 0xae,0x7d,0x00,0x00,0x08,0x00,0x00,0x00,0x48,0x5d,0xb7,0x95,0xbf,0x00,0x00,
+ 0x00,0xda,0x95,0xf5,0x4c,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x41,0xf8,0xcf,
+ 0x01,0xc0,0xdb,0x0f,0x00,0x01,0x01,0x81,0x00,0xea,0xed,0xfd,0xef,0x6f,0xfb,
+ 0xae,0xa5,0x50,0xb4,0xdf,0xff,0xff,0x87,0x00,0x00,0x00,0x40,0x13,0x48,0xff,
+ 0xc5,0xde,0xbd,0x7e,0x17,0x00,0x00,0x00,0x40,0x20,0x00,0x30,0xf5,0xef,0xea,
+ 0xb2,0x10,0x00,0x00,0xfa,0xbf,0xfd,0xb9,0x00,0x21,0x80,0x00,0x00,0x00,0x21,
+ 0x08,0xf0,0xed,0x07,0x60,0xf9,0x07,0x08,0x00,0x40,0x00,0x40,0xea,0xff,0xbf,
+ 0xba,0x5f,0xdd,0xff,0x4d,0x85,0xca,0xff,0xef,0xdd,0xa7,0x00,0x00,0x04,0x41,
+ 0x15,0xc0,0xdf,0xf3,0xee,0x2b,0xbe,0x17,0x00,0x10,0x00,0x04,0x00,0x48,0xb0,
+ 0x7c,0xbd,0xfd,0xbf,0x00,0x00,0x00,0xfa,0xa9,0x1d,0x4c,0x10,0x00,0x12,0x00,
+ 0x00,0x21,0x00,0x00,0x50,0xfd,0xfe,0xff,0x56,0x0e,0x40,0x10,0x10,0x00,0x00,
+ 0xea,0xff,0xdd,0xef,0x7f,0x6b,0xfb,0x2a,0x5a,0xf2,0xb7,0x7d,0xf7,0x87,0x00,
+ 0x81,0x40,0x50,0x05,0xf0,0xde,0x3e,0x33,0xef,0xf6,0x35,0x02,0x04,0x00,0x00,
+ 0x84,0x00,0x84,0xd5,0xd7,0x03,0xba,0x20,0x00,0x00,0x5a,0x2f,0x6d,0x5a,0x04,
+ 0x00,0x00,0x10,0x40,0x00,0x00,0x80,0xf8,0xf7,0xfa,0xdf,0x9c,0x1d,0x01,0x00,
+ 0x00,0x00,0x01,0x62,0x7b,0xaf,0xbd,0xdf,0xec,0xfe,0x7f,0x45,0xfa,0xfd,0xdf,
+ 0x7f,0x97,0x00,0x20,0x00,0x40,0x11,0x82,0x87,0xcf,0xa8,0xac,0x5f,0x11,0x90,
+ 0x00,0x02,0x10,0x00,0x00,0x40,0xee,0xdf,0xc9,0xbd,0x00,0x00,0x00,0xfe,0xdd,
+ 0x3e,0x58,0x01,0x08,0x00,0x04,0x09,0x00,0x80,0x04,0x6a,0xed,0x78,0xb7,0xbe,
+ 0x43,0x10,0x00,0x01,0x20,0x00,0xca,0xff,0xbf,0xff,0x7f,0xdb,0xbf,0x6e,0x55,
+ 0xbd,0xef,0xff,0xfb,0xa7,0x88,0x00,0x00,0x80,0x96,0xc0,0xba,0xb7,0x6e,0x7b,
+ 0x7b,0x8b,0x08,0x00,0x20,0x01,0x00,0x08,0x90,0xd8,0xee,0x74,0xb7,0x08,0x00,
+ 0x00,0x5a,0x73,0xbc,0x5a,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x80,0xcf,
+ 0x7b,0xf3,0x7f,0x01,0x00,0x40,0x00,0x00,0x40,0xea,0xff,0xfb,0xbf,0xbd,0x6e,
+ 0xfb,0xfb,0xcb,0xfa,0xba,0xfb,0xde,0x97,0x00,0x00,0x00,0x40,0x11,0xc0,0xf7,
+ 0x3f,0xc9,0xce,0xef,0x1b,0x08,0x80,0x00,0x00,0x41,0x82,0x00,0x61,0x77,0xa5,
+ 0x9a,0x00,0x00,0x00,0xde,0x73,0x2d,0x5d,0x00,0x40,0x00,0x00,0x00,0x48,0x00,
+ 0x40,0x80,0x4f,0x7f,0xd7,0xe6,0x09,0x00,0x10,0x00,0x01,0x10,0xca,0xbf,0xbf,
+ 0xef,0xff,0xbd,0xef,0xdf,0x7e,0xef,0xff,0xaf,0xff,0x86,0x00,0x00,0x10,0x04,
+ 0x56,0xd0,0x7b,0x2d,0xb8,0xe5,0x7e,0x3f,0x00,0x08,0x00,0x04,0x10,0x00,0x44,
+ 0x60,0xbd,0x10,0xa5,0x00,0x00,0x80,0x5a,0x99,0x3e,0x4c,0x00,0x04,0x40,0x20,
+ 0x00,0x01,0x80,0x00,0x80,0xff,0xf5,0x2f,0xd9,0x01,0x00,0x00,0x00,0x20,0x00,
+ 0xda,0xff,0xfb,0xfe,0xff,0xd2,0xbc,0xff,0xfb,0x7e,0xeb,0xfe,0xdb,0x57,0x40,
+ 0x08,0x00,0x00,0x05,0x84,0x4d,0xcf,0xff,0x13,0x53,0x05,0x04,0x02,0x20,0x21,
+ 0x00,0x00,0x00,0xc1,0x52,0x40,0xb9,0x40,0x00,0x00,0xda,0x6f,0x3d,0x5e,0x80,
+ 0x00,0x00,0x04,0x20,0x00,0x00,0x00,0xa0,0xbc,0x75,0x6e,0x2d,0x00,0x00,0x02,
+ 0x20,0x00,0x00,0xd8,0xfb,0xbf,0x5f,0x7f,0xed,0xfb,0xfa,0x3f,0xff,0xbf,0x77,
+ 0x7f,0x13,0x00,0x00,0x00,0x01,0x15,0x40,0xcb,0x6f,0x82,0x12,0x77,0x05,0x8c,
+ 0x00,0x00,0x00,0x02,0x08,0x08,0xf0,0xd7,0x27,0xbc,0x00,0x00,0x00,0xfa,0xdf,
+ 0x96,0x5c,0x20,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x00,0xf8,0xf7,0xa6,0x1b,
+ 0x24,0x40,0x00,0x00,0x08,0x22,0xda,0xff,0xf7,0xff,0xdf,0xab,0xdd,0xdf,0x6f,
+ 0x6f,0xf7,0xff,0xed,0x57,0x00,0x00,0x02,0x40,0x12,0xd0,0xbb,0xde,0xab,0xce,
+ 0xbb,0x0d,0x20,0x00,0x20,0x00,0x00,0x80,0x42,0xe5,0xfc,0xff,0xbf,0x00,0x00,
+ 0x00,0x7a,0xbf,0x0d,0xce,0x00,0x00,0x00,0x00,0x00,0x20,0x10,0x42,0x08,0xf8,
+ 0xff,0xfc,0x1f,0x00,0x02,0x00,0x08,0x00,0x00,0x9c,0x7f,0xef,0xee,0xf7,0x77,
+ 0x7f,0xfb,0x1a,0xfb,0xfd,0xed,0xbf,0x53,0x10,0x00,0x00,0x90,0x02,0x82,0xbe,
+ 0x3a,0xa0,0xc1,0xf0,0x0f,0x08,0x00,0x09,0x00,0x00,0x01,0x00,0xe0,0xbb,0xf7,
+ 0xbf,0x00,0x00,0x00,0xba,0x6f,0xab,0x5d,0x00,0x20,0x00,0x01,0x20,0x04,0x04,
+ 0x00,0x00,0x00,0xfb,0xcf,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xd4,0xf7,0xbf,
+ 0x7f,0xff,0xab,0xf6,0xb7,0x17,0xff,0xdf,0x7f,0xff,0x5b,0x00,0x00,0x40,0x00,
+ 0x14,0x40,0xc7,0xb7,0xa5,0xfe,0x5e,0x02,0x06,0x00,0x00,0x80,0x10,0x00,0x40,
+ 0x62,0x5e,0xdf,0xaf,0x10,0x00,0x00,0xfa,0x87,0x83,0x5f,0x00,0x00,0x02,0x10,
+ 0x01,0x00,0x00,0x00,0x00,0x00,0xf1,0xcf,0x00,0x00,0x00,0x80,0x00,0x00,0x20,
+ 0x94,0xfd,0xda,0xf7,0xff,0xdf,0xed,0xfe,0x05,0x7f,0xfb,0xde,0xfb,0x53,0x04,
+ 0x22,0x00,0x00,0x09,0x40,0xed,0xef,0xcf,0x37,0xff,0x01,0x09,0x00,0x00,0x02,
+ 0x00,0x40,0x20,0xe5,0xbe,0xbd,0xba,0x00,0x00,0x00,0x5a,0x82,0xa1,0x5e,0x00,
+ 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x61,0x0e,0x00,0x10,0x00,0x00,
+ 0x00,0x01,0x09,0x94,0xbf,0xff,0xdb,0xef,0xd7,0xbf,0xff,0x83,0xff,0xbf,0xf7,
+ 0xff,0x69,0x00,0x00,0x00,0x00,0x02,0x80,0xde,0xde,0x9b,0x9e,0xdb,0x03,0x0d,
+ 0x40,0x40,0x08,0x0f,0x11,0x00,0x70,0xf7,0xab,0xad,0x00,0x00,0x00,0x7a,0xe9,
+ 0x4a,0x4f,0x04,0x00,0x20,0x00,0x84,0x08,0x00,0x11,0x01,0x00,0x20,0x00,0x00,
+ 0x05,0x00,0x00,0x40,0x40,0x00,0x90,0xf7,0xd7,0xfe,0xff,0xaf,0xea,0xb6,0x20,
+ 0xbf,0xed,0xff,0xdf,0x69,0x00,0x00,0x02,0x04,0x08,0x91,0xb0,0x6f,0xfe,0xf3,
+ 0x7f,0x00,0x00,0x10,0x00,0x00,0x01,0x00,0x08,0xa0,0x9b,0xde,0xba,0x00,0x00,
+ 0x00,0xda,0x63,0xe2,0x5f,0x40,0x08,0x01,0x08,0x00,0x02,0x04,0x00,0x20,0x80,
+ 0x04,0x00,0x00,0x00,0x40,0x40,0x08,0x04,0x00,0x90,0xbe,0xff,0xff,0xff,0xbf,
+ 0xff,0x5f,0x04,0xfe,0xff,0xfb,0xf6,0x69,0x00,0x00,0x20,0x00,0x42,0x00,0xb0,
+ 0xbb,0x51,0x7d,0x6b,0x41,0x8e,0x04,0x00,0x89,0x01,0x00,0x40,0xea,0x77,0xb5,
+ 0xbf,0x20,0x00,0x80,0xfa,0xf9,0xff,0x5f,0x00,0x00,0x00,0x40,0x88,0x00,0x40,
+ 0x00,0x00,0x22,0x00,0x10,0x10,0x00,0x11,0x00,0x00,0x00,0x00,0xa0,0xef,0x6d,
+ 0xb5,0xfa,0xff,0x6a,0x17,0xa0,0xee,0x7b,0xff,0xff,0x29,0x00,0x01,0x00,0x80,
+ 0x04,0x40,0xc9,0xfe,0xfa,0xff,0x7b,0x81,0x04,0x00,0x24,0x80,0x40,0x4b,0x01,
+ 0xe0,0xb7,0x6f,0xaf,0x08,0x00,0x00,0xfa,0x62,0xb5,0x4f,0x01,0x41,0x00,0x01,
+ 0x00,0x20,0x00,0x04,0x04,0x00,0x00,0x00,0x02,0x40,0x00,0x10,0x00,0x00,0x00,
+ 0xa9,0xff,0xfb,0xef,0xdf,0xbf,0xbd,0x2d,0x00,0x7b,0xdf,0xdf,0xff,0x21,0x08,
+ 0x00,0x00,0x02,0x20,0x90,0xc0,0xfd,0x46,0xea,0x18,0x81,0x01,0x00,0x00,0x08,
+ 0x51,0xab,0x88,0xf0,0xdf,0xbb,0xbc,0x00,0x00,0x00,0xba,0x2e,0xee,0x5b,0x10,
+ 0x10,0x14,0x10,0x02,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,
+ 0x00,0x00,0x00,0x28,0x77,0xdf,0xf7,0xff,0x3b,0x56,0x00,0xa1,0xcf,0xfb,0x7b,
+ 0xff,0x25,0x00,0x00,0x04,0x00,0x10,0x20,0xa0,0xd7,0xde,0x3b,0x6f,0x41,0x06,
+ 0x00,0x80,0x00,0x47,0x93,0x00,0x42,0x7f,0xff,0xbf,0x08,0x00,0x00,0xfa,0xd6,
+ 0xfb,0x5d,0x00,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x00,0x00,0x42,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x10,0x40,0x68,0xff,0x7f,0xfe,0x6f,0xff,0xbf,0x11,0x80,
+ 0xdf,0xde,0xfe,0xfb,0x14,0x02,0x00,0x01,0x00,0x20,0x14,0xa4,0xae,0x7b,0x3d,
+ 0x1b,0x01,0x20,0x04,0x01,0x00,0x00,0x00,0x00,0xe0,0x7e,0xf7,0xba,0x00,0x00,
+ 0x20,0x3a,0x7f,0xfb,0x4e,0x00,0x00,0x80,0x00,0x08,0x00,0x02,0x40,0x40,0x04,
+ 0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x69,0xff,0xef,0xdb,0xfe,0x3f,
+ 0xa5,0x00,0xe9,0xdf,0xf7,0xef,0xdf,0x16,0x01,0x40,0x40,0x40,0x00,0x03,0xa8,
+ 0xfe,0xaa,0xd2,0x55,0x2a,0x09,0x20,0x00,0x08,0x00,0x00,0x20,0xb8,0xb5,0xbf,
+ 0xb7,0x00,0x00,0x00,0xba,0xf7,0xbf,0x5d,0x00,0x00,0x01,0x00,0x00,0x82,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x40,0x00,0x80,0x52,0x7f,0x77,
+ 0xff,0xff,0xbe,0x2d,0x40,0xd0,0xdd,0x7a,0x7f,0xf7,0x92,0x00,0x10,0x00,0x10,
+ 0x00,0x00,0x00,0xc0,0xff,0x3f,0x00,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x04,
+ 0x66,0xc2,0xef,0xbd,0x40,0x00,0x00,0x7a,0xbf,0xdf,0x4e,0x00,0x09,0x00,0x08,
+ 0x01,0x20,0x20,0x10,0x08,0x40,0x04,0x00,0x80,0x00,0x00,0x04,0x04,0x00,0x84,
+ 0x59,0xef,0xdd,0xfb,0xef,0xbf,0x2a,0x2a,0xd4,0xff,0xdf,0xff,0xff,0x9e,0x00,
+ 0x01,0x00,0x04,0x88,0x00,0x01,0x40,0xf6,0x33,0x00,0x00,0x00,0x00,0x20,0x90,
+ 0x40,0x04,0x01,0xce,0xef,0xf6,0xbb,0x00,0x00,0x00,0xba,0xef,0xab,0x7d,0x04,
+ 0x80,0x10,0x40,0xf8,0x00,0x0c,0x10,0x78,0x10,0x41,0x12,0x37,0x34,0x68,0x00,
+ 0x21,0x00,0x80,0x06,0xfe,0xbf,0x7f,0xfd,0xff,0xaa,0x02,0xf0,0x7f,0xf7,0x7b,
+ 0xff,0x58,0x41,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x22,0x00,0x10,0x00,0x00,
+ 0x01,0x04,0x04,0x00,0x02,0x00,0xe5,0xb9,0xff,0xb7,0x10,0x00,0x00,0x7a,0xff,
+ 0xfe,0x82,0x43,0x00,0x80,0x00,0xd0,0x01,0x1e,0x7c,0x58,0xc0,0x01,0x07,0x6f,
+ 0x28,0xd0,0x01,0x21,0x00,0x20,0x8b,0xfe,0xf7,0xfa,0xdf,0xbf,0xaa,0x00,0xe8,
+ 0xbd,0xfc,0xde,0x6b,0x9a,0x01,0x00,0x00,0x00,0x80,0x10,0x10,0x00,0x20,0x00,
+ 0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x80,0x40,0x81,0xff,0x6f,0xbd,0x00,0x00,
+ 0x00,0xba,0xbf,0x1f,0x04,0x08,0x20,0x00,0x00,0x08,0x03,0x21,0x4c,0x20,0x60,
+ 0x86,0x0c,0x01,0xcc,0x98,0x21,0x21,0x00,0x80,0xba,0xfc,0xef,0xef,0xff,0x3f,
+ 0x44,0x00,0x75,0xff,0xcb,0xff,0x7f,0xe9,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x04,0x80,0x00,0x00,0x01,0x00,0x04,0x01,0x28,0x27,0xbd,0xbe,
+ 0xb5,0x00,0x00,0x00,0xfa,0xfa,0xaf,0x95,0x30,0x08,0x02,0x08,0x08,0x03,0x23,
+ 0x44,0x40,0x22,0x86,0x08,0x01,0xc4,0x18,0x01,0x21,0x00,0x42,0xac,0xfc,0xbb,
+ 0xff,0xbf,0x7d,0xa8,0x40,0x52,0xbd,0xf2,0xff,0x7f,0xac,0x05,0x00,0x84,0x40,
+ 0x20,0x00,0x00,0x80,0x02,0x00,0x00,0x12,0x00,0x48,0x88,0x10,0x00,0x00,0x04,
+ 0x03,0xed,0xdd,0xbd,0x00,0x00,0x00,0x7a,0x7d,0x91,0x5e,0xc4,0x00,0x08,0x80,
+ 0x18,0x21,0x30,0x4d,0x60,0x00,0x84,0x08,0x01,0xc0,0x88,0x01,0x29,0x10,0xc0,
+ 0xb9,0x7c,0xef,0xf7,0x6f,0x7f,0x01,0x06,0x74,0xbd,0x4e,0xff,0x3f,0xed,0x07,
+ 0x88,0x00,0x10,0x00,0x02,0x00,0x20,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x80,0x08,0xee,0xb2,0x7b,0xb6,0x00,0x00,0x00,0xfa,0xed,0x6b,0xc7,0x01,
+ 0x00,0x20,0x20,0x90,0x03,0x18,0x54,0x60,0x00,0x86,0x0d,0x1d,0x50,0xf0,0x00,
+ 0x3f,0x04,0x40,0x69,0xfc,0xff,0xbb,0xfa,0xf5,0x22,0x43,0xc8,0x7e,0xdb,0xf4,
+ 0x3f,0xa7,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x80,0x40,0x00,0x00,
+ 0x01,0x00,0x08,0x01,0x00,0x2c,0x6b,0xf1,0x56,0x3f,0x20,0x00,0x10,0xfa,0xba,
+ 0xb5,0x1e,0x97,0x82,0x08,0x01,0x78,0x01,0x1c,0x5c,0x60,0xc0,0x81,0x0b,0x23,
+ 0x70,0x98,0x01,0x21,0x00,0x88,0x49,0xfd,0xfd,0xff,0xff,0xfd,0x41,0x2e,0x81,
+ 0xf6,0xae,0x4f,0xaf,0xd6,0x25,0x00,0x20,0x04,0x02,0x20,0x00,0x01,0x00,0x10,
+ 0x10,0x00,0x00,0x00,0x40,0x00,0x20,0x80,0xb0,0xff,0x0d,0x95,0xbf,0x00,0x00,
+ 0x00,0xba,0x53,0xfd,0xe5,0x7f,0x08,0x00,0x00,0x08,0x03,0x20,0xc4,0x60,0x60,
+ 0x40,0x18,0x60,0xc0,0x18,0x09,0x21,0x01,0xc1,0x1b,0x73,0x77,0xe7,0xbb,0xef,
+ 0x57,0x25,0x24,0xf8,0xd5,0xbd,0x9f,0xa2,0x09,0x00,0x08,0x41,0x20,0x08,0x08,
+ 0x00,0x08,0x00,0x00,0x20,0x21,0x10,0x04,0x00,0x24,0x60,0xe7,0x85,0x47,0xbc,
+ 0xba,0x08,0x00,0x00,0xda,0xa6,0x59,0x00,0xfe,0x08,0x05,0x10,0x09,0x03,0x20,
+ 0x86,0x20,0x20,0x80,0x10,0x60,0xc0,0x08,0x03,0x21,0x00,0xd0,0xcb,0xfa,0xdf,
+ 0xfb,0xfe,0xfb,0x23,0x4b,0x08,0xea,0x5d,0xef,0xda,0xea,0x15,0x02,0x00,0x10,
+ 0x00,0x01,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x42,0x50,0x10,0xe3,
+ 0x00,0xce,0xa8,0xb7,0x00,0x00,0x00,0xba,0x57,0x37,0xde,0xd0,0x93,0x80,0x00,
+ 0x58,0x43,0x33,0x44,0x60,0x28,0x80,0x08,0x30,0xc6,0xd8,0x41,0x21,0x00,0x40,
+ 0xa7,0xb2,0xff,0xdf,0xf7,0xb7,0x47,0x1e,0x80,0xf5,0xfd,0xbb,0x4f,0x4d,0x35,
+ 0x40,0x08,0x00,0x04,0x00,0x08,0x00,0x00,0x82,0x00,0x04,0x00,0x40,0x20,0x00,
+ 0x02,0x92,0x33,0xfe,0x80,0xdb,0xbd,0x00,0x00,0x00,0xea,0xf5,0x8f,0x55,0xe1,
+ 0x07,0x00,0x00,0xb8,0x81,0x2f,0x7c,0xf8,0xd3,0x87,0x0d,0x1f,0xdc,0x78,0x01,
+ 0x21,0x00,0x50,0x27,0xf4,0xeb,0xfd,0xef,0xff,0xc7,0x0a,0x51,0xee,0xaf,0xfe,
+ 0x0a,0xc1,0x06,0x00,0x02,0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
+ 0x08,0x00,0x20,0x28,0xd8,0x0b,0xff,0xf3,0xb3,0x2e,0x00,0x00,0x00,0xfa,0xc3,
+ 0x47,0xed,0xcf,0x67,0x90,0x20,0x20,0x00,0x08,0x10,0x08,0xa0,0x04,0x07,0x05,
+ 0x10,0x00,0x00,0x00,0x20,0xa8,0xf5,0xe5,0xbb,0xfb,0xfb,0xfb,0x9f,0x1a,0xa4,
+ 0xd0,0xbf,0xeb,0xa7,0xd7,0x11,0x00,0x40,0x10,0x41,0x00,0x00,0x02,0x80,0x00,
+ 0x00,0x80,0x00,0x00,0x00,0x08,0x21,0xe0,0xc3,0x13,0xcb,0xe7,0xb5,0x20,0x00,
+ 0x00,0xfa,0x65,0x71,0xab,0x95,0x5e,0x02,0x04,0x02,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x22,0xa5,0xcd,0xfe,0x7f,0xbf,0xef,
+ 0x5e,0x2a,0x69,0xda,0x7b,0xbd,0xb3,0xd5,0x04,0x01,0x00,0x04,0x00,0x20,0x80,
+ 0x00,0x10,0x10,0x08,0x00,0x04,0x42,0x90,0x00,0x14,0xe4,0xb3,0xee,0xcd,0xaf,
+ 0xbf,0x00,0x00,0x00,0x7a,0x13,0x91,0x7e,0x29,0x9f,0x20,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x57,0xcb,0xf7,
+ 0xd7,0xef,0xbf,0x8f,0x3a,0x85,0xa2,0xbb,0xdf,0xd3,0xd7,0x4c,0x00,0x08,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x10,0x80,0x00,0x02,0x42,0x02,0x72,0x59,
+ 0x7f,0x3b,0x9d,0x37,0x00,0x00,0x80,0xea,0xf8,0xfc,0x04,0x35,0xba,0x22,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,
+ 0x6e,0xc2,0xbf,0xfc,0xf5,0xfb,0x2f,0x6a,0xad,0xa9,0xeb,0xf6,0xd3,0x76,0x15,
+ 0x20,0x40,0x80,0x10,0x01,0x02,0x00,0x00,0x00,0x01,0x04,0x00,0x20,0x00,0x00,
+ 0x0c,0xfa,0xdc,0x81,0x3a,0x97,0xb7,0x10,0x00,0x00,0xda,0xf9,0xa4,0x03,0xef,
+ 0x6e,0x01,0x40,0x04,0x08,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x11,
+ 0x04,0x20,0x88,0xd8,0x93,0xf7,0x6b,0xef,0xd7,0x9f,0xad,0x12,0x85,0xf6,0xbd,
+ 0xc9,0xb7,0x02,0x08,0xfa,0xff,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x91,0x88,0x10,0xbc,0xb8,0x81,0x7d,0xb8,0xba,0x00,0x00,0x00,0xba,0x7c,
+ 0x56,0x80,0xdb,0x6c,0x00,0x15,0x00,0x81,0xe0,0x00,0x01,0x14,0x00,0x08,0x40,
+ 0x10,0x10,0x00,0x80,0x08,0x44,0x7f,0x2e,0xfd,0xff,0xbf,0xff,0x5f,0x35,0x50,
+ 0xa5,0x6e,0xff,0x64,0xd7,0x4b,0x04,0xf8,0xce,0x0e,0x00,0x10,0x00,0x00,0x04,
+ 0x00,0x40,0x00,0x10,0x00,0x00,0x0d,0x7d,0xe6,0x80,0xdb,0x3c,0x33,0x04,0x00,
+ 0x00,0xea,0x5c,0x6e,0x00,0x6f,0x79,0x00,0x00,0x40,0x00,0x18,0x10,0xc0,0x3f,
+ 0x04,0x80,0x88,0x04,0x04,0x00,0x00,0x80,0x82,0xd4,0x2a,0xff,0xdf,0xf7,0xfe,
+ 0xbf,0xa6,0x6a,0xca,0xff,0xb7,0xf4,0xbf,0xaa,0x93,0x68,0x5a,0x28,0x01,0x00,
+ 0x00,0x40,0x00,0x08,0x00,0x12,0x04,0x0a,0x42,0x02,0x6d,0x3d,0x80,0xbf,0x5c,
+ 0xbb,0x00,0x00,0x00,0xba,0x3e,0xfd,0x80,0xfd,0xe8,0x13,0x00,0x02,0x00,0x04,
+ 0x0c,0x30,0x01,0xc0,0x00,0xe0,0x03,0xc1,0x87,0x00,0x80,0x7d,0xfa,0xcd,0xee,
+ 0xf6,0xde,0xef,0xbe,0xde,0x06,0x51,0xcf,0x7e,0xb2,0xca,0x7a,0x04,0x00,0x00,
+ 0x00,0x08,0x00,0x78,0x00,0x00,0x80,0x19,0x00,0x00,0xc1,0x20,0x85,0x3e,0x7b,
+ 0x80,0xb5,0x79,0x3e,0x10,0x00,0x00,0x7a,0x3e,0x7b,0x81,0xdb,0xf9,0x02,0x10,
+ 0x2c,0x00,0x26,0x00,0x90,0x00,0xe0,0x00,0xe0,0x8e,0xc0,0x0d,0x00,0x64,0xab,
+ 0xe2,0xdf,0xfe,0xff,0x7f,0xff,0x7f,0xa9,0xac,0xc8,0xfd,0x7b,0xbb,0xf7,0x4e,
+ 0x05,0x40,0xb3,0x05,0x10,0x80,0x44,0x01,0x00,0x01,0x09,0x60,0x00,0x10,0x48,
+ 0x83,0x2e,0xd7,0x80,0xef,0x69,0xbe,0x00,0x00,0x00,0x7a,0x16,0xb5,0x91,0xb5,
+ 0xda,0x01,0x01,0x7f,0x20,0x90,0x00,0x38,0x11,0x60,0xa0,0x42,0x0a,0x42,0x00,
+ 0x04,0x81,0xfc,0x0f,0x3e,0xdd,0x6d,0xef,0x5f,0xff,0xae,0x22,0x42,0xef,0x3f,
+ 0xf9,0xc6,0x17,0x01,0x40,0x8d,0x41,0x02,0x40,0x02,0x01,0x00,0x41,0x09,0x20,
+ 0x00,0x00,0xe0,0x8b,0x9e,0xbd,0x91,0x7b,0xba,0x36,0x00,0x00,0x00,0x7a,0xbe,
+ 0xf7,0x01,0x77,0x73,0x01,0x80,0xe3,0x08,0xd1,0x20,0x08,0x19,0x25,0xcf,0x30,
+ 0x65,0x67,0x22,0x00,0x20,0xd1,0x95,0x3c,0xf9,0xdf,0xfb,0xfb,0x7b,0x93,0x20,
+ 0xc1,0x5d,0x9f,0xbc,0xa3,0x8d,0x44,0x44,0x97,0x00,0x00,0x00,0x02,0x01,0x00,
+ 0xc5,0x3f,0x00,0x00,0x00,0xe8,0x8f,0x16,0xdb,0x80,0xaf,0xdb,0xbe,0x24,0x00,
+ 0x00,0x5a,0x96,0xda,0x80,0xff,0xb2,0x11,0x80,0xc1,0x80,0xdd,0x28,0x80,0xd4,
+ 0xdf,0x29,0x98,0x3c,0xb8,0xb3,0x41,0x88,0xf6,0x57,0x77,0xf1,0xff,0xff,0xfd,
+ 0xdf,0x5e,0x2b,0xb0,0xfe,0x97,0x5e,0xf8,0xe7,0x93,0x40,0x93,0x20,0x00,0x00,
+ 0x02,0x4d,0x99,0x07,0xcb,0x93,0x8d,0x01,0x31,0x09,0x8e,0xdd,0x81,0xfb,0xf2,
+ 0x3a,0x00,0x00,0x20,0x7a,0x8e,0x5a,0x89,0x55,0xd3,0x81,0x08,0xd0,0x00,0xe7,
+ 0x1b,0x80,0xbc,0x33,0x3c,0x04,0x36,0xe4,0xe9,0x00,0x62,0x2f,0xbf,0xee,0xf2,
+ 0xfd,0xbf,0xff,0x7d,0x2b,0x63,0x60,0xf7,0x4f,0x46,0xbd,0xfa,0x2d,0x00,0x08,
+ 0x08,0x00,0x03,0x46,0x6b,0x4d,0x03,0x4d,0x59,0x4b,0x01,0xa4,0x2a,0x9f,0xb7,
+ 0x80,0x53,0xb3,0xbe,0x04,0x00,0x00,0x7a,0xdb,0xbe,0x21,0xff,0x76,0x05,0x00,
+ 0xe5,0x10,0xc0,0x20,0x08,0x40,0x01,0x00,0x80,0x00,0x00,0x00,0x00,0xe8,0xcd,
+ 0x65,0xfc,0xe4,0x77,0xf7,0x77,0xff,0x5a,0x54,0xa2,0xb6,0x67,0x7f,0x7a,0x0b,
+ 0x77,0x11,0x0c,0x00,0x01,0x20,0x3c,0xd9,0x3b,0x0d,0xc9,0x37,0xb9,0x07,0x48,
+ 0x4a,0x92,0x6c,0x81,0xf5,0xb2,0x3c,0x00,0x00,0x00,0x7a,0xcb,0xdd,0x81,0xed,
+ 0xe7,0x05,0x02,0x3e,0x00,0x60,0x00,0x00,0x90,0x00,0x01,0x00,0x00,0x00,0x00,
+ 0x08,0x11,0xb5,0xde,0xb8,0xc9,0xff,0xff,0xfd,0xff,0xd5,0x90,0xb0,0xba,0x93,
+ 0xdb,0x8d,0xbf,0x42,0x02,0x1e,0x42,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x40,0x65,0x4f,0x9f,0xbf,0x91,0x7f,0xd7,0xbd,0x00,0x00,0x80,0x7a,0x5d,
+ 0x6f,0x21,0xe7,0x66,0x83,0x00,0x0f,0x02,0x40,0x80,0x20,0xd2,0x00,0x00,0x00,
+ 0x00,0x00,0x20,0x00,0xae,0xae,0xd3,0xf3,0x9b,0xd7,0xef,0xbf,0xff,0x1f,0x2c,
+ 0x2d,0xdd,0x99,0x7f,0x57,0x4a,0xac,0x41,0x14,0x08,0x10,0x00,0x00,0x20,0x00,
+ 0x00,0x10,0x00,0x00,0x00,0xe8,0x47,0xd3,0x6d,0x81,0xf7,0x75,0x35,0x04,0x00,
+ 0x00,0x3a,0xd7,0xdc,0x89,0xef,0xe5,0x22,0xa0,0x83,0x00,0x10,0x00,0x00,0x20,
+ 0x00,0x00,0x80,0xc0,0x08,0x00,0x00,0x61,0x11,0xea,0xce,0x36,0x7f,0xbf,0xff,
+ 0xfe,0x69,0x0b,0x4d,0xf7,0xe4,0xca,0xb5,0x8e,0x2b,0x04,0x88,0x00,0x0b,0x08,
+ 0x00,0x00,0x10,0x20,0x00,0x00,0x84,0x00,0x72,0x40,0x8b,0xef,0x81,0x55,0xd3,
+ 0xbd,0x00,0x00,0x10,0x78,0xd5,0xdf,0x81,0x7b,0xa7,0x05,0x01,0x09,0x90,0x00,
+ 0x10,0x04,0x38,0x80,0x00,0x00,0x30,0x01,0x82,0x20,0x40,0xc4,0xb0,0xbf,0x27,
+ 0xfe,0xfb,0xaf,0xff,0x5b,0x93,0x4a,0x75,0xe4,0xfb,0x4a,0x3d,0x29,0x81,0x00,
+ 0xa0,0x34,0x00,0x08,0x08,0x00,0x00,0x02,0x04,0x00,0x50,0xa8,0x00,0xdb,0xad,
+ 0xa0,0xef,0xf7,0xbc,0x00,0x00,0x00,0x7a,0xdb,0xde,0x81,0xed,0xf6,0x22,0x88,
+ 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0c,0x02,0x20,0x00,0xa8,0x7f,
+ 0xaf,0xb6,0x6d,0xee,0xbf,0xfb,0x00,0x00,0x0c,0x54,0xfd,0xec,0xb7,0xfd,0xfd,
+ 0x5f,0x10,0x24,0x04,0x3e,0x00,0x00,0x01,0x04,0x01,0x80,0x80,0x00,0x04,0x32,
+ 0x40,0x95,0xcf,0x81,0x73,0x55,0x2d,0x00,0x00,0x04,0x7a,0xc5,0x5b,0x11,0xff,
+ 0xe5,0x09,0x80,0xff,0x00,0x04,0x02,0x80,0x00,0x00,0x00,0x40,0xe0,0x40,0x00,
+ 0x08,0xe9,0xff,0x7e,0x7d,0x27,0xfe,0xf7,0x8f,0x0b,0xee,0xe3,0x49,0xed,0xd9,
+ 0xff,0x77,0xd7,0xb6,0xef,0xdf,0xfb,0x57,0x40,0x40,0x80,0x00,0x70,0x04,0x00,
+ 0x00,0x80,0x68,0x0d,0x9b,0x7e,0x81,0x7f,0xf3,0xbd,0x20,0x00,0x00,0x5a,0x97,
+ 0xde,0x81,0x6d,0xb7,0x00,0x02,0x20,0x22,0x89,0x80,0x00,0x20,0x10,0x18,0x02,
+ 0x94,0x03,0x04,0x00,0x00,0x00,0x00,0x00,0x90,0x61,0x3f,0x34,0xaa,0xea,0x60,
+ 0xd9,0x16,0x22,0x00,0x89,0x2a,0x55,0xb5,0x6a,0xad,0x56,0x48,0x04,0x00,0x74,
+ 0x10,0x90,0x10,0x44,0x00,0xf6,0x4f,0x96,0xeb,0x88,0xf7,0x5a,0x37,0x00,0x00,
+ 0x00,0x6a,0x8a,0xb9,0x80,0x73,0xd3,0x01,0x20,0x09,0x00,0x04,0x20,0xa0,0x42,
+ 0x00,0x0a,0x02,0xb0,0xef,0xfb,0x77,0x7f,0xf7,0xb7,0xef,0x9f,0xcd,0x57,0x9b,
+ 0x03,0x00,0x52,0xa5,0x7e,0xe6,0xff,0xfe,0xdd,0xfb,0xdd,0xff,0xf7,0x71,0x00,
+ 0xd5,0x3e,0xe5,0xd2,0x96,0x0f,0x00,0x20,0x01,0x00,0x95,0xde,0x81,0xb9,0x73,
+ 0xae,0x04,0x00,0x00,0x58,0xb6,0xf7,0x00,0xbb,0xba,0x4a,0x00,0x00,0x91,0xba,
+ 0x86,0x9a,0xa9,0xf2,0xe1,0x1c,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,
+ 0xd3,0x84,0xf0,0xff,0xff,0x1f,0x06,0xb1,0x0f,0x00,0x00,0x00,0x00,0x12,0x12,
+ 0x24,0x2a,0xc0,0x58,0xa1,0x92,0x28,0x65,0x10,0x00,0x21,0x34,0x84,0x86,0xb9,
+ 0xa0,0x7f,0xdb,0xb6,0x10,0x00,0x00,0xaa,0x26,0x3d,0x91,0xbd,0x59,0x03,0x00,
+ 0x00,0x00,0xa5,0x6a,0x05,0x46,0x24,0x49,0x45,0xdc,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x69,0x82,0xff,0xff,0xff,0xff,0x87,0xce,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x27,0x03,0x00,0x10,0x00,0x00,0x00,0x02,0x44,0x88,0x00,
+ 0xa1,0x36,0x7f,0x81,0xcd,0x19,0xbe,0x00,0x00,0x08,0xfa,0x2e,0x7b,0x01,0x7b,
+ 0xb9,0x43,0x04,0x48,0x94,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xb3,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xf3,0xf8,0xff,0xfd,0xff,0xff,0xbf,0x8e,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0x02,0x04,0x00,0x00,0x00,0x00,0x02,
+ 0x01,0x40,0x2a,0x80,0x36,0xb3,0x80,0xbd,0x79,0xbe,0x00,0x00,0x00,0xba,0x5b,
+ 0x6f,0x00,0xcd,0x49,0x00,0x21,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xd5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfa,0xff,0x5f,0x95,0x00,0xf0,
+ 0xff,0xff,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x25,0x1a,0x42,0x48,0x41,
+ 0x08,0x00,0x48,0x21,0xad,0x31,0x14,0x65,0xef,0x08,0xef,0x0c,0x31,0x10,0x00,
+ 0x80,0xea,0x55,0x0a,0x04,0x58,0xac,0x54,0x00,0x00,0x08,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x10,0x15,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xee,0xb7,
+ 0xad,0xfe,0x8b,0xfe,0xf7,0xa9,0xda,0xff,0xff,0xff,0xff,0xff,0xff,0x95,0xc6,
+ 0x90,0x5a,0x1d,0x73,0x6f,0x27,0x94,0x5a,0xce,0x51,0x55,0x1d,0x40,0xbc,0x5c,
+ 0xbf,0x00,0x00,0x00,0xf8,0xc5,0xfc,0xff,0xbf,0x56,0x9b,0x73,0x6b,0xe7,0xac,
+ 0xbd,0xae,0x95,0xb3,0x56,0x4b,0xe5,0x49,0x0a,0x00,0x02,0x40,0x00,0x00,0x00,
+ 0x3e,0x00,0x00,0x2b,0x20,0x30,0x00,0x70,0x01,0xe0,0x88,0xc0,0x06,0x40,0x80,
+ 0x85,0x84,0x88,0xb4,0x56,0x45,0xa5,0x52,0x6a,0xa9,0xb5,0xab,0xb4,0x68,0xf6,
+ 0xff,0xf7,0xb4,0xbf,0x04,0x00,0x00,0x9a,0xb9,0xd4,0xff,0x97,0x26,0x2d,0x55,
+ 0x6d,0x55,0x1a,0x63,0x4a,0x90,0x54,0x2d,0x2a,0x95,0x56,0x05,0x07,0x06,0xc0,
+ 0x61,0x00,0x00,0xe2,0x00,0x03,0x2f,0x70,0xe0,0x01,0xfc,0x03,0xf0,0x7f,0xc0,
+ 0x1d,0xc0,0x00,0xb7,0x2b,0x53,0x6a,0xd7,0x56,0x49,0x0c,0x2b,0x2d,0xdf,0x67,
+ 0xb5,0x88,0xd8,0xff,0x57,0xae,0xb5,0x00,0x00,0x00,0xda,0x1b,0xb9,0xb6,0x2e,
+ 0xcb,0x4e,0x4b,0x0c,0xa9,0xb5,0xba,0xb7,0xb5,0x96,0xb2,0x57,0xb1,0xed,0x00,
+ 0x0f,0x18,0x80,0xf7,0x00,0x07,0xae,0x09,0x07,0x0c,0x70,0x80,0x03,0xfc,0x07,
+ 0xf8,0x2b,0x80,0x7f,0xc0,0x01,0x5f,0x72,0xb6,0xdf,0xfa,0xbd,0xf7,0xfb,0xea,
+ 0xd6,0x6a,0xda,0x4b,0xdb,0xbd,0xfe,0x3d,0x93,0xbb,0x00,0x00,0x10,0xfa,0xaa,
+ 0xe3,0xba,0x3c,0xab,0xfd,0xf7,0xf7,0xf7,0x6e,0x77,0xad,0x77,0xf7,0xfd,0xd6,
+ 0x4e,0x5f,0x01,0x0f,0x38,0x04,0xf7,0x00,0x17,0x5e,0x81,0x97,0x0c,0x70,0x80,
+ 0x03,0xbe,0x05,0xfc,0x5f,0x08,0x77,0xc0,0x20,0xbe,0x35,0x0a,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x04,0x20,0x56,0xd1,0x2e,0x16,0x47,0x3d,0x00,0x00,
+ 0x00,0xe8,0x27,0xa6,0xb5,0x87,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x80,0x78,0xb7,0x00,0x0f,0xb8,0x00,0xf6,0x00,0xe1,0xdf,0x00,0x07,
+ 0x08,0x70,0x80,0x01,0x6e,0x02,0xbc,0xb0,0x01,0x5f,0xe0,0x80,0x9f,0xca,0xf1,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x87,0x63,0x5b,0xc5,0x69,
+ 0xae,0x40,0x00,0x00,0xfa,0x2c,0x8d,0xaf,0xe3,0xe8,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xaf,0xb7,0x5a,0x08,0x0f,0xb8,0x01,0xf0,0x04,0xf1,
+ 0xb7,0x01,0x07,0x08,0x70,0xc0,0x03,0x3e,0x06,0x6c,0x8d,0x01,0x5e,0x40,0xc8,
+ 0xbf,0xfc,0xc7,0xd5,0xbe,0xc5,0xba,0xea,0xff,0xdd,0xff,0xf7,0xff,0x6f,0x8c,
+ 0x57,0x63,0xe4,0xbb,0x10,0x00,0x00,0xba,0x5c,0x1c,0xd6,0x70,0xe4,0xff,0xfd,
+ 0xf7,0xee,0xfd,0xff,0xff,0x6f,0xfb,0xee,0xd7,0x6d,0xdd,0x00,0x07,0xb8,0x21,
+ 0xb0,0x00,0xe3,0x5c,0x13,0x07,0x08,0xf0,0x80,0x23,0x5e,0x06,0xfc,0xdc,0x21,
+ 0x3e,0xd0,0x80,0xaf,0xae,0xae,0xff,0xff,0xaa,0xfe,0xde,0xdf,0xf7,0xfd,0x55,
+ 0xbf,0x9d,0x0e,0xdd,0xf1,0xb2,0xbd,0x00,0x00,0x00,0xea,0x9e,0x71,0x00,0x1c,
+ 0xf5,0xfa,0x2b,0xfd,0xb7,0xbf,0xcf,0xf5,0xad,0xde,0x7f,0x6b,0x7f,0xbe,0x40,
+ 0x0f,0xb8,0x07,0xf0,0x00,0x37,0x56,0x04,0x47,0x0e,0x71,0x80,0x03,0x1e,0x86,
+ 0x38,0xec,0x01,0x7c,0xe0,0x80,0xb7,0xfa,0xcd,0xdf,0xdd,0x75,0xb7,0x55,0xf7,
+ 0xbd,0xff,0xd5,0xbf,0x2f,0x71,0x00,0x1c,0xf9,0xb9,0x00,0x00,0x40,0x7a,0x3f,
+ 0xe5,0x01,0x2f,0xda,0xdb,0x55,0x5f,0xef,0xed,0xfb,0xbe,0x6d,0xeb,0xaa,0xe3,
+ 0xfb,0x7a,0x01,0x8f,0x18,0x0f,0xf0,0x00,0x0f,0xde,0x01,0x07,0x1c,0x70,0xc0,
+ 0x01,0x0f,0x06,0x3c,0xcc,0x03,0x38,0xc4,0x04,0xaf,0xad,0xaa,0x97,0xfb,0xcd,
+ 0xdb,0xff,0x3a,0xd7,0xfd,0xba,0xfd,0x7f,0xf5,0x21,0xaf,0x7c,0xb7,0x00,0x00,
+ 0x00,0x48,0xfb,0x92,0xff,0xa3,0xfd,0xf6,0xe7,0xfe,0xd6,0x77,0xdf,0xf7,0xa7,
+ 0x7e,0xf3,0x74,0x77,0xaf,0x00,0x07,0x1e,0x1b,0xf0,0x20,0x03,0xae,0x00,0x03,
+ 0x1e,0x30,0xe0,0x00,0x02,0x02,0x08,0x84,0x01,0x18,0xc0,0x00,0x6f,0xbe,0x4d,
+ 0x1c,0xfb,0xba,0xbe,0x55,0xef,0x7b,0xbb,0xad,0xdb,0xad,0x04,0xef,0x25,0xfe,
+ 0xbb,0x20,0x00,0x00,0xfa,0xff,0x84,0x7c,0x80,0xee,0x7b,0x37,0xfb,0xbf,0xfb,
+ 0xeb,0x6d,0x5b,0xfd,0xf1,0x6a,0x5d,0x7d,0x06,0x07,0x0e,0x2c,0x30,0x08,0x00,
+ 0x1e,0x00,0x41,0x7f,0x20,0x38,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x01,0x83,
+ 0xd8,0xfd,0xae,0xa5,0xbd,0x5d,0xef,0xef,0xef,0xfe,0x7e,0x5a,0xef,0xbb,0x28,
+ 0xd6,0x12,0xf7,0x3d,0x00,0x00,0x00,0x9a,0xb5,0x33,0x02,0x2a,0xdb,0xf6,0xd5,
+ 0x6a,0xdd,0xde,0xdf,0xd7,0xf5,0xba,0x2d,0x60,0xb4,0xde,0xfd,0xfa,0xff,0xdf,
+ 0xff,0xff,0xff,0xf7,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x5f,0x5a,0x2a,0x3b,0xf7,0xbe,0xaf,0x5b,0xf7,0xeb,0x73,0xfd,
+ 0x5e,0xcf,0x03,0x20,0xc9,0xbd,0xbb,0x08,0x00,0x00,0x7a,0xf7,0x62,0x01,0x88,
+ 0x75,0xef,0x73,0xfd,0xdb,0xbf,0xef,0xef,0x5b,0x7d,0xcb,0xe5,0x75,0xbf,0xfe,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x75,0x57,0xac,0x75,0xba,0x7c,0xaf,0xe7,
+ 0xfb,0xae,0xad,0xd5,0xbf,0x07,0x50,0x60,0x6f,0xb5,0x00,0x00,0x00,0xba,0x7d,
+ 0x1f,0xfe,0x71,0xb7,0xdf,0xdc,0xb6,0x7d,0xcf,0xbb,0xfb,0xf2,0xd7,0x51,0x51,
+ 0x8f,0x77,0xfb,0xff,0xff,0x7f,0xff,0xff,0xff,0xdf,0xff,0xff,0x6f,0xff,0xaf,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0xdd,0x1e,0xc7,0xaf,0x7d,
+ 0xa7,0xff,0xdf,0xbf,0xdb,0xfa,0xfe,0xfb,0xff,0xa0,0xfe,0xef,0x3f,0x00,0x00,
+ 0x40,0x72,0xdf,0xff,0x00,0xce,0xdf,0xb7,0x7b,0xef,0x77,0x7f,0xff,0xde,0xff,
+ 0xff,0xff,0xff,0xfb,0x5d,0xea,0xff,0xe5,0xef,0xfe,0xff,0xff,0x37,0xff,0xff,
+ 0xdb,0xee,0x7b,0xff,0xff,0xff,0xdd,0xf7,0xff,0x7b,0xab,0xaa,0x2a,0x85,0x54,
+ 0x55,0x54,0x52,0x52,0x51,0x04,0x84,0x14,0x02,0x08,0x04,0x00,0x12,0x09,0x00,
+ 0x80,0x00,0x00,0x10,0xf8,0xff,0xff,0xd7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xbf,0xff,0xde,0x56,0xad,0xaa,0x56,0x53,0x15,0x04,0x0a,0x11,0x01,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0x00,0x20,0x80,0x24,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x22,0x22,0x91,0xac,0xea,0x6a,0x7b,0xed,
+ 0xbe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xb7,
+ 0xde,0xff,0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x20,0x04,0x01,0x00,0x00,0x00,0x4a,0xca,0xaa,0xbe,0xdd,
+ 0x76,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xef,0xfd,0x6d,0x6d,0xab,0xaa,
+ 0x9a,0x4a,0x81,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x02,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x01,0x00,0x20,0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x44,0x10,0xa4,0x08,0x80,0x80,0x00,0x08,
+ 0x80,0x00,0x00,0x00,0x00,0x01,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x02,0x21,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x00,0x00,0x00,
+ 0x20,0x00,0x00,0x10,0x10,0x00,0x80,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x08,0x00,0x00,0x42,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x10,
+ 0x41,0x00,0x02,0x04,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x40,0x04,0x00,0x80,
+ 0x00,0x00,0x20,0x00,0x10,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x02,
+ 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x10,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x10,0x02,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x10,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x22,0x02,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x10,0x00,0x00,0x00,0x20,0x04,0x00,
+ 0x08,0x00,0x04,0x80,0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,
+ 0x00,0x00,0x40,0x24,0x84,0x20,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x00,0x00,0x02,0x00,0x01,0x20,0x10,0x84,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x20,0x00,0x01,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x02,0x80,0x00,0x00,0x00,0x20,0x00,0x00,0x01,0x00,0x00,0x01,
+ 0x00,0x00,0x00,0x24,0x02,0x01,0x00,0x00,0x00,0x00,0x20,0x00,0x14,0x88,0x10,
+ 0x00,0x00,0x84,0x00,0x00,0x00,0x04,0x10,0x00,0x40,0x00,0x40,0x00,0x42,0x00,
+ 0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x21,0x00,0x08,0x04,0x01,0x08,0x00,0x48,
+ 0x00,0x00,0x04,0x08,0x00,0x80,0x00,0x10,0x00,0x20,0x00,0x08,0x00,0x40,0x00,
+ 0x00,0x00,0x00,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
diff --git a/blt/demos/bitmaps/hand/hand01.xbm b/blt/demos/bitmaps/hand/hand01.xbm
new file mode 100644
index 00000000000..096b56c72e7
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand01.xbm
@@ -0,0 +1,8 @@
+#define hand6-1_width 16
+#define hand6-1_height 16
+#define hand6-1_x_hot 8
+#define hand6-1_y_hot 10
+static unsigned char hand6-1_bits[] = {
+ 0x00, 0x00, 0x80, 0x03, 0xe0, 0x0e, 0xa0, 0x1a, 0xa0, 0x2a, 0xa0, 0x2a,
+ 0xa0, 0x2a, 0xb8, 0x2a, 0x28, 0x28, 0x28, 0x20, 0x28, 0x20, 0x08, 0x20,
+ 0x08, 0x20, 0x10, 0x20, 0x20, 0x10, 0xe0, 0x1f};
diff --git a/blt/demos/bitmaps/hand/hand01m.xbm b/blt/demos/bitmaps/hand/hand01m.xbm
new file mode 100644
index 00000000000..eabce6382f0
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand01m.xbm
@@ -0,0 +1,8 @@
+#define hand6-1m_width 16
+#define hand6-1m_height 16
+#define hand6-1m_x_hot 8
+#define hand6-1m_y_hot 9
+static unsigned char hand6-1m_bits[] = {
+ 0x00, 0x00, 0x80, 0x03, 0xe0, 0x0f, 0xe0, 0x1f, 0xe0, 0x3f, 0xe0, 0x3f,
+ 0xe0, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f,
+ 0xf8, 0x3f, 0xf0, 0x3f, 0xe0, 0x1f, 0xe0, 0x1f};
diff --git a/blt/demos/bitmaps/hand/hand02.xbm b/blt/demos/bitmaps/hand/hand02.xbm
new file mode 100644
index 00000000000..39cbf113653
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand02.xbm
@@ -0,0 +1,8 @@
+#define hand6-02_width 16
+#define hand6-02_height 16
+#define hand6-02_x_hot 10
+#define hand6-02_y_hot 11
+static unsigned char hand6-02_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0xbc, 0xaa, 0xa4, 0xaa, 0xa4, 0xa0, 0xbc, 0x80, 0x80, 0x80,
+ 0xf8, 0x80, 0x08, 0x80, 0xf8, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand02m.xbm b/blt/demos/bitmaps/hand/hand02m.xbm
new file mode 100644
index 00000000000..6466efed672
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand02m.xbm
@@ -0,0 +1,8 @@
+#define hand6-02m_width 16
+#define hand6-02m_height 16
+#define hand6-02m_x_hot 10
+#define hand6-02m_y_hot 11
+static unsigned char hand6-02m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0x80, 0xff,
+ 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand03.xbm b/blt/demos/bitmaps/hand/hand03.xbm
new file mode 100644
index 00000000000..9ad0c3d2a8d
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand03.xbm
@@ -0,0 +1,8 @@
+#define hand6-03_width 16
+#define hand6-03_height 16
+#define hand6-03_x_hot 10
+#define hand6-03_y_hot 11
+static unsigned char hand6-03_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0xbc, 0xaa, 0xa4, 0xaa, 0xa4, 0xaa, 0xbc, 0xa0, 0x80, 0x80, 0x80, 0x80,
+ 0xf8, 0x80, 0x08, 0x80, 0xf8, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand03m.xbm b/blt/demos/bitmaps/hand/hand03m.xbm
new file mode 100644
index 00000000000..026b54c0d5f
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand03m.xbm
@@ -0,0 +1,8 @@
+#define hand6-03m_width 16
+#define hand6-03m_height 16
+#define hand6-03m_x_hot 10
+#define hand6-03m_y_hot 11
+static unsigned char hand6-03m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0xbc, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0x80, 0xff, 0x80, 0xff,
+ 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand04.xbm b/blt/demos/bitmaps/hand/hand04.xbm
new file mode 100644
index 00000000000..0fb8628115f
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand04.xbm
@@ -0,0 +1,8 @@
+#define hand6-04_width 16
+#define hand6-04_height 16
+#define hand6-04_x_hot 10
+#define hand6-04_y_hot 11
+static unsigned char hand6-04_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0xbc, 0x6a,
+ 0xa4, 0xaa, 0xa4, 0xaa, 0xbc, 0xaa, 0x80, 0xa0, 0xb8, 0x80, 0xc8, 0x80,
+ 0x98, 0x80, 0x30, 0x80, 0xe0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand04m.xbm b/blt/demos/bitmaps/hand/hand04m.xbm
new file mode 100644
index 00000000000..e3911963eaa
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand04m.xbm
@@ -0,0 +1,8 @@
+#define hand6-04m_width 16
+#define hand6-04m_height 16
+#define hand6-04m_x_hot 10
+#define hand6-04m_y_hot 11
+static unsigned char hand6-04m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0xbc, 0x7f,
+ 0xbc, 0xff, 0xbc, 0xff, 0xbc, 0xff, 0x80, 0xff, 0xb8, 0xff, 0xf8, 0xff,
+ 0xf8, 0xff, 0xf0, 0xff, 0xe0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand05.xbm b/blt/demos/bitmaps/hand/hand05.xbm
new file mode 100644
index 00000000000..57708c3e312
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand05.xbm
@@ -0,0 +1,8 @@
+#define hand6-05_width 16
+#define hand6-05_height 16
+#define hand6-05_x_hot 10
+#define hand6-05_y_hot 11
+static unsigned char hand6-05_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xbc, 0x3b, 0xa4, 0x2a, 0xa4, 0x6a,
+ 0xbc, 0xaa, 0x80, 0xaa, 0x80, 0xaa, 0x80, 0xa0, 0xb8, 0x80, 0xc8, 0x80,
+ 0x98, 0x80, 0x30, 0x80, 0xe0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand05m.xbm b/blt/demos/bitmaps/hand/hand05m.xbm
new file mode 100644
index 00000000000..8d6704c5a15
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand05m.xbm
@@ -0,0 +1,8 @@
+#define hand6-05m_width 16
+#define hand6-05m_height 16
+#define hand6-05m_x_hot 10
+#define hand6-05m_y_hot 11
+static unsigned char hand6-05m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xbc, 0x3f, 0xbc, 0x3f, 0xbc, 0x7f,
+ 0xbc, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0xb8, 0xff, 0xf8, 0xff,
+ 0xf8, 0xff, 0xf0, 0xff, 0xe0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand06.xbm b/blt/demos/bitmaps/hand/hand06.xbm
new file mode 100644
index 00000000000..6e0aae0e247
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand06.xbm
@@ -0,0 +1,8 @@
+#define hand6-06_width 16
+#define hand6-06_height 16
+#define hand6-06_x_hot 10
+#define hand6-06_y_hot 11
+static unsigned char hand6-06_bits[] = {
+ 0x00, 0x00, 0x3c, 0x00, 0x24, 0x0e, 0xa4, 0x3b, 0xbc, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0x80, 0xaa, 0x80, 0xa0, 0xb8, 0x80, 0xc8, 0x80,
+ 0x98, 0x80, 0x30, 0x80, 0xe0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand06m.xbm b/blt/demos/bitmaps/hand/hand06m.xbm
new file mode 100644
index 00000000000..de93e1dfc93
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand06m.xbm
@@ -0,0 +1,8 @@
+#define hand6-06m_width 16
+#define hand6-06m_height 16
+#define hand6-06m_x_hot 10
+#define hand6-06m_y_hot 11
+static unsigned char hand6-06m_bits[] = {
+ 0x00, 0x00, 0x3c, 0x00, 0x3c, 0x0e, 0xbc, 0x3f, 0xbc, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0xb8, 0xff, 0xf8, 0xff,
+ 0xf8, 0xff, 0xf0, 0xff, 0xe0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand07.xbm b/blt/demos/bitmaps/hand/hand07.xbm
new file mode 100644
index 00000000000..dbc002a58f1
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand07.xbm
@@ -0,0 +1,8 @@
+#define hand6-07_width 16
+#define hand6-07_height 16
+#define hand6-07_x_hot 10
+#define hand6-07_y_hot 11
+static unsigned char hand6-07_bits[] = {
+ 0x1e, 0x00, 0x12, 0x00, 0x12, 0x0e, 0x9e, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand07m.xbm b/blt/demos/bitmaps/hand/hand07m.xbm
new file mode 100644
index 00000000000..3b435a87a4f
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand07m.xbm
@@ -0,0 +1,8 @@
+#define hand6-07m_width 16
+#define hand6-07m_height 16
+#define hand6-07m_x_hot 10
+#define hand6-07m_y_hot 11
+static unsigned char hand6-07m_bits[] = {
+ 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x0e, 0x9e, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand08.xbm b/blt/demos/bitmaps/hand/hand08.xbm
new file mode 100644
index 00000000000..2ed12f4bf47
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand08.xbm
@@ -0,0 +1,8 @@
+#define hand6-08_width 16
+#define hand6-08_height 16
+#define hand6-08_x_hot 10
+#define hand6-08_y_hot 11
+static unsigned char hand6-08_bits[] = {
+ 0x00, 0x00, 0x0f, 0x00, 0x09, 0x0e, 0x89, 0x3b, 0x8f, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand08m.xbm b/blt/demos/bitmaps/hand/hand08m.xbm
new file mode 100644
index 00000000000..c1c700e7f4d
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand08m.xbm
@@ -0,0 +1,8 @@
+#define hand6-08m_width 16
+#define hand6-08m_height 16
+#define hand6-08m_x_hot 10
+#define hand6-08m_y_hot 11
+static unsigned char hand6-08m_bits[] = {
+ 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0e, 0x8f, 0x3f, 0x8f, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand09.xbm b/blt/demos/bitmaps/hand/hand09.xbm
new file mode 100644
index 00000000000..589b4155bf3
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand09.xbm
@@ -0,0 +1,8 @@
+#define hand6-09_width 16
+#define hand6-09_height 16
+#define hand6-09_x_hot 10
+#define hand6-09_y_hot 11
+static unsigned char hand6-09_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x89, 0x3b, 0x89, 0x2a, 0x8f, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand09m.xbm b/blt/demos/bitmaps/hand/hand09m.xbm
new file mode 100644
index 00000000000..ec289ccfa15
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand09m.xbm
@@ -0,0 +1,8 @@
+#define hand6-09m_width 16
+#define hand6-09m_height 16
+#define hand6-09m_x_hot 10
+#define hand6-09m_y_hot 11
+static unsigned char hand6-09m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x8f, 0x3f, 0x8f, 0x3f, 0x8f, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand10.xbm b/blt/demos/bitmaps/hand/hand10.xbm
new file mode 100644
index 00000000000..e0c728fef26
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand10.xbm
@@ -0,0 +1,8 @@
+#define hand6-10_width 16
+#define hand6-10_height 16
+#define hand6-10_x_hot 10
+#define hand6-10_y_hot 11
+static unsigned char hand6-10_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x8f, 0x2a, 0x89, 0x6a,
+ 0x89, 0xaa, 0x8f, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand10m.xbm b/blt/demos/bitmaps/hand/hand10m.xbm
new file mode 100644
index 00000000000..49c134b11d7
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand10m.xbm
@@ -0,0 +1,8 @@
+#define hand6-10m_width 16
+#define hand6-10m_height 16
+#define hand6-10m_x_hot 10
+#define hand6-10m_y_hot 11
+static unsigned char hand6-10m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x8f, 0x3f, 0x8f, 0x7f,
+ 0x8f, 0xff, 0x8f, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand11.xbm b/blt/demos/bitmaps/hand/hand11.xbm
new file mode 100644
index 00000000000..719919b9e94
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand11.xbm
@@ -0,0 +1,8 @@
+#define hand6-11_width 16
+#define hand6-11_height 16
+#define hand6-11_x_hot 10
+#define hand6-11_y_hot 11
+static unsigned char hand6-11_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x8f, 0xaa, 0x89, 0xaa, 0xe9, 0xaa, 0xaf, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand11m.xbm b/blt/demos/bitmaps/hand/hand11m.xbm
new file mode 100644
index 00000000000..5ea63b3a0be
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand11m.xbm
@@ -0,0 +1,8 @@
+#define hand6-11m_width 16
+#define hand6-11m_height 16
+#define hand6-11m_x_hot 10
+#define hand6-11m_y_hot 11
+static unsigned char hand6-11m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x8f, 0xff, 0x8f, 0xff, 0xef, 0xff, 0xef, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand12.xbm b/blt/demos/bitmaps/hand/hand12.xbm
new file mode 100644
index 00000000000..c394581efc7
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand12.xbm
@@ -0,0 +1,8 @@
+#define hand6-12_width 16
+#define hand6-12_height 16
+#define hand6-12_x_hot 10
+#define hand6-12_y_hot 11
+static unsigned char hand6-12_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xef, 0xaa, 0xa9, 0xa0, 0xa9, 0x80, 0xaf, 0x80,
+ 0x20, 0x80, 0x60, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand12m.xbm b/blt/demos/bitmaps/hand/hand12m.xbm
new file mode 100644
index 00000000000..d0cf21b19a3
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand12m.xbm
@@ -0,0 +1,8 @@
+#define hand6-12m_width 16
+#define hand6-12m_height 16
+#define hand6-12m_x_hot 10
+#define hand6-12m_y_hot 11
+static unsigned char hand6-12m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff, 0xef, 0xff,
+ 0xe0, 0xff, 0xe0, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand13.xbm b/blt/demos/bitmaps/hand/hand13.xbm
new file mode 100644
index 00000000000..414efb01ea4
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand13.xbm
@@ -0,0 +1,8 @@
+#define hand6-13_width 16
+#define hand6-13_height 16
+#define hand6-13_x_hot 10
+#define hand6-13_y_hot 11
+static unsigned char hand6-13_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xaf, 0x80, 0xa9, 0x80,
+ 0x29, 0x80, 0x6f, 0x80, 0xc0, 0xc0, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand13m.xbm b/blt/demos/bitmaps/hand/hand13m.xbm
new file mode 100644
index 00000000000..6179c56e679
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand13m.xbm
@@ -0,0 +1,8 @@
+#define hand6-13m_width 16
+#define hand6-13m_height 16
+#define hand6-13m_x_hot 10
+#define hand6-13m_y_hot 11
+static unsigned char hand6-13m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xef, 0xff, 0xef, 0xff,
+ 0xef, 0xff, 0xef, 0xff, 0xc0, 0xff, 0x80, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand14.xbm b/blt/demos/bitmaps/hand/hand14.xbm
new file mode 100644
index 00000000000..f1f9c2731a8
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand14.xbm
@@ -0,0 +1,8 @@
+#define hand6-14_width 16
+#define hand6-14_height 16
+#define hand6-14_x_hot 10
+#define hand6-14_y_hot 11
+static unsigned char hand6-14_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3b, 0x80, 0x2a, 0x80, 0x6a,
+ 0x80, 0xaa, 0x80, 0xaa, 0xe0, 0xaa, 0xa0, 0xa0, 0xa0, 0x80, 0xa0, 0x80,
+ 0x2f, 0x80, 0x69, 0x80, 0xc9, 0xc0, 0x8f, 0x7f};
diff --git a/blt/demos/bitmaps/hand/hand14m.xbm b/blt/demos/bitmaps/hand/hand14m.xbm
new file mode 100644
index 00000000000..f623eecbdfe
--- /dev/null
+++ b/blt/demos/bitmaps/hand/hand14m.xbm
@@ -0,0 +1,8 @@
+#define hand6-14m_width 16
+#define hand6-14m_height 16
+#define hand6-14m_x_hot 10
+#define hand6-14m_y_hot 11
+static unsigned char hand6-14m_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x3f, 0x80, 0x3f, 0x80, 0x7f,
+ 0x80, 0xff, 0x80, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff,
+ 0xef, 0xff, 0xef, 0xff, 0xcf, 0xff, 0x8f, 0x7f};
diff --git a/blt/demos/bitmaps/hobbes.xbm b/blt/demos/bitmaps/hobbes.xbm
new file mode 100644
index 00000000000..a3778c364c4
--- /dev/null
+++ b/blt/demos/bitmaps/hobbes.xbm
@@ -0,0 +1,16 @@
+#define hobbes_width 25
+#define hobbes_height 25
+#define hobbes_x_hot 16
+#define hobbes_y_hot 15
+static char hobbes_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x78, 0xe0, 0x07, 0x00, 0xfc, 0xf8, 0x07, 0x00, 0xcc, 0x07, 0x04, 0x00,
+ 0x0c, 0xf0, 0x0b, 0x00, 0x7c, 0x1c, 0x06, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0xe0, 0x03, 0x10, 0x00, 0xe0, 0x41, 0x11, 0x00, 0x20, 0x40, 0x11, 0x00,
+ 0xe0, 0x07, 0x10, 0x00, 0xe0, 0xc1, 0x17, 0x00, 0x10, 0xe0, 0x2f, 0x00,
+ 0x20, 0xe0, 0x6f, 0x00, 0x18, 0xe0, 0x2f, 0x00, 0x20, 0xc6, 0x67, 0x00,
+ 0x18, 0x84, 0x2b, 0x00, 0x20, 0x08, 0x64, 0x00, 0x70, 0xf0, 0x13, 0x00,
+ 0x80, 0x01, 0x08, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+
+
diff --git a/blt/demos/bitmaps/hobbes_mask.xbm b/blt/demos/bitmaps/hobbes_mask.xbm
new file mode 100644
index 00000000000..682ce5fd1be
--- /dev/null
+++ b/blt/demos/bitmaps/hobbes_mask.xbm
@@ -0,0 +1,14 @@
+#define hobbes_width 25
+#define hobbes_height 25
+#define hobbes_x_hot 16
+#define hobbes_y_hot 15
+static unsigned char hobbes_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x78, 0xe0, 0x07, 0x00, 0xfc, 0xf8, 0x07, 0x00, 0xfc, 0xff, 0x07, 0x00,
+ 0xfc, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x0f, 0x00, 0xf8, 0xff, 0x0f, 0x00,
+ 0xe0, 0xff, 0x1f, 0x00, 0xe0, 0xff, 0x1f, 0x00, 0xe0, 0xff, 0x1f, 0x00,
+ 0xe0, 0xff, 0x1f, 0x00, 0xe0, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0x3f, 0x00,
+ 0xe0, 0xff, 0x7f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0x7f, 0x00,
+ 0xf8, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0x7f, 0x00, 0xf0, 0xff, 0x1f, 0x00,
+ 0x80, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
diff --git a/blt/demos/bitmaps/sharky.xbm b/blt/demos/bitmaps/sharky.xbm
new file mode 100644
index 00000000000..25f923fced3
--- /dev/null
+++ b/blt/demos/bitmaps/sharky.xbm
@@ -0,0 +1,129 @@
+#define sharky_width 171
+#define sharky_height 68
+static char sharky_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xdf, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xfd, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xd0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x54, 0xfd, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x49, 0x92, 0x54, 0x55, 0x45, 0xeb,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x41, 0x55, 0x84, 0x44, 0x85, 0xa2, 0x50, 0x94, 0x0f, 0x00,
+ 0x00, 0x06, 0x00, 0xfc, 0x17, 0x00, 0x40, 0x12, 0x12, 0x42, 0x00, 0x01,
+ 0x04, 0x04, 0x51, 0x75, 0x75, 0xd5, 0xae, 0x55, 0x10, 0x00, 0x80, 0x0f,
+ 0x00, 0xfe, 0x1f, 0x00, 0x5c, 0x54, 0x45, 0x89, 0x04, 0x10, 0x00, 0x80,
+ 0x08, 0x55, 0xd5, 0x5e, 0x55, 0xa5, 0x25, 0x15, 0xa0, 0x1f, 0x00, 0xfe,
+ 0x1f, 0x00, 0xde, 0x55, 0xb5, 0x76, 0x65, 0x25, 0x52, 0x22, 0xd0, 0x56,
+ 0x7b, 0xd5, 0xff, 0x5d, 0xb5, 0xa2, 0xd7, 0x1f, 0x00, 0xff, 0x0d, 0x00,
+ 0x17, 0x41, 0x40, 0x00, 0x7a, 0x95, 0x00, 0x91, 0x0a, 0xd9, 0xed, 0x7f,
+ 0xd5, 0x73, 0x5b, 0x55, 0x54, 0x7f, 0x80, 0xde, 0x07, 0x00, 0xab, 0x54,
+ 0x14, 0x49, 0xa8, 0x6e, 0x55, 0x0c, 0x64, 0x75, 0xff, 0xff, 0xbf, 0xde,
+ 0x57, 0xd5, 0x95, 0xfa, 0x43, 0x7f, 0x07, 0x00, 0x17, 0x00, 0x09, 0x00,
+ 0x74, 0xd1, 0x5b, 0xb5, 0xa9, 0xdd, 0xd5, 0xf7, 0xfd, 0x5f, 0x5e, 0x55,
+ 0x52, 0x95, 0xdd, 0xfd, 0x05, 0x00, 0x76, 0x55, 0x52, 0x25, 0xf9, 0x15,
+ 0x76, 0x6f, 0xb6, 0xf7, 0xff, 0xff, 0xff, 0xfb, 0xd7, 0xf7, 0xaa, 0x75,
+ 0xf7, 0xf7, 0x03, 0x00, 0xd8, 0x15, 0x50, 0x00, 0xf8, 0x60, 0xe8, 0xdd,
+ 0x5d, 0x7f, 0xff, 0xff, 0xef, 0xff, 0xfd, 0x5e, 0x5b, 0xff, 0xbf, 0xad,
+ 0x03, 0x00, 0x70, 0x27, 0x05, 0x49, 0xf9, 0x0a, 0x12, 0xb6, 0xf5, 0xfd,
+ 0x7f, 0xdf, 0xfd, 0xff, 0xff, 0xdf, 0xff, 0xbd, 0x6d, 0xd6, 0x07, 0x00,
+ 0xe0, 0x5b, 0x75, 0x04, 0x7c, 0x01, 0x40, 0xa8, 0xee, 0xff, 0xff, 0xff,
+ 0xdf, 0xff, 0xbf, 0xff, 0xeb, 0xd7, 0xd5, 0xbd, 0x05, 0x00, 0x80, 0x7f,
+ 0x05, 0x51, 0xb1, 0x44, 0x95, 0x46, 0x75, 0xe7, 0xff, 0xff, 0xff, 0xde,
+ 0xfb, 0xfb, 0x7e, 0x7d, 0x75, 0xef, 0x07, 0x00, 0x00, 0x7e, 0x5b, 0x12,
+ 0x00, 0x10, 0x00, 0x18, 0x4a, 0x9d, 0xfd, 0xdf, 0xf6, 0xfb, 0xff, 0xdf,
+ 0xd7, 0xa5, 0x4d, 0xd5, 0x06, 0x00, 0x00, 0xf8, 0xd7, 0xad, 0x0a, 0x02,
+ 0x44, 0x82, 0x52, 0x77, 0xef, 0xfd, 0xbf, 0xdf, 0xd6, 0xf6, 0x7e, 0x5f,
+ 0x03, 0xf7, 0x0f, 0x00, 0x00, 0xe0, 0x5f, 0xb6, 0x44, 0x08, 0x11, 0x51,
+ 0x54, 0x4a, 0xbb, 0xf7, 0xed, 0x7a, 0xdf, 0xdd, 0xd5, 0x75, 0x00, 0x5e,
+ 0x1d, 0x00, 0x00, 0x00, 0x3f, 0x93, 0x5d, 0x43, 0x44, 0x08, 0x11, 0x69,
+ 0xd5, 0x5e, 0x7f, 0xdf, 0x7b, 0x77, 0x75, 0x3b, 0x00, 0xf0, 0x2b, 0x00,
+ 0x00, 0x00, 0xfc, 0x5d, 0x67, 0x11, 0x00, 0x21, 0x44, 0x55, 0x7b, 0x75,
+ 0xd5, 0x6b, 0xd5, 0x6d, 0x5f, 0x07, 0x00, 0x50, 0x37, 0x00, 0x00, 0x00,
+ 0xf0, 0xff, 0x9d, 0x84, 0x48, 0x48, 0x89, 0x50, 0xb6, 0xd6, 0xbd, 0x5a,
+ 0x77, 0xdb, 0xd6, 0x3d, 0x00, 0xc0, 0x76, 0x00, 0x00, 0x00, 0x80, 0xff,
+ 0xff, 0x57, 0x40, 0x02, 0x45, 0x44, 0xd7, 0x55, 0x55, 0xd5, 0x54, 0x55,
+ 0x35, 0x33, 0x00, 0x80, 0xdf, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x3f,
+ 0x15, 0x50, 0x50, 0xd1, 0x7f, 0x54, 0xb5, 0x56, 0xdd, 0xf6, 0x1d, 0x5c,
+ 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x40, 0x45, 0x09,
+ 0x41, 0xe4, 0x5f, 0x95, 0x52, 0x55, 0x25, 0x55, 0x07, 0x74, 0x00, 0x00,
+ 0xbc, 0x01, 0x00, 0x00, 0x00, 0x80, 0x54, 0x12, 0x32, 0x11, 0x15, 0xf1,
+ 0xff, 0x52, 0x15, 0x53, 0xa9, 0xdd, 0x1f, 0x10, 0x00, 0x00, 0xe8, 0x02,
+ 0x00, 0x00, 0x00, 0x40, 0x05, 0xc9, 0x44, 0x49, 0x55, 0xf4, 0xf7, 0x12,
+ 0x45, 0x11, 0x55, 0xd5, 0x1f, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00,
+ 0x00, 0xc0, 0xff, 0x56, 0x5b, 0xb5, 0xa4, 0xea, 0x5f, 0x4f, 0x51, 0xcc,
+ 0xd6, 0x3f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xf5, 0x56, 0x5b, 0xf5, 0xff, 0x50, 0x55, 0xdb, 0xff, 0x1f,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff,
+ 0xbf, 0xff, 0x55, 0xff, 0xff, 0xaf, 0xd6, 0xff, 0xc1, 0x17, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xef,
+ 0xfe, 0xf7, 0xff, 0xdf, 0xff, 0x05, 0xe0, 0x0b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xdf, 0xff,
+ 0xff, 0xff, 0x1e, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xaf,
+ 0x1f, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x02, 0x00, 0x3c, 0x00,
+ 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0x00, 0xe0, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0xff, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0xff, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x03,
+ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x00,
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xda, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
diff --git a/blt/demos/bitmaps/xbob.xbm b/blt/demos/bitmaps/xbob.xbm
new file mode 100644
index 00000000000..dce1f9d2096
--- /dev/null
+++ b/blt/demos/bitmaps/xbob.xbm
@@ -0,0 +1,47 @@
+
+#define bob_x_hot 30
+#define bob_y_hot 37
+#define bob_width 61
+#define bob_height 75
+static short bobr_bits[] = { /* a right-going ``Bob'' */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0x07ff, 0x0000,
+ 0x0000, 0xfffe, 0x1fff, 0x0000, 0x8000, 0xffff, 0xfbff, 0x0000,
+ 0xc000, 0xcfff, 0xd19f, 0x0003, 0xf000, 0x8c7f, 0x9133, 0x0007,
+ 0xf800, 0x18a7, 0xb127, 0x0006, 0xfc00, 0x3147, 0xa64e, 0x000e,
+ 0xfe00, 0x214f, 0xae4c, 0x003d, 0xff00, 0x23df, 0xbe8d, 0x007d,
+ 0xff80, 0x67ff, 0xfebd, 0x01ff, 0xff80, 0x7fff, 0xffbf, 0x03ff,
+ 0xffc0, 0xffff, 0xffbf, 0x07f8, 0xffc0, 0xffff, 0x3fbf, 0x07f8,
+ 0xffc0, 0xffff, 0x07ff, 0x0ff8, 0xffc0, 0xffff, 0x003f, 0x0ff8,
+ 0x7fe0, 0xf800, 0x0007, 0x0ff0, 0x3fe0, 0x0000, 0x0000, 0x07f0,
+ 0x3fe0, 0x0000, 0x0000, 0x07f0, 0x3fe0, 0x0000, 0x0000, 0x07f4,
+ 0x3fe0, 0x0000, 0x0000, 0x07e4, 0x3fe0, 0x0000, 0x0000, 0x07e4,
+ 0x3fe0, 0x0000, 0x0000, 0x07e6, 0x3fe0, 0x0000, 0x0000, 0x07e7,
+ 0x3fe0, 0x0000, 0x0000, 0x07e6, 0x3fe0, 0x0000, 0x0000, 0x07e6,
+ 0x3fe0, 0x0000, 0x0000, 0x07e6, 0x3fc0, 0x0000, 0x7800, 0x07f6,
+ 0xbfa0, 0x00ff, 0xff00, 0x07f7, 0x9f70, 0x01ff, 0xff80, 0x07ef,
+ 0x1cf0, 0x0380, 0x01e0, 0x07ef, 0x1ff0, 0x07be, 0x3ff0, 0x07ee,
+ 0x9de0, 0x1f83, 0xe1f8, 0x07dc, 0xc1e0, 0x1f7f, 0xfffc, 0x07c8,
+ 0xc1e0, 0x1e69, 0xca7e, 0x03c0, 0x81e0, 0x1fb8, 0x0ec0, 0x03c0,
+ 0x01e0, 0x1bc0, 0xcfc0, 0x03c1, 0x03c0, 0x11f7, 0x7f00, 0x03c0,
+ 0x03c0, 0x187c, 0x1c00, 0x02c0, 0x02c0, 0x0830, 0x0000, 0x0340,
+ 0x0340, 0x0800, 0x0000, 0x0240, 0x1340, 0x0c00, 0x0000, 0x0260,
+ 0x1240, 0x0e00, 0x0000, 0x03c0, 0x3380, 0x0e80, 0x0000, 0x01a8,
+ 0x3300, 0x0f40, 0x03a0, 0x002c, 0x7400, 0x0f30, 0x0738, 0x002e,
+ 0x7400, 0x1f98, 0x1e1e, 0x002f, 0xfc00, 0xff8f, 0xfc0f, 0x002f,
+ 0xf800, 0xffe3, 0xf803, 0x002f, 0xf800, 0xfffd, 0xff81, 0x003f,
+ 0xb800, 0x1ff9, 0x0ff8, 0x001e, 0x3000, 0xf0f1, 0x030f, 0x000e,
+ 0x3000, 0x01f1, 0x0180, 0x000f, 0x2000, 0xf7f1, 0x00ff, 0x0007,
+ 0x6000, 0x01e3, 0x8060, 0x0007, 0x6000, 0xefc3, 0x803f, 0x0003,
+ 0x4000, 0xffc2, 0xc00f, 0x0003, 0xc000, 0x1fe6, 0xc000, 0x0001,
+ 0x8000, 0xfef4, 0xe03f, 0x0000, 0x8000, 0xfe79, 0xe01f, 0x0000,
+ 0x01c0, 0x3e3d, 0x7000, 0x0000, 0x0630, 0x0f3e, 0x3800, 0x0000,
+ 0x8cc8, 0x071f, 0x3800, 0x0000, 0xccf4, 0x078f, 0x1c00, 0x0000,
+ 0xee72, 0x07f7, 0x0e00, 0x0000, 0xff02, 0x07e3, 0x0700, 0x0000,
+ 0xfe32, 0xffc1, 0x038f, 0x0000, 0xfe3e, 0xff80, 0x01ff, 0x0000,
+ 0x7c7e, 0x0000, 0x007e, 0x0000, 0x3c7c, 0x0000, 0x0000, 0x0000,
+ 0x1cfc, 0x0000, 0x0000, 0x0000, 0x1cf8, 0x0000, 0x0000, 0x0000,
+ 0x0ff0, 0x0000, 0x0000, 0x0000, 0x07e0, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000};
+
+
+
diff --git a/blt/demos/busy1.tcl b/blt/demos/busy1.tcl
new file mode 100755
index 00000000000..7c9c58c68b2
--- /dev/null
+++ b/blt/demos/busy1.tcl
@@ -0,0 +1,254 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+#
+# Script to test the "busy" command.
+#
+
+#
+# General widget class resource attributes
+#
+option add *Button.padX 10
+option add *Button.padY 2
+option add *Scale.relief sunken
+#option add *Scale.orient horizontal
+option add *Entry.relief sunken
+option add *Frame.borderWidth 2
+
+set visual [winfo screenvisual .]
+if { $visual == "staticgray" || $visual == "grayscale" } {
+ set activeBg black
+ set normalBg white
+ set bitmapFg black
+ set bitmapBg white
+ option add *f1.background white
+} else {
+ set activeBg red
+ set normalBg springgreen
+ set bitmapFg blue
+ set bitmapBg green
+ option add *Button.background khaki2
+ option add *Button.activeBackground khaki1
+ option add *Frame.background khaki2
+ option add *f2.tile textureBg
+# option add *Button.tile textureBg
+
+ option add *releaseButton.background limegreen
+ option add *releaseButton.activeBackground springgreen
+ option add *releaseButton.foreground black
+
+ option add *holdButton.background red
+ option add *holdButton.activeBackground pink
+ option add *holdButton.foreground black
+ option add *f1.background springgreen
+}
+
+#
+# Instance specific widget options
+#
+option add *f1.relief sunken
+option add *f1.background $normalBg
+option add *testButton.text "Test"
+option add *quitButton.text "Quit"
+option add *newButton.text "New\nButton"
+option add *holdButton.text "Hold"
+option add *releaseButton.text "Release"
+option add *buttonLabel.text "Buttons"
+option add *entryLabel.text "Entries"
+option add *scaleLabel.text "Scales"
+option add *textLabel.text "Text"
+
+bind keepRaised <Visibility> { raise %W }
+
+proc KeepRaised { w } {
+ bindtags $w keepRaised
+}
+
+set file ./images/chalk.gif
+image create photo textureBg -file $file
+
+#
+# This never gets used; it's reset by the Animate proc. It's
+# here to just demonstrate how to set busy window options via
+# the host window path name
+#
+#option add *f1.busyCursor bogosity
+
+#
+# Counter for new buttons created by the "New button" button
+#
+set numWin 0
+
+#
+# Create two frames. The top frame will be the host window for the
+# busy window. It'll contain widgets to test the effectiveness of
+# the busy window. The bottom frame will contain buttons to
+# control the testing.
+#
+frame .f1
+frame .f2
+
+#
+# Create some widgets to test the busy window and its cursor
+#
+label .buttonLabel
+button .testButton -command {
+ puts stdout "Not busy."
+}
+button .quitButton -command { exit }
+entry .entry
+scale .scale
+text .text -width 20 -height 4
+
+#
+# The following buttons sit in the lower frame to control the demo
+#
+button .newButton -command {
+ global numWin
+ incr numWin
+ set name button#${numWin}
+ button .f1.$name -text "$name" \
+ -command [list .f1 configure -bg blue]
+ table .f1 \
+ .f1.$name $numWin+3,0 -padx 10 -pady 10
+}
+
+button .holdButton -command {
+ if { [busy isbusy .f1] == "" } {
+ global activeBg
+ .f1 configure -bg $activeBg
+ }
+ busy .f1
+ focus -force .
+}
+
+button .releaseButton -command {
+ if { [busy isbusy .f1] == ".f1" } {
+ busy release .f1
+ }
+ global normalBg
+ .f1 configure -bg $normalBg
+}
+
+#
+# Notice that the widgets packed in .f1 and .f2 are not their children
+#
+table .f1 \
+ 0,0 .testButton \
+ 1,0 .scale -fill y \
+ 0,1 .entry -fill x \
+ 1,1 .text -fill both \
+ 2,0 .quitButton -cspan 2
+
+table .f2 \
+ 0,0 .holdButton \
+ 0,1 .releaseButton \
+ 0,2 .newButton
+
+table configure .f1 \
+ .testButton .scale .entry .quitButton -padx 10 -pady 10
+table configure .f2 \
+ .newButton .holdButton .releaseButton -padx 10 -pady 4 -reqwidth 1.i
+
+table configure .f1 r0 r2 -resize none
+table configure .f2 r* -resize none
+
+#
+# Finally, realize and map the top level window
+#
+table . \
+ 0,0 .f1 -fill both \
+ 1,0 .f2 -fill both
+
+table configure . r1 -resize none
+
+table configure .f1 c1 -weight 2.0
+
+# Initialize a list of bitmap file names which make up the animated
+# fish cursor. The bitmap mask files have a "m" appended to them.
+
+set bitmapList {
+ left left1 mid right1 right
+}
+
+#
+# Simple cursor animation routine: Uses the "after" command to
+# circulate through a list of cursors every 0.075 seconds. The
+# first pass through the cursor list may appear sluggish because
+# the bitmaps have to be read from the disk. Tk's cursor cache
+# takes care of it afterwards.
+#
+proc StartAnimation { widget count } {
+ global bitmapList
+ set prefix bitmaps/fish/[lindex $bitmapList $count]
+ set cursor [list @${prefix}.xbm ${prefix}m.xbm blue green ]
+ busy configure $widget -cursor $cursor
+
+ incr count
+ set limit [llength $bitmapList]
+ if { $count >= $limit } {
+ set count 0
+ }
+ global afterId
+ set afterId($widget) [after 125 StartAnimation $widget $count]
+}
+
+proc StopAnimation { widget } {
+ global afterId
+ after cancel $afterId($widget)
+}
+
+proc TranslateBusy { window } {
+ set widget [string trimright $window "_Busy"]
+ if { $widget != "." } {
+ set widget [string trimright $widget "."]
+ }
+ return $widget
+}
+
+if { [info exists tcl_platform] && $tcl_platform(platform) == "unix" } {
+ bind Busy <Map> {
+ StartAnimation [TranslateBusy %W] 0
+ }
+ bind Busy <Unmap> {
+ StopAnimation [TranslateBusy %W]
+ }
+}
+
+#
+# For testing, allow the top level window to be resized
+#
+wm min . 0 0
+
+#
+# Force the demo to stay raised
+#
+raise .
+KeepRaised .
+
diff --git a/blt/demos/busy2.tcl b/blt/demos/busy2.tcl
new file mode 100755
index 00000000000..26baeed21e3
--- /dev/null
+++ b/blt/demos/busy2.tcl
@@ -0,0 +1,259 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -fill both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -fill both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+# namespace import -force blt::tile::*
+}
+#source scripts/demo.tcl
+
+#
+# Script to test the "busy" command.
+#
+
+#
+# General widget class resource attributes
+#
+option add *Button.padX 10
+option add *Button.padY 2
+option add *Scale.relief sunken
+#option add *Scale.orient horizontal
+option add *Entry.relief sunken
+option add *Frame.borderWidth 2
+
+set visual [winfo screenvisual .]
+if { $visual == "staticgray" || $visual == "grayscale" } {
+ set activeBg black
+ set normalBg white
+ set bitmapFg black
+ set bitmapBg white
+ option add *f1.background white
+} else {
+ set activeBg red
+ set normalBg springgreen
+ set bitmapFg blue
+ set bitmapBg green
+ option add *Button.background khaki2
+ option add *Button.activeBackground khaki1
+ option add *Frame.background khaki2
+ option add *f2.tile textureBg
+# option add *Button.tile textureBg
+
+ option add *releaseButton.background limegreen
+ option add *releaseButton.activeBackground springgreen
+ option add *releaseButton.foreground black
+
+ option add *holdButton.background red
+ option add *holdButton.activeBackground pink
+ option add *holdButton.foreground black
+ option add *f1.background springgreen
+}
+
+#
+# Instance specific widget options
+#
+option add *f1.relief sunken
+option add *f1.background $normalBg
+option add *testButton.text "Test"
+option add *quitButton.text "Quit"
+option add *newButton.text "New button"
+option add *holdButton.text "Hold"
+option add *releaseButton.text "Release"
+option add *buttonLabel.text "Buttons"
+option add *entryLabel.text "Entries"
+option add *scaleLabel.text "Scales"
+option add *textLabel.text "Text"
+
+proc LoseFocus {} {
+ focus -force .
+}
+proc KeepRaised { w } {
+ bindtags $w keepRaised
+}
+
+bind keepRaised <Visibility> { raise %W }
+
+set file ./images/chalk.gif
+image create photo textureBg -file $file
+
+#
+# This never gets used; it's reset by the Animate proc. It's
+# here to just demonstrate how to set busy window options via
+# the host window path name
+#
+#option add *f1.busyCursor bogosity
+
+
+#
+# Counter for new buttons created by the "New button" button
+#
+set numWin 0
+
+menu .menu
+.menu add command -label "First"
+.menu add command -label "Second"
+.menu add command -label "Third"
+.menu add command -label "Fourth"
+. configure -menu .menu
+
+#
+# Create two frames. The top frame will be the host window for the
+# busy window. It'll contain widgets to test the effectiveness of
+# the busy window. The bottom frame will contain buttons to
+# control the testing.
+#
+frame .f1
+frame .f2
+
+#
+# Create some widgets to test the busy window and its cursor
+#
+label .buttonLabel
+button .testButton -command {
+ puts stdout "Not busy."
+}
+button .quitButton -command { exit }
+entry .entry
+scale .scale
+text .text -width 20 -height 4
+
+#
+# The following buttons sit in the lower frame to control the demo
+#
+button .newButton -command {
+ global numWin
+ incr numWin
+ set name button#${numWin}
+ button .f1.$name -text "$name" \
+ -command [list .f1 configure -bg blue]
+ table .f1 \
+ .f1.$name $numWin+3,0 -padx 10 -pady 10
+}
+
+button .holdButton -command {
+ if { [busy isbusy .f1] == "" } {
+ global activeBg
+ .f1 configure -bg $activeBg
+ }
+ busy .f1
+ busy .#menu
+ LoseFocus
+}
+button .releaseButton -command {
+ if { [busy isbusy .f1] == ".f1" } {
+ busy release .f1
+ busy release .#menu
+ }
+ global normalBg
+ .f1 configure -bg $normalBg
+}
+
+#
+# Notice that the widgets packed in .f1 and .f2 are not their children
+#
+table .f1 \
+ .testButton 0,0 \
+ .scale 1,0 \
+ .entry 0,1 \
+ .text 1,1 -fill both \
+ .quitButton 2,0
+
+table .f2 \
+ .newButton 0,0 \
+ .holdButton 1,0 \
+ .releaseButton 2,0
+
+table configure .f1 .testButton .scale .entry .quitButton -padx 10 -pady 10 -fill both
+table configure .f2 .newButton .holdButton .releaseButton -padx 10 -pady 10
+table configure .f2 c0 -resize none
+#
+# Finally, realize and map the top level window
+#
+table . \
+ .f1 0,0 \
+ .f2 1,0
+
+table configure . .f1 .f2 -fill both
+# Initialize a list of bitmap file names which make up the animated
+# fish cursor. The bitmap mask files have a "m" appended to them.
+
+table configure . r1 -resize none
+
+set bitmapList { left left1 mid right1 right }
+
+#
+# Simple cursor animation routine: Uses the "after" command to
+# circulate through a list of cursors every 0.075 seconds. The
+# first pass through the cursor list may appear sluggish because
+# the bitmaps have to be read from the disk. Tk's cursor cache
+# takes care of it afterwards.
+#
+proc StartAnimation { widget count } {
+ global bitmapList
+ set prefix "bitmaps/fish/[lindex $bitmapList $count]"
+ set cursor [list @${prefix}.xbm ${prefix}m.xbm black white ]
+ busy configure $widget -cursor $cursor
+
+ incr count
+ set limit [llength $bitmapList]
+ if { $count >= $limit } {
+ set count 0
+ }
+ global afterId
+ set afterId($widget) [after 125 StartAnimation $widget $count]
+}
+
+proc StopAnimation { widget } {
+ global afterId
+ after cancel $afterId($widget)
+}
+
+proc TranslateBusy { window } {
+ #set widget [string trimright $window "_Busy"]
+ set widget [string trimright $window "Busy"]
+ set widget [string trimright $widget "_"]
+# if { [winfo toplevel $widget] != $widget } {
+# set widget [string trimright $widget "."]
+# }
+ return $widget
+}
+
+if { [info exists tcl_platform] && $tcl_platform(platform) == "unix" } {
+ bind Busy <Map> {
+ StartAnimation [TranslateBusy %W] 0
+ }
+ bind Busy <Unmap> {
+ StopAnimation [TranslateBusy %W]
+ }
+}
+
+#
+# For testing, allow the top level window to be resized
+#
+wm min . 0 0
+
+#
+# Force the demo to stay raised
+#
+raise .
+KeepRaised .
diff --git a/blt/demos/container.tcl b/blt/demos/container.tcl
new file mode 100755
index 00000000000..c29e9cf76ac
--- /dev/null
+++ b/blt/demos/container.tcl
@@ -0,0 +1,14 @@
+#!../src/bltwish
+
+package require BLT
+namespace import blt::*
+
+set cmd "xterm -geom +4000+4000"
+#set cmd "xclock -name fred -geom +4000+4000"
+eval bgexec myVar $cmd &
+container .c
+pack .c -fill both -expand yes
+#.c configure -relief raised -bd 2 -name fred
+.c configure -relief raised -bd 2 -command $cmd
+
+
diff --git a/blt/demos/container3.tcl b/blt/demos/container3.tcl
new file mode 100755
index 00000000000..3455357000b
--- /dev/null
+++ b/blt/demos/container3.tcl
@@ -0,0 +1,451 @@
+#!../src/bltwish
+
+package require BLT
+namespace import blt::*
+
+
+image create photo mini-apm-alert -data {
+ R0lGODdhEAAQAPIAALLA3AAAAAAAoMDAwKCgoICAgP8AAP///ywAAAAAEAAQAAADSgiq1bGQ
+ tTNKuBHMMmrwEdeMDfEAhqENROu2Z7q6xhurXJvWsCLnOpsPx3q5bjNjD0VULhdAJTJqnBZ0
+ qZbgBAkIvuBtRnEpYyIJADs=
+}
+
+image create photo mini-apm-empty -data {
+ R0lGODdhEAAQAPIAALLA3AAAAMDAwKCgoICAgP8AAP///wAAACwAAAAAEAAQAAADSQiq1LGQ
+ NSNIuBFMImrwUVE0ZCNGhDisq/hAnNoWw7twgyrbjDDTK57Gl5PVUD4dUBgDFo8w40woZZ1g
+ BJYWCglsgxnFZYyJJAAAOw==
+}
+
+image create photo mini-apm-full -data {
+ R0lGODdhEAAQAPIAALLA3AAAAAAAoMDAwICAgP8AAP///wAAACwAAAAAEAAQAAADQAiq1LGQ
+ NTNIuBFMMmrwEdeMjfBAnKCu6rmkasG6zBAXchva8byzPhQPaPoRi8Ij8jUE0jSEW265CByf
+ 1YsWmwAAOw==
+}
+
+image create photo mini-apm-half -data {
+ R0lGODdhEAAQAPIAALLA3AAAAAAAoMDAwKCgoICAgP8AAP///ywAAAAAEAAQAAADRwiq1bGQ
+ tTNKuBHMMmrwEdeMDfFAHKGu6rmkqsG6zBAbchva8byzKwFNMxAYj8YhB4lUFpnJHTSKKhhx
+ huMQEJhuFZcwJpIAADs=
+}
+
+image create photo mini-apm-loading -data {
+ R0lGODdhEAAQAPMAALLA3AAAAP//AMDAwKCgoICAgP8AAICAAP///wAAAAAAAAAAAAAAAAAA
+ AAAAAAAAACwAAAAAEAAQAAAEXRBIWWo4oNyZKxqHcFzi5BUDKBYhRg1VFcqC9hJ4rFvZkH+p
+ ICzQwxk0gaSSWDQWENCUrfkTTlGCbA0oZWK1ggBw2gNnl5yd9smkPKGI2KcNgCoH8AEdkOQv
+ +xwAEQA7
+}
+
+image create photo mini-apm-online -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAAMDAwICAgP//AP///wAAACwAAAAAEAAQAAADRQi6QCHt
+ sSkKrHAuDLhWFUGEwSeeaMkQxuC+g6M5QW2rs6G7stm+vRwsqAn8iJ/bBLVg4RosneHUeups
+ A+ngCagtblxAAgA7
+}
+
+image create photo mini-apm-unknown -data {
+ R0lGODdhEAAQAPIAALLA3AAAAP//AMDAwICAgP8AAP///wAAACwAAAAAEAAQAAADRAiq1LGQ
+ NTNIuBFMMmrwEdeMjfBAnKCu6rmkQiGvLjPEa9GGt676Nc0tR+MRi6ghDvlSskzGHytYeu4i
+ ASs0A7h4MZEEADs=
+}
+
+image create photo mini-folder -data {
+ R0lGODdhIAAbAPIAALLA3ICAgP//////AMDAwAAAAICAAAAAACwAAAAAIAAbAAADeQgK0e4r
+ yhmDuBgHyqUdBCiGxNZxHzmO5lkRcCzPT10XTKbvPF8UqZVqKBQagAKisrg8BpdQpjPJrEan
+ 1qxyGu0Wsd4wiKsth8BmLVnsRbOt6/QVKVcj3206Hn6vz59+Rn17TXqBW0AGiouMjY6Pijg/
+ k5SVlpeTAAkAOw==
+}
+
+image create photo mini-arch -data {
+ R0lGODdhEAAQAPEAAICAgAAAAMBkMv+gMiwAAAAAEAAQAAACQQSCqXoi46B7YtlVA51Q1sxN
+ EaQhmyKWmoMYzTCoZOUes/kF9SrHtKvxIW47mIr1M/AyxqQtF2wVYzpG7ZC4arcFADs=
+}
+
+image create photo mini-asmail -data {
+ R0lGODdhEAAQAPMAAJSUlP///97e3mNjYzz4NK2trfgUQL29vXNzc0JCQoCAgAAAAAAAAAAA
+ AAAAAAAAACwAAAAAEAAQAAAEVFDJOYGgmA5iMwaGIYwfAAzDqJLT6q4ScMyvOsSyW9i3MhwC
+ hGo3GhR6JuHLeEAoAgUTcGVEWA9Q089WOCQTWROgcBxYv2GxWoxIeK3weDzhqdvrEQA7
+}
+
+image create photo mini-audiovol -data {
+ R0lGODdhDgAOAPEAALLA3AAA7v///2ZmZiwAAAAADgAOAAACMYQdcxgLkdhQK8E0oIMz4+19
+ ztMNwohQ5zkmK8BK0Xtel0I/tgv3uq3y7SiGmARlKAAAOw==
+}
+
+image create photo mini-ball -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP///4CAgCwAAAAAEAAQAAACG4SPqcvtD1mYMAhBncVh6Dl5
+ Xyc6wxml6so+BQA7
+}
+
+image create photo mini-bball -data {
+ R0lGODdhEAAQAPEAALLA3AAA/////4CAgCwAAAAAEAAQAAACG4SPqcvtD1mYMAhBncVh6Dl5
+ Xyc6wxml6so+BQA7
+}
+
+image create photo mini-bomb -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgMDAwP//AP///wAAAAAAACwAAAAAEAAQAAADPAi63K4h
+ vhZWneDaGTV0nbQxgVAMHbkIQcF2wpgFw5sqBJCHHVHlhNwsJEDhggpbhBXTUXgeh62JqVon
+ CQA7
+}
+
+image create photo mini-book1 -data {
+ R0lGODdhEAAQAPIAALLA3AAAAP8AAP//AICAgP///wAAAAAAACwAAAAAEAAQAAADPAi63K4h
+ vBaFkLNai5neWwcEwwCeXVSeIBZcK8uNXNymtckFBeHlkUKPIHpdeMOI48UjEEWUwHPigVKv
+ CQA7
+}
+
+image create photo mini-book2 -data {
+ R0lGODdhEAAQAPEAAL+/vwAAAICAgP///ywAAAAAEAAQAAACOYSPeRHqIUZbLMEhAcNzRf5J
+ lXFxGNiVUgil7Lm6p9p6M1zbK5fbgYgRdDwMgdE4JLUYzKTSAY0CCgA7
+}
+
+image create photo mini-books -data {
+ R0lGODdhEAAQAPIAAICAgP////8AAAAA/wAAAP//AAAAAAAAACwAAAAAEAAQAAADSgi63B3B
+ wRdFGGoSBy5exaYIwjJgRQGECwlgJ0Bs7FjGqFoDLgGvOhGvJIgBV0KXwKdIIVtEX+6Zqcww
+ k11PQVMJZbPG7MspmzkJADs=
+}
+
+image create photo mini-briefcase -data {
+ R0lGODdhEAAQAPIAALLA3ICAAMDAwAAAAICAgP///wAAAAAAACwAAAAAEAAQAAADSQi63B4Q
+ OhaKuCJMFcT4g9ZEZFgClWANAuGpahBegTsUa1eHRY17v5lMRyrWCLwOZpnJzJo6aAZZiVqh
+ z6uVauzKAKCw+LMpJwAAOw==
+}
+
+image create photo mini-bug1 -data {
+ R0lGODdhEAAQAPIAALLA3AAAAMDAwICAgP///wAAAAAAAAAAACwAAAAAEAAQAAADNAi63P5Q
+ hTWjDDUKzEUcAcENEChiIOlgxCCA2MoF2yxjddwEMD1mjA4H4ituLDygZckEJAAAOw==
+}
+
+image create photo mini-bug2 -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgMDAwP///wAAAAAAAAAAACwAAAAAEAAQAAADOAi63A5h
+ xceCpUvYLajQW/A9FsFdjWZ+oFiV1hCiyiyHjT1XbXBbmtzmN+ENTw/QrYMBCAbMZjMBADs=
+}
+
+image create photo mini-bx2 -data {
+ R0lGODdhEAAQAPAAALLA3AAAACwAAAAAEAAQAAACIoSPqcuNAeEKL9Kn7AU7ae5EoAOKDfVN
+ R4pVptGtFknXdQEAOw==
+}
+
+image create photo mini-calc -data {
+ R0lGODdhEAAQAPIAALLA3ICAgP///wAAANnZ2QAAAAAAAAAAACwAAAAAEAAQAAADRAi63Bow
+ SgmCuDjjUQUhQyiKYPcN2hWU1jdJrPfN9NkFA4Hr+c5ZIVAuSLz1jrwYcciM7Z7In2AprP5q
+ WBtgxB05vo0EADs=
+}
+
+image create photo mini-camera -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP///4CAgCwAAAAAEAAQAAACNYSPqRDta4KYNCAnhxviyYo1
+ YBgMJplp5XSWqtByptrC9HyLc7O7bx/q2YA4DQNFgiiXTGYBADs=
+}
+
+image create photo mini-cat -data {
+ R0lGODdhEAAQAPEAAICAgAAAAP//AP8AACwAAAAAEAAQAAACOYSPecGqIUR4YNZoEdw36yh1
+ FTNBoGSa2Cah5xsN2AW/llqHHYjPdErydTiblCG1wvhIo2PJ44gWAAA7
+}
+
+image create photo mini-cave -data {
+ R0lGODdhEAAQAPMAAAD//wBy/QAA/AAAuAD/jwD/Cv+EAHL/AP//AA8Pbf8AAP9FAP/HAAAA
+ AAAAAAAAACwAAAAAEAAQAAAEWhBIGaq4I+c5a7iCNhBEYRzGCFjggCAJARCJ8hKskOwJIAAw
+ Cc5C4yVgiUEBODHydL9dgVRw8haJxeKAtO4WBgSDofAaGYcD1sxD49iJwAE9hbcRZXtPz7dH
+ AAA7
+}
+
+image create photo mini-cd -data {
+ R0lGODdhEAAQAPIAALLA3ICAgP//AMDAwAAAAAD//wD/AP///ywAAAAAEAAQAAADUQiq0b2Q
+ BSGGJSSCUGi1FxQYRfdZx5AxBkmBqcpYbYeCcgCSAaHjmR8tQ8DlcIceB7iBoQyCAmili9Gi
+ 0tXGesVqm7gW5SvZGTCaRQNDTrsVCQA7
+}
+
+image create photo mini-cdlabel -data {
+ R0lGODdhEAAQAPEAALLA3AAAAICAgAAAACwAAAAAEAAQAAACM4SPecEg8SJcIFQpqVWpsfcx
+ 21aVBmmlZTqpbinE66yeVUt7NC2/qAkciYYQEeeITCoTBQA7
+}
+
+image create photo mini-chinese -data {
+ R0lGODdhEAAQAPAAAICAgAAA/ywAAAAAEAAQAAACJoQdB6kXrx5ri0FapZ4R2baFEpVAHFkt
+ WCaunuuFcDq/NV1f91oAADs=
+}
+
+image create photo mini-clipboard -data {
+ R0lGODdhEAAQAPIAALLA3AAAAP//AICAAICAgMDAwAAAgP///ywAAAAAEAAQAAADUgi63B4w
+ OBWhEFXSQUbAX9ARmkgWaCp6BBBwWUSQ7TpyeCe6+WD8QMPOxjEcjsdfrWdEHowG141gjDyB
+ r1vTmSRSD7Gnq7LlRgGzWXB9bqTfswQAOw==
+}
+
+image create photo mini-clock -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP///8DAwCwAAAAAEAAQAAACMoSPqRDda5qY86UwaKAjXCps
+ GiJqYHiU1elFoPSmrNoybDjKZ55me6eAaWpBBxGCTCoKADs=
+}
+
+image create photo mini-colors -data {
+ R0lGODdhEAAQAPIAALLA3ICAgP8AAP//AAAA/wAAAP///wAAACwAAAAAEAAQAAADPgi63P4w
+ skCrtSqILYYfREgEBaBxHyiSJtd94lieWxqzgaEbVuEXuB2v8gPOhL1fcJf04S5QSglQrFYl
+ WGwCADs=
+}
+
+image create photo mini-connect -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgP8AAP//AAAAAAAAAAAAACwAAAAAEAAQAAADLwi63P7Q
+ hbgECJaKMfJ8GzdcWROKHXSKpYl2n7MKsUkIOK3edFAzAh6l0tIMj44EADs=
+}
+
+image create photo mini-crosbone -data {
+ R0lGODdhEAAQAPEAAICAgAAAAP///8DAwCwAAAAAEAAQAAACQUQCqXqMu5qRMJ0HxBV8jygM
+ 4jhoUPAhKTBgVnvAqGtkIcrVJ1uKeoUi0Rwz2SnQmSE1j4PgJXowLRHKr4LNagEFADs=
+}
+
+image create photo mini-cross -data {
+ R0lGODdhEAAQAPEAALLA3P8AAICAgAAAACwAAAAAEAAQAAACKISPqcsbHgQKFEpAM8x0DMYF
+ X8ONYNhgovpApqasU5TItWngiZ72TAEAOw==
+}
+
+image create photo mini-desktop -data {
+ R0lGODdhEAAQAPIAAICAgACAgAAAAAD//8DAwP///4CAAP//ACwAAAAAEAAQAAADQgi6EMIQ
+ BveidEBUG8asWqSNobJZzIku6tq62UIARSEQhkHsRDXXNtwhd/PRgoaD8vB49EjPkekIrAJj
+ K0iTxCUpEgA7
+}
+
+image create photo mini-dfolder -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwP//AAAA/wAAAP///wAAACwAAAAAEAAQAAADSQi63BsQ
+ OhbEsCJMUK/PkcQZZGmSQTF+YTtiwWkWsRcTeB7TBmwQP52BZwvmCLuY74hLGopM5LAVo+4A
+ hax2u93ESJkNlqsFJAAAOw==
+}
+
+image create photo mini-diff -data {
+ R0lGODdhEAAQAPEAALLA3P8AAAAAAP///ywAAAAAEAAQAAACQYSPqcLtckSYQYyLIZDGVnxB
+ HDBYw5dtAbldaKhGrhkyVTODTBnU07PjUSiaTsZBlIEyydbS93M+RVGgo1NUaA0FADs=
+}
+
+image create photo mini-diskette -data {
+ R0lGODdhEAAQAPIAAAAAgAAAAICAgP//AODg4P///8DAwAAAACwAAAAAEAAQAAADQAgQ3B5K
+ qEGrHQoCwrsnmVKMZFmE2/ehZouq6yK2JarcOK4pRu8bOhwQ8AvehsXbjsjzGXnNnk4STWYE
+ goe2kQAAOw==
+}
+
+image create photo mini-display -data {
+ R0lGODdhEAAQAPIAALLA3ICAgP///wAAAAAA/8DAwAAAAAAAACwAAAAAEAAQAAADPgi63Bsw
+ SviEvdgOFcT4INhtwEic6DlyHiGkqscOMLqWbU3cps7ntd9gIpHhMkgSIMQEOTqXQkC5aDYd
+ 2EYCADs=
+}
+
+image create photo mini-doc -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwAAAAP///wAAAAAAAAAAACwAAAAAEAAQAAADNAix3PAw
+ kEmnCPDJSgXBEUcN5BCKnamh6ckKqsJa8fbWMwG76L7OPhkQN4wIjshksMRsqhIAOw==
+}
+
+image create photo mini-doc1 -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP///4CAgCwAAAAAEAAQAAACOYSPicEdeoKYk0EghxbyKUlx
+ zSdomxYMCRhW6lKaW7q2FA3L8xvZbq0bcHgGVgsXcSiJhqDsAgUUAAA7
+}
+
+image create photo mini-dog -data {
+ R0lGODdhEAAQAPEAAICAgAAAAP///8DAwCwAAAAAEAAQAAACPoSPqRbt6oKYgiEpxqA1XKkJ
+ zeRdGTZg4pVybgk0sTvFMENydhJsI3WbTVIRnoiSWqhsMmOIEzRwntGFFVAAADs=
+}
+
+image create photo mini-edit -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgP///wAA/wAAAAAAAAAAACwAAAAAEAAQAAADPwi63CAw
+ RveGvUMEJ7LMw7YQHWZpgUiQXoRta1CaVxBX0ndXtLXjOQ1BpJi9UkRgBBliGEMvZ6/GQFqv
+ lGwjAQA7
+}
+
+image create photo mini-espada -data {
+ R0lGODdhEAAQAPAAAICAgAAAACwAAAAAEAAQAAACIoSPqRbr7RSMJ1jK7I26v+4l4CiOYWVq
+ 0tWo1IQBMHfGSwEAOw==
+}
+
+image create photo mini-exclam -data {
+ R0lGODdhEAAQAPIAALLA3ICAgACAAP///wAAAAAAAAAAAAAAACwAAAAAEAAQAAADLQi6GsKQ
+ hSFEiJJWglf9XOeAV/dtnXKGKZh6AvvGLyOnU1UDZznHPhwhuEslAAA7
+}
+
+image create photo mini-exp -data {
+ R0lGODdhEAAQAPEAALLA3AAAAICAgP//ACwAAAAAEAAQAAACMoSPqRbrzYQDYbQAD97VWtUN
+ 4shJzIiSSZhmDKuuaayJmN1lHGVT2GHSuAJB0OSInBQAADs=
+}
+
+image create photo mini-eye -data {
+ R0lGODdhEAAQAPEAALLA3AAAAIKCgsPDwywAAAAAEAAQAAACK4SPqcvtb4KcYs1wKJIADzF0
+ HTZGgBBU5JiaX0hWh6Bmp5yoHA71/g+EFAAAOw==
+}
+
+image create photo mini-eyes -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgMDAwP///wAAAAAAAAAAACwAAAAAEAAQAAADPwi63P4w
+ tkDDqvYOQoYNW5eBHOeF5leWwcoFQuvKK9xSLIG/sSefO48AQBL1UoIhsZJcUpqLJBQgVUqu
+ 2KwiAQA7
+}
+
+image create photo mini-fax -data {
+ R0lGODdhEAAQAPIAALLA3ICAgAAAAMDAwP///wAAAAAAAAAAACwAAAAAEAAQAAADSAi6HM5w
+ BUIfdI5qIuQk39Yp0zBkokScpTlwJ1DOQuAKZkYHtXvWu17OAaSYhBii0DhMdnC5VZI3Agig
+ LGXEitNuF9fqd7xIAAA7
+}
+
+image create photo mini-fdisk -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwAAAAP8AAP///wAAAAAAACwAAAAAEAAQAAADMgi63P4w
+ yhmqvVYFwbvnwRAUZGmSlfh9hJBu3eW9bjrcVq125hxiQMtAcSsajZOkcpkAADs=
+}
+
+image create photo mini-filemgr -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwAAAAP///wAAAAAAAAAAACwAAAAAEAAQAAADPggK0f5L
+ BUGraCO+jTX5IDF0S0BY1Bhk5TkMlcpKJyp7qHCXb9+TtNBnR8sRGS5YCog0riJN24zn60Gv
+ 2EUCADs=
+}
+
+image create photo mini-folder -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwP//AAAAAP///wAAAAAAACwAAAAAEAAQAAADOwi63BsQ
+ OhbEsCJMUK/PkcQVZGmSATF+rJWOWHu9QSHHdBHvs1rfnhxvlwO6fKFkSAUgOJ9Q6GZKdSQA
+ ADs=
+}
+
+image create photo mini-font -data {
+ R0lGODdhEAAQAPEAALLA3AAAAICAgAAAACwAAAAAEAAQAAACJ4SPqcvtF8IzMYhXrYuCN95p
+ Cwh4Y2YmoBBequWWIpLKMcIm+cQ3BQA7
+}
+
+image create photo mini-fractal -data {
+ R0lGODdhEAAQAPIAALLA3AAAAICAgP8AAP//AMDAwACAAAAA/ywAAAAAEAAQAAADSQi63BpB
+ MAHjqvCNQXh4T7V15Fd8gZF24iZpxqBe3hXPALp9j4HzgcMAuBnmLofDK7AL5ZJE1iMJvZQu
+ kFeOZHTQrt4HlxcuexMAOw==
+}
+
+image create photo mini-frame -data {
+ R0lGODdhEAAQAPEAALLA3P8AAAAAAAAAACwAAAAAEAAQAAACM4SPqcGsGISU4YF43V3izrpx
+ IdMlWFRhZqeeD+rFJniWEHu4ptGwNmSQyF4NlQUw+R2PBQA7
+}
+
+image create photo mini-ftp -data {
+ R0lGODdhEAAQAPEAAICAgP///wAAAAAA/ywAAAAAEAAQAAACNoSPqcEdelwDDwh7w9hN+ItI
+ EhhtphmQhuiolMa1l/eyaQKf3ZzZd3gatHA+l+93+CiVkCagAAA7
+}
+
+image create photo mini-gball -data {
+ R0lGODdhEAAQAPEAALLA3ACAAP///4CAgCwAAAAAEAAQAAACG4SPqcvtD1mYMAhBncVh6Dl5
+ Xyc6wxml6so+BQA7
+}
+
+image create photo mini-go -data {
+ R0lGODdhEAAQAPEAAICAgAAA//8AAP///ywAAAAAEAAQAAACMoSPecEpj8IYzYgJjaTh3JEB
+ zOg9oViVgsK27gtbJ9p52KpN6Rduu4mjpQCmBCmGdBUAADs=
+}
+
+image create photo mini-gopher -data {
+ R0lGODdhEAAQAPIAAICAgICAAIAAAP///wAAAAAAAAAAAAAAACwAAAAAEAAQAAADSQgKESLN
+ LRXAg+41SzO+zbM4Q0mawuQQxECy0ijAGZzKmheOHst+qcvFRwAKQT8joIhkhggK5oywoq4m
+ LFSJNZloB92weExWJAAAOw==
+}
+
+image create photo mini-graph -data {
+ R0lGODdhEAAQAPIAAICAgAAAAP8A/wAA/wD/AP8AAAAAAAAAACwAAAAAEAAQAAADOwi6G86Q
+ CRGindUCV7EGw9BRXzhmlgl4DAeKK8kQRKWyC23Dnqu/J0ChUPndSMNiDRirJDnQKPRDrSoS
+ ADs=
+}
+
+image create photo mini-gv -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP///wAAACwAAAAAEAAQAAACMYSPqcGhGYQU7oE4ZV1ZP0Zp
+ FINgYYhtZjdtF9tyrHussdzRL0yV7e+pNS4kkuUIKAAAOw==
+}
+
+image create photo mini-hammer -data {
+ R0lGODdhEAAQAPEAAL+/vwAAAP///4CAgCwAAAAAEAAQAAACK4SPiRHAr5wQYdqWqt00i6FN
+ TLc0TzA4WAQZq9JG5SurtBzXtn7ktc8L6goAOw==
+}
+
+image create photo mini-happy -data {
+ R0lGODdhEAAQAPEAALLA3AAAAP//AAAAACwAAAAAEAAQAAACM4SPqRDda5qY86VAc70VSxEg
+ WAeO4GFK3xlpbti6GcxU2kjXpbOJFN8TmToQhiNXTCoPBQA7
+}
+
+image create photo mini-hdisk -data {
+ R0lGODdhEAAQAPIAALLA3ICAgMDAwAAAAACAAP///wAAAAAAACwAAAAAEAAQAAADLgi63P4w
+ yhmqvVYFwbvnwRAUZGmSlfh9hJBu6/e62EWr53nXfKgMwKAwOCkajwkAOw==
+}
+
+image create photo mini-heart -data {
+ R0lGODdhEAAQAPAAAICAgAAAACwAAAAAEAAQAAACIoSPqbvh54KKj0pogb16b+RRSRhNIeMx
+ RqauTwvBoBzTbQEAOw==
+}
+
+image create photo mini-hex -data {
+ R0lGODdhEAAQAPEAAICAgP///wAAAAAA/ywAAAAAEAAQAAACO4SPecHdIBIbjQYQ4qLcQqFh
+ 3FRlS2ChZei0DauSJDtYY20a8qjC6Z/DuFq+0myjulFoydjyAIpKpYACADs=
+}
+
+image create photo mini-hextris -data {
+ R0lGODdhEAAQAPIAALLA3ICAgP//AAAAAP8AAAAAAAAAAAAAACwAAAAAEAAQAAADNAi63P5Q
+ hRlXEHjUSzrR0eANQQUMaLlhAvlM3EhZrNgNtSR4H7qPCttHJXQFUwyU0cRsMhMAOw==
+}
+
+image create photo mini-iconify -data {
+ R0lGODdhEAAOAPEAALLA3P8AAAAAAAAAACwAAAAAEAAOAAACKoyPKSHt7xgDtFoqxd0g8+tV
+ iIYtJHacIWqopgWJ7ydPdGnfUsKn0A8pAAA7
+}
+
+image create photo mini-icons -data {
+ R0lGODdhEAAQAPIAAICAgP///wAAAAAA/wD/AP8AAP//AAAAACwAAAAAEAAQAAADPAi63BoQ
+ ihfDBGFo+4jnmDaAgUeQImmS1VWBQiwr8uzceL68bvsUQJJhSAIWhESXEWlgRXpPWu0yvega
+ CQA7
+}
+
+image create photo mini-keyboard -data {
+ R0lGODdhEQAQAPEAAICAgP///wAAAMDAwCwAAAAAEQAQAAACPYSPecHtDgQIqsY5hwiab31R
+ QzQKZRlqwsq2kuTFHQPC2nkPqO32LxZrdGoYnI50gUV8rYul8mL2ntRqogAAOw==
+}
+
+set images1 {
+ apm-alert apm-empty apm-full apm-half apm-loading apm-online
+ apm-unknown folder arch asmail audiovol ball bball
+}
+set images2 {
+ bomb book1 book2 books briefcase bug1 bug2 bx2 calc camera cat
+ cave cd cdlabel chinese clipboard clock colors connect crosbone
+}
+set images3 {
+ cross desktop dfolder diff diskette display doc doc1 dog edit
+ espada exclam exp eye eyes fax fdisk filemgr font fractal
+}
+set images4 {
+ frame ftp gball go gopher graph gv hammer happy hdisk heart hex
+ hextris iconify icons keyboard
+}
+
+proc MakeContainer { count images } {
+ set c .c$count
+ set top .top$count
+ set b .b$count
+
+ blt::container $c -bd 0 -highlightthickness 0
+ toplevel $top
+ wm withdraw $top
+
+ wm protocol $top WM_DELETE_WINDOW "$b invoke"
+
+ frame $top.f -relief raised -highlightthickness 0
+ pack $top.f -expand yes -fill x
+
+ foreach img $images {
+ button $top.f.$img -image mini-$img -bd 1 -command "puts $img" \
+ -highlightthickness 0
+ pack $top.f.$img -side left -padx 0 -pady 0
+ }
+ global $img
+ checkbutton $b -variable $img -onvalue $top -offvalue "" -command \
+ [subst -nocommands { $c configure -window \$$img }]
+ $b select
+ blt::table . \
+ $b $count,0 -anchor w \
+ $c $count,1 -fill x
+ blt::table configure . c0 -resize none
+ blt::table configure . r$count -resize none
+ after 1 [subst {
+ update
+ wm deiconify $top
+ $c configure -window $top
+ }]
+ return $c
+}
+
+MakeContainer 1 $images1
+MakeContainer 2 $images2
+MakeContainer 3 $images3
+MakeContainer 4 $images4
+
+canvas .a
+blt::table . .a -cspan 40
+
diff --git a/blt/demos/dnd1.tcl b/blt/demos/dnd1.tcl
new file mode 100755
index 00000000000..fa8bcf511d1
--- /dev/null
+++ b/blt/demos/dnd1.tcl
@@ -0,0 +1,212 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+if { ([info exists tcl_platform]) && ($tcl_platform(platform) == "windows") } {
+ source scripts/send.tcl
+ SendInit
+ SendVerify
+}
+
+proc OnEnter { widget args } {
+ array set info $args
+ $widget configure -highlightbackground red
+ return 1
+}
+
+proc OnMotion { widget args } {
+ array set info $args
+ set x1 [$widget cget -bd]
+ set x1 20
+ set y1 $x1
+ set x2 [expr [winfo width $widget] - $x1]
+ set y2 [expr [winfo height $widget] - $y1]
+ if { ($info(x) >= $x1) && ($info(x) <= $x2) &&
+ ($info(y) >= $y1) && ($info(y) <= $y2) } {
+ $widget configure -highlightbackground red
+ return 1
+ }
+ $widget configure -highlightbackground grey
+ return 0
+}
+
+proc OnLeave { widget args } {
+ $widget configure -highlightbackground grey
+ return 0
+}
+
+option add *OnEnter OnEnter
+option add *OnLeave OnLeave
+option add *OnMotion OnMotion
+
+# ----------------------------------------------------------------------
+# This procedure is invoked each time a token is grabbed from the
+# sample window. It configures the token to display the current
+# color, and returns the color value that is later passed to the
+# target handler.
+# ----------------------------------------------------------------------
+
+proc PackageSample { widget args } {
+ array set info $args
+ set bg [.sample cget -background]
+ set fg [.sample cget -foreground]
+ $info(token).label configure -background $bg -foreground $fg
+ return 1
+}
+
+proc ShowResult { widget args } {
+ array set info $args
+ puts "drop transaction($info(timestamp)) completed: result was $info(action)"
+}
+
+
+# ----------------------------------------------------------------------
+# Main application window...
+# ----------------------------------------------------------------------
+image create photo openFolder -format gif -data {
+R0lGODdhEAAOAPIAAP///wAAAH9/f9nZ2f//AAAAAAAAAAAAACwAAAAAEAAOAAADOwgqzPoQ
+iDjjAoPkIZuTgCZykBCA2ziaXusRrFUGQ5zeRMCcE76xvJBPozuBVCmT0eUKGAHOqFQqqwIS
+ADs=
+ }
+label .sample -text "Color" -height 12 -width 20 -bd 2 -relief raised \
+ -highlightthickness 2
+
+set cursors {
+ { @bitmaps/hand/hand01.xbm bitmaps/hand/hand01m.xbm black white }
+ { @bitmaps/hand/hand02.xbm bitmaps/hand/hand02m.xbm black white }
+ { @bitmaps/hand/hand03.xbm bitmaps/hand/hand03m.xbm black white }
+ { @bitmaps/hand/hand04.xbm bitmaps/hand/hand04m.xbm black white }
+ { @bitmaps/hand/hand05.xbm bitmaps/hand/hand05m.xbm black white }
+ { @bitmaps/hand/hand06.xbm bitmaps/hand/hand06m.xbm black white }
+ { @bitmaps/hand/hand07.xbm bitmaps/hand/hand07m.xbm black white }
+ { @bitmaps/hand/hand08.xbm bitmaps/hand/hand08m.xbm black white }
+ { @bitmaps/hand/hand09.xbm bitmaps/hand/hand09m.xbm black white }
+ { @bitmaps/hand/hand10.xbm bitmaps/hand/hand10m.xbm black white }
+ { @bitmaps/hand/hand11.xbm bitmaps/hand/hand11m.xbm black white }
+ { @bitmaps/hand/hand12.xbm bitmaps/hand/hand12m.xbm black white }
+ { @bitmaps/hand/hand13.xbm bitmaps/hand/hand13m.xbm black white }
+ { @bitmaps/hand/hand14.xbm bitmaps/hand/hand14m.xbm black white }
+}
+
+
+# Set up the color sample as a drag&drop source and target for "color" values:
+dnd register .sample -source yes -target yes \
+ -package PackageSample \
+ -result ShowResult \
+ -cursors $cursors
+
+dnd getdata .sample color GetColor
+dnd setdata .sample color SetColor
+
+# Establish the appearance of the token window:
+set token [dnd token window .sample]
+label $token.label -text "Color" -bd 2 -highlightthickness 1
+pack $token.label
+dnd token configure .sample -borderwidth 2 \
+ -relief raised -activerelief raised \
+ -outline pink -fill red \
+ -anchor s
+
+if 1 {
+scale .redScale -label "Red" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .red -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .greenScale -label "Green" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .green -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .blueScale -label "Blue" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .blue -width 20 -height 20 -borderwidth 3 -relief sunken
+
+# ----------------------------------------------------------------------
+# This procedure loads a new color value into this editor.
+# ----------------------------------------------------------------------
+proc GetColor { widget args } {
+ return [$widget cget -bg]
+}
+
+proc SetColor { widget args } {
+ array set info $args
+ set rgb [winfo rgb . $info(value)]
+ set r [lindex $rgb 0]
+ set g [lindex $rgb 1]
+ set b [lindex $rgb 2]
+
+ .redScale set [expr round($r/65535.0 * 255)]
+ .greenScale set [expr round($g/65535.0 * 255)]
+ .blueScale set [expr round($b/65535.0 * 255)]
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked whenever an RGB slider changes to
+# update the color samples in this display.
+# ----------------------------------------------------------------------
+proc adjust_color {args} {
+ set rval [.redScale get]
+ .red configure -background [format "#%.2x0000" $rval]
+ set gval [.greenScale get]
+ .green configure -background [format "#00%.2x00" $gval]
+ set bval [.blueScale get]
+ .blue configure -background [format "#0000%.2x" $bval]
+
+ .sample configure -background \
+ [format "#%.2x%.2x%.2x" $rval $gval $bval]
+ if {$rval+$gval+$bval < 1.5*255} {
+ .sample configure -foreground white
+ } else {
+ .sample configure -foreground black
+ }
+}
+table . .redScale 1,0 -fill both
+table . .red 1,1 -fill both
+table . .greenScale 2,0 -fill both
+table . .green 2,1 -fill both
+table . .blueScale 3,0 -fill both
+table . .blue 3,1 -fill both
+
+}
+table . .sample 0,0 -columnspan 2 -fill both -pady {0 4}
+
+proc random {{max 1.0} {min 0.0}} {
+ global randomSeed
+
+ set randomSeed [expr (7141*$randomSeed+54773) % 259200]
+ set num [expr $randomSeed/259200.0*($max-$min)+$min]
+ return $num
+}
+set randomSeed [clock clicks]
+
+.redScale set [expr round([random 255.0])]
+.blueScale set [expr round([random 255.0])]
+.greenScale set [expr round([random 255.0])]
+bind .sample <KeyPress-Escape> { dnd cancel .sample }
+focus .sample
+
+
diff --git a/blt/demos/dnd2.tcl b/blt/demos/dnd2.tcl
new file mode 100755
index 00000000000..9ab27345766
--- /dev/null
+++ b/blt/demos/dnd2.tcl
@@ -0,0 +1,328 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+if { ([info exists tcl_platform]) && ($tcl_platform(platform) == "windows") } {
+ error "This script works only under X11"
+}
+
+canvas .c -width 320 -height 320 -background white
+
+blt::table . .c -fill both
+
+set lastCell ""
+set cellWidth 1
+set cellHeight 1
+proc RedrawWorld { canvas } {
+ global cells cellWidth cellHeight
+
+ $canvas delete all
+
+ set width [winfo width $canvas]
+ set height [winfo height $canvas]
+
+ set cellWidth [expr $width / 8]
+ set cellHeight [expr $height / 8]
+
+ for { set row 0 } { $row < 8 } { incr row } {
+ set y [expr $row * $cellHeight]
+ set h [expr $y + $cellHeight]
+ for { set column 0 } { $column < 8 } { incr column } {
+ set x [expr $column * $cellWidth]
+ set w [expr $x + $cellWidth]
+ $canvas create rectangle $x $y $w $h -fill white -outline "" \
+ -tags "$row,$column"
+ }
+ }
+
+ for { set row 0 } { $row < 8 } { incr row } {
+ set y [expr $row * $cellHeight]
+ $canvas create line 0 $y $width $y
+ }
+
+ for { set column 0 } { $column < 8 } { incr column } {
+ set x [expr $column * $cellWidth]
+ $canvas create line $x 0 $x $height
+ }
+ foreach name [array names cells] {
+ set rc [split $name ,]
+ set row [lindex $rc 0]
+ set column [lindex $rc 1]
+ set x [expr ($column * $cellWidth) + 5]
+ set y [expr ($row * $cellHeight) + 5]
+ set w [expr $cellWidth - 10]
+ set h [expr $cellHeight - 10]
+ set color [lindex $cells($name) 0]
+ set type [lindex $cells($name) 1]
+ set pi1_2 [expr 3.14159265358979323846/180.0]
+ set points {}
+ switch $type {
+ hexagon {
+ lappend points $x [expr $y + $h/2] [expr $x + $w * 1/3] \
+ $y [expr $x + $w * 2/3] $y [expr $x + $w] [expr $y + $h/2] \
+ [expr $x + $w * 2/3] [expr $y + $h] \
+ [expr $x + $w * 1/3] [expr $y + $h]
+ }
+ parallelogram {
+ lappend points $x [expr $y + $h * 2/3] \
+ [expr $x + $w * 2/3] $y \
+ [expr $x + $w] [expr $y + $h * 1/3] \
+ [expr $x + $w * 1/3] [expr $y + $h]
+ }
+ triangle {
+ lappend points \
+ $x [expr $y + $h] \
+ [expr $x + $w * 1/2] $y \
+ [expr $x + $w] [expr $y + $h]
+ }
+ }
+ eval .c create polygon $points -fill $color -outline black
+ }
+}
+
+bind .c <Configure> { RedrawWorld %W }
+
+# ----------------------------------------------------------------------
+# USAGE: random ?<max>? ?<min>?
+#
+# Returns a random number in the range <min> to <max>.
+# If <min> is not specified, the default is 0; if max is not
+# specified, the default is 1.
+# ----------------------------------------------------------------------
+
+proc random {{max 1.0} {min 0.0}} {
+ global randomSeed
+
+ set randomSeed [expr (7141*$randomSeed+54773) % 259200]
+ set num [expr $randomSeed/259200.0*($max-$min)+$min]
+ return $num
+}
+set randomSeed [clock clicks]
+
+set itemTypes { parallelogram hexagon triangle }
+set itemTypes { hexagon triangle parallelogram }
+
+for { set i 0 } { $i < 20 } { incr i } {
+ while { 1 } {
+ set row [expr int([random 8])]
+ set column [expr int([random 8])]
+ set type [expr int([random 3])]
+ set type [lindex $itemTypes $type]
+ if { ![info exists cells($row,$column)] } {
+ set r [expr int([random 256 128])]
+ set g [expr int([random 256 128])]
+ set b [expr int([random 256 128])]
+ set cells($row,$column) [format "#%.2x%.2x%.2x %s" $r $g $b $type]
+ break
+ }
+ }
+}
+
+proc ScreenToCell { widget x y } {
+ global cellWidth cellHeight
+ set column [expr $x / $cellWidth]
+ set row [expr $y / $cellHeight]
+ return $row,$column
+}
+
+
+set count 0
+foreach i [winfo interps] {
+ puts $i
+ if { [string match "dnd2.tcl*" $i] } {
+ incr count
+ }
+}
+
+if { $count == 1 } {
+ toplevel .info
+ raise .info
+ text .info.text -width 65 -height 12 -font { Helvetica 10 } -bg white \
+ -tabs { 0.25i }
+ .info.text insert end {
+ This is a more involved example of the new "dnd" command.
+ Run this script again to get another window. You can then drag
+ and drop symbols between the windows by clicking with the left
+ mouse button on a symbol.
+
+ It demonstates how to
+ o Drag-and-drop on specific areas (canvas items) of a widget.
+ o How to receive and handle Enter/Leave/Motion events in the target.
+ o How to send drag feedback to the source.
+ o Use a drag threshold.
+ }
+ button .info.quit -text "Dismiss" -command { destroy .info }
+ blt::table .info \
+ 0,0 .info.text -fill both \
+ 1,0 .info.quit
+}
+
+
+# -----------------------------------------------------------------
+#
+# Setup finished. Start of drag-and-drop code here.
+#
+
+# Set up the entire canvas as a drag&drop source.
+
+dnd register .c -source yes -dragthreshold 5 -button 1
+
+# Register code to pick up the information about a canvas item
+
+dnd getdata .c color GetColor
+
+proc GetColor { widget args } {
+ array set info $args
+ global itemInfo
+ set id $itemInfo($info(timestamp))
+ set color [$widget itemcget $id -fill]
+ set ncoords [llength [$widget coords $id]]
+ if { $ncoords == 6 } {
+ set type triangle
+ } elseif { $ncoords == 8 } {
+ set type parallelogram
+ } elseif { $ncoords == 12 } {
+ set type hexagon
+ } else {
+ error "unknown type n=$ncoords"
+ }
+ return [list $color $type]
+}
+
+dnd configure .c -package PackageSample
+
+proc PackageSample { widget args } {
+ array set info $args
+
+ # Check if we're over a canvas item
+ set items [$widget find overlapping $info(x) $info(y) $info(x) $info(y)]
+ set pickedItem ""
+ foreach i $items {
+ if { [$widget type $i] == "polygon" } {
+ set pickedItem $i
+ break
+ }
+ }
+ if { $pickedItem == "" } {
+ # Cancel the drag
+ puts "Cancel the drag x=$info(x) y=$info(y)"
+ return 0
+ }
+ set fill [$widget itemcget $pickedItem -fill]
+ set outline [$widget itemcget $pickedItem -outline]
+
+ set ncoords [llength [$widget coords $pickedItem]]
+ if { $ncoords == 6 } {
+ set type triangle
+ } elseif { $ncoords == 8 } {
+ set type parallelogram
+ } elseif { $ncoords == 12 } {
+ set type hexagon
+ } else {
+ error "unknown type n=$ncoords"
+ }
+ set tag [ScreenToCell $widget $info(x) $info(y)]
+ $info(token).label configure -background $fill -foreground $outline \
+ -text $type
+ update idletasks
+ update
+ global itemInfo
+ set itemInfo($info(timestamp)) $pickedItem
+ return 1
+}
+
+# Configure a set of animated cursors.
+
+dnd configure .c -cursors {
+ { @bitmaps/hand/hand01.xbm bitmaps/hand/hand01m.xbm black white }
+ { @bitmaps/hand/hand02.xbm bitmaps/hand/hand02m.xbm black white }
+ { @bitmaps/hand/hand03.xbm bitmaps/hand/hand03m.xbm black white }
+ { @bitmaps/hand/hand04.xbm bitmaps/hand/hand04m.xbm black white }
+ { @bitmaps/hand/hand05.xbm bitmaps/hand/hand05m.xbm black white }
+ { @bitmaps/hand/hand06.xbm bitmaps/hand/hand06m.xbm black white }
+ { @bitmaps/hand/hand07.xbm bitmaps/hand/hand07m.xbm black white }
+ { @bitmaps/hand/hand08.xbm bitmaps/hand/hand08m.xbm black white }
+ { @bitmaps/hand/hand09.xbm bitmaps/hand/hand09m.xbm black white }
+ { @bitmaps/hand/hand10.xbm bitmaps/hand/hand10m.xbm black white }
+ { @bitmaps/hand/hand11.xbm bitmaps/hand/hand11m.xbm black white }
+ { @bitmaps/hand/hand12.xbm bitmaps/hand/hand12m.xbm black white }
+ { @bitmaps/hand/hand13.xbm bitmaps/hand/hand13m.xbm black white }
+ { @bitmaps/hand/hand14.xbm bitmaps/hand/hand14m.xbm black white }
+}
+
+# Create a widget to place in the drag-and-drop token
+
+set token [dnd token window .c]
+
+label $token.label -bd 2 -highlightthickness 1
+pack $token.label
+dnd token configure .c \
+ -borderwidth 2 \
+ -relief raised -activerelief raised \
+ -outline pink -fill red \
+ -anchor s
+
+
+dnd configure .c -target yes
+
+dnd setdata .c color {
+ NewObject
+}
+
+proc NewObject { widget args } {
+ array set info $args
+ set tag [ScreenToCell $widget $info(x) $info(y)]
+ global cells
+ if { [info exists cells($tag)] } {
+ error "Cell already exists"
+ }
+ set cells($tag) $info(value)
+ RedrawWorld $widget
+
+}
+
+dnd configure .c -onmotion OnMotion -onenter OnMotion -onleave OnMotion
+
+proc OnMotion { widget args } {
+ global cells lastCell
+
+ array set info $args
+ set tag [ScreenToCell $widget $info(x) $info(y)]
+ if { $lastCell != "" } {
+ $widget itemconfigure $lastCell -fill white -outline "" -width 1 \
+ -stipple ""
+ }
+ # Check that we're not over a canvas item
+ if { ![info exists cells($tag)] } {
+ $widget itemconfigure $tag -outline lightblue -fill lightblue \
+ -width 2 -stipple BLT
+ set lastCell $tag
+ return 1
+ }
+ return 0
+}
+
diff --git a/blt/demos/dragdrop1.tcl b/blt/demos/dragdrop1.tcl
new file mode 100755
index 00000000000..1a6cbcca710
--- /dev/null
+++ b/blt/demos/dragdrop1.tcl
@@ -0,0 +1,131 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+if { ([info exists tcl_platform]) && ($tcl_platform(platform) == "windows") } {
+ source scripts/send.tcl
+ SendInit
+ SendVerify
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked each time a token is grabbed from the
+# sample window. It configures the token to display the current
+# color, and returns the color value that is later passed to the
+# target handler.
+# ----------------------------------------------------------------------
+proc package_color {token} {
+ set bg [.sample cget -background]
+ set fg [.sample cget -foreground]
+
+ $token.label configure -background $bg -foreground $fg
+ return $bg
+}
+
+# ----------------------------------------------------------------------
+# Main application window...
+# ----------------------------------------------------------------------
+label .sample -text "Color" -height 2 -bd 10 -relief sunken
+
+#
+# Set up the color sample as a drag&drop source for "color" values:
+#
+drag&drop source .sample \
+ -packagecmd {package_color %t} \
+ -sitecmd { puts "%s %t" }
+
+drag&drop source .sample handler color
+
+#
+# Set up the color sample as a drag&drop target for "color" values:
+#
+drag&drop target .sample handler color {set_color %v}
+
+#
+# Establish the appearance of the token window:
+#
+set token [drag&drop token .sample]
+label $token.label -text "Color"
+pack $token.label
+
+scale .redScale -label "Red" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .redSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .greenScale -label "Green" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .greenSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .blueScale -label "Blue" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .blueSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+# ----------------------------------------------------------------------
+# This procedure loads a new color value into this editor.
+# ----------------------------------------------------------------------
+proc set_color {cval} {
+ set rgb [winfo rgb . $cval]
+
+ set rval [expr round([lindex $rgb 0]/65535.0*255)]
+ .redScale set $rval
+
+ set gval [expr round([lindex $rgb 1]/65535.0*255)]
+ .greenScale set $gval
+
+ set bval [expr round([lindex $rgb 2]/65535.0*255)]
+ .blueScale set $bval
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked whenever an RGB slider changes to
+# update the color samples in this display.
+# ----------------------------------------------------------------------
+proc adjust_color {args} {
+ set rval [.redScale get]
+ .redSample configure -background [format "#%.2x0000" $rval]
+ set gval [.greenScale get]
+ .greenSample configure -background [format "#00%.2x00" $gval]
+ set bval [.blueScale get]
+ .blueSample configure -background [format "#0000%.2x" $bval]
+
+ .sample configure -background \
+ [format "#%.2x%.2x%.2x" $rval $gval $bval]
+ if {$rval+$gval+$bval < 1.5*255} {
+ .sample configure -foreground white
+ } else {
+ .sample configure -foreground black
+ }
+}
+
+table . .sample 0,0 -columnspan 2 -fill both -pady {0 4}
+table . .redScale 1,0 -fill both
+table . .redSample 1,1 -fill both
+table . .greenScale 2,0 -fill both
+table . .greenSample 2,1 -fill both
+table . .blueScale 3,0 -fill both
+table . .blueSample 3,1 -fill both
diff --git a/blt/demos/dragdrop2.tcl b/blt/demos/dragdrop2.tcl
new file mode 100755
index 00000000000..eb3c1674b10
--- /dev/null
+++ b/blt/demos/dragdrop2.tcl
@@ -0,0 +1,183 @@
+#!../src/bltwish
+
+package require BLT
+
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+if { ([info exists tcl_platform]) && ($tcl_platform(platform) == "windows") } {
+ source scripts/send.tcl
+ SendInit
+ SendVerify
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked each time a token is grabbed from the
+# sample window. It configures the token to display the current
+# color, and returns the color value that is later passed to the
+# target handler.
+# ----------------------------------------------------------------------
+
+proc package_color {token} {
+ set bg [.sample cget -background]
+ set fg [.sample cget -foreground]
+
+ $token.label configure -text "Color" -background $bg -foreground $fg
+ return $bg
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked each time a token is grabbed from an
+# entry widget. It configures the token to display the current
+# string, and returns the string that is later passed to the target
+# handler.
+# ----------------------------------------------------------------------
+proc package_string {str token} {
+ if {[string length $str] > 20} {
+ set mesg "[string range $str 0 19]..."
+ } else {
+ set mesg $str
+ }
+ $token.label configure -text $mesg
+ return $str
+}
+
+# ----------------------------------------------------------------------
+# Main application window...
+# ----------------------------------------------------------------------
+label .sample -text "Color" -height 2 -borderwidth 3 -relief sunken
+
+#
+# Set up the color sample as a drag&drop source for "color" values
+# and "string" values
+#
+drag&drop source .sample -packagecmd {package_color %t}
+drag&drop source .sample handler color
+drag&drop source .sample handler string
+
+#
+# Set up the color sample as a drag&drop target for "color" values:
+#
+drag&drop target .sample handler color {set_color %v}
+
+#
+# Establish the appearance of the token window:
+#
+set token [drag&drop token .sample -activebackground yellow ]
+label $token.label -text "Color"
+pack $token.label
+
+scale .redScale -label "Red" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .redSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .greenScale -label "Green" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .greenSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+scale .blueScale -label "Blue" -orient horizontal \
+ -from 0 -to 255 -command adjust_color
+frame .blueSample -width 20 -height 20 -borderwidth 3 -relief sunken
+
+frame .color
+label .color.label -text "Color:"
+pack .color.label -side left
+entry .color.value -width 10
+pack .color.value -side left -expand yes -fill both
+
+bind .color.value <KeyPress-Return> {set_color [%W get]}
+
+#
+# Set up the entry widget as a drag&drop source for "string" values:
+#
+drag&drop source .color.value \
+ -packagecmd {package_string [%W get] %t} \
+ -selftarget yes
+drag&drop source .color.value handler string
+
+#
+# Set up the entry widget as a drag&drop target for "string" values:
+#
+drag&drop target .color.value handler string {
+ %W delete 0 end
+ %W insert 0 "%v"
+}
+
+#
+# Establish the appearance of the token window:
+#
+set token [drag&drop token .color.value]
+label $token.label
+pack $token.label
+
+# ----------------------------------------------------------------------
+# This procedure loads a new color value into this editor.
+# ----------------------------------------------------------------------
+proc set_color {cval} {
+ set rgb [winfo rgb . $cval]
+
+ set rval [expr round([lindex $rgb 0]/65535.0*255)]
+ .redScale set $rval
+
+ set gval [expr round([lindex $rgb 1]/65535.0*255)]
+ .greenScale set $gval
+
+ set bval [expr round([lindex $rgb 2]/65535.0*255)]
+ .blueScale set $bval
+}
+
+# ----------------------------------------------------------------------
+# This procedure is invoked whenever an RGB slider changes to
+# update the color samples in this display.
+# ----------------------------------------------------------------------
+proc adjust_color {args} {
+ set rval [.redScale get]
+ .redSample configure -background [format "#%.2x0000" $rval]
+ set gval [.greenScale get]
+ .greenSample configure -background [format "#00%.2x00" $gval]
+ set bval [.blueScale get]
+ .blueSample configure -background [format "#0000%.2x" $bval]
+
+ .sample configure -background \
+ [format "#%.2x%.2x%.2x" $rval $gval $bval]
+ if {$rval+$gval+$bval < 1.5*255} {
+ .sample configure -foreground white
+ } else {
+ .sample configure -foreground black
+ }
+}
+
+table . \
+ 0,0 .sample -columnspan 2 -pady {0 4} \
+ 1,0 .color -columnspan 2 -padx 4 -pady 4 \
+ 2,0 .redScale \
+ 2,1 .redSample \
+ 3,0 .greenScale \
+ 3,1 .greenSample \
+ 4,0 .blueScale \
+ 4,1 .blueSample
+
+eval table configure . [winfo children .] -fill both
diff --git a/blt/demos/eps.tcl b/blt/demos/eps.tcl
new file mode 100755
index 00000000000..8dad92d1eec
--- /dev/null
+++ b/blt/demos/eps.tcl
@@ -0,0 +1,256 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+bltdebug watch ResizeEpsItem
+
+proc MoveEpsItem { canvas tagName x y } {
+ global lastX lastY
+ $canvas move $tagName [expr $x - $lastX] [expr $y - $lastY]
+ set lastX $x; set lastY $y
+}
+
+proc GetEpsBBox { canvas tagName } {
+ global left top right bottom
+ set anchor [$canvas coords $tagName-image]
+ set left [lindex $anchor 0]
+ set top [lindex $anchor 1]
+ set width [$canvas itemcget $tagName-image -width]
+ set height [$canvas itemcget $tagName-image -height]
+ set right [expr $left + $width]
+ set bottom [expr $top + $height]
+}
+
+proc SaveImageCoords { canvas x y } {
+ global lastX lastY
+ set lastX $x
+ set lastY $y
+ $canvas configure -cursor sb_h_double_arrow
+}
+
+array set cursors {
+ sw bottom_left_corner
+ ne top_right_corner
+ se bottom_right_corner
+ nw top_left_corner
+}
+
+proc StartResize { canvas tagName x y anchor } {
+ global left top right bottom image
+
+ GetEpsBBox $canvas $tagName
+ $canvas itemconfigure $tagName-image -quick yes
+ $canvas itemconfigure $tagName-grip -fill red
+ $canvas create line $left $top $right $bottom \
+ -tags "$tagName $tagName-cross $tagName-l1" \
+ -fill red -width 2
+
+ $canvas create line $left $bottom $right $top \
+ -tags "$tagName $tagName-cross $tagName-l2" \
+ -fill red -width 2
+ $canvas raise $tagName-grip
+ global cursors
+ $canvas configure -cursor $cursors($anchor)
+ global lastX lastY
+ set lastX $x
+ set lastY $y
+}
+
+proc EndResize { canvas tagName x y anchor } {
+ $canvas itemconfigure $tagName-image -quick no \
+ -showimage yes
+ ResizeEpsItem $canvas $anchor $tagName $x $y
+ $canvas itemconfigure $tagName-grip -fill green
+ $canvas delete $tagName-cross
+ $canvas configure -cursor ""
+}
+
+proc ResetGrips { canvas tagName } {
+ global gripSize
+ global left top right bottom
+
+ GetEpsBBox $canvas $tagName
+ $canvas coords $tagName-nw \
+ $left $top [expr $left + $gripSize] [expr $top + $gripSize]
+ $canvas coords $tagName-se \
+ [expr $right - $gripSize] [expr $bottom - $gripSize] $right $bottom
+ $canvas coords $tagName-ne \
+ [expr $right - $gripSize] [expr $top + $gripSize] $right $top
+ $canvas coords $tagName-sw \
+ $left $bottom [expr $left + $gripSize] [expr $bottom - $gripSize]
+ $canvas coords $tagName-l1 $left $top $right $bottom
+ $canvas coords $tagName-l2 $left $bottom $right $top
+}
+
+proc ResizeEpsItem { canvas anchor tagName x y } {
+ global lastX lastY left top right bottom
+
+ GetEpsBBox $canvas $tagName
+ switch $anchor {
+ sw {
+ set left $x ; set bottom $y
+ set cursor bottom_left_corner
+ }
+ ne {
+ set right $x ; set top $y
+ set cursor top_right_corner
+ }
+ se {
+ set right $x ; set bottom $y
+ set cursor bottom_right_corner
+ }
+ nw {
+ set left $x ; set top $y
+ set cursor top_left_corner
+ }
+ default {
+ error "anchor can't be $anchor"
+ }
+ }
+ set w [expr $right - $left]
+ set h [expr $bottom - $top]
+ set options ""
+ if { $w > 1 } {
+ append options "-width $w "
+ }
+ if { $h > 1 } {
+ append options "-height $h "
+ }
+ $canvas coords $tagName-image $left $top
+ eval $canvas itemconfigure $tagName-image $options
+ GetEpsBBox $canvas $tagName
+ ResetGrips $canvas $tagName
+}
+
+set numGroups 0
+set id 0
+
+proc MakeEps { canvas {epsFile ""} {imageFile ""} } {
+ global numGroups id gripSize image
+
+# set image [image create photo -width 200 -height 200]
+# if { $imageFile != "" } {
+# $image configure -file $imageFile
+# }
+ set tagName "epsGroup[incr numGroups]"
+ $canvas create eps 20 20 \
+ -anchor nw \
+ -borderwidth 4 \
+ -tags "$tagName $tagName-image" \
+ -titlecolor white \
+ -titlerotate 90 \
+ -titleanchor nw \
+ -font *helvetica*24* \
+ -stipple BLT \
+ -outline orange4 \
+ -fill orange \
+ -file $epsFile \
+
+# -image $image
+
+ set gripSize 8
+ GetEpsBBox $canvas $tagName
+ global left top right bottom
+ $canvas create rectangle \
+ $left $top [expr $left + $gripSize] [expr $top + $gripSize] \
+ -tags "$tagName $tagName-grip $tagName-nw" \
+ -fill red -outline ""
+ $canvas create rectangle \
+ [expr $right - $gripSize] [expr $bottom - $gripSize] $right $bottom \
+ -tags "$tagName $tagName-grip $tagName-se" \
+ -fill red -outline ""
+ $canvas create rectangle \
+ [expr $right - $gripSize] [expr $top + $gripSize] $right $top \
+ -tags "$tagName $tagName-grip $tagName-ne" \
+ -fill red -outline ""
+ $canvas create rectangle \
+ $left $bottom [expr $left + $gripSize] [expr $bottom - $gripSize] \
+ -tags "$tagName $tagName-grip $tagName-sw" \
+ -fill red -outline ""
+
+ $canvas bind $tagName <ButtonRelease-1> \
+ "$canvas configure -cursor {}"
+ $canvas bind $tagName-image <ButtonPress-1> \
+ "SaveImageCoords $canvas %x %y"
+ $canvas bind $tagName-image <B1-Motion> \
+ "MoveEpsItem $canvas $tagName %x %y"
+
+ foreach grip { sw ne se nw } {
+ $canvas bind $tagName-$grip <ButtonPress-1> \
+ "StartResize $canvas $tagName %x %y $grip"
+ $canvas bind $tagName-$grip <B1-Motion> \
+ "ResizeEpsItem $canvas $grip $tagName %x %y"
+ $canvas bind $tagName-$grip <ButtonRelease-1> \
+ "EndResize $canvas $tagName %x %y $grip"
+ $canvas raise $tagName-$grip
+ }
+}
+
+source scripts/stipples.tcl
+
+#
+# Script to test the BLT "eps" canvas item.
+#
+
+canvas .layout -bg white
+
+button .print -text "Print" -command {
+ wm iconify .
+ update
+ .layout postscript -file eps.ps
+ wm deiconify .
+ update
+}
+button .quit -text "Quit" -command {
+ exit 0
+}
+
+table . \
+ 0,0 .layout -fill both -cspan 2 \
+ 1,0 .print \
+ 1,1 .quit \
+
+table configure . r1 -resize none
+
+foreach file { ./images/out.ps xy.ps test.ps } {
+ if { [file exists $file] } {
+ MakeEps .layout $file
+ }
+}
+
+.layout create rectangle 10 10 50 50 -fill blue -outline white
+
+.layout create text 200 200 \
+ -text "This is a text item" \
+ -fill yellow \
+ -anchor w \
+ -font *helvetica*24*
+
+.layout create rectangle 50 50 150 150 -fill green -outline red
+
+wm colormapwindows . .layout
+
+.layout configure -scrollregion [.layout bbox all]
diff --git a/blt/demos/graph1.tcl b/blt/demos/graph1.tcl
new file mode 100755
index 00000000000..df67386da3e
--- /dev/null
+++ b/blt/demos/graph1.tcl
@@ -0,0 +1,132 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+if { [winfo screenvisual .] != "staticgray" } {
+ option add *print.background yellow
+ option add *quit.background red
+ set image [image create photo -file ./images/rain.gif]
+ option add *Graph.Tile $image
+ option add *Label.Tile $image
+ option add *Frame.Tile $image
+ option add *Htext.Tile $image
+ option add *TileOffset 0
+}
+
+set graph [graph .g]
+htext .header \
+ -text {\
+This is an example of the graph widget. It displays two-variable data
+with assorted line attributes and symbols. To create a postscript file
+"xy.ps", press the %%
+ button $htext(widget).print -text print -command {
+ puts stderr [time {
+ blt::busy hold .
+ update
+ .g postscript output demo1.eps
+ update
+ blt::busy release .
+ update
+ }]
+ }
+ $htext(widget) append $htext(widget).print
+%% button.}
+
+source scripts/graph1.tcl
+
+htext .footer \
+ -text {Hit the %%
+button $htext(widget).quit -text quit -command { exit }
+$htext(widget) append $htext(widget).quit
+%% button when you've seen enough.%%
+label $htext(widget).logo -bitmap BLT
+$htext(widget) append $htext(widget).logo -padx 20
+%%}
+scrollbar .xbar \
+ -command { .g axis view x } \
+ -orient horizontal
+scrollbar .ybar \
+ -command { .g axis view y } \
+ -orient vertical
+table . \
+ 0,0 .header -cspan 3 -fill x \
+ 1,0 .g -fill both -cspan 3 -rspan 3 \
+ 2,3 .ybar -fill y -padx 0 -pady 0 \
+ 4,1 .xbar -fill x \
+ 5,0 .footer -cspan 3 -fill x
+
+table configure . c3 r0 r4 r5 -resize none
+
+.g postscript configure \
+ -center yes \
+ -maxpect yes \
+ -landscape no \
+ -preview yes
+.g axis configure x \
+ -scrollcommand { .xbar set }
+.g axis configure y \
+ -scrollcommand { .ybar set }
+.g legend configure \
+ -activerelief flat \
+ -activeborderwidth 1
+.g axis configure y2 \
+ -hide no \
+ -title "Y2"
+.g pen configure "activeLine" \
+ -showvalues y
+.g element bind all <Enter> {
+ %W legend activate [%W element get current]
+}
+.g element bind all <Leave> {
+ %W legend deactivate [%W element get current]
+}
+.g axis bind all <Enter> {
+ set axis [%W axis get current]
+ %W axis configure $axis -background lightblue2
+}
+.g axis bind all <Leave> {
+ set axis [%W axis get current]
+ %W axis configure $axis -background ""
+}
+.g configure -leftvariable left
+trace variable left w "UpdateTable .g"
+proc UpdateTable { graph p1 p2 how } {
+ table configure . c0 -width [$graph extents leftmargin]
+ table configure . c2 -width [$graph extents rightmargin]
+ table configure . r1 -height [$graph extents topmargin]
+ table configure . r3 -height [$graph extents bottommargin]
+}
+
+set image2 [image create photo -file images/blt98.gif]
+.g element configure line2 -areapattern @bitmaps/sharky.xbm \
+
+# -areaforeground blue -areabackground ""
+.g element configure line3 -areatile $image2
+.g configure -title [pwd]
diff --git a/blt/demos/graph2.tcl b/blt/demos/graph2.tcl
new file mode 100755
index 00000000000..309b5b79277
--- /dev/null
+++ b/blt/demos/graph2.tcl
@@ -0,0 +1,142 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+source scripts/stipples.tcl
+
+if { ![string match "*gray*" [winfo screenvisual .]] } {
+ option add *Button.Background red
+ option add *TextMarker.Foreground black
+ option add *TextMarker.Background yellow
+ option add *LineMarker.Foreground black
+ option add *LineMarker.Background yellow
+ option add *PolyMarker.Fill yellow2
+ option add *PolyMarker.Outline ""
+ option add *PolyMarker.Stipple bdiagonal1
+ option add *activeLine.Color red4
+ option add *activeLine.Fill red2
+ option add *Element.Color purple
+}
+
+set data {
+ R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+ AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+ ZTKAsiCtWq0JADs=
+}
+set image [image create photo -format gif -data $data]
+
+set graph [graph .g]
+table . \
+ 0,0 $graph -fill both
+
+source scripts/graph2.tcl
+
+$graph postscript configure \
+ -maxpect yes \
+ -landscape yes
+$graph configure \
+ -width 5i \
+ -height 5i
+$graph axis configure x \
+ -title "X Axis"
+
+if 1 {
+ $graph element configure line3 \
+ -areatile $image
+ $graph element configure line1 \
+ -areapattern @bitmaps/sharky.xbm \
+ -areaforeground red \
+ -areabackground ""
+}
+
+set fileName testImg.jpg
+if { [file exists $fileName] } {
+ set image [image create photo]
+ winop readjpeg $fileName $image
+ if 1 {
+ puts stderr [time {
+ $graph marker create image -image $image \
+ -coords "-360.0 -1.0 360.0 1.0" \
+ -under yes \
+ -mapx degrees \
+ -name $fileName
+ }]
+ }
+}
+
+
+bind $graph <Control-ButtonPress-3> { MakeSnapshot }
+bind $graph <Shift-ButtonPress-3> {
+ %W postscript output demo2.ps
+ %W snap -format emf demo2.emf
+}
+
+set unique 0
+proc MakeSnapshot {} {
+ update idletasks
+ global unique
+ set top ".snapshot[incr unique]"
+ set im [image create photo]
+ $graph snap $im 210 150
+
+ toplevel $top
+ wm title $top "Snapshot \#$unique of \"[$graph cget -title]\""
+ label $top.lab -image $im
+ button $top.but -text "Dismiss" -command "DestroySnapshot $top"
+ table $top $top.lab
+ table $top $top.but -pady 4
+ focus $top.but
+}
+
+proc DestroySnapshot { win } {
+ set im [$win.lab cget -image]
+ $im write test.ppm
+ image delete $im
+ destroy $win
+ exit
+}
+
+if { $tcl_platform(platform) == "windows" } {
+ if 0 {
+ set printer [printer open [lindex [printer names] 0]]
+ printer getattrs $printer attrs
+ puts $attrs(Orientation)
+ set attrs(Orientation) Landscape
+ set attrs(DocumentName) "This is my print job"
+ printer setattrs $printer attrs
+ printer getattrs $printer attrs
+ puts $attrs(Orientation)
+ after 5000 {
+ $graph print2 $printer
+ printer close $printer
+ }
+ }
+ if 0 {
+ after 2000 {$graph snap -format emf CLIPBOARD}
+ }
+}
diff --git a/blt/demos/graph3.tcl b/blt/demos/graph3.tcl
new file mode 100755
index 00000000000..45bbe4128ff
--- /dev/null
+++ b/blt/demos/graph3.tcl
@@ -0,0 +1,105 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+source scripts/stipples.tcl
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *Button.Background red
+ option add *TextMarker.Foreground black
+ option add *TextMarker.Background yellow
+ option add *LineMarker.Foreground black
+ option add *LineMarker.Background yellow
+ option add *PolyMarker.Fill yellow2
+ option add *PolyMarker.Outline ""
+ option add *PolyMarker.Stipple fdiagonal1
+ option add *activeLine.Color red4
+ option add *activeLine.Fill red2
+ option add *Element.Color purple
+}
+
+image create photo bgTexture \
+ -file ./images/chalk.gif
+
+option add *Tile bgTexture
+option add *Button.Tile ""
+option add *Text.font -*-times*-bold-r-*-*-18-*-*
+option add *header.font -*-times*-medium-r-*-*-18-*-*
+option add *footer.font -*-times*-medium-r-*-*-18-*-*
+option add *HighlightThickness 0
+
+set graph [graph .g]
+source scripts/graph3.tcl
+
+
+
+text .header \
+ -wrap word \
+ -width 0 \
+ -height 3
+
+set text {
+This is an example of a bitmap marker. Try zooming in on
+a region by clicking the left button, moving the pointer,
+and clicking again. Notice that the bitmap scales too.
+To restore the last view, click on the right button.
+}
+regsub -all "\n" $text "" text
+.header insert end "$text\n"
+.header configure -state disabled
+
+htext .footer -text {Hit the %%
+ set im [image create photo -file ./images/stopsign.gif]
+ button $htext(widget).quit -image $im -command { exit }
+ $htext(widget) append $htext(widget).quit
+%% button when you've seen enough. %%
+ label $htext(widget).logo -bitmap BLT
+ $htext(widget) append $htext(widget).logo
+%%}
+
+table . \
+ .header 0,0 -fill x -padx 4 -pady 4\
+ $graph 1,0 -fill both \
+ .footer 2,0 -fill x -padx 4 -pady 4
+
+table configure . r0 r2 -resize none
+
+source scripts/ps.tcl
+
+bind $graph <Shift-ButtonPress-1> {
+ MakePsLayout $graph
+}
+
+if 0 {
+set printer [printer open [lindex [printer names] 0]]
+after 2000 {
+ $graph print2 $printer
+}
+}
+after 2000 {
+ PsDialog $graph
+}
diff --git a/blt/demos/graph4.tcl b/blt/demos/graph4.tcl
new file mode 100755
index 00000000000..1902296419d
--- /dev/null
+++ b/blt/demos/graph4.tcl
@@ -0,0 +1,2292 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set tcl_precision 15
+
+set graph .graph
+image create photo bgTexture -file ./images/chalk.gif
+
+option add *default normal
+option add *Button.tile bgTexture
+
+option add *Htext.font -*-times*-bold-r-*-*-18-*-*
+option add *Text.font -*-times*-bold-r-*-*-18-*-*
+option add *header.font -*-times*-medium-r-*-*-18-*-*
+option add *footer.font -*-times*-medium-r-*-*-18-*-*
+option add *Graph.relief raised
+#option add *Graph.borderWidth 2
+option add *Graph.Legend.activeBackground white
+option add *Graph.height 5i
+option add *Graph.plotBackground black
+option add *Graph.width 7i
+option add *Graph.tile bgTexture
+option add *Graph.halo 0
+
+option add *Graph.title "s27.out"
+option add *Graph.font -*-helvetica-bold-r-*-*-18-*
+
+option add *Axis.tickFont -*-courier-medium-r-*-*-12-*
+option add *Axis.titleFont -*-helvetica-bold-r-*-*-14-*
+option add *Axis.titleColor red2
+option add *x.title "Time"
+option add *y.title "Signals"
+
+option add *Crosshairs.Color white
+
+option add *activeLine.Fill navyblue
+option add *activeLine.LineWidth 2
+option add *Element.ScaleSymbols yes
+option add *Element.Smooth natural
+
+option add *Symbol square
+option add *Element.LineWidth 1
+option add *Pen.LineWidth 1
+option add *Pixels 1
+
+option add *Grid.color grey50
+option add *Grid.dashes "2 4"
+option add *Grid.hide no
+
+option add *Legend.ActiveRelief sunken
+option add *Legend.Position right
+option add *Legend.Relief flat
+option add *Legend.font -*-lucida-medium-r-*-*-12-*-*-*-*-*-*-*
+option add *Legend.Pad 0
+option add *Legend.hide no
+
+option add *LineMarker.Dashes 5
+option add *LineMarker.Foreground white
+option add *zoomOutline.outline yellow
+
+option add *TextMarker.Background {}
+option add *TextMarker.Foreground white
+
+vector create x -variable ""
+for { set i 1 } { $i <= 39 } { incr i } {
+ vector create "v$i" -variable ""
+}
+
+x set {
+ 0 1e-10 2e-10 3e-10 4e-10 5e-10 6e-10 7e-10 8e-10 9e-10
+ 1e-09 1.1e-09 1.2e-09 1.3e-09 1.4e-09 1.5e-09 1.6e-09 1.7e-09
+ 1.8e-09 1.9e-09 2e-09 2.1e-09 2.2e-09 2.3e-09 2.4e-09 2.5e-09
+ 2.6e-09 2.7e-09 2.8e-09 2.9e-09 3e-09 3.1e-09 3.2e-09 3.3e-09
+ 3.4e-09 3.5e-09 3.6e-09 3.7e-09 3.8e-09 3.9e-09 4e-09 4.1e-09
+ 4.2e-09 4.3e-09 4.4e-09 4.5e-09 4.6e-09 4.7e-09 4.8e-09
+ 4.9e-09 5e-09 5.1e-09 5.2e-09 5.3e-09 5.4e-09 5.5e-09 5.6e-09
+ 5.7e-09 5.8e-09 5.9e-09 6e-09 6.1e-09 6.2e-09 6.3e-09 6.4e-09
+ 6.5e-09 6.6e-09 6.7e-09 6.8e-09 6.9e-09 7e-09 7.1e-09 7.2e-09
+ 7.3e-09 7.4e-09 7.5e-09 7.6e-09 7.7e-09 7.8e-09 7.9e-09
+ 8e-09 8.1e-09 8.2e-09 8.3e-09 8.4e-09 8.5e-09 8.6e-09 8.7e-09
+ 8.8e-09 8.9e-09 9e-09 9.1e-09 9.2e-09 9.3e-09 9.4e-09 9.5e-09
+ 9.6e-09 9.7e-09 9.8e-09 9.9e-09 1e-08 1.01e-08 1.02e-08
+ 1.03e-08 1.04e-08 1.05e-08 1.06e-08 1.07e-08 1.08e-08 1.09e-08
+ 1.1e-08 1.11e-08 1.12e-08 1.13e-08 1.14e-08 1.15e-08 1.16e-08
+ 1.17e-08 1.18e-08 1.19e-08 1.2e-08 1.21e-08 1.22e-08 1.23e-08
+ 1.24e-08 1.25e-08 1.26e-08 1.27e-08 1.28e-08 1.29e-08 1.3e-08
+ 1.31e-08 1.32e-08 1.33e-08 1.34e-08 1.35e-08 1.36e-08 1.37e-08
+ 1.38e-08 1.39e-08 1.4e-08 1.41e-08 1.42e-08 1.43e-08 1.44e-08
+ 1.45e-08 1.46e-08 1.47e-08 1.48e-08 1.49e-08 1.5e-08 1.51e-08
+ 1.52e-08 1.53e-08 1.54e-08 1.55e-08 1.56e-08 1.57e-08 1.58e-08
+ 1.59e-08 1.6e-08 1.61e-08 1.62e-08 1.63e-08 1.64e-08 1.65e-08
+ 1.66e-08 1.67e-08 1.68e-08 1.69e-08 1.7e-08 1.71e-08 1.72e-08
+ 1.73e-08 1.74e-08 1.75e-08 1.76e-08 1.77e-08 1.78e-08 1.79e-08
+ 1.8e-08 1.81e-08 1.82e-08 1.83e-08 1.84e-08 1.85e-08 1.86e-08
+ 1.87e-08 1.88e-08 1.89e-08 1.9e-08 1.91e-08 1.92e-08 1.93e-08
+ 1.94e-08 1.95e-08 1.96e-08 1.97e-08 1.98e-08 1.99e-08 2e-08
+ 2.01e-08 2.02e-08 2.03e-08 2.04e-08 2.05e-08 2.06e-08 2.07e-08
+ 2.08e-08 2.09e-08 2.1e-08 2.11e-08 2.12e-08 2.13e-08 2.14e-08
+ 2.15e-08 2.16e-08 2.17e-08 2.18e-08 2.19e-08 2.2e-08 2.21e-08
+ 2.22e-08 2.23e-08 2.24e-08 2.25e-08 2.26e-08 2.27e-08 2.28e-08
+ 2.29e-08 2.3e-08 2.31e-08 2.32e-08 2.33e-08 2.34e-08 2.35e-08
+ 2.36e-08 2.37e-08 2.38e-08 2.39e-08 2.4e-08 2.41e-08 2.42e-08
+ 2.43e-08 2.44e-08 2.45e-08 2.46e-08 2.47e-08 2.48e-08 2.49e-08
+ 2.5e-08 2.51e-08 2.52e-08 2.53e-08 2.54e-08 2.55e-08 2.56e-08
+ 2.57e-08 2.58e-08 2.59e-08 2.6e-08 2.61e-08 2.62e-08 2.63e-08
+ 2.64e-08 2.65e-08 2.66e-08 2.67e-08 2.68e-08 2.69e-08 2.7e-08
+ 2.71e-08 2.72e-08 2.73e-08 2.74e-08 2.75e-08 2.76e-08 2.77e-08
+ 2.78e-08 2.79e-08 2.8e-08 2.81e-08 2.82e-08 2.83e-08 2.84e-08
+ 2.85e-08 2.86e-08 2.87e-08 2.88e-08 2.89e-08 2.9e-08 2.91e-08
+ 2.92e-08 2.93e-08 2.94e-08 2.95e-08 2.96e-08 2.97e-08 2.98e-08
+ 2.99e-08 3e-08 3.01e-08 3.02e-08 3.03e-08 3.04e-08 3.05e-08
+ 3.06e-08 3.07e-08 3.08e-08 3.09e-08 3.1e-08 3.11e-08 3.12e-08
+ 3.13e-08 3.14e-08 3.15e-08 3.16e-08 3.17e-08 3.18e-08 3.19e-08
+ 3.2e-08 3.21e-08 3.22e-08 3.23e-08 3.24e-08 3.25e-08 3.26e-08
+ 3.27e-08 3.28e-08 3.29e-08 3.3e-08 3.31e-08 3.32e-08 3.33e-08
+ 3.34e-08 3.35e-08 3.36e-08 3.37e-08 3.38e-08 3.39e-08 3.4e-08
+ 3.41e-08 3.42e-08 3.43e-08 3.44e-08 3.45e-08 3.46e-08 3.47e-08
+ 3.48e-08 3.49e-08 3.5e-08 3.51e-08 3.52e-08 3.53e-08 3.54e-08
+ 3.55e-08 3.56e-08 3.57e-08 3.58e-08 3.59e-08 3.6e-08
+}
+v1 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+
+v2 set {
+ 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1
+ 5.32907e-15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5
+ 5 5 5 5 5 5 5 5 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0
+}
+v3 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 8.88178e-16
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 2.13718e-14 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0
+}
+v4 set {
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+v5 set {
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+v6 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 8.88178e-16
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 2.13718e-14 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0
+}
+v7 set {
+ 5 5.16904 4.84159 3.34542 0.317102 0.103304 0.0275721 0.0221534
+ 0.017689 0.0142639 0.0113974 0.00918238 0.00742541 0.00616602
+ 0.00481195 0.00397049 -0.0659889 -0.025671 0.165495 0.986891
+ 3.05229 4.55511 4.91611 4.98192 4.99428 4.99833 4.99095
+ 4.97295 4.95493 4.93428 4.90723 4.94799 4.98584 4.99566
+ 4.99813 4.99907 4.99947 4.99965 4.99976 4.99984 4.99989
+ 4.99992 4.99994 4.99996 4.99998 5.00002 5.00006 5.00002
+ 4.99996 4.99994 4.99999 5.00003 5.00002 5 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99996 4.99997 4.99997 4.99998
+ 4.99998 4.99999 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5.16575
+ 4.69986 2.43862 0.0230224 0.035229 -0.0210607 -0.0292766
+ -0.0172693 -0.00271479 -0.000912251 -0.000349106 -0.000116866
+ -4.24733e-05 -1.39536e-05 -3.01179e-05 -0.0657192 -0.0204835
+ 0.183378 1.07181 3.118 4.46472 4.84158 4.94795 4.98173 4.99236
+ 4.99762 5.01939 5.0433 5.05332 5.04959 5.03955 5.02851 5.02052
+ 5.01422 5.00965 5.00631 5.00405 5.00248 5.00083 5.00012
+ 5.00209 5.00387 5.00347 4.99917 4.99213 4.98411 4.97521
+ 4.96332 4.94601 4.9304 4.94633 4.97936 4.99264 4.99685 4.99857
+ 4.99925 4.99954 4.9997 4.99973 4.9997 4.99973 4.99979 4.99983
+ 4.99986 4.99988 4.9999 4.9999 4.99992 4.99993 4.99994 4.99995
+ 4.99996 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5.14242 4.76101 3.16003 0.299374
+ 0.0645506 -0.000498424 -2.45108e-05 -2.27986e-05 -5.24401e-05
+ -4.9884e-05 -4.92491e-05 -2.93354e-05 -3.21402e-05 -2.11851e-05
+ -3.37925e-05 -0.0657892 -0.020563 0.182582 1.06058 3.12484
+ 4.46552 4.84146 4.95102 4.98556 4.99472 4.99806 4.99909
+ 4.99955 4.99976 4.99994 4.99992 5.00029 4.99967 4.99849
+ 4.99736 4.99884 5.00099 5.00377 5.00215 4.99994 4.99893
+ 4.99788 4.99862 5.00055 5.00134 5.00127 5.00073 5.00039
+ 5.00018 5.00006 5.00001 4.99985 5.00026 5.00018 5.00003
+ 4.99981 4.99985 4.99987 4.99985 4.99982 4.99982 4.99982
+ 4.99983 4.99985 4.99987 4.99989 4.99991 4.99992 4.99994
+ 4.99995 4.99995 4.99994 4.99994 4.99996 4.99999 5.00002
+ 5.00008 5.00009 5.00006 5.00001 5 4.99999 4.99998 4.99997
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999 4.99999
+ 4.99999 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99998 4.99998
+ 4.99998
+}
+v8 set {
+ 5 5.03758 5.04711 4.96911 4.20882 3.96295 4.01117 4.15521
+ 4.2967 4.42274 4.5295 4.6176 4.69014 4.74831 4.7966 4.83537
+ 4.80526 4.787 4.79295 4.88588 5.08978 5.15615 5.10778 5.07718
+ 5.06652 5.08225 4.9744 4.52977 3.77452 2.69426 1.15294 0.245509
+ 0.0981544 0.0567527 0.0367487 0.0252578 0.0180599 0.0133837
+ 0.0101497 0.0078616 0.00620186 0.00499056 0.0041027 0.00344223
+ 0.00295808 0.00260089 0.00229887 0.00200817 0.00176397 0.00160116
+ 0.00147381 0.00134645 0.00125029 0.00116043 0.00107371 0.00101981
+ 0.000965921 0.000912028 0.000858135 0.000804242 0.000761669
+ 0.00072672 0.000691771 0.000656823 0.000621874 0.000588722
+ 0.00057041 0.000552098 0.000533785 0.000515473 0.000497162
+ 0.00047885 0.000460537 0.000442226 0.000423914 0.000405601
+ 0.000388399 0.000378694 0.000368989 0.000359284 0.00034958
+ 0.000339875 0.00033017 0.000320465 0.00031076 0.000301055
+ 0.00029135 0.000282207 0.000276247 0.000270287 0.000264327
+ 0.000258367 0.000252407 0.000246447 0.000240487 0.000234527
+ 0.000228567 0.000222607 0.000217086 0.000213696 0.000210307
+ 0.000206918 0.000203528 0.000200139 0.00019675 0.00019336
+ 0.000189971 0.000186582 0.000183192 0.000179803 0.000176414
+ 0.000173025 0.000169635 0.000166246 0.000162857 0.000159467
+ 0.000156078 0.000152689 0.000149299 0.00014591 0.00014255
+ 0.0316021 0.163272 0.348732 0.603651 0.35745 0.135965 0.0707354
+ 0.0314595 0.0201047 0.00994945 0.00389601 0.00138839 0.00060778
+ 0.000329648 0.000492396 -0.0732035 -0.0844077 -0.0789062
+ -0.0390837 0.0197559 0.0183094 -0.00180099 -0.0189565 -0.0424144
+ -0.0735904 -0.0892423 0.285039 1.13702 2.10809 2.95826 3.60164
+ 4.0435 4.35771 4.57254 4.71769 4.81329 4.87534 4.91487 4.94264
+ 4.97375 5.01526 5.06517 5.10154 5.06259 4.89005 4.5787 4.12226
+ 3.46151 2.49023 1.2586 0.32725 0.116753 0.0701865 0.0455509
+ 0.0286914 0.0178176 0.0117599 0.00902715 0.00760583 0.00637745
+ 0.00543811 0.00439377 0.00352448 0.0030151 0.00285771 0.002465
+ 0.00203114 0.00173004 0.0014839 0.00125177 0.00105327 0.000894905
+ 0.000766372 0.000658894 0.000569105 0.000492114 0.000427938
+ 0.000370217 0.000314758 0.000266569 0.000233726 0.000209048
+ 0.000191957 0.000177169 0.000166604 0.000161 0.000157314
+ 0.000143828 0.000130342 0.000116857 0.000103371 8.98855e-05
+ 7.63998e-05 6.29141e-05 5.76583e-05 5.30027e-05 4.8347e-05
+ 4.36913e-05 3.90357e-05 3.438e-05 2.97243e-05 2.72507e-05
+ 2.59083e-05 2.45659e-05 2.32235e-05 2.18811e-05 2.05387e-05
+ 1.91963e-05 1.78539e-05 1.65115e-05 1.51691e-05 1.38267e-05
+ 1.24843e-05 1.11419e-05 9.79954e-06 8.51574e-06 7.69807e-06
+ 6.8804e-06 6.06273e-06 5.24506e-06 0.0287318 0.0317111 -0.0320087
+ -0.103609 0.0369639 0.0121128 0.00961197 0.00934971 0.00820853
+ 0.00699769 0.00607002 0.00535541 0.00476552 0.00427601 0.00376357
+ -0.073012 -0.0866964 -0.0809538 -0.038005 0.0277001 0.0188906
+ 0.00614597 0.00373629 0.00489787 0.0146573 0.0191052 0.0151708
+ 0.0124224 0.0105859 0.00879272 0.00729464 0.0070047 0.00449575
+ -0.00626652 -0.0252417 -0.0147287 0.022538 0.0822905 0.0947372
+ 0.0657516 0.0445506 0.0316753 0.0220971 0.0158101 0.0140971
+ 0.0161498 0.0139876 0.0122447 0.0106994 0.009397 0.00822236
+ 0.00686509 0.00797431 0.00751269 0.00671173 0.00595243 0.00524633
+ 0.00459528 0.00401688 0.00350109 0.00303954 0.00260569 0.00222792
+ 0.00191033 0.00163917 0.00140949 0.00121464 0.0010471 0.000900638
+ 0.000768847 0.000645236 0.000524807 0.000460275 0.000442237
+ 0.000446775 0.000397026 0.000301585 0.000228994 0.000190894
+ 0.000166569 0.000152261 0.000137953 0.000123644 0.000109336
+ 9.50281e-05 8.56557e-05 7.78437e-05 7.00318e-05 6.22198e-05
+ 5.44079e-05 4.87539e-05 4.57761e-05 4.27982e-05 3.98203e-05
+ 3.68425e-05 3.38646e-05 3.08868e-05 2.79089e-05 2.4931e-05
+ 2.19532e-05 1.89753e-05 1.75244e-05 1.64095e-05 1.52946e-05
+ 1.41797e-05 1.30648e-05 1.19499e-05 1.0835e-05 9.72011e-06
+ 8.60521e-06 7.4903e-06 6.5117e-06 6.10334e-06 5.69497e-06
+ 5.2866e-06 4.87824e-06 4.46987e-06 4.06151e-06 3.65314e-06
+ 3.24477e-06
+}
+
+v9 set {
+ 1.86175 1.99708 2.07867 2.01211 2.43309 3.27194 3.63896
+ 3.90426 4.11074 4.27932 4.41496 4.52543 4.61491 4.68862
+ 4.7479 4.79666 4.72895 4.68886 4.70354 4.81353 5.01568 5.14184
+ 5.10482 5.07362 5.05143 5.03638 5.02323 5.01465 5.00853
+ 5.00383 4.99985 5.00454 5.00652 5.00546 5.00411 5.003 5.00214
+ 5.00151 5.00106 5.00073 5.0005 5.00034 5.00023 5.00015 5.0001
+ 5.00005 5 5.00001 5.00005 5.00005 5.00003 5 4.99998 4.99996
+ 4.99994 4.99995 4.99997 4.99998 5 5.00001 5.00002 5.00002
+ 5.00003 5.00003 5.00003 5.00003 5.00003 5.00003 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.17392 4.94828 3.78491
+ 1.52079 0.608874 0.244031 0.127087 0.0552995 0.0361032 0.0169025
+ 0.006364 0.00217624 0.000921391 0.000457305 0.000786754
+ -0.120016 -0.148054 -0.15898 -0.0801463 0.16463 0.174017
+ 0.0799249 0.0318788 0.0129696 0.00483397 0.0025677 0.0042079
+ 0.00350003 0.00178404 -8.72902e-05 -0.00128497 -0.00142213
+ -0.00130018 -0.00106874 -0.000789207 -0.000824335 -0.00104518
+ -0.00136799 -0.004366 -0.0102621 -0.0109254 -0.00649259
+ -0.00194842 0.00029793 0.00148673 0.00221085 0.00228291
+ 0.00185261 0.00139687 0.00148183 0.00562266 0.00844119 0.00754627
+ 0.00657396 0.00591212 0.00539269 0.0049282 0.00448417 0.0040572
+ 0.00363719 0.00320392 0.00279607 0.00243938 0.00211505 0.00182302
+ 0.00156254 0.0013341 0.00113834 0.000971865 0.00082776 0.000706193
+ 0.000602499 0.000515059 0.000441401 0.00037897 0.000325459
+ 0.00028083 0.000242096 0.000207274 0.000176444 0.000150372
+ 0.000126407 0.000103373 9.05522e-05 8.53555e-05 8.63685e-05
+ 9.02593e-05 8.37346e-05 7.72099e-05 7.06852e-05 6.41605e-05
+ 5.76358e-05 5.11112e-05 4.45865e-05 4.08176e-05 3.72497e-05
+ 3.36818e-05 3.01138e-05 2.65459e-05 2.2978e-05 1.94101e-05
+ 1.76154e-05 1.67399e-05 1.58645e-05 1.4989e-05 1.41136e-05
+ 1.32381e-05 1.23626e-05 1.14872e-05 1.06117e-05 9.73629e-06
+ 8.86083e-06 7.98538e-06 7.10993e-06 6.23447e-06 5.44363e-06
+ 5.32578e-06 5.20792e-06 5.09007e-06 4.97222e-06 0.0784323
+ 0.0474527 -0.0764232 -0.151146 0.0615785 0.0144489 0.00974161
+ 0.00947176 0.00849005 0.00728201 0.00630581 0.00554032 0.00487809
+ 0.00441504 0.00384139 -0.118943 -0.149894 -0.161173 -0.0825299
+ 0.171686 0.176912 0.0816085 0.0335236 0.013791 0.0056976
+ 0.00238833 0.00105348 0.000526199 0.00025969 0.000396026
+ 0.000837835 0.00170131 0.00196699 -0.000553314 -0.0061621
+ -0.0111895 -0.0142698 -0.0124608 -0.00795847 -0.00467822
+ -0.0043058 -0.00874449 -0.0118584 -0.00871386 -0.00377892
+ 1.95244e-05 0.00218952 0.00325486 0.00386497 0.00422837
+ 0.00446883 0.00447065 0.00486647 0.00547838 0.00565398 0.00559092
+ 0.00538752 0.00507015 0.00466305 0.00420756 0.00373465 0.00328404
+ 0.00287059 0.00250057 0.00216124 0.00184861 0.00156815 0.00134624
+ 0.00117857 0.00103412 0.0008948 0.000761012 0.000619853
+ 0.000462614 0.000319965 0.000287666 0.000356415 0.000379946
+ 0.000339183 0.00027972 0.000252982 0.000226244 0.000199507
+ 0.000172769 0.000146031 0.000130097 0.000117578 0.000105059
+ 9.25401e-05 8.00213e-05 7.11204e-05 6.67061e-05 6.22918e-05
+ 5.78775e-05 5.34632e-05 4.90489e-05 4.46346e-05 4.02203e-05
+ 3.5806e-05 3.13916e-05 2.69773e-05 2.4827e-05 2.31747e-05
+ 2.15225e-05 1.98702e-05 1.8218e-05 1.65658e-05 1.49135e-05
+ 1.32613e-05 1.1609e-05 9.95678e-06 8.50108e-06 7.86765e-06
+ 7.23422e-06 6.60079e-06 5.96736e-06 5.33393e-06 4.7005e-06
+ 4.06707e-06 3.43363e-06
+}
+v10 set {
+ 1.86175 1.99308 2.16619 2.46661 3.09359 3.76864 4.31299
+ 4.65564 4.83425 4.92153 4.96157 4.98063 4.98649 4.99039
+ 4.9945 4.9972 4.96206 4.89882 4.83865 4.83202 4.91016 5.04479
+ 5.06078 5.04827 5.03474 5.0246 5.01639 5.00996 5.00569 5.00239
+ 5.00043 5.00296 5.00437 5.00382 5.00287 5.00208 5.00148
+ 5.00104 5.00073 5.0005 5.00034 5.00023 5.00016 5.00011 5.00008
+ 5.00007 5.00007 5.00004 5 4.99998 4.99998 4.99997 4.99998
+ 4.99999 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 5.10081
+ 5.10949 4.98359 5.00733 5.15145 4.37298 2.36126 0.470759
+ 0.0577238 0.0115884 0.00262611 0.000671499 0.000389038 0.000291291
+ 0.000317347 -0.0167823 -0.0158344 -0.0140559 0.0104849 0.0865874
+ 0.107813 0.0524688 0.0214369 0.00876443 0.00341595 0.00170778
+ 0.00259042 0.0022241 0.00118519 1.10217e-06 -0.000784506
+ -0.000948169 -0.000856256 -0.000696719 -0.000485987 -0.000724787
+ -0.000981491 -0.001454 -0.00552498 -0.0114992 -0.0105266
+ -0.00543527 -0.000982798 0.00127356 0.00224212 0.00275439
+ 0.00281098 0.0025471 0.00230368 0.00222576 0.00485522 0.00729453
+ 0.00691796 0.0062615 0.00573987 0.0052688 0.00481185 0.00436934
+ 0.00394326 0.00352712 0.00309978 0.00270038 0.00235335 0.00203742
+ 0.00175256 0.00150067 0.00128126 0.00109323 0.000933619
+ 0.000795113 0.000678182 0.00057843 0.000494345 0.000423609
+ 0.000363821 0.000312766 0.000269856 0.000232389 0.000198382
+ 0.000168126 0.00014267 0.000119293 9.69034e-05 8.5669e-05
+ 8.26828e-05 8.64066e-05 9.26665e-05 8.5454e-05 7.82416e-05
+ 7.10291e-05 6.38167e-05 5.66043e-05 4.93918e-05 4.21794e-05
+ 3.86073e-05 3.53007e-05 3.19941e-05 2.86876e-05 2.5381e-05
+ 2.20744e-05 1.87678e-05 1.70933e-05 1.62648e-05 1.54363e-05
+ 1.46079e-05 1.37794e-05 1.2951e-05 1.21225e-05 1.12941e-05
+ 1.04656e-05 9.63716e-06 8.80871e-06 7.98026e-06 7.1518e-06
+ 6.32335e-06 5.5374e-06 5.08959e-06 4.64178e-06 4.19397e-06
+ 3.74616e-06 0.0438026 0.0242078 -0.0602019 -0.0840866 0.00148461
+ -0.00292489 0.000442098 0.00219489 0.00281478 0.00290756
+ 0.00277945 0.00263896 0.00240099 0.00223283 0.001947 -0.0153629
+ -0.0148815 -0.0128673 0.0126017 0.0905161 0.11051 0.0538958
+ 0.022562 0.00935726 0.00397422 0.00172534 0.000790207 0.000416322
+ 0.000191632 0.000469721 0.0009779 0.00192566 0.00200688
+ -0.0016502 -0.00733932 -0.0128113 -0.0147608 -0.0115456
+ -0.00668995 -0.00401368 -0.00463908 -0.0101197 -0.0118993
+ -0.0076276 -0.00262656 0.000813059 0.00264455 0.00350796
+ 0.00399494 0.0043049 0.00451658 0.00444739 0.00503842 0.00559516
+ 0.00568213 0.00556459 0.0053176 0.00496654 0.00454337 0.00408592
+ 0.00362171 0.00317793 0.00277001 0.00240394 0.00207009 0.00176575
+ 0.00149725 0.00129045 0.00114257 0.00101135 0.000871672
+ 0.000723764 0.000580438 0.000427507 0.000296956 0.000281834
+ 0.000376628 0.000412266 0.000367547 0.000295305 0.000264513
+ 0.000233721 0.000202929 0.000172137 0.000141345 0.000124721
+ 0.000112577 0.000100433 8.82893e-05 7.61453e-05 6.75517e-05
+ 6.33609e-05 5.91701e-05 5.49792e-05 5.07884e-05 4.65976e-05
+ 4.24067e-05 3.82159e-05 3.40251e-05 2.98342e-05 2.56434e-05
+ 2.36401e-05 2.21181e-05 2.05961e-05 1.90741e-05 1.75521e-05
+ 1.60301e-05 1.45081e-05 1.29861e-05 1.14641e-05 9.94208e-06
+ 8.59252e-06 7.96439e-06 7.33626e-06 6.70813e-06 6.07999e-06
+ 5.45186e-06 4.82373e-06 4.1956e-06 3.56747e-06
+}
+v11 set {
+ 1.86175 1.73419 1.42874 1.04055 0.943004 0.268275 0.0826455
+ 0.0388346 0.0214104 0.0135431 0.00961322 0.00712846 0.00588262
+ 0.00432397 0.00377774 0.00270134 -0.00393731 -0.00542187
+ -0.00126596 0.0113777 0.0134522 0.00477056 -0.00211067 -0.00229253
+ -0.00173355 -0.00122404 -0.00113426 -0.000744931 -0.000520112
+ -0.000410048 -0.000220439 0.000508104 5.15856e-05 -0.000112593
+ -0.000118917 -9.57394e-05 -7.15727e-05 -5.11847e-05 -3.58275e-05
+ -2.47166e-05 -1.68866e-05 -1.14082e-05 -7.66646e-06 -5.12139e-06
+ -3.63426e-06 -3.01815e-06 -2.64862e-06 -1.4947e-06 -1.91403e-07
+ -2.5763e-08 -7.73699e-07 -1.52164e-06 -1.07268e-06 -3.81696e-07
+ 2.6727e-07 4.75489e-07 6.83708e-07 8.91926e-07 1.10014e-06
+ 1.30836e-06 1.2482e-06 1.00726e-06 7.66311e-07 5.25364e-07
+ 2.84417e-07 6.27857e-08 7.43904e-10 -6.12979e-08 -1.2334e-07
+ -1.85382e-07 -2.47423e-07 -3.09465e-07 -3.71507e-07 -4.33549e-07
+ -4.95591e-07 -5.57633e-07 -6.04571e-07 -5.4944e-07 -4.9431e-07
+ -4.3918e-07 -3.84049e-07 -3.28919e-07 -2.73789e-07 -2.18659e-07
+ -1.63528e-07 -1.08398e-07 -5.32678e-08 1.062e-09 5.08502e-08
+ 1.00638e-07 1.50427e-07 2.00215e-07 2.50003e-07 2.99791e-07
+ 3.4958e-07 3.99368e-07 4.49156e-07 4.98944e-07 5.34512e-07
+ 5.01032e-07 4.67553e-07 4.34073e-07 4.00593e-07 3.67113e-07
+ 3.33633e-07 3.00153e-07 2.66674e-07 2.33194e-07 1.99714e-07
+ 1.66234e-07 1.32754e-07 9.92744e-08 6.57945e-08 3.23147e-08
+ -1.16513e-09 -3.4645e-08 -6.81248e-08 -1.01605e-07 -1.35084e-07
+ -1.68564e-07 -2.18729e-07 0.0114926 -0.0245378 -0.111828
+ 0.0964775 1.61491 3.22668 4.22041 4.54492 4.82845 4.94868
+ 4.98588 4.99609 4.9981 4.99908 4.99788 4.98395 4.99294 4.99724
+ 5.01939 5.0471 5.00902 4.98194 4.98496 4.99188 4.99623 4.99862
+ 5.00025 4.99974 4.99953 4.99946 4.99958 5.00012 4.99997
+ 4.99992 4.99988 4.99985 4.9998 4.9997 4.9988 4.99806 4.99982
+ 5.00143 5.00159 5.00098 5.00053 5.00028 5.00007 4.99977
+ 4.99992 5.00005 5.00133 5.0009 4.99993 4.99972 4.99975 4.9998
+ 4.99982 4.99983 4.99983 4.99983 4.99983 4.99984 4.99986
+ 4.99987 4.99989 4.9999 4.99991 4.99992 4.99994 4.99995 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5.01457 4.99482 4.96561 4.99326
+ 5.03452 5.00424 5.00101 5.00045 5.00004 4.99965 4.99997
+ 4.99994 4.99958 4.99999 4.99936 4.9839 4.99248 4.99717 5.01976
+ 5.04869 5.0087 4.98143 4.98488 4.99199 4.99622 4.9983 4.99928
+ 4.99971 4.99986 5.00031 5.00022 5.00035 5.0001 4.99884 4.99811
+ 4.99803 4.99887 5.00078 5.00151 5.00116 5.00007 4.99843
+ 4.99915 5.00107 5.00168 5.00141 5.00092 5.00055 5.0003 5.00016
+ 5.0001 5.00001 5.00016 5.0002 5.00009 4.99993 4.99975 4.99984
+ 4.99991 4.99991 4.99982 4.99974 4.99974 4.99985 4.99995
+ 4.99999 4.99998 5.00004 5.00013 5.00015 5.00007 4.99988
+ 4.99982 4.99985 4.99995 5.00006 5.0002 5.00025 5.0002 5.00009
+ 5.00006 5.00004 5.00002 5 4.99998 4.99997 4.99998 4.99998
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99998
+}
+v12 set {
+ 5 5.16975 4.78685 2.94241 0.126698 0.0487004 -0.00422591
+ -0.00130689 -0.000486756 -0.000195875 -0.000108988 -6.66736e-05
+ -7.26005e-05 -5.63608e-05 -3.81859e-05 -2.123e-05 -0.0646846
+ -0.0184474 0.182248 1.06731 3.10988 4.46133 4.84133 4.95113
+ 4.98364 4.99455 4.99694 4.99727 4.9994 4.99975 5.0001 5.00132
+ 5.00089 5.00039 5.00019 5.00011 5.00006 5.00005 5.00004
+ 5.00001 4.99992 4.99992 5.00002 5.00013 5.00017 5.00009
+ 4.99992 4.99991 4.99994 4.99996 4.99998 4.99999 5.00001
+ 5.00004 5.00006 5.00005 5.00004 5.00003 5.00002 5.00001
+ 5 4.99999 4.99999 4.99998 4.99998 4.99997 4.99997 4.99998
+ 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5.14699 4.78074
+ 3.19424 0.305663 0.0611255 -0.00179951 -0.0012032 0.000405978
+ 0.000989399 0.000445194 0.000191447 8.30476e-05 3.96236e-05
+ 1.91866e-05 1.70665e-05 -0.0655239 -0.0210234 0.1827 1.06848
+ 3.11554 4.46518 4.84212 4.94853 4.98244 4.99434 4.9997 5.00081
+ 5.00009 4.99972 4.99985 4.99974 4.9995 4.99949 4.99958 4.99973
+ 4.99948 4.99914 4.99874 4.99946 5.00309 5.0091 5.01576 5.01835
+ 5.01852 5.0176 5.01625 5.01479 5.01345 5.01264 5.011 5.01092
+ 5.01344 5.01363 5.01289 5.01184 5.01071 5.00956 5.00848
+ 5.00751 5.00663 5.00577 5.00497 5.00427 5.00365 5.0031 5.00264
+ 5.00224 5.00191 5.00163 5.00138 5.00117 5.00099 5.00083
+ 5.00071 5.00061 5.00053 5.00045 5.00037 5.00029 5.00022
+ 5.00019 5.0002 5.00023 5.00024 5.00023 5.00023 5.00022 5.0002
+ 5.00018 5.00016 5.00014 5.00011 5.00009 5.00007 5.00006
+ 5.00005 5.00005 5.00004 5.00003 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.14298 4.79809 3.32704
+ 0.498385 0.105773 0.0160646 0.0319912 0.0299434 0.0240102
+ 0.0185844 0.0130411 0.0106532 0.00864871 0.00744519 0.00660887
+ -0.0612913 -0.0203719 0.174998 0.991787 3.06292 4.60005
+ 4.93058 4.98917 5.00033 4.9999 4.99909 4.9966 4.9955 4.99488
+ 4.99374 4.9943 5.00131 5.00506 4.99311 4.96288 4.93567 4.92439
+ 4.94236 4.9732 4.98864 4.99458 5.00031 5.00694 5.01525 5.01945
+ 5.01998 5.01953 5.01874 5.01766 5.0164 5.01509 5.01326 5.01423
+ 5.01455 5.01361 5.01245 5.01122 5.01002 5.00888 5.00783
+ 5.00687 5.00596 5.00514 5.00442 5.00379 5.00325 5.00279
+ 5.0024 5.00208 5.0018 5.00153 5.00126 5.00107 5.00094 5.00085
+ 5.00078 5.00072 5.00063 5.00053 5.00042 5.00038 5.00034
+ 5.0003 5.00027 5.00023 5.00021 5.00019 5.00017 5.00015 5.00013
+ 5.00012 5.00011 5.0001 5.0001 5.00009 5.00008 5.00007 5.00007
+ 5.00006 5.00005 5.00005 5.00004 5.00004 5.00003 5.00003
+ 5.00002 5.00002 5.00002 5.00001 5.00001 5 5 5 5.00001 5.00001
+ 5.00001 5.00002 5.00002 5.00002 5.00002
+}
+v13 set {
+ 9.73784e-10 0.0189926 0.0926769 0.206309 0.111533 0.0953491
+ 0.0426966 0.0214177 0.0117943 0.00741442 0.00528816 0.00398417
+ 0.0032967 0.00266499 0.00206647 0.00158788 -0.0371391 -0.0439528
+ -0.0408653 -0.0188706 0.0150241 0.0126852 0.00209817 -0.000239206
+ -5.31488e-05 0.000876324 -0.00451221 -0.0165223 -0.0284127
+ -0.0427584 -0.0502453 -0.0257366 -0.00903938 -0.00376456
+ -0.00233385 -0.00169922 -0.00130397 -0.00102542 -0.000811435
+ -0.000648115 -0.000529266 -0.00043795 -0.00036574 -0.00030716
+ -0.00026221 -0.000229662 -0.000205112 -0.000181038 -0.000162045
+ -0.000148988 -0.000137633 -0.000126278 -0.000115562 -0.000104976
+ -9.49324e-05 -9.0585e-05 -8.62375e-05 -8.18901e-05 -7.75426e-05
+ -7.31952e-05 -6.93752e-05 -6.59106e-05 -6.24461e-05 -5.89815e-05
+ -5.55169e-05 -5.22412e-05 -5.05263e-05 -4.88114e-05 -4.70966e-05
+ -4.53817e-05 -4.36668e-05 -4.19519e-05 -4.0237e-05 -3.85222e-05
+ -3.68073e-05 -3.50924e-05 -3.34782e-05 -3.25442e-05 -3.16102e-05
+ -3.06763e-05 -2.97423e-05 -2.88083e-05 -2.78744e-05 -2.69404e-05
+ -2.60064e-05 -2.50725e-05 -2.41385e-05 -2.32635e-05 -2.27232e-05
+ -2.21829e-05 -2.16426e-05 -2.11023e-05 -2.0562e-05 -2.00217e-05
+ -1.94814e-05 -1.89411e-05 -1.84007e-05 -1.78604e-05 -1.73647e-05
+ -1.70853e-05 -1.68059e-05 -1.65265e-05 -1.62471e-05 -1.59677e-05
+ -1.56883e-05 -1.54089e-05 -1.51295e-05 -1.48501e-05 -1.45707e-05
+ -1.42913e-05 -1.40119e-05 -1.37325e-05 -1.34531e-05 -1.31737e-05
+ -1.28943e-05 -1.26149e-05 -1.23355e-05 -1.20561e-05 -1.17767e-05
+ -1.14973e-05 -1.10954e-05 0.0152675 0.0228237 -0.00460678
+ -0.0341525 0.0232109 -0.0138039 -0.0416538 -0.0458764 -0.0201967
+ -0.00878316 -0.00379173 -0.00164621 -0.000785131 -0.00037575
+ -0.000352375 -0.0545586 -0.0746881 -0.0771865 -0.05386 -0.0022199
+ 0.0136703 0.00633526 0.00138826 -0.00108934 0.0038886 0.0298077
+ 0.0475776 0.0481003 0.0464167 0.047818 0.042789 0.035207
+ 0.0264423 0.0193959 0.0151614 0.00624257 -0.00913057 -0.0310696
+ -0.0430238 0.016426 0.189762 0.49025 0.820116 1.13919 1.43549
+ 1.70658 1.95183 2.17414 2.38506 2.5657 2.73958 2.97905 3.21403
+ 3.43025 3.62645 3.8028 3.96002 4.09996 4.22443 4.33427 4.42886
+ 4.51097 4.5817 4.64326 4.6957 4.74132 4.7797 4.81298 4.84102
+ 4.86512 4.88523 4.90224 4.91649 4.92846 4.93868 4.94755
+ 4.95483 4.96114 4.96682 4.97161 4.97502 4.9776 4.97944 4.98141
+ 4.98319 4.98467 4.98585 4.9869 4.98796 4.98902 4.99008 4.99114
+ 4.9922 4.99326 4.9938 4.99429 4.99479 4.99528 4.99578 4.99628
+ 4.99677 4.99704 4.99718 4.99733 4.99747 4.99762 4.99777
+ 4.99791 4.99806 4.9982 4.99835 4.9985 4.99864 4.99879 4.99893
+ 4.99907 4.99916 4.99925 4.99934 4.99943 5.01473 4.92293
+ 4.61974 4.0316 3.7835 3.74195 3.78344 3.87272 3.97386 4.07319
+ 4.16686 4.25256 4.33126 4.40264 4.46697 4.49249 4.51807
+ 4.55803 4.64055 4.78574 4.86074 4.88334 4.8999 4.91455 4.92814
+ 4.93926 4.94761 4.95433 4.95907 4.9654 4.98317 5.0208 5.05134
+ 4.85852 4.16041 3.00077 1.68376 0.672707 0.240838 0.0794725
+ -0.0106347 -0.00879443 0.107196 0.368163 0.701424 1.03581
+ 1.3601 1.6678 1.95731 2.22701 2.47544 2.69099 2.92327 3.16648
+ 3.3877 3.59067 3.77344 3.93584 4.08066 4.20863 4.32065 4.41791
+ 4.50211 4.57423 4.63614 4.68888 4.73377 4.7721 4.80519 4.83338
+ 4.85732 4.87815 4.89514 4.90927 4.92108 4.93122 4.94014
+ 4.94845 4.95601 4.96251 4.96576 4.969 4.97225 4.9755 4.97874
+ 4.98087 4.98265 4.98442 4.9862 4.98797 4.98924 4.9899 4.99055
+ 4.9912 4.99186 4.99251 4.99316 4.99381 4.99447 4.99512 4.99577
+ 4.99609 4.99634 4.99659 4.99683 4.99708 4.99732 4.99757
+ 4.99782 4.99806 4.99831 4.99853 4.99863 4.99873 4.99883
+ 4.99893 4.99903 4.99913 4.99923 4.99933
+}
+
+v14 set {
+ 1.86175 2.00147 1.85141 1.0654 0.275481 0.205547 0.0712627
+ 0.0313387 0.0151431 0.00864531 0.00593861 0.00438111 0.0037479
+ 0.00305857 0.00221221 0.0017081 -0.0896128 -0.109079 -0.121356
+ -0.0542001 0.175821 0.177442 0.0814591 0.0333042 0.0134909
+ 0.00625777 0.00100092 -0.00552776 -0.00411139 -0.00150395
+ -0.000564784 3.48169e-05 -0.000287014 -0.000538515 -0.000456537
+ -0.000325677 -0.000275468 -0.000166452 -8.27481e-05 -8.28704e-05
+ -7.47644e-05 -4.60552e-05 -2.61481e-06 2.26359e-05 2.53852e-05
+ -1.39853e-06 -4.23456e-05 -4.0907e-05 -2.8501e-05 -1.5945e-05
+ -9.01122e-06 -2.07747e-06 1.49328e-06 4.38398e-06 6.84248e-06
+ 4.76711e-06 2.69173e-06 6.16362e-07 -1.45901e-06 -3.53438e-06
+ -4.14256e-06 -3.76238e-06 -3.3822e-06 -3.00202e-06 -2.62184e-06
+ -2.24878e-06 -1.93456e-06 -1.62033e-06 -1.3061e-06 -9.91867e-07
+ -6.77638e-07 -3.63409e-07 -4.91792e-08 2.6505e-07 5.7928e-07
+ 8.93509e-07 1.16076e-06 1.11055e-06 1.06034e-06 1.01014e-06
+ 9.59927e-07 9.09719e-07 8.59511e-07 8.09302e-07 7.59094e-07
+ 7.08886e-07 6.58678e-07 5.99251e-07 4.87523e-07 3.75795e-07
+ 2.64068e-07 1.5234e-07 4.06119e-08 -7.1116e-08 -1.82844e-07
+ -2.94572e-07 -4.063e-07 -5.18027e-07 -6.08517e-07 -5.95879e-07
+ -5.83241e-07 -5.70604e-07 -5.57966e-07 -5.45328e-07 -5.3269e-07
+ -5.20053e-07 -5.07415e-07 -4.94777e-07 -4.8214e-07 -4.69502e-07
+ -4.56864e-07 -4.44226e-07 -4.31589e-07 -4.18951e-07 -4.06313e-07
+ -3.93676e-07 -3.81038e-07 -3.684e-07 -3.55762e-07 -3.43125e-07
+ 1.06736e-05 0.0797407 0.0437947 -0.0645098 -0.0877312 0.0653203
+ -0.00621184 -0.0353188 -0.0491378 -0.0251957 -0.0110996
+ -0.00481123 -0.0020941 -0.000998038 -0.000478747 -0.000445332
+ -0.102046 -0.135753 -0.154351 -0.0827509 0.163348 0.174012
+ 0.0794822 0.0310624 0.0112213 0.00249061 0.00130764 0.00181315
+ 0.00163875 0.00101454 0.000497435 0.000195258 5.31901e-05
+ 2.4607e-05 6.62736e-05 7.90718e-05 4.0372e-05 -0.000141184
+ -0.000280623 5.5608e-05 0.000799565 0.000920189 0.000931616
+ 0.000494527 0.000162303 -8.24884e-05 -0.000183938 -0.000203899
+ -0.000144788 -9.87063e-05 -0.000227929 2.93932e-05 0.000208563
+ 1.88958e-06 -7.6335e-05 -0.000172472 -0.000165656 -0.000145889
+ -0.000177311 -0.000191058 -0.000168287 -0.00015755 -0.00013142
+ -8.10488e-05 -6.36115e-05 -7.8699e-05 -8.11282e-05 -7.98625e-05
+ -5.98807e-05 -3.40879e-05 -1.95464e-05 -1.79247e-05 -4.45514e-05
+ -7.47995e-05 -8.7682e-05 -7.50806e-05 -3.25561e-05 -4.34114e-05
+ -7.69099e-05 -0.000141101 -0.00018743 -0.000148471 -5.06546e-05
+ 0.000120195 0.000177635 0.000177052 0.000146344 9.75126e-05
+ 8.31233e-05 6.8734e-05 5.43447e-05 3.99554e-05 2.55661e-05
+ 1.11768e-05 -3.21253e-06 -3.88937e-06 -3.56628e-06 -3.24318e-06
+ -2.92008e-06 -2.59699e-06 -2.27389e-06 -1.9508e-06 -1.73227e-06
+ -1.56796e-06 -1.40365e-06 -1.23934e-06 -1.07503e-06 -9.10722e-07
+ -7.46412e-07 -5.82101e-07 -4.1779e-07 -2.5348e-07 -8.91694e-08
+ 7.51412e-08 2.39452e-07 4.03762e-07 5.95733e-07 1.00771e-06
+ 1.41969e-06 1.83167e-06 2.24365e-06 0.0828257 0.231038 0.465438
+ 1.54516 2.8461 3.19221 3.40395 3.6382 3.80758 3.93848 4.04882
+ 4.15428 4.247 4.32917 4.40235 4.36941 4.397 4.48862 4.64552
+ 4.86595 5.03475 5.0348 5.02627 5.01967 5.01542 5.00925 4.98613
+ 4.9519 4.91581 4.87357 4.82302 4.80403 4.82565 4.86102 4.89483
+ 4.92253 4.94428 4.96257 4.97608 4.98373 4.98823 4.99182
+ 4.99437 4.99635 4.99745 4.99802 4.99843 4.99873 4.99895
+ 4.99912 4.99925 4.99931 4.99962 4.99973 4.99972 4.99971
+ 4.9997 4.99969 4.9997 4.99971 4.99973 4.99974 4.99976 4.99978
+ 4.9998 4.99982 4.99985 4.99987 4.99989 4.9999 4.99991 4.99991
+ 4.99993 4.99994 4.99997 5.00001 5.00006 5.00008 5.00006
+ 5.00002 5 4.99999 4.99998 4.99997 4.99995 4.99995 4.99995
+ 4.99995 4.99995 4.99995 4.99995 4.99996 4.99997 4.99997
+ 4.99998 4.99999 5 5 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5 5 5 4.99999 4.99999 4.99999
+
+}
+v15 set {
+ 1.86175 2.00199 2.08919 1.84314 1.08254 0.214737 0.0377351
+ 0.00952455 0.00232763 0.000563614 0.000263477 0.000148642
+ 0.000285086 0.000242592 7.34699e-05 -1.53467e-05 -0.0161874
+ -0.0157876 -0.0141194 0.0132576 0.0903272 0.109938 0.0535295
+ 0.0224216 0.00940945 0.00466825 -0.000649972 -0.00654752
+ -0.00333248 -0.00103671 -0.000508276 -5.8896e-05 -0.00043938
+ -0.000544704 -0.00044444 -0.000307093 -0.00024517 -0.000154538
+ -8.78602e-05 -7.10461e-05 -6.06485e-05 -3.91039e-05 -8.45988e-06
+ 9.43442e-06 1.28351e-05 -2.16734e-06 -2.6142e-05 -2.54768e-05
+ -1.88997e-05 -1.17906e-05 -7.3808e-06 -2.97101e-06 1.19146e-07
+ 2.94246e-06 5.38942e-06 3.88851e-06 2.38761e-06 8.86704e-07
+ -6.14201e-07 -2.11511e-06 -2.59565e-06 -2.38885e-06 -2.18205e-06
+ -1.97525e-06 -1.76845e-06 -1.56241e-06 -1.36258e-06 -1.16276e-06
+ -9.62939e-07 -7.63116e-07 -5.63293e-07 -3.6347e-07 -1.63647e-07
+ 3.61756e-08 2.35999e-07 4.35822e-07 6.07653e-07 5.90323e-07
+ 5.72994e-07 5.55665e-07 5.38336e-07 5.21007e-07 5.03678e-07
+ 4.86349e-07 4.6902e-07 4.51691e-07 4.34361e-07 4.11899e-07
+ 3.60315e-07 3.08731e-07 2.57146e-07 2.05562e-07 1.53977e-07
+ 1.02393e-07 5.08082e-08 -7.76222e-10 -5.23607e-08 -1.03945e-07
+ -1.47815e-07 -1.54225e-07 -1.60635e-07 -1.67045e-07 -1.73455e-07
+ -1.79864e-07 -1.86274e-07 -1.92684e-07 -1.99094e-07 -2.05504e-07
+ -2.11914e-07 -2.18324e-07 -2.24734e-07 -2.31144e-07 -2.37554e-07
+ -2.43964e-07 -2.50373e-07 -2.56783e-07 -2.63193e-07 -2.69603e-07
+ -2.76013e-07 -2.82423e-07 2.92534e-06 0.0446777 0.024278
+ -0.0518987 -0.0636547 0.00983929 -0.000518204 -0.000265194
+ 0.000154772 0.000299538 3.12715e-05 -3.18225e-05 -2.48268e-05
+ -1.16701e-05 -6.05117e-06 7.61116e-06 -0.0163668 -0.0158244
+ -0.0141177 0.0100085 0.0857144 0.107784 0.051862 0.0204448
+ 0.00629858 0.000967736 0.00121674 0.00190276 0.00154009
+ 0.000860922 0.000410386 0.000164585 3.99493e-05 1.93797e-05
+ 5.67594e-05 0.000110126 2.49925e-05 -7.17815e-05 -0.000142299
+ -1.63109e-05 0.000439529 0.000562489 0.000594599 0.000326164
+ 0.000126423 -4.26063e-05 -0.000122927 -0.000114152 -6.72706e-05
+ -6.41242e-05 -0.000135588 2.61507e-05 0.000134036 6.43734e-06
+ -4.6223e-05 -0.000112047 -0.000101388 -8.67847e-05 -0.000117664
+ -0.000133957 -0.000116558 -0.000100873 -7.65448e-05 -4.44964e-05
+ -3.6677e-05 -5.26632e-05 -5.45172e-05 -5.13545e-05 -3.73869e-05
+ -1.99732e-05 -1.0907e-05 -1.10081e-05 -3.02609e-05 -5.18517e-05
+ -6.13597e-05 -5.30706e-05 -2.39572e-05 -3.24146e-05 -5.70062e-05
+ -0.000103448 -0.000135376 -0.0001024 -2.39007e-05 0.000110929
+ 0.000151226 0.000142044 0.000105922 5.62834e-05 4.78476e-05
+ 3.94117e-05 3.09759e-05 2.25401e-05 1.41042e-05 5.66837e-06
+ -2.76747e-06 -3.08639e-06 -2.81341e-06 -2.54043e-06 -2.26745e-06
+ -1.99447e-06 -1.72149e-06 -1.44851e-06 -1.26226e-06 -1.12096e-06
+ -9.79661e-07 -8.38363e-07 -6.97065e-07 -5.55768e-07 -4.1447e-07
+ -2.73173e-07 -1.31875e-07 9.42259e-09 1.5072e-07 2.92018e-07
+ 4.33315e-07 5.74613e-07 7.10363e-07 8.01984e-07 8.93604e-07
+ 9.85225e-07 1.07685e-06 0.04474 0.0928765 0.141327 0.0176048
+ -0.071675 -0.0124613 0.989022 2.28104 3.40619 4.21417 4.67173
+ 4.87438 4.96044 4.98996 4.99858 4.96672 4.89502 4.79391
+ 4.76433 4.8387 4.98612 5.0161 5.01722 5.01437 5.01256 4.99827
+ 4.95807 4.9209 4.88217 4.83006 4.78461 4.80759 4.85548 4.89604
+ 4.9254 4.94617 4.96126 4.97374 4.98255 4.98792 4.99126 4.99361
+ 4.99554 4.99699 4.99792 4.99846 4.99881 4.99905 4.99924
+ 4.99938 4.99949 4.99955 4.9997 4.9998 4.99982 4.99982 4.99982
+ 4.99982 4.99982 4.99983 4.99984 4.99985 4.99986 4.99987
+ 4.99988 4.99989 4.9999 4.99992 4.99993 4.99994 4.99995 4.99995
+ 4.99996 4.99996 4.99998 4.99999 5.00001 5.00002 5.00002
+ 5.00001 5.00001 5 4.99999 4.99999 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+
+}
+v16 set {
+ 1.86175 1.73073 1.50572 1.89001 3.39004 4.36034 4.79012
+ 4.93798 4.98305 4.99539 4.9979 4.99904 4.99772 4.9983 4.99935
+ 4.99975 4.98837 4.99456 4.99728 5.01838 5.04568 5.00759
+ 4.98112 4.98479 4.99197 4.99641 4.99747 4.99775 5.00043
+ 5.0007 5.00035 5.00023 4.99976 5.00002 5.00007 5.0002 4.99993
+ 5.00003 5.00021 5.00006 4.99993 4.99992 5.00002 5.00013
+ 5.00017 5.00009 4.99992 4.99991 4.99993 4.99996 4.99998
+ 4.99999 5.00001 5.00003 5.00005 5.00004 5.00004 5.00003
+ 5.00002 5.00001 5 4.99999 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00002 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 5 5.01498 4.99342 4.96899 5.00301 5.02627 4.9977
+ 4.99548 4.99757 5.00277 5.00245 5.0014 5.00069 5.00032 5.00014
+ 5.00009 4.9867 4.99262 4.99607 5.01805 5.04713 5.00927 4.98184
+ 4.98483 4.9914 4.99616 4.99902 4.9999 4.99987 4.99979 4.99981
+ 4.99989 4.99994 4.99998 5.0002 5.00001 5.00008 5.00008 5.0001
+ 5.00021 5.00032 5.00025 5.00019 5.00006 5.00007 4.99994
+ 4.99997 4.99999 5.00023 5.00008 4.99993 4.99998 4.99986
+ 4.99982 5.00003 4.99985 4.99996 5.00014 5 4.99984 4.99979
+ 4.99982 4.99993 5.00008 5.00011 5.00002 4.99996 4.9999 4.99994
+ 5.00001 5.00007 5.00009 4.99995 4.99978 4.99971 4.99976
+ 4.99997 4.99996 4.99989 4.99972 4.99955 4.99953 4.99959
+ 4.99976 4.9999 5.00005 5.00023 5.00039 5.00034 5.00029 5.00024
+ 5.00019 5.00014 5.00009 5.00004 5.00003 5.00002 5.00001
+ 5 5 4.99999 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5.00001 5.00002 5.00003 5.00004 5.01564 5.03395 5.04932
+ 5.11868 3.92502 1.31888 0.163888 0.0946876 0.0789578 0.0565084
+ 0.0260333 0.0156986 0.00907667 0.00613629 0.00468417 -0.00174008
+ -0.0021422 0.000586962 0.0124937 0.0147977 0.00838454 0.00039383
+ -0.000522021 -0.000426598 -0.000290214 -0.00173713 -0.00384132
+ -0.00382945 -0.00429219 -0.00580193 -0.00393246 0.0017543
+ 0.00423045 0.00408931 0.0031976 0.00245457 0.00187293 0.00159068
+ 0.00105697 0.000609902 0.000358825 0.000334125 0.000212708
+ 0.000168116 8.97349e-05 5.21578e-05 3.84527e-05 2.93033e-05
+ 2.10067e-05 1.59954e-05 1.13917e-05 5.49738e-06 2.77217e-05
+ 6.51259e-06 -6.65468e-06 2.09837e-06 -6.617e-06 -4.80187e-06
+ 1.55031e-06 4.26536e-06 7.69457e-07 -1.46213e-06 -7.25202e-07
+ 3.26501e-06 6.55807e-06 7.524e-06 6.07209e-06 6.00701e-06
+ 5.41166e-06 3.86573e-06 1.10651e-06 -2.74603e-06 -2.18566e-06
+ 2.3658e-06 8.59956e-06 8.35046e-06 2.90621e-06 -8.75982e-07
+ -1.87189e-06 -2.1528e-06 -1.94875e-06 -1.74471e-06 -1.54067e-06
+ -1.33662e-06 -1.13258e-06 -8.40567e-07 -5.20743e-07 -2.00918e-07
+ 1.18906e-07 4.38731e-07 6.11382e-07 6.01529e-07 5.91675e-07
+ 5.81822e-07 5.71968e-07 5.62115e-07 5.52261e-07 5.42407e-07
+ 5.32554e-07 5.227e-07 5.12847e-07 4.72812e-07 4.26137e-07
+ 3.79462e-07 3.32786e-07 2.86111e-07 2.39436e-07 1.92761e-07
+ 1.46086e-07 9.94107e-08 5.27356e-08 -2.77779e-10 -7.98079e-08
+ -1.59338e-07 -2.38868e-07 -3.18398e-07 -3.97928e-07 -4.77458e-07
+ -5.56988e-07 -6.36519e-07
+}
+v17 set {
+ 5 5.16963 4.84136 3.33754 0.316206 0.103113 0.0273341 0.0221102
+ 0.0177008 0.0143758 0.0115203 0.00929231 0.00752716 0.00625439
+ 0.00489872 0.00403656 -0.0657317 -0.0256467 0.165394 0.985963
+ 3.05067 4.55799 4.89728 4.92464 4.8882 4.90592 4.97315 4.99241
+ 4.99694 4.99845 4.99905 4.99939 4.99959 4.99971 4.9998 4.99986
+ 4.9999 4.99993 4.99995 4.99996 4.99997 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 5 5.00001 5.00003 5.00005
+ 5.00004 5.00002 5 4.99999 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99998 4.99998 4.99999 4.99999 5 5 5 5 5 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5.00025 5.1657 4.69981 2.43895
+ 0.0229743 0.0351406 -0.0211974 -0.0312063 -0.0160331 -0.0021718
+ -0.000766597 -0.000251052 -5.49363e-05 -3.36364e-06 -2.01983e-06
+ -9.70575e-06 -0.0657007 -0.0205247 0.183332 1.07163 3.11839
+ 4.46213 4.84163 4.95195 4.99159 5.02084 5.04029 5.04138
+ 5.0271 5.00445 4.97957 4.95702 4.95231 4.97819 4.99191 4.9963
+ 4.99822 4.99878 4.99903 4.99925 4.99942 4.9995 4.99954 4.99957
+ 4.99961 4.99966 4.9997 4.99974 4.99977 4.99981 4.99983 4.99986
+ 4.99988 4.9999 4.99991 4.99992 4.99994 4.99995 4.99995 4.99996
+ 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 4.99999 4.99998 4.99997 4.99996 5.14239 4.76219
+ 3.16574 0.299969 0.0631609 -0.00118611 -0.00026052 -5.96333e-05
+ -1.44904e-05 -4.3859e-06 -2.99454e-06 1.10547e-06 4.84662e-06
+ 1.30971e-05 2.23082e-05 -0.0655844 -0.0204818 0.182507 1.05954
+ 3.12277 4.46735 4.83915 4.94512 4.97679 4.98654 4.9966 5.00833
+ 5.00776 5.00432 5.00199 5.00086 5.00033 5.00008 5 5.00001
+ 5 5.00005 5.00002 4.99981 4.99991 4.99998 4.99979 4.99979
+ 4.99984 4.9998 4.9998 5.00006 5.00002 5.00001 5 5 4.99992
+ 4.99998 4.99999 5.00002 5.00014 4.99999 4.99987 4.99993
+ 5.00003 5.00011 5.00005 4.99996 4.99987 4.99985 4.99994
+ 5.00009 5.0001 5 4.99993 4.99997 5.00008 5.00015 5.00021
+ 5.00021 5.00007 4.99978 4.99965 4.99973 4.9999 4.99992 4.99995
+ 4.99997 4.99999 5.00001 5.00002 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00002
+}
+v18 set {
+ 5 5.0333 5.02472 4.92559 4.18383 3.93923 3.9961 4.14293
+ 4.28591 4.41336 4.52157 4.61101 4.68472 4.7439 4.79294 4.83239
+ 4.80697 4.78808 4.79322 4.8838 5.08529 5.21863 4.88852 3.90198
+ 2.14586 0.383977 0.101103 0.0525711 0.0318287 0.020895 0.0146908
+ 0.010831 0.00830272 0.00656377 0.00532066 0.00440078 0.00369956
+ 0.00315713 0.00272614 0.00237965 0.00209659 0.00186339 0.00167014
+ 0.0015081 0.00137172 0.00125607 0.00115393 0.00106076 0.000980166
+ 0.000918015 0.000862837 0.00080766 0.000763488 0.000721541
+ 0.000680825 0.000653026 0.000625226 0.000597426 0.000569627
+ 0.000541827 0.000519087 0.000499756 0.000480424 0.000461093
+ 0.000441761 0.000423291 0.000411941 0.00040059 0.00038924
+ 0.000377889 0.000366539 0.000355188 0.000343838 0.000332487
+ 0.000321137 0.000309786 0.000299055 0.000292509 0.000285963
+ 0.000279417 0.000272871 0.000266325 0.000259779 0.000253233
+ 0.000246686 0.00024014 0.000233594 0.000227387 0.0002231
+ 0.000218813 0.000214526 0.00021024 0.000205953 0.000201666
+ 0.000197379 0.000193092 0.000188805 0.000184519 0.000180526
+ 0.000177963 0.0001754 0.000172837 0.000170274 0.000167711
+ 0.000165148 0.000162585 0.000160022 0.000157459 0.000154895
+ 0.000152332 0.000149769 0.000147206 0.000144643 0.00014208
+ 0.000139517 0.000136954 0.000134391 0.000131828 0.000129265
+ 0.000126702 0.000132838 0.0311184 0.163151 0.34986 0.604501
+ 0.357125 0.136137 0.0711304 0.0346959 0.0212674 0.00872193
+ 0.00252206 0.000455269 7.59332e-05 2.91532e-05 0.000320562
+ -0.0720911 -0.0840491 -0.0791345 -0.0404143 0.0182035 -0.0235871
+ -0.0426072 -0.0597501 0.00824773 0.481404 1.32496 2.11949
+ 2.57317 2.58202 2.15054 1.33786 0.45702 0.153772 0.0913584
+ 0.0604989 0.0421591 0.0271456 0.0170021 0.0115815 0.00907886
+ 0.00742466 0.00626096 0.00531127 0.00450501 0.00381927 0.00323718
+ 0.00274374 0.00232494 0.00196885 0.00166686 0.00141134 0.00119437
+ 0.0010109 0.000855534 0.000723378 0.000611408 0.000516704
+ 0.000436769 0.000369523 0.000313026 0.00026526 0.000223976
+ 0.000188972 0.000159042 0.000134148 0.000112688 9.49738e-05
+ 7.97877e-05 6.721e-05 5.65115e-05 4.77194e-05 4.03591e-05
+ 3.42848e-05 2.92627e-05 2.50435e-05 2.1412e-05 1.84532e-05
+ 1.58624e-05 1.34673e-05 1.14461e-05 1.00935e-05 9.12375e-06
+ 8.50202e-06 7.81431e-06 7.20729e-06 6.73936e-06 6.3702e-06
+ 5.90049e-06 5.43077e-06 4.96105e-06 4.49133e-06 4.02162e-06
+ 3.5519e-06 3.08218e-06 2.79099e-06 2.51281e-06 2.23463e-06
+ 1.95645e-06 1.67827e-06 1.40009e-06 1.12191e-06 1.01376e-06
+ 9.9375e-07 9.73741e-07 9.53733e-07 9.33724e-07 9.13715e-07
+ 8.93707e-07 8.73698e-07 8.5369e-07 8.33681e-07 8.13673e-07
+ 7.93664e-07 7.73655e-07 7.53647e-07 7.21781e-07 5.956e-07
+ 4.69419e-07 3.43239e-07 2.17058e-07 0.0284032 0.0374438
+ -0.0157543 -0.0680497 0.0504768 0.0100294 0.00222261 0.000528697
+ 0.000132929 3.99489e-05 2.46066e-05 4.56327e-06 -6.54853e-06
+ 1.33783e-05 -3.68221e-05 -0.0724498 -0.0843663 -0.0792935
+ -0.0406426 0.0200019 0.0426259 0.0220753 0.00668555 -0.000968483
+ 0.024662 0.0383437 0.0911513 0.087848 0.0602076 0.0390559
+ 0.0260573 0.0180444 0.012974 0.00985409 0.00788132 0.0064228
+ 0.005545 0.00453571 0.00364245 0.00310278 0.00270523 0.00236439
+ 0.0020945 0.00186808 0.00167493 0.00151731 0.00138594 0.00126945
+ 0.00116695 0.0010762 0.000996366 0.000928387 0.000864414
+ 0.000808258 0.000759574 0.000713865 0.000666712 0.000632716
+ 0.000601262 0.000572163 0.000543986 0.000515253 0.0004897
+ 0.000468112 0.000449313 0.000432981 0.000417911 0.000401307
+ 0.000382712 0.000366678 0.000355736 0.000349171 0.000335727
+ 0.000317091 0.000296086 0.000283543 0.000277366 0.000272233
+ 0.000267001 0.000263147 0.000256699 0.000250251 0.000243803
+ 0.000237355 0.000230907 0.000225424 0.000220247 0.000215069
+ 0.000209892 0.000204714 0.000200213 0.000196548 0.000192884
+ 0.00018922 0.000185556 0.000181892 0.000178228 0.000174564
+ 0.0001709 0.000167236 0.000163572 0.000160824 0.000158279
+ 0.000155733 0.000153187 0.000150641 0.000148095 0.000145549
+ 0.000143003 0.000140457 0.000137911 0.000135457 0.000133386
+ 0.000131315 0.000129245 0.000127174 0.000125103 0.000123032
+ 0.000120961 0.000118891
+}
+v19 set {
+ 1.86175 1.99994 2.0833 2.01627 2.42503 3.25769 3.62134 3.88827
+ 4.09688 4.26773 4.40529 4.51734 4.60827 4.68313 4.74346
+ 4.79302 4.72815 4.68959 4.70421 4.81316 5.01375 5.14493
+ 5.10305 5.0699 5.04484 5.03751 5.03348 5.02504 5.01799 5.01271
+ 5.00895 5.00628 5.0044 5.00309 5.00216 5.00151 5.00105 5.00073
+ 5.00051 5.00034 5.00023 5.00015 5.0001 5.00007 5.00003 4.99998
+ 4.99993 4.99993 4.99995 4.99999 5.00001 5.00003 5.00002
+ 5.00001 5 5 5 5 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00017 5.17398
+ 4.94779 3.78508 1.52302 0.608808 0.244311 0.126053 0.0597175
+ 0.038422 0.0158174 0.00481338 0.00107847 0.000301256 0.000114861
+ 0.00059489 -0.118904 -0.147478 -0.158986 -0.080544 0.165361
+ 0.171378 0.0776087 0.0435738 0.0428235 0.0423755 0.0347695
+ 0.0225061 0.0155539 0.0121357 0.0107997 0.0103976 0.0124406
+ 0.016814 0.0167556 0.0149852 0.01459 0.0141182 0.0131934
+ 0.0120286 0.0108692 0.0097184 0.00855881 0.00744912 0.00643877
+ 0.00554044 0.00475165 0.00406535 0.00347158 0.00295981 0.00251995
+ 0.00214318 0.00182101 0.00154613 0.00131196 0.0011119 0.000941587
+ 0.000796999 0.000674582 0.000571283 0.000484276 0.000410649
+ 0.000347005 0.000292984 0.000246715 0.000208143 0.00017489
+ 0.000147412 0.000123854 0.000104332 8.77229e-05 7.40686e-05
+ 6.2637e-05 5.32e-05 4.53946e-05 3.88343e-05 3.31864e-05
+ 2.85905e-05 2.45725e-05 2.08671e-05 1.77301e-05 1.55911e-05
+ 1.40153e-05 1.29421e-05 1.18693e-05 1.09815e-05 1.03484e-05
+ 9.87664e-06 9.14446e-06 8.41228e-06 7.68011e-06 6.94793e-06
+ 6.21575e-06 5.48357e-06 4.7514e-06 4.38454e-06 4.04432e-06
+ 3.7041e-06 3.36388e-06 3.02366e-06 2.68344e-06 2.34322e-06
+ 2.15196e-06 2.03791e-06 1.92386e-06 1.80982e-06 1.69577e-06
+ 1.58173e-06 1.46768e-06 1.35363e-06 1.23959e-06 1.12554e-06
+ 1.0115e-06 8.9745e-07 7.83404e-07 6.69358e-07 4.76113e-07
+ -3.47071e-07 -1.17025e-06 -1.99344e-06 -2.81662e-06 0.0783754
+ 0.0500262 -0.0659563 -0.120914 0.0815957 0.0154255 0.00347177
+ 0.000840357 0.000214582 6.54655e-05 3.91709e-05 8.07396e-06
+ -4.44265e-07 1.74384e-05 -4.52725e-05 -0.119379 -0.147984
+ -0.159247 -0.0824604 0.169014 0.177628 0.0758742 0.010558
+ -0.0346506 -0.0710288 -0.0838952 -0.0599521 -0.034568 -0.0181615
+ -0.00968034 -0.00547115 -0.00333511 -0.00232468 -0.00181159
+ -0.00143841 -0.00116601 -0.000839755 -0.000569764 -0.000578683
+ -0.000490551 -0.000411712 -0.000437859 -0.000408185 -0.000356644
+ -0.000311332 -0.000269006 -0.000221396 -0.000210054 -0.0001923
+ -0.000175122 -0.000161039 -0.0001428 -0.000126123 -0.000127893
+ -8.14516e-05 -0.000120166 -0.000154909 -0.000112733 -8.40377e-05
+ -7.11342e-05 -8.09538e-05 -9.77789e-05 -9.82402e-05 -7.73531e-05
+ -5.28255e-05 -3.1096e-05 -1.87967e-05 -1.96552e-05 -4.16655e-05
+ -5.77185e-05 -5.24142e-05 -2.83153e-05 -1.90012e-05 -1.54415e-05
+ -2.52569e-05 -6.23747e-05 -0.000130543 -0.000149394 -0.000110886
+ -4.35517e-05 -4.17084e-05 -3.98651e-05 -3.80218e-05 -3.61785e-05
+ -3.43352e-05 -3.36249e-05 -3.32729e-05 -3.29208e-05 -3.25687e-05
+ -3.22166e-05 -3.17143e-05 -3.10258e-05 -3.03372e-05 -2.96486e-05
+ -2.89601e-05 -2.82715e-05 -2.75829e-05 -2.68944e-05 -2.62058e-05
+ -2.55173e-05 -2.48287e-05 -2.43043e-05 -2.38159e-05 -2.33276e-05
+ -2.28393e-05 -2.2351e-05 -2.18626e-05 -2.13743e-05 -2.0886e-05
+ -2.03977e-05 -1.99093e-05 -1.945e-05 -1.91122e-05 -1.87744e-05
+ -1.84366e-05 -1.80987e-05 -1.77609e-05 -1.74231e-05 -1.70853e-05
+ -1.67474e-05
+}
+v20 set {
+ 1.86175 1.99724 2.17266 2.48439 3.15933 3.85231 4.38091
+ 4.69033 4.85034 4.92851 4.96453 4.98188 4.98736 4.991 4.99482
+ 4.9973 4.96422 4.89989 4.83907 4.83151 4.90868 5.04854 5.06104
+ 5.04571 5.03219 5.03025 5.02273 5.01707 5.0123 5.0087 5.00611
+ 5.00429 5.00301 5.00211 5.00148 5.00103 5.00072 5.0005 5.00035
+ 5.00024 5.00016 5.00011 5.00007 5.00005 5.00003 5.00001
+ 4.99999 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 5 5 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 5 4.99981 5.10081 5.10903 4.98404 5.00999 5.14946 4.36501
+ 2.23938 0.325144 0.00660272 -0.0102186 -0.0082401 -0.00556785
+ -0.00374178 -0.00264763 -0.00202823 -0.0182241 -0.0169551
+ -0.0150395 0.0103736 0.0877592 0.104382 0.0515938 0.0373818
+ 0.0411547 0.0397009 0.0308946 0.0205793 0.0154037 0.0129191
+ 0.0119327 0.011527 0.0124295 0.0161152 0.0161076 0.0145391
+ 0.0144541 0.0139287 0.0129215 0.0117239 0.0105795 0.00942983
+ 0.00827423 0.00718354 0.00619954 0.00532868 0.00456631 0.00390448
+ 0.00333254 0.00284003 0.00241714 0.00205524 0.0017458 0.00148202
+ 0.00125739 0.0010655 0.000902213 0.000763611 0.000646279
+ 0.000547291 0.000463934 0.000393401 0.000332424 0.000280655
+ 0.000236328 0.000199386 0.000167536 0.000141218 0.000118654
+ 9.99559e-05 8.40479e-05 7.09694e-05 6.00188e-05 5.09786e-05
+ 4.3502e-05 3.72191e-05 3.18114e-05 2.74071e-05 2.35539e-05
+ 1.99967e-05 1.69871e-05 1.49449e-05 1.3451e-05 1.24492e-05
+ 1.14256e-05 1.05669e-05 9.94487e-06 9.47514e-06 8.77318e-06
+ 8.07123e-06 7.36927e-06 6.66731e-06 5.96536e-06 5.2634e-06
+ 4.56144e-06 4.23044e-06 3.92649e-06 3.62254e-06 3.31858e-06
+ 3.01463e-06 2.71068e-06 2.40673e-06 2.23063e-06 2.12082e-06
+ 2.01102e-06 1.90121e-06 1.7914e-06 1.68159e-06 1.57178e-06
+ 1.46197e-06 1.35216e-06 1.24235e-06 1.13255e-06 1.02274e-06
+ 9.12929e-07 8.0312e-07 6.33171e-07 -1.51288e-08 -6.63428e-07
+ -1.31173e-06 -1.96003e-06 0.0437517 0.0265689 -0.0515377
+ -0.0658688 0.010727 -0.000511921 -8.36924e-05 2.13278e-05
+ 1.45207e-05 4.54862e-06 -6.14726e-06 2.0062e-06 1.02709e-06
+ 1.4152e-05 -3.08225e-05 -0.0166501 -0.0157139 -0.013957
+ 0.0107537 0.0873717 0.111302 0.0454129 -0.00530142 -0.0468336
+ -0.0790063 -0.0826944 -0.0534753 -0.0288705 -0.0149009 -0.00801592
+ -0.0046342 -0.00291835 -0.00213019 -0.00170055 -0.001352
+ -0.00110593 -0.000742655 -0.000532042 -0.000544742 -0.000479206
+ -0.000407307 -0.000403575 -0.000366209 -0.000324161 -0.000286183
+ -0.000247579 -0.000214281 -0.000203435 -0.000186896 -0.000171033
+ -0.00015779 -0.000145259 -0.000128069 -0.000122647 -9.89398e-05
+ -0.000114926 -0.000132195 -0.000107872 -8.91015e-05 -7.87996e-05
+ -8.14061e-05 -8.9098e-05 -8.83368e-05 -7.6122e-05 -6.14668e-05
+ -4.75402e-05 -3.81855e-05 -3.69696e-05 -4.78656e-05 -5.61346e-05
+ -5.35007e-05 -4.1459e-05 -3.35411e-05 -2.52374e-05 -2.37479e-05
+ -4.6406e-05 -9.41884e-05 -0.000109222 -8.52676e-05 -4.25166e-05
+ -4.10125e-05 -3.95085e-05 -3.80045e-05 -3.65004e-05 -3.49964e-05
+ -3.41627e-05 -3.3541e-05 -3.29193e-05 -3.22976e-05 -3.16758e-05
+ -3.10334e-05 -3.03653e-05 -2.96971e-05 -2.9029e-05 -2.83609e-05
+ -2.76928e-05 -2.70246e-05 -2.63565e-05 -2.56884e-05 -2.50203e-05
+ -2.43521e-05 -2.38716e-05 -2.34324e-05 -2.29932e-05 -2.25539e-05
+ -2.21147e-05 -2.16755e-05 -2.12362e-05 -2.0797e-05 -2.03578e-05
+ -1.99186e-05 -1.95079e-05 -1.9217e-05 -1.8926e-05 -1.8635e-05
+ -1.8344e-05 -1.8053e-05 -1.7762e-05 -1.74711e-05 -1.71801e-05
+
+}
+v21 set {
+ 1.86175 1.73273 1.42016 1.02483 0.944013 0.274107 0.0823742
+ 0.0379366 0.020816 0.0132952 0.00955525 0.00717008 0.00592286
+ 0.00437379 0.00383557 0.00273694 -0.0037467 -0.0054191 -0.00131454
+ 0.0112179 0.0133918 0.00519747 -0.00260113 -0.00252847 -0.00181292
+ 0.000183398 -0.000667607 -0.000750747 -0.000594314 -0.000433904
+ -0.000308985 -0.000217858 -0.000152926 -0.000107454 -7.54076e-05
+ -5.2675e-05 -3.66299e-05 -2.54341e-05 -1.75095e-05 -1.18848e-05
+ -7.97289e-06 -5.30239e-06 -3.53615e-06 -2.38504e-06 -2.40158e-06
+ -3.84485e-06 -5.29435e-06 -2.57099e-06 1.95189e-06 3.55083e-06
+ 2.06179e-06 5.72753e-07 3.30469e-07 3.40296e-07 3.60221e-07
+ 4.86081e-07 6.1194e-07 7.37799e-07 8.63659e-07 9.89518e-07
+ 9.21274e-07 7.22275e-07 5.23276e-07 3.24277e-07 1.25278e-07
+ -5.59467e-08 -9.03265e-08 -1.24706e-07 -1.59086e-07 -1.93466e-07
+ -2.27846e-07 -2.62226e-07 -2.96605e-07 -3.30985e-07 -3.65365e-07
+ -3.99745e-07 -4.24266e-07 -3.82163e-07 -3.40061e-07 -2.97959e-07
+ -2.55857e-07 -2.13755e-07 -1.71652e-07 -1.2955e-07 -8.7448e-08
+ -4.53457e-08 -3.24353e-09 3.76901e-08 7.19937e-08 1.06297e-07
+ 1.40601e-07 1.74904e-07 2.09208e-07 2.43512e-07 2.77815e-07
+ 3.12119e-07 3.46422e-07 3.80726e-07 4.04507e-07 3.77191e-07
+ 3.49876e-07 3.22561e-07 2.95246e-07 2.67931e-07 2.40616e-07
+ 2.13301e-07 1.85986e-07 1.58671e-07 1.31356e-07 1.04041e-07
+ 7.67256e-08 4.94105e-08 2.20955e-08 -5.21962e-09 -3.25347e-08
+ -5.98498e-08 -8.71649e-08 -1.1448e-07 -1.41795e-07 -1.6911e-07
+ 7.87893e-06 0.0114592 -0.0245712 -0.111637 0.0961324 1.61168
+ 3.22343 4.20442 4.53535 4.83834 4.95464 4.98874 4.99746
+ 4.99883 4.99948 4.99815 4.98431 4.99298 4.99718 5.01948
+ 5.04749 5.008 4.98243 4.98985 4.99781 4.99887 4.99679 4.99616
+ 4.99743 4.99859 4.99936 4.99972 5.00058 5.00123 5.0002 4.99945
+ 4.99983 4.9998 4.99966 4.99958 4.99956 4.99956 4.99956 4.99958
+ 4.99961 4.99965 4.99969 4.99973 4.99977 4.9998 4.99983 4.99985
+ 4.99987 4.99989 4.99991 4.99992 4.99993 4.99994 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 4.99999 4.99998 4.99997 4.99996 5.01454
+ 4.99566 4.96796 4.99819 5.03232 5.00034 4.99867 4.99937
+ 4.99977 4.99992 4.99997 4.99999 5.00001 5.00021 4.99974
+ 4.98462 4.99301 4.99723 5.01936 5.04807 5.00929 4.9789 4.97876
+ 4.98244 4.9863 4.99575 5.0069 5.00863 5.00624 5.00357 5.0019
+ 5.00098 5.00048 5.00025 5.00016 5.00011 5.00013 5.00009
+ 4.99982 4.99994 5.00005 4.99994 4.99988 4.99989 4.99997
+ 5.00003 5.00005 5.00002 5.00001 5.00001 5.00001 4.99993
+ 4.99999 5 5.00021 4.99997 4.99981 5 5.00009 5.0001 5.00001
+ 4.99991 4.9999 5 5.00011 5.00017 5.00018 5.00018 5.00014
+ 5.00007 4.99999 4.9999 4.9999 5.00001 5.00016 5.00014 4.99999
+ 4.99993 4.99999 5.00009 5.00007 5.00006 5.00004 5.00003
+ 5.00001 5.00001 5 4.99999 4.99998 4.99997 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5
+}
+v22 set {
+ 7.10441e-10 0.00107105 0.000637109 -0.00236346 -0.018079
+ -0.0120077 -0.00217059 0.00266679 0.00403383 0.00403836
+ 0.00356705 0.00303303 0.00244716 0.00198586 0.0016855 0.00136497
+ -3.96022e-05 -0.000367409 -3.77079e-05 0.00194085 0.00506964
+ -0.0400214 -0.0402572 0.0524434 0.286234 0.803011 1.44795
+ 2.02473 2.54768 3.02748 3.4415 3.78287 4.09667 4.35152 4.53987
+ 4.67614 4.77407 4.84319 4.89227 4.92702 4.95119 4.96764
+ 4.97846 4.98557 4.98982 4.99209 4.99371 4.99569 4.99727
+ 4.99802 4.99834 4.99867 4.99892 4.99915 4.99936 4.99939
+ 4.99943 4.99946 4.9995 4.99953 4.99957 4.9996 4.99963 4.99967
+ 4.9997 4.99973 4.99974 4.99975 4.99976 4.99977 4.99978 4.9998
+ 4.99981 4.99982 4.99983 4.99984 4.99985 4.99986 4.99986
+ 4.99986 4.99987 4.99987 4.99988 4.99988 4.99989 4.99989
+ 4.9999 4.9999 4.9999 4.9999 4.99991 4.99991 4.99991 4.99991
+ 4.99992 4.99992 4.99992 4.99992 4.99993 4.99993 4.99993
+ 4.99993 4.99993 4.99993 4.99993 4.99993 4.99994 4.99994
+ 4.99994 4.99994 4.99994 4.99994 4.99994 4.99994 4.99995
+ 4.99995 4.99995 4.99995 4.99995 4.99995 4.99995 5.00145
+ 5.00659 5.01209 5.01931 5.00279 4.99273 4.99217 4.99295
+ 4.99471 4.99594 4.99696 4.9978 4.99844 4.99891 4.99924 4.99635
+ 4.99699 4.99813 5.00068 5.00307 5.0588 4.96365 4.54012 3.6307
+ 2.35176 1.0322 0.354379 0.115986 0.0435668 0.0245112 0.020786
+ 0.0164656 0.0118409 0.00849698 0.00597078 0.0040105 0.0026076
+ 0.0016597 0.00118185 0.00121067 0.00153587 0.00174836 0.00136519
+ -0.000189116 -0.00315555 -0.00646603 -0.00898042 -0.010203
+ -0.0110896 -0.0123764 -0.00953841 -0.00225795 0.000818314
+ 0.00152252 0.00150269 0.00119025 0.000767068 0.000308852
+ -3.79272e-05 -0.00019691 -0.000186642 -9.73653e-05 -8.49784e-06
+ 2.04147e-05 -9.91086e-06 -1.55959e-05 -1.80499e-05 -1.77097e-05
+ -1.51548e-05 -1.1978e-05 -9.84916e-06 -1.29728e-05 -1.67235e-05
+ -1.74153e-05 -1.39958e-05 -5.92272e-06 -8.08216e-06 -1.53077e-05
+ -2.92531e-05 -3.91049e-05 -2.98935e-05 -7.32122e-06 3.18534e-05
+ 4.39134e-05 4.18753e-05 3.22759e-05 1.86766e-05 1.58432e-05
+ 1.30098e-05 1.01765e-05 7.34312e-06 4.50975e-06 1.67639e-06
+ -1.15697e-06 -1.23877e-06 -1.11991e-06 -1.00106e-06 -8.82208e-07
+ -7.63355e-07 -6.44502e-07 -5.2565e-07 -4.29318e-07 -3.44661e-07
+ -2.60004e-07 -1.75347e-07 -9.06904e-08 -6.03349e-09 7.86234e-08
+ 1.6328e-07 2.47937e-07 3.32594e-07 4.17251e-07 5.01908e-07
+ 5.86565e-07 6.71222e-07 7.36123e-07 6.43886e-07 5.5165e-07
+ 4.59414e-07 3.67178e-07 0.000334759 -4.60833e-05 -0.00106139
+ -0.00166624 0.000859563 0.00102606 0.00410037 0.00419931
+ 0.00518997 0.00459791 0.00503125 0.00523877 0.00452158 0.00339924
+ 0.00233399 0.000876915 0.000546439 0.000444299 0.000983968
+ 0.00119304 -0.0429422 -0.0403983 0.0534896 0.288013 0.807345
+ 1.44247 2.03448 2.57021 3.05049 3.47332 3.8131 4.1009 4.34677
+ 4.53512 4.67127 4.76531 4.82526 4.86593 4.89586 4.91904
+ 4.93806 4.95348 4.96597 4.97629 4.9843 4.98983 4.99335 4.9957
+ 4.99741 4.99864 4.99946 4.99994 5.00047 5.00073 5.00086
+ 5.00092 5.00094 5.00091 5.00087 5.00081 5.00074 5.00067
+ 5.00059 5.00052 5.00046 5.0004 5.00034 5.0003 5.00026 5.00022
+ 5.00019 5.00016 5.00014 5.00012 5.0001 5.00009 5.00007 5.00006
+ 5.00006 5.00005 5.00004 5.00004 5.00004 5.00003 5.00003
+ 5.00003 5.00002 5.00002 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00002 5.00002
+}
+v23 set {
+ 5 5.00284 5.01266 5.01895 4.98936 4.99575 4.99217 4.99545
+ 4.99775 4.99894 4.99946 4.99968 4.99975 4.99977 4.99986
+ 4.9999 4.99528 4.99808 5.00039 5.00392 5.00512 4.99985 4.99863
+ 4.99942 4.99992 5.00017 4.99897 4.99803 4.99784 4.99739
+ 4.99883 5.00365 5.00298 5.00133 5.00048 5.00019 5.00008
+ 5.00005 5.00004 5.00003 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 4.99999 4.99997 4.99995 4.99996
+ 4.99998 5 5.00001 5.00001 5.00002 5.00002 5.00003 5.00003
+ 5.00002 5.00002 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5.00217 5.00108 4.99547 4.99658 5.00667
+ 4.99641 4.99532 4.99938 5.00328 5.00222 5.00114 5.00052
+ 5.00024 5.00011 5.00009 4.99285 4.99591 4.99897 5.00403
+ 5.00786 5.00318 4.99942 4.9992 4.99949 5.001 5.00408 5.00319
+ 5.00063 4.99995 5.00014 4.99982 4.99832 4.99838 4.99865
+ 4.99912 4.99836 4.99735 4.99606 4.99814 5.00958 5.02973
+ 5.05293 5.06103 4.99342 4.80726 4.50744 4.07509 3.41358
+ 2.37924 1.03194 0.261552 0.142392 0.0904482 0.0555071 0.0322869
+ 0.018289 0.0113802 0.00875182 0.00757055 0.00629906 0.00523
+ 0.00403349 0.0031953 0.00280864 0.00286119 0.00250389 0.00202815
+ 0.001723 0.00147312 0.0012411 0.00104401 0.000886204 0.000758277
+ 0.000651915 0.00056348 0.000487966 0.000424048 0.000365613
+ 0.000308178 0.000258725 0.000228061 0.000207976 0.000198491
+ 0.00018518 0.000172716 0.000163197 0.000155007 0.000141734
+ 0.000128461 0.000115188 0.000101915 8.86417e-05 7.53686e-05
+ 6.20956e-05 5.69164e-05 5.23275e-05 4.77385e-05 4.31495e-05
+ 3.85605e-05 3.39716e-05 2.93826e-05 2.69449e-05 2.56224e-05
+ 2.42999e-05 2.29774e-05 2.16549e-05 2.03324e-05 1.90099e-05
+ 1.76873e-05 1.63648e-05 1.50423e-05 1.37198e-05 1.23973e-05
+ 1.10748e-05 9.75232e-06 8.48447e-06 7.65129e-06 6.81811e-06
+ 5.98494e-06 5.15176e-06 0.00056893 -0.00787906 -0.0217381
+ -0.0370066 -0.00770505 0.00659312 0.00975477 0.00949456
+ 0.00777552 0.00655645 0.00568776 0.00508782 0.00458121 0.00410187
+ 0.00365665 0.0015121 0.00160863 0.00263181 0.00638941 0.00772607
+ 0.00225583 0.0010843 0.000882939 0.000801563 0.00075632
+ 0.000554992 0.000435131 0.0003474 0.000217667 0.000491602
+ 0.0012267 0.00250446 0.000212058 -0.0174972 -0.0527527 -0.0479071
+ 0.194908 1.45838 3.40677 4.49242 4.86894 4.97215 5.01218
+ 5.04342 5.06228 5.03069 4.87169 4.57056 4.11523 3.38264
+ 2.19691 0.715839 0.172818 0.102162 0.0627162 0.0363388 0.020289
+ 0.0119414 0.00826608 0.0066417 0.00549092 0.00492505 0.00439443
+ 0.0037156 0.00306471 0.00247451 0.00195965 0.0014822 0.0010815
+ 0.000904464 0.0010514 0.00152308 0.00120752 0.000228447
+ -0.00102833 -0.00116644 -0.00042067 4.78758e-05 5.09599e-05
+ -4.45756e-05 -3.22966e-06 3.81163e-05 7.94622e-05 0.000120808
+ 0.000162154 0.000161895 0.000148481 0.000135068 0.000121654
+ 0.000108241 9.81453e-05 9.2164e-05 8.61827e-05 8.02014e-05
+ 7.42201e-05 6.82388e-05 6.22576e-05 5.62763e-05 5.0295e-05
+ 4.43137e-05 3.83324e-05 3.54323e-05 3.321e-05 3.09877e-05
+ 2.87654e-05 2.65431e-05 2.43209e-05 2.20986e-05 1.98763e-05
+ 1.7654e-05 1.54317e-05 1.34612e-05 1.25441e-05 1.1627e-05
+ 1.07099e-05 9.79276e-06 8.87564e-06 7.95851e-06 7.04139e-06
+ 6.12427e-06
+}
+v24 set {
+ 5 5.01099 5.00866 4.97845 4.92369 4.9273 4.97413 4.9929
+ 4.99826 4.99958 4.99978 5.00005 4.99968 4.99959 5.00014
+ 4.99979 4.99914 4.99982 5.00023 5.00295 5.00664 4.99854
+ 4.99647 5.00438 5.01722 5.03681 5.04766 5.04799 5.04867
+ 5.04873 5.04685 5.04413 5.0367 5.02505 5.01726 5.01183 5.00806
+ 5.00549 5.00371 5.00246 5.00162 5.00105 5.00069 5.00045
+ 5.00031 5.00024 5.00019 5.00012 5.00007 5.00004 5.00001
+ 4.99998 4.99999 4.99999 5 5.00001 5.00001 5.00002 5.00002
+ 5.00003 5.00003 5.00003 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5.00418 4.99953 4.99152
+ 4.99807 5.00497 5.00112 5.00055 5.00038 5.00018 5.00006
+ 5.00006 5.00007 5.00006 5.00004 5.00004 4.99853 4.99945
+ 4.99998 5.00304 5.00935 5.00742 4.99181 4.97421 4.93603
+ 4.8853 4.8927 4.93984 4.97458 4.99039 4.99614 4.99801 4.99851
+ 4.99869 4.99924 5.00108 5.00181 5.00119 5.00059 5.00031
+ 5.00022 5.00018 5.00011 5.00001 5.00006 4.99981 4.99977
+ 4.99982 5.00012 4.99993 5.00008 5.00043 5.00048 5.00024
+ 5.00008 4.99984 4.99993 5.00011 4.99996 4.9998 4.99977 4.9998
+ 4.99993 5.00008 5.00011 5.00002 4.99995 4.99989 4.99993
+ 5 5.00007 5.00009 4.99994 4.99977 4.9997 4.99975 4.99996
+ 4.99996 4.99988 4.9997 4.99952 4.9995 4.99956 4.99973 4.99988
+ 5.00005 5.00025 5.00042 5.00036 5.00031 5.00025 5.0002 5.00014
+ 5.00009 5.00003 5.00002 5.00001 5.00001 5 4.99999 4.99998
+ 4.99998 4.99997 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5.00284
+ 5.00442 5.00381 4.98997 4.99092 5.00733 5.07791 4.98237
+ 4.86434 4.76835 4.74067 4.79278 4.85094 4.90068 4.93603
+ 4.95698 4.96984 4.97856 4.98869 4.99904 5.0005 4.99524 5.00181
+ 5.01878 5.05177 5.07986 4.98917 4.56217 3.68 2.3539 1.18541
+ 0.505772 0.221044 0.115287 0.0760938 0.0589194 0.0476784
+ 0.0457213 0.0412911 0.033889 0.0259741 0.0191452 0.0139018
+ 0.0100235 0.00711788 0.00497657 0.00349368 0.00250021 0.00176179
+ 0.00121843 0.000838368 0.000582711 0.000423458 0.000294608
+ 0.000201251 0.000133748 8.6227e-05 5.44252e-05 3.30514e-05
+ 1.93926e-05 1.09814e-05 5.29857e-06 1.92247e-06 3.08708e-07
+ -3.74311e-07 -6.11121e-07 -7.27807e-07 -4.87604e-07 -4.80493e-07
+ -9.15925e-07 -2.03774e-06 -4.01128e-06 -2.46644e-06 2.10626e-06
+ 8.22422e-06 1.04922e-05 9.83047e-06 7.27106e-06 3.29654e-06
+ -2.06736e-06 -2.18019e-06 -2.29303e-06 -2.40586e-06 -2.51869e-06
+ -2.63153e-06 -2.24615e-06 -1.70325e-06 -1.16036e-06 -6.17468e-07
+ -7.45754e-08 2.45198e-07 2.88285e-07 3.31373e-07 3.7446e-07
+ 4.17548e-07 4.60635e-07 5.03723e-07 5.4681e-07 5.89898e-07
+ 6.32985e-07 6.76073e-07 6.19054e-07 5.4001e-07 4.60967e-07
+ 3.81923e-07 3.02879e-07 2.23836e-07 1.44792e-07 6.57488e-08
+ -1.32948e-08 -9.23383e-08 -1.6698e-07 -2.23206e-07 -2.79432e-07
+ -3.35658e-07 -3.91884e-07 -4.48109e-07 -5.04335e-07 -5.60561e-07
+ -6.16787e-07
+}
+v25 set {
+ 1.34824 1.35838 1.36465 1.34675 1.29167 1.23161 1.2201 1.2185
+ 1.2181 1.21798 1.21793 1.21788 1.21785 1.21782 1.21779 1.21776
+ 1.21655 1.21656 1.21669 1.21871 1.22421 1.22247 1.21858
+ 1.2228 1.23803 1.27737 1.10647 0.395248 0.0600669 0.027687
+ 0.0192374 0.015425 0.0130881 0.00977445 0.00696598 0.00491122
+ 0.00341952 0.00237078 0.00162339 0.00109178 0.000726647
+ 0.000478886 0.00031568 0.000207902 0.000143494 0.000109768
+ 8.62987e-05 5.69775e-05 3.36547e-05 2.30356e-05 1.86108e-05
+ 1.41861e-05 1.08293e-05 7.68835e-06 4.79593e-06 4.51019e-06
+ 4.22444e-06 3.9387e-06 3.65295e-06 3.36721e-06 3.04559e-06
+ 2.69981e-06 2.35403e-06 2.00825e-06 1.66247e-06 1.34508e-06
+ 1.26225e-06 1.17941e-06 1.09657e-06 1.01373e-06 9.30893e-07
+ 8.48054e-07 7.65216e-07 6.82378e-07 5.9954e-07 5.16702e-07
+ 4.37489e-07 3.82774e-07 3.2806e-07 2.73346e-07 2.18632e-07
+ 1.63917e-07 1.09203e-07 5.4489e-08 -2.2523e-10 -5.49395e-08
+ -1.09654e-07 -1.52862e-07 -1.3079e-07 -1.08718e-07 -8.6646e-08
+ -6.45739e-08 -4.25019e-08 -2.04298e-08 1.64229e-09 2.37144e-08
+ 4.57864e-08 6.78585e-08 8.71693e-08 9.30725e-08 9.89758e-08
+ 1.04879e-07 1.10782e-07 1.16685e-07 1.22589e-07 1.28492e-07
+ 1.34395e-07 1.40298e-07 1.46201e-07 1.52105e-07 1.58008e-07
+ 1.63911e-07 1.69814e-07 1.75718e-07 1.81621e-07 1.87524e-07
+ 1.93427e-07 1.9933e-07 2.05234e-07 2.11137e-07 2.19788e-07
+ 0.000393944 -0.000218983 -0.00105784 0.00172403 -0.00027134
+ -0.000204147 8.79968e-06 5.93762e-05 5.83554e-05 4.13815e-05
+ 3.71369e-05 3.03372e-05 2.25336e-05 1.5986e-05 1.07284e-05
+ -7.5239e-05 5.60593e-05 6.97571e-05 0.000667617 0.000960856
+ 0.00131749 -0.00759564 -0.0217897 -0.0450321 -0.076646 -0.128569
+ -0.186391 -0.202175 -0.206953 -0.2082 -0.208416 -0.208669
+ -0.208934 -0.209111 -0.209234 -0.209329 -0.209389 -0.209416
+ -0.2094 -0.209329 -0.20926 -0.209204 -0.209208 -0.209285
+ -0.209454 -0.209641 -0.20977 -0.209811 -0.209833 -0.209887
+ -0.209653 -0.209127 -0.208893 -0.208811 -0.208777 -0.208758
+ -0.208747 -0.20874 -0.208726 -0.208697 -0.208657 -0.208611
+ -0.208565 -0.208524 -0.208488 -0.208451 -0.208412 -0.208373
+ -0.208333 -0.208294 -0.208256 -0.208219 -0.208183 -0.208145
+ -0.208107 -0.208066 -0.208029 -0.207993 -0.207959 -0.207923
+ -0.207883 -0.207838 -0.207789 -0.207747 -0.20771 -0.207675
+ -0.207642 -0.207605 -0.207568 -0.207531 -0.207494 -0.207457
+ -0.20742 -0.207383 -0.207346 -0.207308 -0.207271 -0.207233
+ -0.207196 -0.207158 -0.207121 -0.207084 -0.207046 -0.207009
+ -0.206972 -0.206935 -0.206898 -0.206861 -0.206823 -0.206786
+ -0.206749 -0.206712 -0.206675 -0.206638 -0.2066 -0.206563
+ -0.206526 -0.206489 -0.206452 -0.206415 -0.203384 -0.20015
+ -0.196872 -0.205024 -0.210727 -0.206779 -0.0685263 0.586138
+ 1.4665 2.22945 2.77554 3.076 3.24926 3.34515 3.40164 3.43006
+ 3.43713 3.43075 3.42886 3.4384 3.46567 3.49025 3.51287 3.53821
+ 3.57841 3.39846 2.80753 2.22947 1.7549 1.30429 0.707786
+ 0.303206 0.131352 0.0671706 0.0429955 0.032461 0.0257161
+ 0.0239521 0.0217397 0.0179705 0.0138745 0.0102813 0.00749643
+ 0.0054328 0.00386817 0.0027004 0.00189442 0.00135552 0.000954715
+ 0.000659981 0.000453435 0.000313993 0.000231347 0.000159665
+ 0.000108122 7.10528e-05 4.50233e-05 2.77892e-05 1.62765e-05
+ 8.9893e-06 4.5471e-06 1.54614e-06 -1.6542e-07 -8.68508e-07
+ -1.04369e-06 -9.63086e-07 -8.44294e-07 -6.57339e-07 -7.35885e-07
+ -9.80056e-07 -1.39772e-06 -2.10199e-06 -1.37474e-06 6.13269e-07
+ 3.3028e-06 4.60941e-06 4.91053e-06 4.14186e-06 2.45258e-06
+ -8.7388e-09 -3.59647e-07 -7.10554e-07 -1.06146e-06 -1.41237e-06
+ -1.76328e-06 -1.63073e-06 -1.34534e-06 -1.05995e-06 -7.74561e-07
+ -4.8917e-07 -2.95733e-07 -2.16326e-07 -1.3692e-07 -5.75135e-08
+ 2.18929e-08 1.01299e-07 1.80706e-07 2.60112e-07 3.39519e-07
+ 4.18925e-07 4.98332e-07 4.83984e-07 4.4901e-07 4.14035e-07
+ 3.79061e-07 3.44087e-07 3.09112e-07 2.74138e-07 2.39163e-07
+ 2.04189e-07 1.69215e-07 1.26002e-07 4.83213e-08 -2.9359e-08
+ -1.07039e-07 -1.8472e-07 -2.624e-07 -3.4008e-07 -4.1776e-07
+ -4.95441e-07
+}
+v26 set {
+ 7.10441e-10 0.000309731 -0.000308186 -0.001694 -0.00360784
+ 8.40909e-05 0.00203175 0.0012896 0.000596548 0.000277191
+ 0.000161134 0.000120439 8.4915e-05 9.49929e-05 6.18812e-05
+ 1.65433e-05 1.89682e-05 3.97578e-05 4.95446e-05 0.000225325
+ 0.000214579 -0.00230134 -0.000451102 0.00997237 0.0341443
+ 0.0449314 0.0424411 0.0341996 0.0315315 0.0308892 0.0291614
+ 0.024365 0.0190282 0.0188976 0.017238 0.0138526 0.0105645
+ 0.00778548 0.00561753 0.0039871 0.00279554 0.00194075 0.0013468
+ 0.000934775 0.000664723 0.000498911 0.000377384 0.000254183
+ 0.000163421 0.000120773 9.65058e-05 7.22384e-05 5.60316e-05
+ 4.14549e-05 2.79516e-05 2.57096e-05 2.34677e-05 2.12257e-05
+ 1.89837e-05 1.67417e-05 1.46737e-05 1.27228e-05 1.07719e-05
+ 8.82099e-06 6.87009e-06 5.0896e-06 4.71705e-06 4.34451e-06
+ 3.97196e-06 3.59941e-06 3.22686e-06 2.85431e-06 2.48176e-06
+ 2.10921e-06 1.73666e-06 1.36411e-06 1.02855e-06 9.42931e-07
+ 8.57316e-07 7.71701e-07 6.86086e-07 6.00471e-07 5.14856e-07
+ 4.29241e-07 3.43626e-07 2.58011e-07 1.72396e-07 9.85409e-08
+ 9.14091e-08 8.42773e-08 7.71456e-08 7.00138e-08 6.2882e-08
+ 5.57503e-08 4.86185e-08 4.14867e-08 3.4355e-08 2.72232e-08
+ 2.05821e-08 1.63235e-08 1.2065e-08 7.80643e-09 3.54786e-09
+ -7.10696e-10 -4.96926e-09 -9.22782e-09 -1.34864e-08 -1.77449e-08
+ -2.20035e-08 -2.62621e-08 -3.05206e-08 -3.47792e-08 -3.90378e-08
+ -4.32963e-08 -4.75549e-08 -5.18134e-08 -5.6072e-08 -6.03306e-08
+ -6.45891e-08 -6.88477e-08 -8.76373e-06 0.000131607 -0.00021685
+ -0.000433027 0.00047234 0.000211593 -0.000189601 3.2492e-05
+ 0.000575955 7.72235e-05 -0.000285172 -0.000242061 -0.000135112
+ -3.50117e-05 -2.75868e-05 5.48974e-05 1.80604e-07 5.48911e-05
+ 3.97478e-05 0.000192909 0.000297932 0.00402253 -0.0122366
+ -0.047853 -0.0963082 -0.108071 -0.0567275 -0.0239271 -0.0178628
+ -0.0233027 -0.031853 -0.0400843 -0.0482725 -0.0576154 -0.0627218
+ -0.0511236 -0.0279524 -0.0150986 -0.00931091 -0.00652876
+ -0.00479286 -0.00344346 -0.00249578 -0.0019532 -0.00157977
+ -0.00131848 -0.00111251 -0.000939229 -0.000797445 -0.000708384
+ -0.000630452 -0.000539722 -0.000508862 -0.000480596 -0.000439484
+ -0.000407217 -0.000363866 -0.000329506 -0.000318642 -0.000307362
+ -0.000286511 -0.000266253 -0.000242943 -0.000218107 -0.000204661
+ -0.00020241 -0.000194435 -0.000185062 -0.000173042 -0.000160549
+ -0.000151407 -0.000145626 -0.000145976 -0.000147342 -0.000145288
+ -0.000137979 -0.000124481 -0.000123218 -0.000127453 -0.000139006
+ -0.000145486 -0.000129764 -9.82749e-05 -4.72596e-05 -3.08671e-05
+ -3.28834e-05 -4.52254e-05 -6.25389e-05 -6.32516e-05 -6.39643e-05
+ -6.4677e-05 -6.53897e-05 -6.61023e-05 -6.6815e-05 -6.75277e-05
+ -6.61005e-05 -6.45173e-05 -6.29341e-05 -6.13509e-05 -5.97676e-05
+ -5.81844e-05 -5.66012e-05 -5.54231e-05 -5.4455e-05 -5.3487e-05
+ -5.25189e-05 -5.15508e-05 -5.05828e-05 -4.96147e-05 -4.86466e-05
+ -4.76785e-05 -4.67105e-05 -4.57424e-05 -4.47743e-05 -4.38063e-05
+ -4.28382e-05 -4.18821e-05 -4.10211e-05 -4.016e-05 -3.9299e-05
+ -3.8438e-05 4.29885e-05 5.14113e-05 -0.000127986 -0.000611463
+ -0.000149428 0.000882394 0.00297059 -0.00405825 -0.00591067
+ -0.00546997 -0.00158744 0.00190677 0.00298403 0.00268595
+ 0.00196161 0.00130289 0.000783347 0.000520683 0.000565306
+ 0.00053419 -0.00224696 -0.000920818 0.0132755 0.0322504
+ 0.0442808 0.0638615 0.0701007 0.0539356 0.0247771 0.056244
+ 0.294266 0.831368 1.45424 2.02898 2.54559 2.9937 3.35333
+ 3.72609 4.06363 4.32789 4.52413 4.66504 4.7652 4.83637 4.88631
+ 4.92109 4.94464 4.96046 4.97218 4.98079 4.98679 4.99076
+ 4.99361 4.99555 4.99686 4.99783 4.99853 4.99902 4.99936
+ 4.99959 4.99973 4.99983 4.9999 4.99993 4.99996 4.99998 5
+ 5.00001 5 4.99999 4.99997 4.99994 4.99993 4.99994 4.99996
+ 4.99999 5.00004 5.00006 5.00005 5.00003 5.00002 5.00001
+ 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+
+}
+v27 set {
+ 5 4.99984 4.99796 4.99478 4.9889 4.98738 4.98896 4.99087
+ 4.99262 4.99419 4.99552 4.99659 4.99743 4.99807 4.99855
+ 4.9989 4.99894 4.99908 4.99935 5.00001 5.0007 5.00132 5.00032
+ 4.99976 5.00134 5.00339 5.00315 5.00157 5.00091 5.00058
+ 5.00012 4.99944 4.99886 4.9994 4.99934 4.99899 4.99876 4.99868
+ 4.99872 4.99883 4.99898 4.99914 4.9993 4.99944 4.99956 4.99967
+ 4.99976 4.99982 4.99986 4.9999 4.99993 4.99997 4.99997 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00009 5.00028 5.00015 4.99983
+ 5.00036 4.99996 4.99834 4.99783 5.00383 5.00734 5.00387
+ 5.00058 4.99893 4.99836 4.99832 4.99854 4.99873 4.99905
+ 4.99927 4.99952 4.99969 4.99834 4.99536 4.99163 4.99073
+ 5.0053 5.03631 5.03103 4.9008 4.62503 4.21887 3.70902 3.09967
+ 2.35791 1.41912 0.519675 0.210458 0.131362 0.0980819 0.0708209
+ 0.0471701 0.0323272 0.0253535 0.0199144 0.0152615 0.0117228
+ 0.00917696 0.00738117 0.00609292 0.00512664 0.00436184 0.0037961
+ 0.00331639 0.00289006 0.0025477 0.00226529 0.00202925 0.00182793
+ 0.00165474 0.00150531 0.00137529 0.00125983 0.00115603 0.00106455
+ 0.000982977 0.000911255 0.000846819 0.000790092 0.000738698
+ 0.000692816 0.00065107 0.000613595 0.000579642 0.000548935
+ 0.00052106 0.000495598 0.000472174 0.000450849 0.000431118
+ 0.000412667 0.000395868 0.000381319 0.000368487 0.000357327
+ 0.000344212 0.000330334 0.00031622 0.000303298 0.000295809
+ 0.00028832 0.000280831 0.000273342 0.000265853 0.000258364
+ 0.000250875 0.000245118 0.000239488 0.000233857 0.000228227
+ 0.000222596 0.000216966 0.000211336 0.000207047 0.000203455
+ 0.000199863 0.00019627 0.000192678 0.000189085 0.000185493
+ 0.0001819 0.000178308 0.000174716 0.000171123 0.000167531
+ 0.000163938 0.000160346 0.000156835 0.000153973 0.00015111
+ 0.000148248 0.000145385 0.000296579 -3.96718e-05 -0.000449085
+ 0.000323433 0.000750086 0.000268264 0.000149028 -0.000100249
+ 7.00956e-05 0.00012605 0.00022592 0.000193036 0.000120453
+ 8.07865e-05 7.65771e-05 -3.27828e-05 0.000116759 0.000169498
+ 0.000409804 0.000414965 0.00092323 -0.00590633 -0.0175477
+ -0.032433 -0.0559842 -0.0820373 0.0688484 0.626629 1.32929
+ 2.01657 2.60925 3.12329 3.38952 3.14128 2.38463 1.23802
+ 0.316019 0.107832 0.0694707 0.051837 0.035247 0.0209999
+ 0.0116618 0.00967674 0.00789182 0.00574566 0.00386872 0.00258612
+ 0.00167126 0.00104169 0.000641093 0.000401246 0.000277928
+ 0.000171775 0.000102266 5.89376e-05 3.29258e-05 1.80463e-05
+ 1.0057e-05 6.4571e-06 5.10093e-06 4.06791e-06 3.62716e-06
+ 3.63321e-06 3.99625e-06 4.64368e-06 5.20886e-06 4.77728e-06
+ 3.23919e-06 1.14113e-06 -1.29416e-06 -4.15607e-06 -1.88532e-06
+ 5.24411e-06 1.38678e-05 1.28823e-05 3.6758e-06 -2.52285e-06
+ -3.97133e-06 -4.03071e-06 -3.37154e-06 -2.71238e-06 -2.05321e-06
+ -1.39404e-06 -7.34872e-07 -3.73325e-07 -1.05873e-07 1.61578e-07
+ 4.2903e-07 6.96482e-07 8.18468e-07 7.60065e-07 7.01662e-07
+ 6.43258e-07 5.84855e-07 5.26452e-07 4.68049e-07 4.09646e-07
+ 3.51243e-07 2.9284e-07 2.34437e-07 1.71213e-07 1.06928e-07
+ 4.2644e-08 -2.16403e-08 -8.59247e-08 -1.50209e-07 -2.14493e-07
+ -2.78778e-07 -3.43062e-07 -4.07346e-07 -4.55065e-07 -4.3348e-07
+ -4.11896e-07 -3.90311e-07 -3.68726e-07 -3.47141e-07 -3.25556e-07
+ -3.03971e-07 -2.82386e-07
+}
+v28 set {
+ 0.368163 0.361756 0.327463 0.269513 0.149476 0.0805716 0.0501146
+ 0.03403 0.0230886 0.0160474 0.0116071 0.00870013 0.00679614
+ 0.00542384 0.00432512 0.00340653 -0.00129719 -0.00399429
+ -0.00318719 0.00443085 0.0150156 0.0334147 0.0132288 -0.0189751
+ -0.0508377 -0.0252174 -0.0142489 -0.00675908 -0.0038653
+ -0.00243423 -0.00168891 -0.00120901 -0.000900426 -0.000685575
+ -0.000557595 -0.000457268 -0.000377427 -0.000315269 -0.000266613
+ -0.000228397 -0.000198283 -0.000174248 -0.000154886 -0.00013892
+ -0.000125864 -0.000115189 -0.000105841 -9.66611e-05 -8.84262e-05
+ -8.23872e-05 -7.74668e-05 -7.25463e-05 -6.79992e-05 -6.35276e-05
+ -5.92413e-05 -5.68994e-05 -5.45574e-05 -5.22154e-05 -4.98735e-05
+ -4.75315e-05 -4.54981e-05 -4.36726e-05 -4.18471e-05 -4.00216e-05
+ -3.81961e-05 -3.64559e-05 -3.54209e-05 -3.43858e-05 -3.33508e-05
+ -3.23157e-05 -3.12807e-05 -3.02456e-05 -2.92105e-05 -2.81755e-05
+ -2.71404e-05 -2.61054e-05 -2.51232e-05 -2.44984e-05 -2.38736e-05
+ -2.32487e-05 -2.26239e-05 -2.19991e-05 -2.13742e-05 -2.07494e-05
+ -2.01246e-05 -1.94998e-05 -1.88749e-05 -1.82865e-05 -1.79044e-05
+ -1.75224e-05 -1.71403e-05 -1.67582e-05 -1.63762e-05 -1.59941e-05
+ -1.56121e-05 -1.523e-05 -1.4848e-05 -1.44659e-05 -1.41138e-05
+ -1.39075e-05 -1.37011e-05 -1.34947e-05 -1.32883e-05 -1.30819e-05
+ -1.28755e-05 -1.26691e-05 -1.24627e-05 -1.22563e-05 -1.205e-05
+ -1.18436e-05 -1.16372e-05 -1.14308e-05 -1.12244e-05 -1.1018e-05
+ -1.08116e-05 -1.06052e-05 -1.03988e-05 -1.01924e-05 -9.98605e-06
+ -9.77966e-06 -2.85319e-05 0.00281092 0.00180106 -0.000981083
+ 0.00551926 -0.00119763 -0.0295069 -0.0367677 0.064749 0.119022
+ 0.0882007 0.0552062 0.03418 0.0223243 0.015545 0.011949
+ 0.00757134 0.00667655 0.00583243 0.00644443 0.00650959 -0.0302575
+ -0.0437806 -0.0355466 0.0381776 0.282109 0.674178 1.07582
+ 1.45189 1.789 2.08649 2.34663 2.57245 2.81211 3.04778 3.2523
+ 3.45877 3.65593 3.83396 3.9923 4.13368 4.25864 4.36719 4.46064
+ 4.54086 4.60962 4.66835 4.71838 4.76094 4.79716 4.82796
+ 4.85413 4.87634 4.89518 4.91116 4.92476 4.93631 4.94608
+ 4.95434 4.9613 4.96715 4.97211 4.97638 4.98001 4.98312 4.98571
+ 4.98795 4.98979 4.99138 4.99269 4.99381 4.99474 4.99551
+ 4.99615 4.99668 4.99713 4.99752 4.99783 4.99811 4.99836
+ 4.99858 4.99873 4.99884 4.99892 4.999 4.99907 4.99912 4.99916
+ 4.99921 4.99926 4.99932 4.99937 4.99942 4.99948 4.99953
+ 4.99956 4.99958 4.99961 4.99963 4.99966 4.99968 4.99971
+ 4.99972 4.99973 4.99974 4.99975 4.99976 4.99977 4.99978
+ 4.99979 4.9998 4.9998 4.99981 4.99982 4.99983 4.99984 4.99985
+ 4.99986 4.99986 4.99987 4.99987 5.00498 5.00354 4.99359
+ 4.98981 5.00498 5.00099 5.00041 5.00022 5.00015 5.00012
+ 5.0001 5.00008 5.00005 5.00003 5 4.99431 4.99459 4.99591
+ 5.00087 5.01029 5.03935 4.92784 4.51643 3.78356 2.68745
+ 1.43417 0.583128 0.205094 0.0777337 0.0391566 0.02723 0.023883
+ 0.018808 0.010165 0.00254623 -0.00377463 -0.0038097 0.00144145
+ 0.00267231 0.00193045 0.00144538 0.00121758 0.00112893 0.00109424
+ 0.0010226 0.000948072 0.000882573 0.000826996 0.000776391
+ 0.000729719 0.000686499 0.000647333 0.000610108 0.000575631
+ 0.000545069 0.000515485 0.000488514 0.000465316 0.000443215
+ 0.000422454 0.00040292 0.00038488 0.000368472 0.000353628
+ 0.000339643 0.000326197 0.000313483 0.000302884 0.000294038
+ 0.000284003 0.000270941 0.000254925 0.000246511 0.000244089
+ 0.000245538 0.000242099 0.000235728 0.000227482 0.000218001
+ 0.000207257 0.000202127 0.000196997 0.000191868 0.000186738
+ 0.000181608 0.00017758 0.000173899 0.000170219 0.000166538
+ 0.000162857 0.000159576 0.00015679 0.000154005 0.000151219
+ 0.000148433 0.000145647 0.000142861 0.000140076 0.00013729
+ 0.000134504 0.000131718 0.000129603 0.000127635 0.000125668
+ 0.0001237 0.000121732 0.000119765 0.000117797 0.000115829
+ 0.000113862 0.000111894 0.000109993 0.000108372 0.000106751
+ 0.00010513 0.000103509 0.000101887 0.000100266 9.86449e-05
+ 9.70237e-05
+}
+v29 set {
+ 5 4.99899 4.99654 4.99327 4.9863 4.98954 4.99212 4.99378
+ 4.9951 4.99624 4.99715 4.99786 4.99839 4.99879 4.99909 4.99931
+ 4.99922 4.99933 4.99971 5.00064 5.00084 5.00123 4.99865
+ 4.99853 4.99983 5.00457 5.00242 5.00105 5.00062 5.00042
+ 4.99971 4.9994 4.9992 4.9996 4.99955 4.99932 4.99918 4.99915
+ 4.99919 4.99927 4.99937 4.99948 4.99957 4.99966 4.99974
+ 4.9998 4.99985 4.99989 4.99992 4.99993 4.99994 4.99994 4.99996
+ 4.99998 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 4.9997 4.99998 4.99954 4.99963
+ 5.00059 4.99945 4.99732 4.99957 5.00919 5.00558 5.00033
+ 4.99851 4.9983 4.99854 4.99871 4.99928 4.99914 4.99939 4.99952
+ 4.9998 4.99976 4.99744 4.99598 4.99478 4.99806 5.01911 5.04602
+ 5.05469 5.01317 4.89484 4.69655 4.42036 4.06069 3.60793
+ 3.12531 2.72975 2.45187 2.25081 2.09841 1.98509 1.90211
+ 1.84084 1.79411 1.7574 1.72763 1.70283 1.68188 1.66389 1.64823
+ 1.63438 1.62201 1.61088 1.60081 1.59163 1.58323 1.57549
+ 1.56835 1.56173 1.55558 1.54985 1.54451 1.53951 1.53479
+ 1.53035 1.52615 1.5222 1.51845 1.5149 1.51153 1.50834 1.50529
+ 1.5024 1.49964 1.497 1.49449 1.49208 1.48977 1.48755 1.48542
+ 1.48336 1.48138 1.47948 1.47765 1.4759 1.47419 1.47255 1.47096
+ 1.46949 1.46823 1.46696 1.4657 1.46444 1.46317 1.46191 1.46065
+ 1.45956 1.4585 1.45743 1.45636 1.45529 1.45422 1.45315 1.45226
+ 1.45145 1.45064 1.44983 1.44902 1.44821 1.4474 1.44659 1.44579
+ 1.44498 1.44417 1.44336 1.44255 1.44174 1.44094 1.44019
+ 1.43944 1.43868 1.43793 1.43765 1.43679 1.43515 1.43405
+ 1.43478 1.43387 1.43345 1.43184 1.43086 1.43021 1.43003
+ 1.42988 1.42944 1.42883 1.42818 1.42702 1.42642 1.42595
+ 1.42586 1.42616 1.42783 1.41733 1.38106 1.30738 1.3877 2.09819
+ 3.05285 3.58059 3.77601 3.87609 4.02557 4.24887 4.4608 4.60411
+ 4.72109 4.8255 4.90465 4.97379 5.01253 5.01532 5.01239 5.0092
+ 5.00665 5.00474 5.00333 5.00232 5.00163 5.00117 5.00082
+ 5.00057 5.00039 5.00027 5.00019 5.00013 5.00009 5.00006
+ 5.00004 5.00003 5.00002 5.00001 5.00001 5 5 5 4.99998 4.99995
+ 4.99992 4.99996 5.00005 5.00012 5.00008 4.99996 4.9999 4.99985
+ 4.99986 4.99997 5.00021 5.0003 5.00024 5.00009 5.00007 5.00005
+ 5.00003 5.00001 4.99998 4.99998 4.99998 4.99999 4.99999
+ 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998
+}
+v30 set {
+ 7.10441e-10 5.70385e-05 0.000226143 0.000131916 -0.000887764
+ -8.01837e-05 -3.49653e-05 9.40039e-05 0.000118663 0.000108025
+ 8.6059e-05 6.33268e-05 4.99295e-05 3.16843e-05 3.60692e-05
+ 2.07572e-05 -8.6375e-05 3.44583e-05 8.07397e-05 0.000196296
+ 0.000115615 -7.12768e-05 -0.000129812 -4.18679e-05 7.94364e-05
+ 0.000182034 -5.41226e-05 -0.000451819 -0.000713937 -0.00129863
+ -0.00262186 -0.00213417 -0.00133767 0.000775698 0.000969902
+ 0.000549281 0.000280946 0.000140321 8.6919e-05 7.22446e-05
+ 6.5631e-05 6.45263e-05 6.63087e-05 7.17391e-05 7.59042e-05
+ 7.59172e-05 7.03353e-05 6.33558e-05 5.31136e-05 4.64278e-05
+ 4.40594e-05 4.16909e-05 4.05674e-05 3.96957e-05 3.87875e-05
+ 3.74977e-05 3.62079e-05 3.49181e-05 3.36283e-05 3.23385e-05
+ 3.12427e-05 3.02775e-05 2.93124e-05 2.83472e-05 2.7382e-05
+ 2.64613e-05 2.59077e-05 2.5354e-05 2.48004e-05 2.42468e-05
+ 2.36931e-05 2.31395e-05 2.25859e-05 2.20322e-05 2.14786e-05
+ 2.0925e-05 2.03916e-05 1.9995e-05 1.95984e-05 1.92019e-05
+ 1.88053e-05 1.84087e-05 1.80122e-05 1.76156e-05 1.7219e-05
+ 1.68225e-05 1.64259e-05 1.6051e-05 1.57991e-05 1.55471e-05
+ 1.52952e-05 1.50433e-05 1.47913e-05 1.45394e-05 1.42875e-05
+ 1.40356e-05 1.37836e-05 1.35317e-05 1.32978e-05 1.31513e-05
+ 1.30048e-05 1.28583e-05 1.27118e-05 1.25653e-05 1.24188e-05
+ 1.22724e-05 1.21259e-05 1.19794e-05 1.18329e-05 1.16864e-05
+ 1.15399e-05 1.13934e-05 1.12469e-05 1.11005e-05 1.0954e-05
+ 1.08075e-05 1.0661e-05 1.05145e-05 1.0368e-05 1.02215e-05
+ 1.76447e-05 7.21516e-05 -3.59786e-05 -0.000159618 0.000156236
+ 0.000135106 -0.000336402 -0.000302283 0.000699323 0.000473866
+ -0.000156146 -0.000225625 -0.000123592 -3.78116e-05 8.47472e-06
+ 2.43387e-06 -7.44762e-05 7.80111e-05 9.43608e-05 0.000170159
+ 8.83919e-05 -0.00018802 -0.000373512 -0.000390597 0.000156875
+ 0.0032343 0.00776304 -0.000566905 -0.00760695 -0.0159226
+ -0.0245989 -0.0331402 -0.0100902 0.067837 0.266702 0.910818
+ 1.82282 2.69714 3.43247 3.98325 4.32893 4.51529 4.67087
+ 4.79288 4.87574 4.92797 4.95902 4.97655 4.98622 4.99195
+ 4.99526 4.99735 4.9991 4.99974 4.99982 4.99974 4.99961 4.9995
+ 4.99943 4.9994 4.9994 4.99942 4.99944 4.99948 4.99952 4.99956
+ 4.99961 4.99965 4.9997 4.99974 4.99977 4.99981 4.99983 4.99986
+ 4.99988 4.9999 4.99991 4.99992 4.99993 4.99994 4.99995 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999 4.99999
+ 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5
+ 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5.00019 4.99888 4.99663 4.99457 4.99902
+ 5.00229 5.00323 5.00302 5.0023 5.0015 5.00085 5.00041 5.00013
+ 4.99993 4.99979 4.99948 4.99954 4.99983 5.00055 5.00109
+ 5.00009 4.9987 4.998 4.99755 4.99676 4.99618 5.01091 5.05272
+ 5.04156 4.80112 4.27692 3.42343 2.23953 0.967179 0.429813
+ 0.540757 1.32991 2.32147 3.14903 3.78143 4.22325 4.47978
+ 4.59448 4.69875 4.79798 4.87419 4.92339 4.95249 4.97174
+ 4.98408 4.99124 4.99478 4.99729 4.99868 4.9992 4.99941 4.99947
+ 4.99946 4.99943 4.9994 4.99939 4.9994 4.99942 4.99946 4.99951
+ 4.99956 4.99961 4.99967 4.99973 4.99977 4.9998 4.99981 4.99983
+ 4.99984 4.99987 4.99992 5.00001 5.00005 5.00001 4.99994
+ 4.99995 4.99995 4.99996 4.99996 4.99996 4.99997 4.99997
+ 4.99997 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99998
+}
+v31 set {
+ 1.8179e-09 -5.28841e-06 -1.44913e-05 -3.62932e-05 -9.75719e-05
+ 0.000141781 3.73396e-05 -1.65603e-05 -1.5271e-05 -6.73884e-06
+ 4.40157e-06 -4.85345e-06 -1.02964e-05 2.03126e-05 -1.89457e-05
+ -8.75564e-06 7.67422e-06 4.71103e-06 1.29798e-05 6.13469e-06
+ -1.14363e-05 -0.0394563 -0.0477298 -0.0622012 -0.0519225
+ 0.262499 0.943611 1.67052 2.31017 2.84028 3.28467 3.61582
+ 3.85887 4.13011 4.36511 4.54063 4.67013 4.76408 4.83263
+ 4.8825 4.91837 4.94373 4.96117 4.97318 4.98093 4.98562 4.98906
+ 4.99267 4.99539 4.99666 4.99731 4.99797 4.99844 4.99887
+ 4.99927 4.99933 4.99938 4.99944 4.99949 4.99955 4.9996 4.99965
+ 4.9997 4.99975 4.9998 4.99985 4.99986 4.99987 4.99989 4.9999
+ 4.99991 4.99992 4.99993 4.99995 4.99996 4.99997 4.99998
+ 4.99998 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99997 5.00002 5.00004 5.0001 5.0001 4.99987 5.00009
+ 5.00021 5.00002 5.00004 4.99988 5.00013 4.99993 5.00026
+ 4.99973 5 5.00006 5.00009 5.00004 5.00004 5.04854 4.82711
+ 4.04208 2.64155 0.838902 0.19014 0.0982549 0.0723197 0.0576863
+ 0.0427644 0.0301979 0.020146 0.0135728 0.00980358 0.00774482
+ 0.00586604 0.0036687 0.00211511 0.00121906 0.000647581 0.000828436
+ 0.00190938 0.00224254 0.00199956 0.00165488 0.00135612 0.00113715
+ 0.000984181 0.000877175 0.000789973 0.000741139 0.000689338
+ 0.000625676 0.000586082 0.000550152 0.000529573 0.000505606
+ 0.000482117 0.000460574 0.000441649 0.000424674 0.000408398
+ 0.000391914 0.000376272 0.000361487 0.000348181 0.000336045
+ 0.000324466 0.000313545 0.000303046 0.000293056 0.00028356
+ 0.000274586 0.000266155 0.000258279 0.000250938 0.000243789
+ 0.000236912 0.000230244 0.000224186 0.000219291 0.000215346
+ 0.000212468 0.000207291 0.000200862 0.00019368 0.000186767
+ 0.000183515 0.000180263 0.00017701 0.000173758 0.000170506
+ 0.000167253 0.000164001 0.000161164 0.000158357 0.00015555
+ 0.000152743 0.000149936 0.000147129 0.000144322 0.000142066
+ 0.000140096 0.000138127 0.000136157 0.000134187 0.000132218
+ 0.000130248 0.000128278 0.000126308 0.000124339 0.000122369
+ 0.000120399 0.000118429 0.00011646 0.000114527 0.000112892
+ 0.000111258 0.000109623 0.000107988 0.000103598 6.86052e-05
+ 3.337e-05 7.00783e-05 0.000218764 0.000221318 0.000118593
+ -0.000113962 5.78552e-05 9.42068e-05 0.000237037 0.000171302
+ 0.0001033 6.16066e-05 5.52908e-05 6.30233e-05 7.01897e-05
+ 8.48573e-05 0.000106859 8.37213e-05 -0.0391541 -0.047722
+ -0.0618454 -0.0169804 0.345725 1.03426 1.74825 2.37152 2.88737
+ 3.32173 3.66761 3.9707 4.17762 3.98832 3.30483 2.09737 0.710892
+ 0.148159 0.0707463 0.0555808 0.045618 0.0319116 0.0199589
+ 0.0133357 0.00898528 0.00586075 0.00375478 0.00245443 0.00156038
+ 0.000962344 0.000590953 0.000375107 0.000250243 0.00015882
+ 0.000100203 6.18122e-05 3.7372e-05 2.23009e-05 1.32569e-05
+ 8.29437e-06 5.72457e-06 3.96832e-06 2.98935e-06 2.59699e-06
+ 2.75024e-06 3.38689e-06 4.0453e-06 3.50095e-06 1.64988e-06
+ -3.84371e-07 -2.03828e-06 -3.46401e-06 -1.24301e-06 4.63458e-06
+ 1.14104e-05 1.02619e-05 2.15487e-06 -2.98487e-06 -3.67221e-06
+ -2.94279e-06 -2.58649e-06 -2.23019e-06 -1.87389e-06 -1.5176e-06
+ -1.1613e-06 -7.92127e-07 -4.18889e-07 -4.56502e-08 3.27588e-07
+ 7.00827e-07 8.79539e-07 8.17025e-07 7.5451e-07 6.91996e-07
+ 6.29481e-07 5.66966e-07 5.04452e-07 4.41937e-07 3.79422e-07
+ 3.16908e-07 2.54393e-07 1.90078e-07 1.25366e-07 6.0654e-08
+ -4.05776e-09 -6.87696e-08 -1.33481e-07 -1.98193e-07 -2.62905e-07
+ -3.27617e-07 -3.92329e-07 -4.40392e-07 -4.18802e-07 -3.97213e-07
+ -3.75624e-07 -3.54035e-07 -3.32446e-07 -3.10856e-07 -2.89267e-07
+ -2.67678e-07
+}
+v32 set {
+ 1.10294 1.10297 1.10291 1.10277 1.10259 1.10294 1.10313
+ 1.10306 1.10299 1.10296 1.10295 1.10295 1.10294 1.10294
+ 1.10294 1.10294 1.10294 1.10294 1.10294 1.10296 1.10296
+ 1.00547 0.998599 1.5201 2.49297 3.31258 3.73162 3.84757
+ 3.92505 4.02965 4.16599 4.30294 4.41541 4.52886 4.64414
+ 4.73865 4.81065 4.86391 4.90315 4.93188 4.95258 4.96726
+ 4.97738 4.98436 4.98888 4.99162 4.99363 4.99573 4.99731
+ 4.99804 4.99843 4.99881 4.99909 4.99934 4.99957 4.9996 4.99964
+ 4.99967 4.9997 4.99973 4.99977 4.9998 4.99983 4.99986 4.99988
+ 4.99991 4.99992 4.99992 4.99993 4.99994 4.99994 4.99995
+ 4.99996 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 5.00028 4.99988 4.99968
+ 5.00019 4.99987 5.00021 4.99973 4.99977 4.99996 4.99997
+ 5.0002 4.99957 5.00026 4.99947 5.00074 5.00003 4.99987 4.99979
+ 5.00008 4.99997 5.08794 5.05993 4.76875 3.99197 3.10174
+ 2.5197 2.21771 2.04 1.92235 1.83874 1.77592 1.72665 1.686
+ 1.65276 1.6286 1.61299 1.60039 1.58934 1.57954 1.57083 1.56306
+ 1.55604 1.54963 1.54375 1.53832 1.53331 1.52865 1.52432
+ 1.52026 1.51645 1.51287 1.50949 1.50629 1.50327 1.50039
+ 1.49766 1.49505 1.49257 1.49019 1.48792 1.48574 1.48365
+ 1.48164 1.47971 1.47784 1.47604 1.47431 1.47264 1.47102
+ 1.46945 1.46794 1.46647 1.46505 1.46367 1.46233 1.46103
+ 1.45976 1.45853 1.45733 1.45616 1.45502 1.45392 1.45284
+ 1.45179 1.45076 1.44975 1.4488 1.44795 1.44711 1.44626 1.44541
+ 1.44457 1.44372 1.44287 1.44212 1.44138 1.44063 1.43989
+ 1.43914 1.4384 1.43766 1.43701 1.43641 1.43581 1.43522 1.43462
+ 1.43402 1.43342 1.43282 1.43223 1.43163 1.43103 1.43043
+ 1.42984 1.42924 1.42865 1.42808 1.42752 1.42695 1.42639
+ 1.42584 1.42529 1.42472 1.42412 1.42365 1.42326 1.42304
+ 1.42162 1.42082 1.42032 1.42029 1.42026 1.41995 1.41947
+ 1.41894 1.41841 1.4179 1.41742 1.41699 1.41656 1.32097 1.30963
+ 1.78765 2.64656 3.35764 3.747 3.86589 3.94217 4.04185 4.18453
+ 4.3561 4.53439 4.68621 4.74905 4.77848 4.84629 4.91261 4.97541
+ 5.01284 5.01548 5.01248 5.00924 5.00666 5.00475 5.00334
+ 5.00234 5.00164 5.00118 5.00083 5.00058 5.0004 5.00028 5.00019
+ 5.00013 5.00009 5.00007 5.00004 5.00003 5.00002 5.00001
+ 5.00001 5.00001 5 5 4.99999 4.99995 4.99992 4.99996 5.00006
+ 5.00012 5.00009 4.99997 4.9999 4.99985 4.99986 4.99997 5.00021
+ 5.00031 5.00024 5.0001 5.00007 5.00005 5.00003 5.00001 4.99998
+ 4.99998 4.99999 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 4.99999 4.99999 4.99999 4.99998 4.99998 4.99998
+}
+v33 set {
+ 5 5.00012 5.00023 5.0003 4.99972 4.99988 4.99984 4.99991
+ 4.99996 4.99999 5.00008 5.00009 4.99986 5.00003 5.00007
+ 4.99995 4.9999 4.99997 5.00013 5.00014 5.00013 4.99701 4.99763
+ 4.99742 4.99998 5.02836 5.07262 4.96856 4.57267 3.85637
+ 2.79544 1.45942 0.408016 0.084885 0.0271375 0.0119294 0.00707546
+ 0.0051087 0.00373035 0.00264737 0.00186477 0.00130379 0.000915857
+ 0.000653121 0.000483893 0.000380852 0.000302362 0.000219498
+ 0.000154435 0.000121928 0.000104026 8.61242e-05 7.48526e-05
+ 6.49216e-05 5.56238e-05 5.29689e-05 5.03139e-05 4.7659e-05
+ 4.5004e-05 4.23491e-05 4.00356e-05 3.79522e-05 3.58687e-05
+ 3.37852e-05 3.17018e-05 2.97592e-05 2.89804e-05 2.82016e-05
+ 2.74228e-05 2.66441e-05 2.58653e-05 2.50865e-05 2.43077e-05
+ 2.35289e-05 2.27501e-05 2.19714e-05 2.12346e-05 2.07821e-05
+ 2.03295e-05 1.98769e-05 1.94244e-05 1.89718e-05 1.85192e-05
+ 1.80667e-05 1.76141e-05 1.71615e-05 1.6709e-05 1.62828e-05
+ 1.60061e-05 1.57294e-05 1.54527e-05 1.5176e-05 1.48993e-05
+ 1.46226e-05 1.43459e-05 1.40692e-05 1.37925e-05 1.35158e-05
+ 1.3262e-05 1.31191e-05 1.29761e-05 1.28332e-05 1.26903e-05
+ 1.25474e-05 1.24045e-05 1.22615e-05 1.21186e-05 1.19757e-05
+ 1.18328e-05 1.16898e-05 1.15469e-05 1.1404e-05 1.12611e-05
+ 1.11182e-05 1.09752e-05 1.08323e-05 1.06894e-05 1.05465e-05
+ 1.04036e-05 1.02606e-05 1.00185e-05 3.8343e-05 -3.06781e-05
+ -0.000111758 0.000111673 0.000130815 -0.000210491 -0.000231304
+ 0.000310226 0.000265303 3.0878e-05 -4.48405e-05 -1.2852e-05
+ -7.84469e-06 3.29986e-05 -1.23286e-05 -6.07871e-05 5.35082e-05
+ 7.69194e-05 0.000126221 6.57178e-05 0.00223349 -0.0148854
+ -0.0476636 -0.0491447 0.220125 1.11174 2.03988 2.90209 3.61069
+ 4.13554 4.50679 4.71501 4.83916 4.91027 4.95284 4.98086
+ 4.99151 4.98651 4.97113 4.95075 4.93102 4.93683 4.95457
+ 4.97071 4.98212 4.98948 4.99386 4.99636 4.99785 4.9987 4.99927
+ 4.99989 5.00014 5.00007 4.99988 4.99982 4.99976 4.99973
+ 4.99972 4.99972 4.99973 4.99974 4.99975 4.99977 4.99979
+ 4.99981 4.99984 4.99986 4.99988 4.99989 4.99991 4.99992
+ 4.99993 4.99994 4.99995 4.99996 4.99996 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999
+ 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5.00012 4.99946 4.99839 4.99733
+ 4.99948 5.00114 5.00158 5.00147 5.00113 5.00073 5.00043
+ 5.0002 5.00006 4.99995 4.99986 4.99973 4.99976 4.9999 5.00029
+ 5.00055 4.99704 4.99734 4.9972 5.00278 5.03354 5.07184 4.94057
+ 4.51936 3.75638 2.60982 1.23803 0.315016 0.0796102 0.0252894
+ 0.0165723 0.0827785 0.491298 1.40686 2.33436 3.1251 3.7691
+ 4.22201 4.49976 4.68115 4.80513 4.88509 4.93208 4.95861
+ 4.97579 4.98655 4.99268 4.99571 4.99771 4.99881 4.99929
+ 4.99954 4.99965 4.9997 4.99971 4.99971 4.99971 4.99971 4.99972
+ 4.99974 4.99976 4.99978 4.99981 4.99984 4.99987 4.99989
+ 4.99991 4.99991 4.99992 4.99992 4.99993 4.99997 5.00003
+ 5.00006 5.00004 5.00001 5 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99998 4.99998
+}
+v34 set {
+ 5 5.00207 5.00813 5.01486 5.00156 5.0018 4.99861 4.99844
+ 4.99888 4.9993 4.99956 4.99971 4.99979 4.99983 4.99987 4.99989
+ 4.99671 4.9974 4.99864 5.00131 5.00377 5.0021 5.00039 4.99993
+ 5.00004 5.0009 5.00109 4.99636 4.98617 4.96778 4.92047 4.89528
+ 4.91112 4.9559 4.98286 4.99369 4.99812 4.99951 4.99994 5.00014
+ 5.00008 4.99994 4.99984 4.99989 4.99998 5.00004 5.00004
+ 5.00006 5.00005 5.00001 4.99997 4.99992 4.99993 4.99994
+ 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996
+ 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 5.00131 5.00072 4.9977 4.99811
+ 5.00325 4.99647 4.98948 4.99459 5.00262 5.00276 5.00156
+ 5.00072 5.0003 5.00013 4.99995 4.99668 4.99775 4.99917 5.00173
+ 5.00386 5.00188 4.99888 4.99757 4.99951 5.01712 5.0557 5.07088
+ 5.07704 5.07758 5.06958 5.04223 5.03331 5.0279 5.03408 5.07611
+ 5.01911 4.68594 3.99152 2.92195 1.69878 0.809 0.344091 0.154663
+ 0.0788717 0.0467212 0.0336168 0.0280514 0.0254947 0.024173
+ 0.0223567 0.0220555 0.0271514 0.0295872 0.0296052 0.0283971
+ 0.0264726 0.0241813 0.0218244 0.0195349 0.017368 0.0152495
+ 0.013295 0.0115444 0.00996982 0.00857091 0.00733891 0.00627261
+ 0.0053494 0.00456316 0.00388373 0.00331073 0.00282181 0.00240991
+ 0.00206389 0.00177187 0.00152283 0.00131167 0.00112558 0.000954373
+ 0.000805726 0.00069326 0.000600991 0.000525743 0.00047355
+ 0.00044359 0.000434815 0.000436053 0.000402511 0.000368969
+ 0.000335427 0.000301886 0.000268344 0.000234802 0.00020126
+ 0.000184967 0.000169932 0.000154896 0.000139861 0.000124825
+ 0.00010979 9.47546e-05 8.67896e-05 8.24901e-05 7.81906e-05
+ 7.38911e-05 6.95915e-05 6.5292e-05 6.09925e-05 5.66929e-05
+ 5.23934e-05 4.80939e-05 4.37943e-05 3.94948e-05 3.51953e-05
+ 3.08957e-05 2.67968e-05 2.42936e-05 2.17904e-05 1.92872e-05
+ 1.6784e-05 0.00125927 -0.00794344 -0.0305499 -0.0621697
+ -0.0463796 -0.0224608 -0.00538381 0.00546086 0.0108675 0.012883
+ 0.0131787 0.0127271 0.0119702 0.0110398 0.0100635 0.00649617
+ 0.00489388 0.00545863 0.0098351 0.0167428 0.0126563 0.00697542
+ 0.00427027 0.00330002 0.00390774 0.00408999 -0.00259143
+ -0.0160578 -0.0451849 -0.0409651 0.1301 0.597429 1.3848
+ 2.63426 3.81272 4.51373 4.8412 4.98731 4.88165 4.37165 3.40034
+ 2.17681 1.12217 0.505129 0.219703 0.104992 0.0622333 0.0448317
+ 0.0355782 0.0311867 0.0293529 0.0274615 0.0288739 0.0307845
+ 0.0304909 0.029245 0.0273602 0.0251006 0.022697 0.0202765
+ 0.0179357 0.0157106 0.0136562 0.0117951 0.0101273 0.00865784
+ 0.00739394 0.00634364 0.00551356 0.00480538 0.00415747 0.00356084
+ 0.00297585 0.00236711 0.00181853 0.00160713 0.00169822 0.00166542
+ 0.00145504 0.00120252 0.00109259 0.000982658 0.00087273
+ 0.000762802 0.000652874 0.000584068 0.000528263 0.000472458
+ 0.000416653 0.000360848 0.000321155 0.000301442 0.000281729
+ 0.000262016 0.000242303 0.00022259 0.000202877 0.000183164
+ 0.000163451 0.000143738 0.000124025 0.000114582 0.000107399
+ 0.000100216 9.30332e-05 8.58502e-05 7.86672e-05 7.14841e-05
+ 6.43011e-05 5.7118e-05 4.9935e-05 4.35378e-05 4.04281e-05
+ 3.73184e-05 3.42088e-05 3.10991e-05 2.79894e-05 2.48798e-05
+ 2.17701e-05 1.86604e-05
+}
+v35 set {
+ 7.24585e-12 2.21843e-05 3.20014e-05 1.25076e-05 -2.44947e-05
+ 1.8425e-05 5.50546e-06 3.53025e-05 -1.07551e-05 -3.94383e-06
+ -2.27848e-06 -9.04789e-05 7.44215e-05 -2.7662e-05 0.000200038
+ -2.11998e-05 -2.09011e-05 2.37098e-05 2.18751e-05 -2.28422e-05
+ -6.23659e-05 3.58241e-05 1.76386e-05 -4.28311e-05 0.000355626
+ 0.00156903 0.00100999 -0.0085304 -0.02067 -0.0389485 -0.0651568
+ -0.128475 -0.314362 -0.406837 -0.421558 -0.421277 -0.418176
+ -0.414481 -0.410845 -0.407348 -0.403971 -0.400716 -0.397582
+ -0.394563 -0.391658 -0.388866 -0.386178 -0.383585 -0.381094
+ -0.378789 -0.376569 -0.37435 -0.372256 -0.370188 -0.36815
+ -0.366422 -0.364694 -0.362967 -0.361239 -0.359511 -0.357888
+ -0.356334 -0.354781 -0.353227 -0.351674 -0.350152 -0.348888
+ -0.347625 -0.346361 -0.345098 -0.343834 -0.342571 -0.341307
+ -0.340044 -0.33878 -0.337517 -0.336279 -0.335215 -0.334152
+ -0.333088 -0.332024 -0.330961 -0.329897 -0.328833 -0.32777
+ -0.326706 -0.325642 -0.324601 -0.323683 -0.322766 -0.321849
+ -0.320932 -0.320014 -0.319097 -0.31818 -0.317263 -0.316345
+ -0.315428 -0.314545 -0.313825 -0.313106 -0.312387 -0.311667
+ -0.310948 -0.310228 -0.309509 -0.308789 -0.30807 -0.307351
+ -0.306631 -0.305912 -0.305192 -0.304473 -0.303754 -0.303034
+ -0.302315 -0.301595 -0.300876 -0.300157 -0.299437 -0.298716
+ -0.29798 -0.297329 -0.296691 -0.295837 -0.29516 -0.294725
+ -0.294044 -0.292917 -0.292351 -0.291965 -0.291365 -0.290687
+ -0.290027 -0.289376 -0.288772 -0.288193 -0.287505 -0.286892
+ -0.28626 -0.285714 -0.284545 -0.289246 -0.298717 -0.298492
+ -0.214163 0.181451 0.0749974 0.0454707 0.0292987 0.0196837
+ 0.0124119 0.00884715 0.00527181 0.00585821 0.0296361 0.169856
+ 0.361207 0.538856 0.67469 0.685933 0.392802 0.17772 0.0813085
+ 0.0424601 0.0246654 0.0175258 0.0144256 0.0129859 0.012205
+ 0.0112846 0.010933 0.0134813 0.0147254 0.0147981 0.0142156
+ 0.0132732 0.0121355 0.0109587 0.00981238 0.00872731 0.00767007
+ 0.00669346 0.00581341 0.00502167 0.00431819 0.00369842 0.00316168
+ 0.00269663 0.00230035 0.00195801 0.00166928 0.00142286 0.00121522
+ 0.00104072 0.000893384 0.000767675 0.000661268 0.000567659
+ 0.000481766 0.000407101 0.000350044 0.000302721 0.000263424
+ 0.000236813 0.00022199 0.000218182 0.000219548 0.0002027
+ 0.000185853 0.000169006 0.000152158 0.000135311 0.000118463
+ 0.000101616 9.33782e-05 8.57685e-05 7.81588e-05 7.0549e-05
+ 6.29393e-05 5.53296e-05 4.77199e-05 4.36954e-05 4.15296e-05
+ 3.93637e-05 3.71978e-05 3.50319e-05 3.28661e-05 3.07002e-05
+ 2.85343e-05 2.63685e-05 2.42026e-05 2.20367e-05 1.98709e-05
+ 1.7705e-05 1.55391e-05 1.34772e-05 1.22416e-05 1.10061e-05
+ 9.77055e-06 8.535e-06 0.000631271 -0.00362586 -0.0146235
+ -0.0308486 -0.0237466 -0.0117522 -0.00304171 0.00251033
+ 0.00531986 0.0063897 0.00657351 0.00636494 0.00599705 0.00553442
+ 0.00505994 0.00330925 0.00246671 0.0027006 0.00473161 0.00830333
+ 0.00649147 0.00356815 0.00217448 0.00187579 0.00270447 0.00219543
+ -0.00546118 -0.0179576 -0.0445306 -0.0649309 0.0197935 0.473629
+ 0.87268 0.269542 0.0086094 0.0844602 0.606456 1.04929 0.906014
+ 0.916205 0.919425 0.872867 0.556244 0.262457 0.11838 0.0571226
+ 0.0333451 0.0237133 0.0185096 0.0159617 0.0148663 0.0138683
+ 0.0144081 0.0153797 0.0152551 0.0146487 0.0137192 0.0125973
+ 0.0113996 0.0101903 0.00901851 0.00790495 0.00687502 0.00593994
+ 0.00510092 0.00436111 0.00372439 0.0031945 0.00277537 0.00241888
+ 0.002095 0.00179943 0.00150419 0.00119264 0.00090934 0.000802394
+ 0.000852816 0.000838368 0.000730842 0.000601028 0.000546616
+ 0.000492205 0.000437793 0.000383381 0.000328969 0.00029454
+ 0.000266428 0.000238317 0.000210205 0.000182093 0.000162091
+ 0.000152145 0.000142198 0.000132252 0.000122306 0.000112359
+ 0.000102413 9.24665e-05 8.25201e-05 7.25738e-05 6.26274e-05
+ 5.78553e-05 5.42216e-05 5.05878e-05 4.69541e-05 4.33204e-05
+ 3.96867e-05 3.60529e-05 3.24192e-05 2.87855e-05 2.51518e-05
+ 2.19153e-05 2.03406e-05 1.8766e-05 1.71913e-05 1.56167e-05
+ 1.4042e-05 1.24674e-05 1.08927e-05 9.31806e-06
+}
+v36 set {
+ 5 5.01426 5.02852 5.01923 4.77685 4.56471 4.52338 4.56813
+ 4.63122 4.693 4.74776 4.79385 4.83258 4.86358 4.88918 4.91021
+ 4.90553 4.89733 4.89554 4.91953 5.00757 5.07101 5.06318
+ 5.05241 5.05535 5.08042 5.07251 4.90973 4.56136 3.98637
+ 3.237 2.67216 2.33678 2.13529 2.00544 1.91429 1.84638 1.79461
+ 1.75338 1.71958 1.69175 1.6686 1.64918 1.63258 1.61836 1.60607
+ 1.59506 1.58483 1.57575 1.56847 1.56193 1.55538 1.54968
+ 1.54416 1.5388 1.53523 1.53165 1.52807 1.52449 1.52091 1.51771
+ 1.51477 1.51182 1.50888 1.50593 1.50309 1.50113 1.49917
+ 1.4972 1.49524 1.49328 1.49132 1.48935 1.48739 1.48543 1.48346
+ 1.48157 1.48012 1.47868 1.47724 1.47579 1.47435 1.47291
+ 1.47146 1.47002 1.46857 1.46713 1.46574 1.46462 1.4635 1.46238
+ 1.46126 1.46014 1.45902 1.4579 1.45678 1.45567 1.45455 1.45349
+ 1.45275 1.45201 1.45127 1.45053 1.44979 1.44905 1.44831
+ 1.44757 1.44683 1.44609 1.44535 1.44461 1.44387 1.44313
+ 1.44239 1.44165 1.44091 1.44017 1.43943 1.43869 1.43795
+ 1.43721 1.43874 1.43976 1.43619 1.43182 1.43726 1.43084
+ 1.42587 1.42383 1.42642 1.42728 1.42736 1.4271 1.42669 1.42621
+ 1.42569 1.41703 1.41244 1.41019 1.41199 1.41833 1.42502
+ 1.41504 1.37535 1.28381 1.44779 2.33713 3.25835 3.67554
+ 3.84975 4.01125 4.2253 4.45433 4.62215 4.74478 4.82998 4.8868
+ 4.92396 4.94768 4.96498 4.98537 5.0128 5.04467 5.06722 5.06535
+ 5.01475 4.91956 4.80647 4.7242 4.7059 4.73552 4.76379 4.81684
+ 4.87376 4.92276 4.96112 4.9884 5.0045 5.00999 5.00933 5.00619
+ 5.00384 5.00342 5.00373 5.00362 5.00309 5.00272 5.00239
+ 5.00204 5.00172 5.00146 5.00124 5.00105 5.00089 5.00076
+ 5.00065 5.00057 5.00048 5.00041 5.00034 5.00028 5.00023
+ 5.00019 5.00015 5.00015 5.00016 5.0002 5.00023 5.00021 5.00019
+ 5.00017 5.00015 5.00012 5.0001 5.00008 5.00007 5.00006 5.00005
+ 5.00004 5.00003 5.00002 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00062 4.99506 4.9835 4.96726 4.9728 4.97877
+ 4.98675 4.9966 5.00406 5.00679 5.00629 5.00561 5.00487 5.00429
+ 5.00384 5.002 5.00164 5.00229 5.00484 5.00769 5.00019 5.00242
+ 5.01319 5.0335 5.07265 5.10129 5.11485 5.12551 5.13953 5.16048
+ 5.18862 5.22811 5.25656 5.25627 5.19975 4.9139 4.24745 3.43732
+ 2.8202 2.43224 2.17409 2.01333 1.93951 1.94622 1.98861 2.02217
+ 2.05383 2.08376 2.11184 2.13793 2.16191 2.18267 2.20502
+ 2.22837 2.24958 2.26901 2.28648 2.302 2.31582 2.32802 2.33869
+ 2.34795 2.35596 2.36282 2.3687 2.37371 2.37797 2.38161 2.38476
+ 2.38743 2.3897 2.39168 2.39329 2.39463 2.39575 2.39671 2.39756
+ 2.39835 2.39907 2.39968 2.39999 2.4003 2.40061 2.40091 2.40122
+ 2.40142 2.40159 2.40176 2.40193 2.4021 2.40222 2.40228 2.40234
+ 2.4024 2.40247 2.40253 2.40259 2.40265 2.40271 2.40277 2.40284
+ 2.40287 2.40289 2.40291 2.40294 2.40296 2.40298 2.40301
+ 2.40303 2.40305 2.40308 2.4031 2.40311 2.40312 2.40313 2.40314
+ 2.40315 2.40316 2.40317 2.40318
+}
+v37 set {
+ 5 5.01732 5.03181 5.05944 5.12686 5.20725 5.28103 5.31254
+ 5.32901 5.33709 5.3408 5.34257 5.34311 5.34347 5.34386 5.34411
+ 5.3406 5.33484 5.32942 5.32904 5.33644 5.34869 5.35001 5.34882
+ 5.34758 5.34672 5.34599 5.34496 5.34364 5.34165 5.33712
+ 5.33502 5.3366 5.34067 5.34306 5.34398 5.34434 5.34442 5.34443
+ 5.34443 5.34441 5.34439 5.34437 5.34437 5.34438 5.34438
+ 5.34438 5.34438 5.34438 5.34437 5.34437 5.34436 5.34436
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.35377 5.35451
+ 5.34265 5.34488 5.35861 5.28622 4.90033 4.75027 4.89731
+ 4.97098 4.99293 4.99832 4.99909 4.99956 4.99858 4.99829
+ 4.9998 5.00035 5.0038 5.00989 5.00251 4.99438 4.9953 4.99761
+ 4.99985 5.00152 5.0011 5.00046 4.99996 4.99925 4.99862 4.99919
+ 4.99961 5.00048 5.00234 4.99654 4.98235 4.95936 4.83738
+ 4.53021 4.21004 4.00593 3.91207 3.88059 3.87822 3.89117
+ 3.91278 3.94044 3.97376 4.01152 4.05052 4.10679 4.17908
+ 4.25673 4.33414 4.40875 4.47879 4.54342 4.60258 4.65595
+ 4.70291 4.74414 4.78018 4.81185 4.83915 4.86291 4.88301
+ 4.90048 4.91528 4.92802 4.9387 4.94777 4.95539 4.9618 4.96725
+ 4.97195 4.97588 4.97932 4.98247 4.98512 4.98697 4.98831
+ 4.98919 4.99015 4.99101 4.99169 4.99222 4.99282 4.99341
+ 4.994 4.9946 4.99519 4.99578 4.99638 4.99667 4.99693 4.9972
+ 4.99747 4.99773 4.998 4.99827 4.99841 4.99849 4.99856 4.99864
+ 4.99872 4.9988 4.99888 4.99896 4.99904 4.99911 4.99919 4.99927
+ 4.99935 4.99943 4.9995 4.99955 4.9996 4.99965 4.9997 5.00736
+ 4.98252 4.87516 4.66727 4.49142 4.43103 4.4301 4.4571 4.49729
+ 4.5407 4.5835 4.62363 4.66114 4.69577 4.72738 4.74632 4.75971
+ 4.77576 4.80671 4.87073 4.91665 4.93252 4.94418 4.95331
+ 4.96094 4.96727 4.97148 4.97471 4.97612 4.98276 5.00247
+ 5.04086 5.08628 5.10673 5.08887 5.0564 5.02767 5.01336 4.99685
+ 4.97422 4.90866 4.67035 4.33117 4.07888 3.94432 3.89105
+ 3.88174 3.89292 3.91442 3.94564 3.98708 4.0355 4.09134 4.16315
+ 4.24088 4.31918 4.39527 4.46693 4.53337 4.59405 4.6486 4.69693
+ 4.73938 4.77617 4.80809 4.83551 4.85895 4.87894 4.89596
+ 4.91081 4.92417 4.93651 4.94552 4.95198 4.9565 4.96096 4.96523
+ 4.96972 4.97428 4.97868 4.98064 4.9826 4.98455 4.98651 4.98847
+ 4.98967 4.99064 4.9916 4.99257 4.99353 4.99422 4.99457 4.99493
+ 4.99528 4.99563 4.99598 4.99633 4.99668 4.99703 4.99738
+ 4.99773 4.9979 4.99804 4.99817 4.9983 4.99843 4.99856 4.99869
+ 4.99883 4.99896 4.99909 4.99921 4.99926 4.99931 4.99937
+ 4.99942 4.99948 4.99953 4.99959 4.99964
+}
+v38 set {
+ 4.49849 4.53282 4.58329 4.66625 4.83345 4.97823 5.0207 5.01816
+ 5.01116 5.00595 5.00296 5.00148 5.00073 5.00062 5.00033
+ 5.0003 4.99864 4.99661 4.99652 4.99928 5.00361 5.12573 5.17251
+ 5.22612 5.33479 5.44503 5.44432 5.44379 5.44334 5.443 5.44276
+ 5.44258 5.44246 5.44238 5.44232 5.44228 5.44225 5.44223
+ 5.44221 5.4422 5.44219 5.44219 5.44218 5.44218 5.44218 5.44218
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44214 5.44214 5.44214 5.44214 5.44214 5.44214 5.44214
+ 5.44214 5.44214 5.44214 5.44214 5.44212 5.45159 5.45236
+ 5.44064 5.44307 5.45616 5.38122 4.77163 3.53297 2.74466
+ 2.34448 2.11802 1.9783 1.88656 1.82001 1.77389 1.72955 1.69632
+ 1.66971 1.6526 1.65236 1.56034 1.53764 1.97139 2.75096 3.39212
+ 3.74042 3.82345 3.85696 3.88547 3.91862 3.9585 4.00467 4.05903
+ 4.1254 4.19533 4.26791 4.34517 4.42112 4.49238 4.55807 4.6179
+ 4.6713 4.71815 4.75889 4.79418 4.82456 4.85062 4.87291 4.89196
+ 4.90823 4.92209 4.93388 4.9439 4.95242 4.95968 4.96585 4.97108
+ 4.9755 4.97923 4.98237 4.98503 4.98732 4.98927 4.99094 4.99233
+ 4.99353 4.99452 4.99538 4.99608 4.99668 4.99718 4.9976 4.99794
+ 4.99822 4.99847 4.99867 4.99884 4.99899 4.99913 4.99924
+ 4.99932 4.99938 4.99943 4.99947 4.99951 4.99953 4.99955
+ 4.99958 4.99961 4.99964 4.99967 4.99969 4.99972 4.99975
+ 4.99977 4.99978 4.99979 4.99981 4.99982 4.99983 4.99985
+ 4.99986 4.99986 4.99987 4.99987 4.99988 4.99988 4.99988
+ 4.99989 4.99989 4.9999 4.9999 4.99991 4.99991 4.99992 4.99992
+ 4.99993 4.99993 4.99993 4.99994 5.00381 5.00064 4.99246
+ 4.99823 5.00349 5.00076 5.00033 5.00015 5.00009 5.00007
+ 5.00005 5.00004 5.00003 5.00002 4.99988 4.99732 4.99728
+ 4.9978 5.00187 5.00927 5.08712 5.07654 4.92855 4.4863 3.76162
+ 3.00049 2.49834 2.20883 2.03492 1.92384 1.84676 1.79021
+ 1.74716 1.7132 1.68576 1.66309 1.64406 1.62785 1.61383 1.60162
+ 1.59081 1.58117 1.57253 1.56473 1.55765 1.55117 1.54527
+ 1.53988 1.53485 1.53012 1.5257 1.5216 1.51773 1.51411 1.51071
+ 1.50746 1.50438 1.50146 1.49868 1.49603 1.4935 1.49109 1.48878
+ 1.48657 1.48445 1.48242 1.48046 1.47858 1.47677 1.47502
+ 1.47333 1.4717 1.47012 1.46859 1.46711 1.46568 1.46428 1.46292
+ 1.4616 1.46034 1.45923 1.45812 1.45701 1.4559 1.45479 1.45378
+ 1.45279 1.45181 1.45082 1.44983 1.44893 1.44813 1.44732
+ 1.44652 1.44571 1.44491 1.4441 1.4433 1.44249 1.44169 1.44089
+ 1.44019 1.43951 1.43883 1.43815 1.43747 1.4368 1.43612 1.43544
+ 1.43476 1.43408 1.43342 1.43283 1.43223 1.43163 1.43104
+ 1.43044 1.42984 1.42924 1.42865
+}
+v39 set {
+ 5 5.01048 5.01221 4.98887 4.76261 4.54943 4.51564 4.56249
+ 4.62621 4.68843 4.74374 4.79044 4.82972 4.86127 4.88724
+ 4.90862 4.90791 4.89858 4.89589 4.91767 5.00405 5.16956
+ 5.12391 4.7557 3.87953 3.01124 2.48482 2.20424 2.03812 1.92679
+ 1.84956 1.79256 1.74907 1.71487 1.68724 1.6644 1.64513 1.6287
+ 1.61446 1.60197 1.59095 1.58117 1.57245 1.5646 1.55752 1.55109
+ 1.54516 1.53958 1.53444 1.53008 1.52606 1.52205 1.51843
+ 1.5149 1.51146 1.50893 1.50639 1.50387 1.50133 1.4988 1.49651
+ 1.49436 1.49222 1.49007 1.48793 1.48585 1.48433 1.4828 1.48128
+ 1.47975 1.47823 1.4767 1.47518 1.47365 1.47213 1.4706 1.46912
+ 1.46795 1.46678 1.46561 1.46444 1.46327 1.4621 1.46093 1.45976
+ 1.45859 1.45741 1.45628 1.45534 1.45441 1.45347 1.45254
+ 1.4516 1.45067 1.44973 1.4488 1.44786 1.44693 1.44604 1.44539
+ 1.44475 1.4441 1.44345 1.44281 1.44216 1.44151 1.44086 1.44022
+ 1.43957 1.43892 1.43828 1.43763 1.43698 1.43633 1.43569
+ 1.43504 1.43439 1.43375 1.4331 1.43245 1.4318 1.43157 1.43089
+ 1.43001 1.43042 1.42899 1.42439 1.42216 1.43447 1.44048
+ 1.43705 1.43314 1.43039 1.42861 1.42739 1.42651 1.42548
+ 1.42488 1.4243 1.42392 1.4235 1.32443 1.31149 1.78169 2.64844
+ 3.43211 3.95252 4.20231 4.3746 4.49948 4.58929 4.65742 4.71183
+ 4.77057 4.83196 4.88354 4.92894 4.96625 4.99235 5.00651
+ 5.00941 5.00813 5.00689 5.00588 5.00504 5.00431 5.00368
+ 5.00314 5.00268 5.00228 5.00194 5.00165 5.0014 5.00118 5.001
+ 5.00085 5.00072 5.00061 5.00052 5.00044 5.00037 5.00031
+ 5.00027 5.00022 5.00019 5.00016 5.00013 5.00011 5.00009
+ 5.00008 5.00007 5.00006 5.00005 5.00004 5.00003 5.00003
+ 5.00003 5.00002 5.00002 5.00002 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5.00001
+ 5.00002 5.00003 5.00004 5.00022 4.99974 4.99942 4.99997
+ 5.00063 5.00002 5.00003 4.99994 4.99998 4.99999 5 5 5 5
+ 5 4.99981 4.99998 5.00004 5.00036 5.00049 5.12012 5.16315
+ 5.19712 5.21835 4.87874 4.10151 3.31555 2.74207 2.38075
+ 2.15872 2.01614 1.91886 1.84852 1.79401 1.75052 1.71508
+ 1.68672 1.66467 1.64602 1.62985 1.61576 1.60343 1.59256
+ 1.58287 1.57418 1.56632 1.55922 1.55282 1.54687 1.54132
+ 1.53618 1.53143 1.52698 1.52282 1.51895 1.51527 1.5118 1.50851
+ 1.5054 1.50244 1.49963 1.49695 1.4944 1.49196 1.48963 1.4874
+ 1.48527 1.48322 1.48124 1.47934 1.47751 1.47574 1.47403
+ 1.47239 1.4708 1.46926 1.46777 1.46632 1.46491 1.46355 1.46237
+ 1.4612 1.46002 1.45884 1.45766 1.45659 1.45555 1.45451 1.45346
+ 1.45242 1.45147 1.45062 1.44978 1.44894 1.44809 1.44725
+ 1.4464 1.44556 1.44472 1.44387 1.44303 1.4423 1.44159 1.44088
+ 1.44017 1.43947 1.43876 1.43805 1.43734 1.43664 1.43593
+ 1.43524 1.43462 1.434 1.43338 1.43276 1.43213 1.43151 1.43089
+ 1.43027
+}
+
+set attributes {
+ V1 v1 red red
+ V2 v2 green red
+ V3 v3 blue red
+ V4 v4 yellow red
+ V5 v5 magenta red
+ V6 v6 cyan red
+ V7 v7 white red
+ V8 v8 red green
+ V9 v9 green green
+ V10 v10 blue green
+ V11 v11 yellow green
+ V12 v12 magenta green
+ V13 v13 cyan green
+ V14 v14 red red
+ V15 v15 green red
+ V16 v16 blue red
+ V17 v17 yellow red
+ V18 v18 magenta red
+ V19 v19 cyan red
+ V20 v20 white red
+ V21 v21 red green
+ V22 v22 green green
+ V23 v23 blue green
+ V24 v24 yellow green
+ V25 v25 magenta green
+ V26 v26 cyan green
+ V27 v27 red red
+ V28 v28 green red
+ V29 v29 blue red
+ V30 v30 yellow red
+ V31 v31 magenta red
+ V32 v32 cyan red
+ V33 v33 white red
+ V34 v34 red green
+ V35 v35 green green
+ V36 v36 blue green
+ V37 v37 yellow green
+ V38 v38 magenta green
+ V39 v39 cyan green
+}
+
+text .header -wrap word -width 0 -height 6
+
+set text {
+To zoom in on a region of the graph, simply click once on the left
+mouse button to pick one corner of the area to be zoomed. Move the
+mouse to the other corner and click again.
+}
+
+regsub -all "\n" $text "" text
+.header insert end "$text\n"
+.header insert end { You can click on the }
+set im [image create photo -file ./images/qv100.t.gif]
+button .header.snap -image $im -command { MakeSnapshot }
+.header window create end -window .header.snap
+.header insert end { button to see a photo image snapshot.}
+.header configure -state disabled
+graph $graph
+
+htext .footer -text {Hit the %%
+ set im [image create photo -file ./images/stopsign.gif]
+ button $htext(widget).quit -image $im -command { exit }
+ $htext(widget) append $htext(widget).quit
+%% button when you've seen enough. %%
+ label $htext(widget).logo -bitmap BLT
+ $htext(widget) append $htext(widget).logo
+%%}
+
+foreach {label yData outline color} $attributes {
+ .graph element create $label -x x -y $yData -outline $outline -color $color
+}
+
+set unique 0
+
+proc Sharpen { photo } {
+ #set kernel { -1 -1 -1 -1 16 -1 -1 -1 -1 }
+ set kernel { 0 -1 0 -1 4.9 -1 0 -1 0 }
+ winop convolve $photo $photo $kernel
+}
+
+proc MakeSnapshot {} {
+ update idletasks
+ global unique
+ set top ".snapshot[incr unique]"
+ set im1 [image create photo]
+ set im2 [image create photo]
+ .graph snap $im1
+ blt::winop snap .graph $im2
+ set thumb1 [image create photo -width 210 -height 150 -gamma 1.8]
+ winop resample $im1 $thumb1 sinc
+ set thumb2 [image create photo -width 210 -height 150 -gamma 1.8]
+ winop resample $im2 $thumb2 sinc
+ #Sharpen $thumb
+ image delete $im1
+ image delete $im2
+ toplevel $top
+ wm title $top "Snapshot \#$unique of \"[.graph cget -title]\""
+ label $top.l1 -image $thumb1
+ label $top.l2 -image $thumb2
+
+ button $top.but -text "Dismiss" -command "DestroySnapshot $top"
+ table $top 0,0 $top.l1 0,1 $top.l2
+ table $top $top.but -pady 4
+ focus $top.but
+}
+
+proc DestroySnapshot { win } {
+ set im [$win.l1 cget -image]
+ $im write -format ppm test.ppm
+ image delete $im
+ destroy $win
+}
+
+table . \
+ .header 0,0 -fill x \
+ .graph 1,0 -fill both \
+ .footer 2,0 -fill x
+
+table configure . r0 r2 -resize none
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+Blt_PrintKey $graph
+
+$graph element bind all <Enter> {
+ %W legend activate [%W element get current]
+}
+
+$graph element bind all <Leave> {
+ %W legend deactivate [%W element get current]
+}
+
diff --git a/blt/demos/graph5.tcl b/blt/demos/graph5.tcl
new file mode 100755
index 00000000000..cf63d3471ab
--- /dev/null
+++ b/blt/demos/graph5.tcl
@@ -0,0 +1,89 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ #namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+option add *Element.ScaleSymbols true
+option add *Axis.loose true
+option add *Pixels .8c
+option add *Element.lineWidth 0
+option add *Legend.ActiveRelief raised
+option add *Legend.padY 0
+option add *Button*Font { Courier 14 } widgetDefault
+option add *Legend*Font { Courier 14 bold } widgetDefault
+option add *Graph.Font { Courier 18 bold } widgetDefault
+option add *Graph.title "Element Symbol Types"
+option add *Graph.width 8i
+option add *Graph.height 6i
+option add *Graph.plotPadY .25i
+option add *Graph.plotPadX .25i
+
+set graph .graph
+
+graph $graph
+
+vector x -variable ""
+x set { 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 }
+
+for { set i 0 } { $i < 11 } { incr i } {
+ set vecName "y${i}"
+ vector ${vecName}
+ $vecName length 11
+ $vecName variable y
+ set y(:) [expr $i*100.0]
+}
+
+set attributes {
+ none "None" red red4 y0
+ arrow "Arrow" brown brown4 y10
+ circle "Circle" yellow yellow4 y2
+ cross "Cross" cyan cyan4 y6
+ diamond "Diamond" green green4 y3
+ plus "Plus" magenta magenta4 y9
+ splus "Splus" Purple purple4 y7
+ scross "Scross" red red4 y8
+ square "Square" orange orange4 y1
+ triangle "Triangle" blue blue4 y4
+ "@bitmaps/hobbes.xbm @bitmaps/hobbes_mask.xbm"
+ "Bitmap" yellow black y5
+}
+
+set count 0
+foreach {symbol label fill color yVec} $attributes {
+ $graph element create line${count} \
+ -label $label -symbol $symbol -color $color -fill $fill -x x -y $yVec
+ incr count
+}
+$graph element configure line0 -dashes { 2 4 2 } -linewidth 2
+button .quit -text Quit -command exit
+table . \
+ $graph 0,0 -fill both \
+ .quit 1,0 -fill x
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+Blt_PrintKey $graph
diff --git a/blt/demos/graph6.tcl b/blt/demos/graph6.tcl
new file mode 100755
index 00000000000..e76ed373aad
--- /dev/null
+++ b/blt/demos/graph6.tcl
@@ -0,0 +1,2343 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set tcl_precision 15
+
+set graph .graph
+
+option add *Graph.Width 10i
+option add *Graph.leftMargin .75i
+option add *Graph.Height 6i
+option add *Graph.plotBackground black
+
+option add *LineMarker.color white
+option add *LineMarker.Dashes 5
+option add *TextMarker.foreground white
+option add *TextMarker.Background {}
+
+option add *Graph.x.hide yes
+option add *Graph.x.title ""
+option add *Graph.y.rotate 90
+#option add *Graph.y.stepSize 2.0
+option add *Graph.title ""
+option add *graph.Title "Example s27"
+option add *graph.x.hide no
+option add *graph.topMargin 0
+option add *graph.bottomMargin 0
+option add *x.Title Time
+option add *y.Title Signals
+option add *Pixels 1
+
+option add *Reduce 0.5
+option add *bufferElements no
+
+option add *Element.color green4
+option add *Element.ScaleSymbols true
+option add *Element.Color grey70
+option add *Element.Symbol none
+option add *Element.LineWidth 1
+#option add *Element.Smooth natural
+option add *Element.Smooth catrom
+
+option add *activeLine.LineWidth 2
+option add *activeLine.Color white
+option add *activeLine.Color green1
+
+#option add *Legend.Hide yes
+option add *Legend.Position right
+option add *Legend.Relief flat
+option add *Legend.activeRelief sunken
+option add *Legend.borderWidth 2
+option add *Legend.Font -*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*
+option add *Grid.hide no
+option add *Grid.dashes "1 5"
+
+#option add *foreground white
+option add *zoomOutline.outline yellow
+
+graph .graph
+
+vector x -variable ""
+for { set i 1 } { $i <= 39 } { incr i } {
+ vector create v${i} -variable ""
+}
+
+x set {
+ 0 1e-10 2e-10 3e-10 4e-10 5e-10 6e-10 7e-10 8e-10 9e-10
+ 1e-09 1.1e-09 1.2e-09 1.3e-09 1.4e-09 1.5e-09 1.6e-09 1.7e-09
+ 1.8e-09 1.9e-09 2e-09 2.1e-09 2.2e-09 2.3e-09 2.4e-09 2.5e-09
+ 2.6e-09 2.7e-09 2.8e-09 2.9e-09 3e-09 3.1e-09 3.2e-09 3.3e-09
+ 3.4e-09 3.5e-09 3.6e-09 3.7e-09 3.8e-09 3.9e-09 4e-09 4.1e-09
+ 4.2e-09 4.3e-09 4.4e-09 4.5e-09 4.6e-09 4.7e-09 4.8e-09
+ 4.9e-09 5e-09 5.1e-09 5.2e-09 5.3e-09 5.4e-09 5.5e-09 5.6e-09
+ 5.7e-09 5.8e-09 5.9e-09 6e-09 6.1e-09 6.2e-09 6.3e-09 6.4e-09
+ 6.5e-09 6.6e-09 6.7e-09 6.8e-09 6.9e-09 7e-09 7.1e-09 7.2e-09
+ 7.3e-09 7.4e-09 7.5e-09 7.6e-09 7.7e-09 7.8e-09 7.9e-09
+ 8e-09 8.1e-09 8.2e-09 8.3e-09 8.4e-09 8.5e-09 8.6e-09 8.7e-09
+ 8.8e-09 8.9e-09 9e-09 9.1e-09 9.2e-09 9.3e-09 9.4e-09 9.5e-09
+ 9.6e-09 9.7e-09 9.8e-09 9.9e-09 1e-08 1.01e-08 1.02e-08
+ 1.03e-08 1.04e-08 1.05e-08 1.06e-08 1.07e-08 1.08e-08 1.09e-08
+ 1.1e-08 1.11e-08 1.12e-08 1.13e-08 1.14e-08 1.15e-08 1.16e-08
+ 1.17e-08 1.18e-08 1.19e-08 1.2e-08 1.21e-08 1.22e-08 1.23e-08
+ 1.24e-08 1.25e-08 1.26e-08 1.27e-08 1.28e-08 1.29e-08 1.3e-08
+ 1.31e-08 1.32e-08 1.33e-08 1.34e-08 1.35e-08 1.36e-08 1.37e-08
+ 1.38e-08 1.39e-08 1.4e-08 1.41e-08 1.42e-08 1.43e-08 1.44e-08
+ 1.45e-08 1.46e-08 1.47e-08 1.48e-08 1.49e-08 1.5e-08 1.51e-08
+ 1.52e-08 1.53e-08 1.54e-08 1.55e-08 1.56e-08 1.57e-08 1.58e-08
+ 1.59e-08 1.6e-08 1.61e-08 1.62e-08 1.63e-08 1.64e-08 1.65e-08
+ 1.66e-08 1.67e-08 1.68e-08 1.69e-08 1.7e-08 1.71e-08 1.72e-08
+ 1.73e-08 1.74e-08 1.75e-08 1.76e-08 1.77e-08 1.78e-08 1.79e-08
+ 1.8e-08 1.81e-08 1.82e-08 1.83e-08 1.84e-08 1.85e-08 1.86e-08
+ 1.87e-08 1.88e-08 1.89e-08 1.9e-08 1.91e-08 1.92e-08 1.93e-08
+ 1.94e-08 1.95e-08 1.96e-08 1.97e-08 1.98e-08 1.99e-08 2e-08
+ 2.01e-08 2.02e-08 2.03e-08 2.04e-08 2.05e-08 2.06e-08 2.07e-08
+ 2.08e-08 2.09e-08 2.1e-08 2.11e-08 2.12e-08 2.13e-08 2.14e-08
+ 2.15e-08 2.16e-08 2.17e-08 2.18e-08 2.19e-08 2.2e-08 2.21e-08
+ 2.22e-08 2.23e-08 2.24e-08 2.25e-08 2.26e-08 2.27e-08 2.28e-08
+ 2.29e-08 2.3e-08 2.31e-08 2.32e-08 2.33e-08 2.34e-08 2.35e-08
+ 2.36e-08 2.37e-08 2.38e-08 2.39e-08 2.4e-08 2.41e-08 2.42e-08
+ 2.43e-08 2.44e-08 2.45e-08 2.46e-08 2.47e-08 2.48e-08 2.49e-08
+ 2.5e-08 2.51e-08 2.52e-08 2.53e-08 2.54e-08 2.55e-08 2.56e-08
+ 2.57e-08 2.58e-08 2.59e-08 2.6e-08 2.61e-08 2.62e-08 2.63e-08
+ 2.64e-08 2.65e-08 2.66e-08 2.67e-08 2.68e-08 2.69e-08 2.7e-08
+ 2.71e-08 2.72e-08 2.73e-08 2.74e-08 2.75e-08 2.76e-08 2.77e-08
+ 2.78e-08 2.79e-08 2.8e-08 2.81e-08 2.82e-08 2.83e-08 2.84e-08
+ 2.85e-08 2.86e-08 2.87e-08 2.88e-08 2.89e-08 2.9e-08 2.91e-08
+ 2.92e-08 2.93e-08 2.94e-08 2.95e-08 2.96e-08 2.97e-08 2.98e-08
+ 2.99e-08 3e-08 3.01e-08 3.02e-08 3.03e-08 3.04e-08 3.05e-08
+ 3.06e-08 3.07e-08 3.08e-08 3.09e-08 3.1e-08 3.11e-08 3.12e-08
+ 3.13e-08 3.14e-08 3.15e-08 3.16e-08 3.17e-08 3.18e-08 3.19e-08
+ 3.2e-08 3.21e-08 3.22e-08 3.23e-08 3.24e-08 3.25e-08 3.26e-08
+ 3.27e-08 3.28e-08 3.29e-08 3.3e-08 3.31e-08 3.32e-08 3.33e-08
+ 3.34e-08 3.35e-08 3.36e-08 3.37e-08 3.38e-08 3.39e-08 3.4e-08
+ 3.41e-08 3.42e-08 3.43e-08 3.44e-08 3.45e-08 3.46e-08 3.47e-08
+ 3.48e-08 3.49e-08 3.5e-08 3.51e-08 3.52e-08 3.53e-08 3.54e-08
+ 3.55e-08 3.56e-08 3.57e-08 3.58e-08 3.59e-08 3.6e-08
+}
+
+wm min . 0 0
+
+v1 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+
+.graph element create V1 -x x -y v1
+
+v2 set {
+ 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1
+ 5.32907e-15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5
+ 5 5 5 5 5 5 5 5 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0
+}
+
+.graph element create V2 -x x -y v2
+
+v3 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 8.88178e-16
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 2.13718e-14 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0
+}
+
+.graph element create V3 -x x -y v3
+
+v4 set {
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+
+.graph element create V4 -x x -y v4
+
+v5 set {
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+}
+
+.graph element create V5 -x x -y v5
+v6 set {
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 8.88178e-16
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 1 2 3 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 4 3 2 1 2.13718e-14 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0
+}
+.graph element create V6 -x x -y v6
+v7 set {
+ 5 5.16904 4.84159 3.34542 0.317102 0.103304 0.0275721 0.0221534
+ 0.017689 0.0142639 0.0113974 0.00918238 0.00742541 0.00616602
+ 0.00481195 0.00397049 -0.0659889 -0.025671 0.165495 0.986891
+ 3.05229 4.55511 4.91611 4.98192 4.99428 4.99833 4.99095
+ 4.97295 4.95493 4.93428 4.90723 4.94799 4.98584 4.99566
+ 4.99813 4.99907 4.99947 4.99965 4.99976 4.99984 4.99989
+ 4.99992 4.99994 4.99996 4.99998 5.00002 5.00006 5.00002
+ 4.99996 4.99994 4.99999 5.00003 5.00002 5 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99996 4.99997 4.99997 4.99998
+ 4.99998 4.99999 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5.16575
+ 4.69986 2.43862 0.0230224 0.035229 -0.0210607 -0.0292766
+ -0.0172693 -0.00271479 -0.000912251 -0.000349106 -0.000116866
+ -4.24733e-05 -1.39536e-05 -3.01179e-05 -0.0657192 -0.0204835
+ 0.183378 1.07181 3.118 4.46472 4.84158 4.94795 4.98173 4.99236
+ 4.99762 5.01939 5.0433 5.05332 5.04959 5.03955 5.02851 5.02052
+ 5.01422 5.00965 5.00631 5.00405 5.00248 5.00083 5.00012
+ 5.00209 5.00387 5.00347 4.99917 4.99213 4.98411 4.97521
+ 4.96332 4.94601 4.9304 4.94633 4.97936 4.99264 4.99685 4.99857
+ 4.99925 4.99954 4.9997 4.99973 4.9997 4.99973 4.99979 4.99983
+ 4.99986 4.99988 4.9999 4.9999 4.99992 4.99993 4.99994 4.99995
+ 4.99996 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5.14242 4.76101 3.16003 0.299374
+ 0.0645506 -0.000498424 -2.45108e-05 -2.27986e-05 -5.24401e-05
+ -4.9884e-05 -4.92491e-05 -2.93354e-05 -3.21402e-05 -2.11851e-05
+ -3.37925e-05 -0.0657892 -0.020563 0.182582 1.06058 3.12484
+ 4.46552 4.84146 4.95102 4.98556 4.99472 4.99806 4.99909
+ 4.99955 4.99976 4.99994 4.99992 5.00029 4.99967 4.99849
+ 4.99736 4.99884 5.00099 5.00377 5.00215 4.99994 4.99893
+ 4.99788 4.99862 5.00055 5.00134 5.00127 5.00073 5.00039
+ 5.00018 5.00006 5.00001 4.99985 5.00026 5.00018 5.00003
+ 4.99981 4.99985 4.99987 4.99985 4.99982 4.99982 4.99982
+ 4.99983 4.99985 4.99987 4.99989 4.99991 4.99992 4.99994
+ 4.99995 4.99995 4.99994 4.99994 4.99996 4.99999 5.00002
+ 5.00008 5.00009 5.00006 5.00001 5 4.99999 4.99998 4.99997
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999 4.99999
+ 4.99999 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99998 4.99998
+ 4.99998
+}
+
+.graph element create V7 -x x -y v7
+
+v8 set {
+ 5 5.03758 5.04711 4.96911 4.20882 3.96295 4.01117 4.15521
+ 4.2967 4.42274 4.5295 4.6176 4.69014 4.74831 4.7966 4.83537
+ 4.80526 4.787 4.79295 4.88588 5.08978 5.15615 5.10778 5.07718
+ 5.06652 5.08225 4.9744 4.52977 3.77452 2.69426 1.15294 0.245509
+ 0.0981544 0.0567527 0.0367487 0.0252578 0.0180599 0.0133837
+ 0.0101497 0.0078616 0.00620186 0.00499056 0.0041027 0.00344223
+ 0.00295808 0.00260089 0.00229887 0.00200817 0.00176397 0.00160116
+ 0.00147381 0.00134645 0.00125029 0.00116043 0.00107371 0.00101981
+ 0.000965921 0.000912028 0.000858135 0.000804242 0.000761669
+ 0.00072672 0.000691771 0.000656823 0.000621874 0.000588722
+ 0.00057041 0.000552098 0.000533785 0.000515473 0.000497162
+ 0.00047885 0.000460537 0.000442226 0.000423914 0.000405601
+ 0.000388399 0.000378694 0.000368989 0.000359284 0.00034958
+ 0.000339875 0.00033017 0.000320465 0.00031076 0.000301055
+ 0.00029135 0.000282207 0.000276247 0.000270287 0.000264327
+ 0.000258367 0.000252407 0.000246447 0.000240487 0.000234527
+ 0.000228567 0.000222607 0.000217086 0.000213696 0.000210307
+ 0.000206918 0.000203528 0.000200139 0.00019675 0.00019336
+ 0.000189971 0.000186582 0.000183192 0.000179803 0.000176414
+ 0.000173025 0.000169635 0.000166246 0.000162857 0.000159467
+ 0.000156078 0.000152689 0.000149299 0.00014591 0.00014255
+ 0.0316021 0.163272 0.348732 0.603651 0.35745 0.135965 0.0707354
+ 0.0314595 0.0201047 0.00994945 0.00389601 0.00138839 0.00060778
+ 0.000329648 0.000492396 -0.0732035 -0.0844077 -0.0789062
+ -0.0390837 0.0197559 0.0183094 -0.00180099 -0.0189565 -0.0424144
+ -0.0735904 -0.0892423 0.285039 1.13702 2.10809 2.95826 3.60164
+ 4.0435 4.35771 4.57254 4.71769 4.81329 4.87534 4.91487 4.94264
+ 4.97375 5.01526 5.06517 5.10154 5.06259 4.89005 4.5787 4.12226
+ 3.46151 2.49023 1.2586 0.32725 0.116753 0.0701865 0.0455509
+ 0.0286914 0.0178176 0.0117599 0.00902715 0.00760583 0.00637745
+ 0.00543811 0.00439377 0.00352448 0.0030151 0.00285771 0.002465
+ 0.00203114 0.00173004 0.0014839 0.00125177 0.00105327 0.000894905
+ 0.000766372 0.000658894 0.000569105 0.000492114 0.000427938
+ 0.000370217 0.000314758 0.000266569 0.000233726 0.000209048
+ 0.000191957 0.000177169 0.000166604 0.000161 0.000157314
+ 0.000143828 0.000130342 0.000116857 0.000103371 8.98855e-05
+ 7.63998e-05 6.29141e-05 5.76583e-05 5.30027e-05 4.8347e-05
+ 4.36913e-05 3.90357e-05 3.438e-05 2.97243e-05 2.72507e-05
+ 2.59083e-05 2.45659e-05 2.32235e-05 2.18811e-05 2.05387e-05
+ 1.91963e-05 1.78539e-05 1.65115e-05 1.51691e-05 1.38267e-05
+ 1.24843e-05 1.11419e-05 9.79954e-06 8.51574e-06 7.69807e-06
+ 6.8804e-06 6.06273e-06 5.24506e-06 0.0287318 0.0317111 -0.0320087
+ -0.103609 0.0369639 0.0121128 0.00961197 0.00934971 0.00820853
+ 0.00699769 0.00607002 0.00535541 0.00476552 0.00427601 0.00376357
+ -0.073012 -0.0866964 -0.0809538 -0.038005 0.0277001 0.0188906
+ 0.00614597 0.00373629 0.00489787 0.0146573 0.0191052 0.0151708
+ 0.0124224 0.0105859 0.00879272 0.00729464 0.0070047 0.00449575
+ -0.00626652 -0.0252417 -0.0147287 0.022538 0.0822905 0.0947372
+ 0.0657516 0.0445506 0.0316753 0.0220971 0.0158101 0.0140971
+ 0.0161498 0.0139876 0.0122447 0.0106994 0.009397 0.00822236
+ 0.00686509 0.00797431 0.00751269 0.00671173 0.00595243 0.00524633
+ 0.00459528 0.00401688 0.00350109 0.00303954 0.00260569 0.00222792
+ 0.00191033 0.00163917 0.00140949 0.00121464 0.0010471 0.000900638
+ 0.000768847 0.000645236 0.000524807 0.000460275 0.000442237
+ 0.000446775 0.000397026 0.000301585 0.000228994 0.000190894
+ 0.000166569 0.000152261 0.000137953 0.000123644 0.000109336
+ 9.50281e-05 8.56557e-05 7.78437e-05 7.00318e-05 6.22198e-05
+ 5.44079e-05 4.87539e-05 4.57761e-05 4.27982e-05 3.98203e-05
+ 3.68425e-05 3.38646e-05 3.08868e-05 2.79089e-05 2.4931e-05
+ 2.19532e-05 1.89753e-05 1.75244e-05 1.64095e-05 1.52946e-05
+ 1.41797e-05 1.30648e-05 1.19499e-05 1.0835e-05 9.72011e-06
+ 8.60521e-06 7.4903e-06 6.5117e-06 6.10334e-06 5.69497e-06
+ 5.2866e-06 4.87824e-06 4.46987e-06 4.06151e-06 3.65314e-06
+ 3.24477e-06
+}
+
+.graph element create V8 -x x -y v8
+
+v9 set {
+ 1.86175 1.99708 2.07867 2.01211 2.43309 3.27194 3.63896
+ 3.90426 4.11074 4.27932 4.41496 4.52543 4.61491 4.68862
+ 4.7479 4.79666 4.72895 4.68886 4.70354 4.81353 5.01568 5.14184
+ 5.10482 5.07362 5.05143 5.03638 5.02323 5.01465 5.00853
+ 5.00383 4.99985 5.00454 5.00652 5.00546 5.00411 5.003 5.00214
+ 5.00151 5.00106 5.00073 5.0005 5.00034 5.00023 5.00015 5.0001
+ 5.00005 5 5.00001 5.00005 5.00005 5.00003 5 4.99998 4.99996
+ 4.99994 4.99995 4.99997 4.99998 5 5.00001 5.00002 5.00002
+ 5.00003 5.00003 5.00003 5.00003 5.00003 5.00003 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.17392 4.94828 3.78491
+ 1.52079 0.608874 0.244031 0.127087 0.0552995 0.0361032 0.0169025
+ 0.006364 0.00217624 0.000921391 0.000457305 0.000786754
+ -0.120016 -0.148054 -0.15898 -0.0801463 0.16463 0.174017
+ 0.0799249 0.0318788 0.0129696 0.00483397 0.0025677 0.0042079
+ 0.00350003 0.00178404 -8.72902e-05 -0.00128497 -0.00142213
+ -0.00130018 -0.00106874 -0.000789207 -0.000824335 -0.00104518
+ -0.00136799 -0.004366 -0.0102621 -0.0109254 -0.00649259
+ -0.00194842 0.00029793 0.00148673 0.00221085 0.00228291
+ 0.00185261 0.00139687 0.00148183 0.00562266 0.00844119 0.00754627
+ 0.00657396 0.00591212 0.00539269 0.0049282 0.00448417 0.0040572
+ 0.00363719 0.00320392 0.00279607 0.00243938 0.00211505 0.00182302
+ 0.00156254 0.0013341 0.00113834 0.000971865 0.00082776 0.000706193
+ 0.000602499 0.000515059 0.000441401 0.00037897 0.000325459
+ 0.00028083 0.000242096 0.000207274 0.000176444 0.000150372
+ 0.000126407 0.000103373 9.05522e-05 8.53555e-05 8.63685e-05
+ 9.02593e-05 8.37346e-05 7.72099e-05 7.06852e-05 6.41605e-05
+ 5.76358e-05 5.11112e-05 4.45865e-05 4.08176e-05 3.72497e-05
+ 3.36818e-05 3.01138e-05 2.65459e-05 2.2978e-05 1.94101e-05
+ 1.76154e-05 1.67399e-05 1.58645e-05 1.4989e-05 1.41136e-05
+ 1.32381e-05 1.23626e-05 1.14872e-05 1.06117e-05 9.73629e-06
+ 8.86083e-06 7.98538e-06 7.10993e-06 6.23447e-06 5.44363e-06
+ 5.32578e-06 5.20792e-06 5.09007e-06 4.97222e-06 0.0784323
+ 0.0474527 -0.0764232 -0.151146 0.0615785 0.0144489 0.00974161
+ 0.00947176 0.00849005 0.00728201 0.00630581 0.00554032 0.00487809
+ 0.00441504 0.00384139 -0.118943 -0.149894 -0.161173 -0.0825299
+ 0.171686 0.176912 0.0816085 0.0335236 0.013791 0.0056976
+ 0.00238833 0.00105348 0.000526199 0.00025969 0.000396026
+ 0.000837835 0.00170131 0.00196699 -0.000553314 -0.0061621
+ -0.0111895 -0.0142698 -0.0124608 -0.00795847 -0.00467822
+ -0.0043058 -0.00874449 -0.0118584 -0.00871386 -0.00377892
+ 1.95244e-05 0.00218952 0.00325486 0.00386497 0.00422837
+ 0.00446883 0.00447065 0.00486647 0.00547838 0.00565398 0.00559092
+ 0.00538752 0.00507015 0.00466305 0.00420756 0.00373465 0.00328404
+ 0.00287059 0.00250057 0.00216124 0.00184861 0.00156815 0.00134624
+ 0.00117857 0.00103412 0.0008948 0.000761012 0.000619853
+ 0.000462614 0.000319965 0.000287666 0.000356415 0.000379946
+ 0.000339183 0.00027972 0.000252982 0.000226244 0.000199507
+ 0.000172769 0.000146031 0.000130097 0.000117578 0.000105059
+ 9.25401e-05 8.00213e-05 7.11204e-05 6.67061e-05 6.22918e-05
+ 5.78775e-05 5.34632e-05 4.90489e-05 4.46346e-05 4.02203e-05
+ 3.5806e-05 3.13916e-05 2.69773e-05 2.4827e-05 2.31747e-05
+ 2.15225e-05 1.98702e-05 1.8218e-05 1.65658e-05 1.49135e-05
+ 1.32613e-05 1.1609e-05 9.95678e-06 8.50108e-06 7.86765e-06
+ 7.23422e-06 6.60079e-06 5.96736e-06 5.33393e-06 4.7005e-06
+ 4.06707e-06 3.43363e-06
+}
+
+.graph element create V9 -x x -y v9
+
+v10 set {
+ 1.86175 1.99308 2.16619 2.46661 3.09359 3.76864 4.31299
+ 4.65564 4.83425 4.92153 4.96157 4.98063 4.98649 4.99039
+ 4.9945 4.9972 4.96206 4.89882 4.83865 4.83202 4.91016 5.04479
+ 5.06078 5.04827 5.03474 5.0246 5.01639 5.00996 5.00569 5.00239
+ 5.00043 5.00296 5.00437 5.00382 5.00287 5.00208 5.00148
+ 5.00104 5.00073 5.0005 5.00034 5.00023 5.00016 5.00011 5.00008
+ 5.00007 5.00007 5.00004 5 4.99998 4.99998 4.99997 4.99998
+ 4.99999 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 5.10081
+ 5.10949 4.98359 5.00733 5.15145 4.37298 2.36126 0.470759
+ 0.0577238 0.0115884 0.00262611 0.000671499 0.000389038 0.000291291
+ 0.000317347 -0.0167823 -0.0158344 -0.0140559 0.0104849 0.0865874
+ 0.107813 0.0524688 0.0214369 0.00876443 0.00341595 0.00170778
+ 0.00259042 0.0022241 0.00118519 1.10217e-06 -0.000784506
+ -0.000948169 -0.000856256 -0.000696719 -0.000485987 -0.000724787
+ -0.000981491 -0.001454 -0.00552498 -0.0114992 -0.0105266
+ -0.00543527 -0.000982798 0.00127356 0.00224212 0.00275439
+ 0.00281098 0.0025471 0.00230368 0.00222576 0.00485522 0.00729453
+ 0.00691796 0.0062615 0.00573987 0.0052688 0.00481185 0.00436934
+ 0.00394326 0.00352712 0.00309978 0.00270038 0.00235335 0.00203742
+ 0.00175256 0.00150067 0.00128126 0.00109323 0.000933619
+ 0.000795113 0.000678182 0.00057843 0.000494345 0.000423609
+ 0.000363821 0.000312766 0.000269856 0.000232389 0.000198382
+ 0.000168126 0.00014267 0.000119293 9.69034e-05 8.5669e-05
+ 8.26828e-05 8.64066e-05 9.26665e-05 8.5454e-05 7.82416e-05
+ 7.10291e-05 6.38167e-05 5.66043e-05 4.93918e-05 4.21794e-05
+ 3.86073e-05 3.53007e-05 3.19941e-05 2.86876e-05 2.5381e-05
+ 2.20744e-05 1.87678e-05 1.70933e-05 1.62648e-05 1.54363e-05
+ 1.46079e-05 1.37794e-05 1.2951e-05 1.21225e-05 1.12941e-05
+ 1.04656e-05 9.63716e-06 8.80871e-06 7.98026e-06 7.1518e-06
+ 6.32335e-06 5.5374e-06 5.08959e-06 4.64178e-06 4.19397e-06
+ 3.74616e-06 0.0438026 0.0242078 -0.0602019 -0.0840866 0.00148461
+ -0.00292489 0.000442098 0.00219489 0.00281478 0.00290756
+ 0.00277945 0.00263896 0.00240099 0.00223283 0.001947 -0.0153629
+ -0.0148815 -0.0128673 0.0126017 0.0905161 0.11051 0.0538958
+ 0.022562 0.00935726 0.00397422 0.00172534 0.000790207 0.000416322
+ 0.000191632 0.000469721 0.0009779 0.00192566 0.00200688
+ -0.0016502 -0.00733932 -0.0128113 -0.0147608 -0.0115456
+ -0.00668995 -0.00401368 -0.00463908 -0.0101197 -0.0118993
+ -0.0076276 -0.00262656 0.000813059 0.00264455 0.00350796
+ 0.00399494 0.0043049 0.00451658 0.00444739 0.00503842 0.00559516
+ 0.00568213 0.00556459 0.0053176 0.00496654 0.00454337 0.00408592
+ 0.00362171 0.00317793 0.00277001 0.00240394 0.00207009 0.00176575
+ 0.00149725 0.00129045 0.00114257 0.00101135 0.000871672
+ 0.000723764 0.000580438 0.000427507 0.000296956 0.000281834
+ 0.000376628 0.000412266 0.000367547 0.000295305 0.000264513
+ 0.000233721 0.000202929 0.000172137 0.000141345 0.000124721
+ 0.000112577 0.000100433 8.82893e-05 7.61453e-05 6.75517e-05
+ 6.33609e-05 5.91701e-05 5.49792e-05 5.07884e-05 4.65976e-05
+ 4.24067e-05 3.82159e-05 3.40251e-05 2.98342e-05 2.56434e-05
+ 2.36401e-05 2.21181e-05 2.05961e-05 1.90741e-05 1.75521e-05
+ 1.60301e-05 1.45081e-05 1.29861e-05 1.14641e-05 9.94208e-06
+ 8.59252e-06 7.96439e-06 7.33626e-06 6.70813e-06 6.07999e-06
+ 5.45186e-06 4.82373e-06 4.1956e-06 3.56747e-06
+}
+
+.graph element create V10 -x x -y v10
+
+v11 set {
+ 1.86175 1.73419 1.42874 1.04055 0.943004 0.268275 0.0826455
+ 0.0388346 0.0214104 0.0135431 0.00961322 0.00712846 0.00588262
+ 0.00432397 0.00377774 0.00270134 -0.00393731 -0.00542187
+ -0.00126596 0.0113777 0.0134522 0.00477056 -0.00211067 -0.00229253
+ -0.00173355 -0.00122404 -0.00113426 -0.000744931 -0.000520112
+ -0.000410048 -0.000220439 0.000508104 5.15856e-05 -0.000112593
+ -0.000118917 -9.57394e-05 -7.15727e-05 -5.11847e-05 -3.58275e-05
+ -2.47166e-05 -1.68866e-05 -1.14082e-05 -7.66646e-06 -5.12139e-06
+ -3.63426e-06 -3.01815e-06 -2.64862e-06 -1.4947e-06 -1.91403e-07
+ -2.5763e-08 -7.73699e-07 -1.52164e-06 -1.07268e-06 -3.81696e-07
+ 2.6727e-07 4.75489e-07 6.83708e-07 8.91926e-07 1.10014e-06
+ 1.30836e-06 1.2482e-06 1.00726e-06 7.66311e-07 5.25364e-07
+ 2.84417e-07 6.27857e-08 7.43904e-10 -6.12979e-08 -1.2334e-07
+ -1.85382e-07 -2.47423e-07 -3.09465e-07 -3.71507e-07 -4.33549e-07
+ -4.95591e-07 -5.57633e-07 -6.04571e-07 -5.4944e-07 -4.9431e-07
+ -4.3918e-07 -3.84049e-07 -3.28919e-07 -2.73789e-07 -2.18659e-07
+ -1.63528e-07 -1.08398e-07 -5.32678e-08 1.062e-09 5.08502e-08
+ 1.00638e-07 1.50427e-07 2.00215e-07 2.50003e-07 2.99791e-07
+ 3.4958e-07 3.99368e-07 4.49156e-07 4.98944e-07 5.34512e-07
+ 5.01032e-07 4.67553e-07 4.34073e-07 4.00593e-07 3.67113e-07
+ 3.33633e-07 3.00153e-07 2.66674e-07 2.33194e-07 1.99714e-07
+ 1.66234e-07 1.32754e-07 9.92744e-08 6.57945e-08 3.23147e-08
+ -1.16513e-09 -3.4645e-08 -6.81248e-08 -1.01605e-07 -1.35084e-07
+ -1.68564e-07 -2.18729e-07 0.0114926 -0.0245378 -0.111828
+ 0.0964775 1.61491 3.22668 4.22041 4.54492 4.82845 4.94868
+ 4.98588 4.99609 4.9981 4.99908 4.99788 4.98395 4.99294 4.99724
+ 5.01939 5.0471 5.00902 4.98194 4.98496 4.99188 4.99623 4.99862
+ 5.00025 4.99974 4.99953 4.99946 4.99958 5.00012 4.99997
+ 4.99992 4.99988 4.99985 4.9998 4.9997 4.9988 4.99806 4.99982
+ 5.00143 5.00159 5.00098 5.00053 5.00028 5.00007 4.99977
+ 4.99992 5.00005 5.00133 5.0009 4.99993 4.99972 4.99975 4.9998
+ 4.99982 4.99983 4.99983 4.99983 4.99983 4.99984 4.99986
+ 4.99987 4.99989 4.9999 4.99991 4.99992 4.99994 4.99995 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5.01457 4.99482 4.96561 4.99326
+ 5.03452 5.00424 5.00101 5.00045 5.00004 4.99965 4.99997
+ 4.99994 4.99958 4.99999 4.99936 4.9839 4.99248 4.99717 5.01976
+ 5.04869 5.0087 4.98143 4.98488 4.99199 4.99622 4.9983 4.99928
+ 4.99971 4.99986 5.00031 5.00022 5.00035 5.0001 4.99884 4.99811
+ 4.99803 4.99887 5.00078 5.00151 5.00116 5.00007 4.99843
+ 4.99915 5.00107 5.00168 5.00141 5.00092 5.00055 5.0003 5.00016
+ 5.0001 5.00001 5.00016 5.0002 5.00009 4.99993 4.99975 4.99984
+ 4.99991 4.99991 4.99982 4.99974 4.99974 4.99985 4.99995
+ 4.99999 4.99998 5.00004 5.00013 5.00015 5.00007 4.99988
+ 4.99982 4.99985 4.99995 5.00006 5.0002 5.00025 5.0002 5.00009
+ 5.00006 5.00004 5.00002 5 4.99998 4.99997 4.99998 4.99998
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99998
+}
+
+.graph element create V11 -x x -y v11
+
+v12 set {
+ 5 5.16975 4.78685 2.94241 0.126698 0.0487004 -0.00422591
+ -0.00130689 -0.000486756 -0.000195875 -0.000108988 -6.66736e-05
+ -7.26005e-05 -5.63608e-05 -3.81859e-05 -2.123e-05 -0.0646846
+ -0.0184474 0.182248 1.06731 3.10988 4.46133 4.84133 4.95113
+ 4.98364 4.99455 4.99694 4.99727 4.9994 4.99975 5.0001 5.00132
+ 5.00089 5.00039 5.00019 5.00011 5.00006 5.00005 5.00004
+ 5.00001 4.99992 4.99992 5.00002 5.00013 5.00017 5.00009
+ 4.99992 4.99991 4.99994 4.99996 4.99998 4.99999 5.00001
+ 5.00004 5.00006 5.00005 5.00004 5.00003 5.00002 5.00001
+ 5 4.99999 4.99999 4.99998 4.99998 4.99997 4.99997 4.99998
+ 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5.14699 4.78074
+ 3.19424 0.305663 0.0611255 -0.00179951 -0.0012032 0.000405978
+ 0.000989399 0.000445194 0.000191447 8.30476e-05 3.96236e-05
+ 1.91866e-05 1.70665e-05 -0.0655239 -0.0210234 0.1827 1.06848
+ 3.11554 4.46518 4.84212 4.94853 4.98244 4.99434 4.9997 5.00081
+ 5.00009 4.99972 4.99985 4.99974 4.9995 4.99949 4.99958 4.99973
+ 4.99948 4.99914 4.99874 4.99946 5.00309 5.0091 5.01576 5.01835
+ 5.01852 5.0176 5.01625 5.01479 5.01345 5.01264 5.011 5.01092
+ 5.01344 5.01363 5.01289 5.01184 5.01071 5.00956 5.00848
+ 5.00751 5.00663 5.00577 5.00497 5.00427 5.00365 5.0031 5.00264
+ 5.00224 5.00191 5.00163 5.00138 5.00117 5.00099 5.00083
+ 5.00071 5.00061 5.00053 5.00045 5.00037 5.00029 5.00022
+ 5.00019 5.0002 5.00023 5.00024 5.00023 5.00023 5.00022 5.0002
+ 5.00018 5.00016 5.00014 5.00011 5.00009 5.00007 5.00006
+ 5.00005 5.00005 5.00004 5.00003 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.14298 4.79809 3.32704
+ 0.498385 0.105773 0.0160646 0.0319912 0.0299434 0.0240102
+ 0.0185844 0.0130411 0.0106532 0.00864871 0.00744519 0.00660887
+ -0.0612913 -0.0203719 0.174998 0.991787 3.06292 4.60005
+ 4.93058 4.98917 5.00033 4.9999 4.99909 4.9966 4.9955 4.99488
+ 4.99374 4.9943 5.00131 5.00506 4.99311 4.96288 4.93567 4.92439
+ 4.94236 4.9732 4.98864 4.99458 5.00031 5.00694 5.01525 5.01945
+ 5.01998 5.01953 5.01874 5.01766 5.0164 5.01509 5.01326 5.01423
+ 5.01455 5.01361 5.01245 5.01122 5.01002 5.00888 5.00783
+ 5.00687 5.00596 5.00514 5.00442 5.00379 5.00325 5.00279
+ 5.0024 5.00208 5.0018 5.00153 5.00126 5.00107 5.00094 5.00085
+ 5.00078 5.00072 5.00063 5.00053 5.00042 5.00038 5.00034
+ 5.0003 5.00027 5.00023 5.00021 5.00019 5.00017 5.00015 5.00013
+ 5.00012 5.00011 5.0001 5.0001 5.00009 5.00008 5.00007 5.00007
+ 5.00006 5.00005 5.00005 5.00004 5.00004 5.00003 5.00003
+ 5.00002 5.00002 5.00002 5.00001 5.00001 5 5 5 5.00001 5.00001
+ 5.00001 5.00002 5.00002 5.00002 5.00002
+}
+
+.graph element create V12 -x x -y v12
+
+v13 set {
+ 9.73784e-10 0.0189926 0.0926769 0.206309 0.111533 0.0953491
+ 0.0426966 0.0214177 0.0117943 0.00741442 0.00528816 0.00398417
+ 0.0032967 0.00266499 0.00206647 0.00158788 -0.0371391 -0.0439528
+ -0.0408653 -0.0188706 0.0150241 0.0126852 0.00209817 -0.000239206
+ -5.31488e-05 0.000876324 -0.00451221 -0.0165223 -0.0284127
+ -0.0427584 -0.0502453 -0.0257366 -0.00903938 -0.00376456
+ -0.00233385 -0.00169922 -0.00130397 -0.00102542 -0.000811435
+ -0.000648115 -0.000529266 -0.00043795 -0.00036574 -0.00030716
+ -0.00026221 -0.000229662 -0.000205112 -0.000181038 -0.000162045
+ -0.000148988 -0.000137633 -0.000126278 -0.000115562 -0.000104976
+ -9.49324e-05 -9.0585e-05 -8.62375e-05 -8.18901e-05 -7.75426e-05
+ -7.31952e-05 -6.93752e-05 -6.59106e-05 -6.24461e-05 -5.89815e-05
+ -5.55169e-05 -5.22412e-05 -5.05263e-05 -4.88114e-05 -4.70966e-05
+ -4.53817e-05 -4.36668e-05 -4.19519e-05 -4.0237e-05 -3.85222e-05
+ -3.68073e-05 -3.50924e-05 -3.34782e-05 -3.25442e-05 -3.16102e-05
+ -3.06763e-05 -2.97423e-05 -2.88083e-05 -2.78744e-05 -2.69404e-05
+ -2.60064e-05 -2.50725e-05 -2.41385e-05 -2.32635e-05 -2.27232e-05
+ -2.21829e-05 -2.16426e-05 -2.11023e-05 -2.0562e-05 -2.00217e-05
+ -1.94814e-05 -1.89411e-05 -1.84007e-05 -1.78604e-05 -1.73647e-05
+ -1.70853e-05 -1.68059e-05 -1.65265e-05 -1.62471e-05 -1.59677e-05
+ -1.56883e-05 -1.54089e-05 -1.51295e-05 -1.48501e-05 -1.45707e-05
+ -1.42913e-05 -1.40119e-05 -1.37325e-05 -1.34531e-05 -1.31737e-05
+ -1.28943e-05 -1.26149e-05 -1.23355e-05 -1.20561e-05 -1.17767e-05
+ -1.14973e-05 -1.10954e-05 0.0152675 0.0228237 -0.00460678
+ -0.0341525 0.0232109 -0.0138039 -0.0416538 -0.0458764 -0.0201967
+ -0.00878316 -0.00379173 -0.00164621 -0.000785131 -0.00037575
+ -0.000352375 -0.0545586 -0.0746881 -0.0771865 -0.05386 -0.0022199
+ 0.0136703 0.00633526 0.00138826 -0.00108934 0.0038886 0.0298077
+ 0.0475776 0.0481003 0.0464167 0.047818 0.042789 0.035207
+ 0.0264423 0.0193959 0.0151614 0.00624257 -0.00913057 -0.0310696
+ -0.0430238 0.016426 0.189762 0.49025 0.820116 1.13919 1.43549
+ 1.70658 1.95183 2.17414 2.38506 2.5657 2.73958 2.97905 3.21403
+ 3.43025 3.62645 3.8028 3.96002 4.09996 4.22443 4.33427 4.42886
+ 4.51097 4.5817 4.64326 4.6957 4.74132 4.7797 4.81298 4.84102
+ 4.86512 4.88523 4.90224 4.91649 4.92846 4.93868 4.94755
+ 4.95483 4.96114 4.96682 4.97161 4.97502 4.9776 4.97944 4.98141
+ 4.98319 4.98467 4.98585 4.9869 4.98796 4.98902 4.99008 4.99114
+ 4.9922 4.99326 4.9938 4.99429 4.99479 4.99528 4.99578 4.99628
+ 4.99677 4.99704 4.99718 4.99733 4.99747 4.99762 4.99777
+ 4.99791 4.99806 4.9982 4.99835 4.9985 4.99864 4.99879 4.99893
+ 4.99907 4.99916 4.99925 4.99934 4.99943 5.01473 4.92293
+ 4.61974 4.0316 3.7835 3.74195 3.78344 3.87272 3.97386 4.07319
+ 4.16686 4.25256 4.33126 4.40264 4.46697 4.49249 4.51807
+ 4.55803 4.64055 4.78574 4.86074 4.88334 4.8999 4.91455 4.92814
+ 4.93926 4.94761 4.95433 4.95907 4.9654 4.98317 5.0208 5.05134
+ 4.85852 4.16041 3.00077 1.68376 0.672707 0.240838 0.0794725
+ -0.0106347 -0.00879443 0.107196 0.368163 0.701424 1.03581
+ 1.3601 1.6678 1.95731 2.22701 2.47544 2.69099 2.92327 3.16648
+ 3.3877 3.59067 3.77344 3.93584 4.08066 4.20863 4.32065 4.41791
+ 4.50211 4.57423 4.63614 4.68888 4.73377 4.7721 4.80519 4.83338
+ 4.85732 4.87815 4.89514 4.90927 4.92108 4.93122 4.94014
+ 4.94845 4.95601 4.96251 4.96576 4.969 4.97225 4.9755 4.97874
+ 4.98087 4.98265 4.98442 4.9862 4.98797 4.98924 4.9899 4.99055
+ 4.9912 4.99186 4.99251 4.99316 4.99381 4.99447 4.99512 4.99577
+ 4.99609 4.99634 4.99659 4.99683 4.99708 4.99732 4.99757
+ 4.99782 4.99806 4.99831 4.99853 4.99863 4.99873 4.99883
+ 4.99893 4.99903 4.99913 4.99923 4.99933
+}
+
+.graph element create V13 -x x -y v13
+
+v14 set {
+ 1.86175 2.00147 1.85141 1.0654 0.275481 0.205547 0.0712627
+ 0.0313387 0.0151431 0.00864531 0.00593861 0.00438111 0.0037479
+ 0.00305857 0.00221221 0.0017081 -0.0896128 -0.109079 -0.121356
+ -0.0542001 0.175821 0.177442 0.0814591 0.0333042 0.0134909
+ 0.00625777 0.00100092 -0.00552776 -0.00411139 -0.00150395
+ -0.000564784 3.48169e-05 -0.000287014 -0.000538515 -0.000456537
+ -0.000325677 -0.000275468 -0.000166452 -8.27481e-05 -8.28704e-05
+ -7.47644e-05 -4.60552e-05 -2.61481e-06 2.26359e-05 2.53852e-05
+ -1.39853e-06 -4.23456e-05 -4.0907e-05 -2.8501e-05 -1.5945e-05
+ -9.01122e-06 -2.07747e-06 1.49328e-06 4.38398e-06 6.84248e-06
+ 4.76711e-06 2.69173e-06 6.16362e-07 -1.45901e-06 -3.53438e-06
+ -4.14256e-06 -3.76238e-06 -3.3822e-06 -3.00202e-06 -2.62184e-06
+ -2.24878e-06 -1.93456e-06 -1.62033e-06 -1.3061e-06 -9.91867e-07
+ -6.77638e-07 -3.63409e-07 -4.91792e-08 2.6505e-07 5.7928e-07
+ 8.93509e-07 1.16076e-06 1.11055e-06 1.06034e-06 1.01014e-06
+ 9.59927e-07 9.09719e-07 8.59511e-07 8.09302e-07 7.59094e-07
+ 7.08886e-07 6.58678e-07 5.99251e-07 4.87523e-07 3.75795e-07
+ 2.64068e-07 1.5234e-07 4.06119e-08 -7.1116e-08 -1.82844e-07
+ -2.94572e-07 -4.063e-07 -5.18027e-07 -6.08517e-07 -5.95879e-07
+ -5.83241e-07 -5.70604e-07 -5.57966e-07 -5.45328e-07 -5.3269e-07
+ -5.20053e-07 -5.07415e-07 -4.94777e-07 -4.8214e-07 -4.69502e-07
+ -4.56864e-07 -4.44226e-07 -4.31589e-07 -4.18951e-07 -4.06313e-07
+ -3.93676e-07 -3.81038e-07 -3.684e-07 -3.55762e-07 -3.43125e-07
+ 1.06736e-05 0.0797407 0.0437947 -0.0645098 -0.0877312 0.0653203
+ -0.00621184 -0.0353188 -0.0491378 -0.0251957 -0.0110996
+ -0.00481123 -0.0020941 -0.000998038 -0.000478747 -0.000445332
+ -0.102046 -0.135753 -0.154351 -0.0827509 0.163348 0.174012
+ 0.0794822 0.0310624 0.0112213 0.00249061 0.00130764 0.00181315
+ 0.00163875 0.00101454 0.000497435 0.000195258 5.31901e-05
+ 2.4607e-05 6.62736e-05 7.90718e-05 4.0372e-05 -0.000141184
+ -0.000280623 5.5608e-05 0.000799565 0.000920189 0.000931616
+ 0.000494527 0.000162303 -8.24884e-05 -0.000183938 -0.000203899
+ -0.000144788 -9.87063e-05 -0.000227929 2.93932e-05 0.000208563
+ 1.88958e-06 -7.6335e-05 -0.000172472 -0.000165656 -0.000145889
+ -0.000177311 -0.000191058 -0.000168287 -0.00015755 -0.00013142
+ -8.10488e-05 -6.36115e-05 -7.8699e-05 -8.11282e-05 -7.98625e-05
+ -5.98807e-05 -3.40879e-05 -1.95464e-05 -1.79247e-05 -4.45514e-05
+ -7.47995e-05 -8.7682e-05 -7.50806e-05 -3.25561e-05 -4.34114e-05
+ -7.69099e-05 -0.000141101 -0.00018743 -0.000148471 -5.06546e-05
+ 0.000120195 0.000177635 0.000177052 0.000146344 9.75126e-05
+ 8.31233e-05 6.8734e-05 5.43447e-05 3.99554e-05 2.55661e-05
+ 1.11768e-05 -3.21253e-06 -3.88937e-06 -3.56628e-06 -3.24318e-06
+ -2.92008e-06 -2.59699e-06 -2.27389e-06 -1.9508e-06 -1.73227e-06
+ -1.56796e-06 -1.40365e-06 -1.23934e-06 -1.07503e-06 -9.10722e-07
+ -7.46412e-07 -5.82101e-07 -4.1779e-07 -2.5348e-07 -8.91694e-08
+ 7.51412e-08 2.39452e-07 4.03762e-07 5.95733e-07 1.00771e-06
+ 1.41969e-06 1.83167e-06 2.24365e-06 0.0828257 0.231038 0.465438
+ 1.54516 2.8461 3.19221 3.40395 3.6382 3.80758 3.93848 4.04882
+ 4.15428 4.247 4.32917 4.40235 4.36941 4.397 4.48862 4.64552
+ 4.86595 5.03475 5.0348 5.02627 5.01967 5.01542 5.00925 4.98613
+ 4.9519 4.91581 4.87357 4.82302 4.80403 4.82565 4.86102 4.89483
+ 4.92253 4.94428 4.96257 4.97608 4.98373 4.98823 4.99182
+ 4.99437 4.99635 4.99745 4.99802 4.99843 4.99873 4.99895
+ 4.99912 4.99925 4.99931 4.99962 4.99973 4.99972 4.99971
+ 4.9997 4.99969 4.9997 4.99971 4.99973 4.99974 4.99976 4.99978
+ 4.9998 4.99982 4.99985 4.99987 4.99989 4.9999 4.99991 4.99991
+ 4.99993 4.99994 4.99997 5.00001 5.00006 5.00008 5.00006
+ 5.00002 5 4.99999 4.99998 4.99997 4.99995 4.99995 4.99995
+ 4.99995 4.99995 4.99995 4.99995 4.99996 4.99997 4.99997
+ 4.99998 4.99999 5 5 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5 5 5 4.99999 4.99999 4.99999
+
+}
+
+.graph element create V14 -x x -y v14
+
+v15 set {
+ 1.86175 2.00199 2.08919 1.84314 1.08254 0.214737 0.0377351
+ 0.00952455 0.00232763 0.000563614 0.000263477 0.000148642
+ 0.000285086 0.000242592 7.34699e-05 -1.53467e-05 -0.0161874
+ -0.0157876 -0.0141194 0.0132576 0.0903272 0.109938 0.0535295
+ 0.0224216 0.00940945 0.00466825 -0.000649972 -0.00654752
+ -0.00333248 -0.00103671 -0.000508276 -5.8896e-05 -0.00043938
+ -0.000544704 -0.00044444 -0.000307093 -0.00024517 -0.000154538
+ -8.78602e-05 -7.10461e-05 -6.06485e-05 -3.91039e-05 -8.45988e-06
+ 9.43442e-06 1.28351e-05 -2.16734e-06 -2.6142e-05 -2.54768e-05
+ -1.88997e-05 -1.17906e-05 -7.3808e-06 -2.97101e-06 1.19146e-07
+ 2.94246e-06 5.38942e-06 3.88851e-06 2.38761e-06 8.86704e-07
+ -6.14201e-07 -2.11511e-06 -2.59565e-06 -2.38885e-06 -2.18205e-06
+ -1.97525e-06 -1.76845e-06 -1.56241e-06 -1.36258e-06 -1.16276e-06
+ -9.62939e-07 -7.63116e-07 -5.63293e-07 -3.6347e-07 -1.63647e-07
+ 3.61756e-08 2.35999e-07 4.35822e-07 6.07653e-07 5.90323e-07
+ 5.72994e-07 5.55665e-07 5.38336e-07 5.21007e-07 5.03678e-07
+ 4.86349e-07 4.6902e-07 4.51691e-07 4.34361e-07 4.11899e-07
+ 3.60315e-07 3.08731e-07 2.57146e-07 2.05562e-07 1.53977e-07
+ 1.02393e-07 5.08082e-08 -7.76222e-10 -5.23607e-08 -1.03945e-07
+ -1.47815e-07 -1.54225e-07 -1.60635e-07 -1.67045e-07 -1.73455e-07
+ -1.79864e-07 -1.86274e-07 -1.92684e-07 -1.99094e-07 -2.05504e-07
+ -2.11914e-07 -2.18324e-07 -2.24734e-07 -2.31144e-07 -2.37554e-07
+ -2.43964e-07 -2.50373e-07 -2.56783e-07 -2.63193e-07 -2.69603e-07
+ -2.76013e-07 -2.82423e-07 2.92534e-06 0.0446777 0.024278
+ -0.0518987 -0.0636547 0.00983929 -0.000518204 -0.000265194
+ 0.000154772 0.000299538 3.12715e-05 -3.18225e-05 -2.48268e-05
+ -1.16701e-05 -6.05117e-06 7.61116e-06 -0.0163668 -0.0158244
+ -0.0141177 0.0100085 0.0857144 0.107784 0.051862 0.0204448
+ 0.00629858 0.000967736 0.00121674 0.00190276 0.00154009
+ 0.000860922 0.000410386 0.000164585 3.99493e-05 1.93797e-05
+ 5.67594e-05 0.000110126 2.49925e-05 -7.17815e-05 -0.000142299
+ -1.63109e-05 0.000439529 0.000562489 0.000594599 0.000326164
+ 0.000126423 -4.26063e-05 -0.000122927 -0.000114152 -6.72706e-05
+ -6.41242e-05 -0.000135588 2.61507e-05 0.000134036 6.43734e-06
+ -4.6223e-05 -0.000112047 -0.000101388 -8.67847e-05 -0.000117664
+ -0.000133957 -0.000116558 -0.000100873 -7.65448e-05 -4.44964e-05
+ -3.6677e-05 -5.26632e-05 -5.45172e-05 -5.13545e-05 -3.73869e-05
+ -1.99732e-05 -1.0907e-05 -1.10081e-05 -3.02609e-05 -5.18517e-05
+ -6.13597e-05 -5.30706e-05 -2.39572e-05 -3.24146e-05 -5.70062e-05
+ -0.000103448 -0.000135376 -0.0001024 -2.39007e-05 0.000110929
+ 0.000151226 0.000142044 0.000105922 5.62834e-05 4.78476e-05
+ 3.94117e-05 3.09759e-05 2.25401e-05 1.41042e-05 5.66837e-06
+ -2.76747e-06 -3.08639e-06 -2.81341e-06 -2.54043e-06 -2.26745e-06
+ -1.99447e-06 -1.72149e-06 -1.44851e-06 -1.26226e-06 -1.12096e-06
+ -9.79661e-07 -8.38363e-07 -6.97065e-07 -5.55768e-07 -4.1447e-07
+ -2.73173e-07 -1.31875e-07 9.42259e-09 1.5072e-07 2.92018e-07
+ 4.33315e-07 5.74613e-07 7.10363e-07 8.01984e-07 8.93604e-07
+ 9.85225e-07 1.07685e-06 0.04474 0.0928765 0.141327 0.0176048
+ -0.071675 -0.0124613 0.989022 2.28104 3.40619 4.21417 4.67173
+ 4.87438 4.96044 4.98996 4.99858 4.96672 4.89502 4.79391
+ 4.76433 4.8387 4.98612 5.0161 5.01722 5.01437 5.01256 4.99827
+ 4.95807 4.9209 4.88217 4.83006 4.78461 4.80759 4.85548 4.89604
+ 4.9254 4.94617 4.96126 4.97374 4.98255 4.98792 4.99126 4.99361
+ 4.99554 4.99699 4.99792 4.99846 4.99881 4.99905 4.99924
+ 4.99938 4.99949 4.99955 4.9997 4.9998 4.99982 4.99982 4.99982
+ 4.99982 4.99982 4.99983 4.99984 4.99985 4.99986 4.99987
+ 4.99988 4.99989 4.9999 4.99992 4.99993 4.99994 4.99995 4.99995
+ 4.99996 4.99996 4.99998 4.99999 5.00001 5.00002 5.00002
+ 5.00001 5.00001 5 4.99999 4.99999 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+
+}
+.graph element create V15 -x x -y v15
+
+v16 set {
+ 1.86175 1.73073 1.50572 1.89001 3.39004 4.36034 4.79012
+ 4.93798 4.98305 4.99539 4.9979 4.99904 4.99772 4.9983 4.99935
+ 4.99975 4.98837 4.99456 4.99728 5.01838 5.04568 5.00759
+ 4.98112 4.98479 4.99197 4.99641 4.99747 4.99775 5.00043
+ 5.0007 5.00035 5.00023 4.99976 5.00002 5.00007 5.0002 4.99993
+ 5.00003 5.00021 5.00006 4.99993 4.99992 5.00002 5.00013
+ 5.00017 5.00009 4.99992 4.99991 4.99993 4.99996 4.99998
+ 4.99999 5.00001 5.00003 5.00005 5.00004 5.00004 5.00003
+ 5.00002 5.00001 5 4.99999 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00002 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 5 5.01498 4.99342 4.96899 5.00301 5.02627 4.9977
+ 4.99548 4.99757 5.00277 5.00245 5.0014 5.00069 5.00032 5.00014
+ 5.00009 4.9867 4.99262 4.99607 5.01805 5.04713 5.00927 4.98184
+ 4.98483 4.9914 4.99616 4.99902 4.9999 4.99987 4.99979 4.99981
+ 4.99989 4.99994 4.99998 5.0002 5.00001 5.00008 5.00008 5.0001
+ 5.00021 5.00032 5.00025 5.00019 5.00006 5.00007 4.99994
+ 4.99997 4.99999 5.00023 5.00008 4.99993 4.99998 4.99986
+ 4.99982 5.00003 4.99985 4.99996 5.00014 5 4.99984 4.99979
+ 4.99982 4.99993 5.00008 5.00011 5.00002 4.99996 4.9999 4.99994
+ 5.00001 5.00007 5.00009 4.99995 4.99978 4.99971 4.99976
+ 4.99997 4.99996 4.99989 4.99972 4.99955 4.99953 4.99959
+ 4.99976 4.9999 5.00005 5.00023 5.00039 5.00034 5.00029 5.00024
+ 5.00019 5.00014 5.00009 5.00004 5.00003 5.00002 5.00001
+ 5 5 4.99999 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5.00001 5.00002 5.00003 5.00004 5.01564 5.03395 5.04932
+ 5.11868 3.92502 1.31888 0.163888 0.0946876 0.0789578 0.0565084
+ 0.0260333 0.0156986 0.00907667 0.00613629 0.00468417 -0.00174008
+ -0.0021422 0.000586962 0.0124937 0.0147977 0.00838454 0.00039383
+ -0.000522021 -0.000426598 -0.000290214 -0.00173713 -0.00384132
+ -0.00382945 -0.00429219 -0.00580193 -0.00393246 0.0017543
+ 0.00423045 0.00408931 0.0031976 0.00245457 0.00187293 0.00159068
+ 0.00105697 0.000609902 0.000358825 0.000334125 0.000212708
+ 0.000168116 8.97349e-05 5.21578e-05 3.84527e-05 2.93033e-05
+ 2.10067e-05 1.59954e-05 1.13917e-05 5.49738e-06 2.77217e-05
+ 6.51259e-06 -6.65468e-06 2.09837e-06 -6.617e-06 -4.80187e-06
+ 1.55031e-06 4.26536e-06 7.69457e-07 -1.46213e-06 -7.25202e-07
+ 3.26501e-06 6.55807e-06 7.524e-06 6.07209e-06 6.00701e-06
+ 5.41166e-06 3.86573e-06 1.10651e-06 -2.74603e-06 -2.18566e-06
+ 2.3658e-06 8.59956e-06 8.35046e-06 2.90621e-06 -8.75982e-07
+ -1.87189e-06 -2.1528e-06 -1.94875e-06 -1.74471e-06 -1.54067e-06
+ -1.33662e-06 -1.13258e-06 -8.40567e-07 -5.20743e-07 -2.00918e-07
+ 1.18906e-07 4.38731e-07 6.11382e-07 6.01529e-07 5.91675e-07
+ 5.81822e-07 5.71968e-07 5.62115e-07 5.52261e-07 5.42407e-07
+ 5.32554e-07 5.227e-07 5.12847e-07 4.72812e-07 4.26137e-07
+ 3.79462e-07 3.32786e-07 2.86111e-07 2.39436e-07 1.92761e-07
+ 1.46086e-07 9.94107e-08 5.27356e-08 -2.77779e-10 -7.98079e-08
+ -1.59338e-07 -2.38868e-07 -3.18398e-07 -3.97928e-07 -4.77458e-07
+ -5.56988e-07 -6.36519e-07
+}
+.graph element create V16 -x x -y v16
+
+v17 set {
+ 5 5.16963 4.84136 3.33754 0.316206 0.103113 0.0273341 0.0221102
+ 0.0177008 0.0143758 0.0115203 0.00929231 0.00752716 0.00625439
+ 0.00489872 0.00403656 -0.0657317 -0.0256467 0.165394 0.985963
+ 3.05067 4.55799 4.89728 4.92464 4.8882 4.90592 4.97315 4.99241
+ 4.99694 4.99845 4.99905 4.99939 4.99959 4.99971 4.9998 4.99986
+ 4.9999 4.99993 4.99995 4.99996 4.99997 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 5 5.00001 5.00003 5.00005
+ 5.00004 5.00002 5 4.99999 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99998 4.99998 4.99999 4.99999 5 5 5 5 5 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5 5.00025 5.1657 4.69981 2.43895
+ 0.0229743 0.0351406 -0.0211974 -0.0312063 -0.0160331 -0.0021718
+ -0.000766597 -0.000251052 -5.49363e-05 -3.36364e-06 -2.01983e-06
+ -9.70575e-06 -0.0657007 -0.0205247 0.183332 1.07163 3.11839
+ 4.46213 4.84163 4.95195 4.99159 5.02084 5.04029 5.04138
+ 5.0271 5.00445 4.97957 4.95702 4.95231 4.97819 4.99191 4.9963
+ 4.99822 4.99878 4.99903 4.99925 4.99942 4.9995 4.99954 4.99957
+ 4.99961 4.99966 4.9997 4.99974 4.99977 4.99981 4.99983 4.99986
+ 4.99988 4.9999 4.99991 4.99992 4.99994 4.99995 4.99995 4.99996
+ 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 4.99999 4.99998 4.99997 4.99996 5.14239 4.76219
+ 3.16574 0.299969 0.0631609 -0.00118611 -0.00026052 -5.96333e-05
+ -1.44904e-05 -4.3859e-06 -2.99454e-06 1.10547e-06 4.84662e-06
+ 1.30971e-05 2.23082e-05 -0.0655844 -0.0204818 0.182507 1.05954
+ 3.12277 4.46735 4.83915 4.94512 4.97679 4.98654 4.9966 5.00833
+ 5.00776 5.00432 5.00199 5.00086 5.00033 5.00008 5 5.00001
+ 5 5.00005 5.00002 4.99981 4.99991 4.99998 4.99979 4.99979
+ 4.99984 4.9998 4.9998 5.00006 5.00002 5.00001 5 5 4.99992
+ 4.99998 4.99999 5.00002 5.00014 4.99999 4.99987 4.99993
+ 5.00003 5.00011 5.00005 4.99996 4.99987 4.99985 4.99994
+ 5.00009 5.0001 5 4.99993 4.99997 5.00008 5.00015 5.00021
+ 5.00021 5.00007 4.99978 4.99965 4.99973 4.9999 4.99992 4.99995
+ 4.99997 4.99999 5.00001 5.00002 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00002
+}
+.graph element create V17 -x x -y v17
+
+v18 set {
+ 5 5.0333 5.02472 4.92559 4.18383 3.93923 3.9961 4.14293
+ 4.28591 4.41336 4.52157 4.61101 4.68472 4.7439 4.79294 4.83239
+ 4.80697 4.78808 4.79322 4.8838 5.08529 5.21863 4.88852 3.90198
+ 2.14586 0.383977 0.101103 0.0525711 0.0318287 0.020895 0.0146908
+ 0.010831 0.00830272 0.00656377 0.00532066 0.00440078 0.00369956
+ 0.00315713 0.00272614 0.00237965 0.00209659 0.00186339 0.00167014
+ 0.0015081 0.00137172 0.00125607 0.00115393 0.00106076 0.000980166
+ 0.000918015 0.000862837 0.00080766 0.000763488 0.000721541
+ 0.000680825 0.000653026 0.000625226 0.000597426 0.000569627
+ 0.000541827 0.000519087 0.000499756 0.000480424 0.000461093
+ 0.000441761 0.000423291 0.000411941 0.00040059 0.00038924
+ 0.000377889 0.000366539 0.000355188 0.000343838 0.000332487
+ 0.000321137 0.000309786 0.000299055 0.000292509 0.000285963
+ 0.000279417 0.000272871 0.000266325 0.000259779 0.000253233
+ 0.000246686 0.00024014 0.000233594 0.000227387 0.0002231
+ 0.000218813 0.000214526 0.00021024 0.000205953 0.000201666
+ 0.000197379 0.000193092 0.000188805 0.000184519 0.000180526
+ 0.000177963 0.0001754 0.000172837 0.000170274 0.000167711
+ 0.000165148 0.000162585 0.000160022 0.000157459 0.000154895
+ 0.000152332 0.000149769 0.000147206 0.000144643 0.00014208
+ 0.000139517 0.000136954 0.000134391 0.000131828 0.000129265
+ 0.000126702 0.000132838 0.0311184 0.163151 0.34986 0.604501
+ 0.357125 0.136137 0.0711304 0.0346959 0.0212674 0.00872193
+ 0.00252206 0.000455269 7.59332e-05 2.91532e-05 0.000320562
+ -0.0720911 -0.0840491 -0.0791345 -0.0404143 0.0182035 -0.0235871
+ -0.0426072 -0.0597501 0.00824773 0.481404 1.32496 2.11949
+ 2.57317 2.58202 2.15054 1.33786 0.45702 0.153772 0.0913584
+ 0.0604989 0.0421591 0.0271456 0.0170021 0.0115815 0.00907886
+ 0.00742466 0.00626096 0.00531127 0.00450501 0.00381927 0.00323718
+ 0.00274374 0.00232494 0.00196885 0.00166686 0.00141134 0.00119437
+ 0.0010109 0.000855534 0.000723378 0.000611408 0.000516704
+ 0.000436769 0.000369523 0.000313026 0.00026526 0.000223976
+ 0.000188972 0.000159042 0.000134148 0.000112688 9.49738e-05
+ 7.97877e-05 6.721e-05 5.65115e-05 4.77194e-05 4.03591e-05
+ 3.42848e-05 2.92627e-05 2.50435e-05 2.1412e-05 1.84532e-05
+ 1.58624e-05 1.34673e-05 1.14461e-05 1.00935e-05 9.12375e-06
+ 8.50202e-06 7.81431e-06 7.20729e-06 6.73936e-06 6.3702e-06
+ 5.90049e-06 5.43077e-06 4.96105e-06 4.49133e-06 4.02162e-06
+ 3.5519e-06 3.08218e-06 2.79099e-06 2.51281e-06 2.23463e-06
+ 1.95645e-06 1.67827e-06 1.40009e-06 1.12191e-06 1.01376e-06
+ 9.9375e-07 9.73741e-07 9.53733e-07 9.33724e-07 9.13715e-07
+ 8.93707e-07 8.73698e-07 8.5369e-07 8.33681e-07 8.13673e-07
+ 7.93664e-07 7.73655e-07 7.53647e-07 7.21781e-07 5.956e-07
+ 4.69419e-07 3.43239e-07 2.17058e-07 0.0284032 0.0374438
+ -0.0157543 -0.0680497 0.0504768 0.0100294 0.00222261 0.000528697
+ 0.000132929 3.99489e-05 2.46066e-05 4.56327e-06 -6.54853e-06
+ 1.33783e-05 -3.68221e-05 -0.0724498 -0.0843663 -0.0792935
+ -0.0406426 0.0200019 0.0426259 0.0220753 0.00668555 -0.000968483
+ 0.024662 0.0383437 0.0911513 0.087848 0.0602076 0.0390559
+ 0.0260573 0.0180444 0.012974 0.00985409 0.00788132 0.0064228
+ 0.005545 0.00453571 0.00364245 0.00310278 0.00270523 0.00236439
+ 0.0020945 0.00186808 0.00167493 0.00151731 0.00138594 0.00126945
+ 0.00116695 0.0010762 0.000996366 0.000928387 0.000864414
+ 0.000808258 0.000759574 0.000713865 0.000666712 0.000632716
+ 0.000601262 0.000572163 0.000543986 0.000515253 0.0004897
+ 0.000468112 0.000449313 0.000432981 0.000417911 0.000401307
+ 0.000382712 0.000366678 0.000355736 0.000349171 0.000335727
+ 0.000317091 0.000296086 0.000283543 0.000277366 0.000272233
+ 0.000267001 0.000263147 0.000256699 0.000250251 0.000243803
+ 0.000237355 0.000230907 0.000225424 0.000220247 0.000215069
+ 0.000209892 0.000204714 0.000200213 0.000196548 0.000192884
+ 0.00018922 0.000185556 0.000181892 0.000178228 0.000174564
+ 0.0001709 0.000167236 0.000163572 0.000160824 0.000158279
+ 0.000155733 0.000153187 0.000150641 0.000148095 0.000145549
+ 0.000143003 0.000140457 0.000137911 0.000135457 0.000133386
+ 0.000131315 0.000129245 0.000127174 0.000125103 0.000123032
+ 0.000120961 0.000118891
+}
+.graph element create V18 -x x -y v18
+
+v19 set {
+ 1.86175 1.99994 2.0833 2.01627 2.42503 3.25769 3.62134 3.88827
+ 4.09688 4.26773 4.40529 4.51734 4.60827 4.68313 4.74346
+ 4.79302 4.72815 4.68959 4.70421 4.81316 5.01375 5.14493
+ 5.10305 5.0699 5.04484 5.03751 5.03348 5.02504 5.01799 5.01271
+ 5.00895 5.00628 5.0044 5.00309 5.00216 5.00151 5.00105 5.00073
+ 5.00051 5.00034 5.00023 5.00015 5.0001 5.00007 5.00003 4.99998
+ 4.99993 4.99993 4.99995 4.99999 5.00001 5.00003 5.00002
+ 5.00001 5 5 5 5 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00017 5.17398
+ 4.94779 3.78508 1.52302 0.608808 0.244311 0.126053 0.0597175
+ 0.038422 0.0158174 0.00481338 0.00107847 0.000301256 0.000114861
+ 0.00059489 -0.118904 -0.147478 -0.158986 -0.080544 0.165361
+ 0.171378 0.0776087 0.0435738 0.0428235 0.0423755 0.0347695
+ 0.0225061 0.0155539 0.0121357 0.0107997 0.0103976 0.0124406
+ 0.016814 0.0167556 0.0149852 0.01459 0.0141182 0.0131934
+ 0.0120286 0.0108692 0.0097184 0.00855881 0.00744912 0.00643877
+ 0.00554044 0.00475165 0.00406535 0.00347158 0.00295981 0.00251995
+ 0.00214318 0.00182101 0.00154613 0.00131196 0.0011119 0.000941587
+ 0.000796999 0.000674582 0.000571283 0.000484276 0.000410649
+ 0.000347005 0.000292984 0.000246715 0.000208143 0.00017489
+ 0.000147412 0.000123854 0.000104332 8.77229e-05 7.40686e-05
+ 6.2637e-05 5.32e-05 4.53946e-05 3.88343e-05 3.31864e-05
+ 2.85905e-05 2.45725e-05 2.08671e-05 1.77301e-05 1.55911e-05
+ 1.40153e-05 1.29421e-05 1.18693e-05 1.09815e-05 1.03484e-05
+ 9.87664e-06 9.14446e-06 8.41228e-06 7.68011e-06 6.94793e-06
+ 6.21575e-06 5.48357e-06 4.7514e-06 4.38454e-06 4.04432e-06
+ 3.7041e-06 3.36388e-06 3.02366e-06 2.68344e-06 2.34322e-06
+ 2.15196e-06 2.03791e-06 1.92386e-06 1.80982e-06 1.69577e-06
+ 1.58173e-06 1.46768e-06 1.35363e-06 1.23959e-06 1.12554e-06
+ 1.0115e-06 8.9745e-07 7.83404e-07 6.69358e-07 4.76113e-07
+ -3.47071e-07 -1.17025e-06 -1.99344e-06 -2.81662e-06 0.0783754
+ 0.0500262 -0.0659563 -0.120914 0.0815957 0.0154255 0.00347177
+ 0.000840357 0.000214582 6.54655e-05 3.91709e-05 8.07396e-06
+ -4.44265e-07 1.74384e-05 -4.52725e-05 -0.119379 -0.147984
+ -0.159247 -0.0824604 0.169014 0.177628 0.0758742 0.010558
+ -0.0346506 -0.0710288 -0.0838952 -0.0599521 -0.034568 -0.0181615
+ -0.00968034 -0.00547115 -0.00333511 -0.00232468 -0.00181159
+ -0.00143841 -0.00116601 -0.000839755 -0.000569764 -0.000578683
+ -0.000490551 -0.000411712 -0.000437859 -0.000408185 -0.000356644
+ -0.000311332 -0.000269006 -0.000221396 -0.000210054 -0.0001923
+ -0.000175122 -0.000161039 -0.0001428 -0.000126123 -0.000127893
+ -8.14516e-05 -0.000120166 -0.000154909 -0.000112733 -8.40377e-05
+ -7.11342e-05 -8.09538e-05 -9.77789e-05 -9.82402e-05 -7.73531e-05
+ -5.28255e-05 -3.1096e-05 -1.87967e-05 -1.96552e-05 -4.16655e-05
+ -5.77185e-05 -5.24142e-05 -2.83153e-05 -1.90012e-05 -1.54415e-05
+ -2.52569e-05 -6.23747e-05 -0.000130543 -0.000149394 -0.000110886
+ -4.35517e-05 -4.17084e-05 -3.98651e-05 -3.80218e-05 -3.61785e-05
+ -3.43352e-05 -3.36249e-05 -3.32729e-05 -3.29208e-05 -3.25687e-05
+ -3.22166e-05 -3.17143e-05 -3.10258e-05 -3.03372e-05 -2.96486e-05
+ -2.89601e-05 -2.82715e-05 -2.75829e-05 -2.68944e-05 -2.62058e-05
+ -2.55173e-05 -2.48287e-05 -2.43043e-05 -2.38159e-05 -2.33276e-05
+ -2.28393e-05 -2.2351e-05 -2.18626e-05 -2.13743e-05 -2.0886e-05
+ -2.03977e-05 -1.99093e-05 -1.945e-05 -1.91122e-05 -1.87744e-05
+ -1.84366e-05 -1.80987e-05 -1.77609e-05 -1.74231e-05 -1.70853e-05
+ -1.67474e-05
+}
+.graph element create V19 -x x -y v19
+
+v20 set {
+ 1.86175 1.99724 2.17266 2.48439 3.15933 3.85231 4.38091
+ 4.69033 4.85034 4.92851 4.96453 4.98188 4.98736 4.991 4.99482
+ 4.9973 4.96422 4.89989 4.83907 4.83151 4.90868 5.04854 5.06104
+ 5.04571 5.03219 5.03025 5.02273 5.01707 5.0123 5.0087 5.00611
+ 5.00429 5.00301 5.00211 5.00148 5.00103 5.00072 5.0005 5.00035
+ 5.00024 5.00016 5.00011 5.00007 5.00005 5.00003 5.00001
+ 4.99999 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 5 5 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 5 4.99981 5.10081 5.10903 4.98404 5.00999 5.14946 4.36501
+ 2.23938 0.325144 0.00660272 -0.0102186 -0.0082401 -0.00556785
+ -0.00374178 -0.00264763 -0.00202823 -0.0182241 -0.0169551
+ -0.0150395 0.0103736 0.0877592 0.104382 0.0515938 0.0373818
+ 0.0411547 0.0397009 0.0308946 0.0205793 0.0154037 0.0129191
+ 0.0119327 0.011527 0.0124295 0.0161152 0.0161076 0.0145391
+ 0.0144541 0.0139287 0.0129215 0.0117239 0.0105795 0.00942983
+ 0.00827423 0.00718354 0.00619954 0.00532868 0.00456631 0.00390448
+ 0.00333254 0.00284003 0.00241714 0.00205524 0.0017458 0.00148202
+ 0.00125739 0.0010655 0.000902213 0.000763611 0.000646279
+ 0.000547291 0.000463934 0.000393401 0.000332424 0.000280655
+ 0.000236328 0.000199386 0.000167536 0.000141218 0.000118654
+ 9.99559e-05 8.40479e-05 7.09694e-05 6.00188e-05 5.09786e-05
+ 4.3502e-05 3.72191e-05 3.18114e-05 2.74071e-05 2.35539e-05
+ 1.99967e-05 1.69871e-05 1.49449e-05 1.3451e-05 1.24492e-05
+ 1.14256e-05 1.05669e-05 9.94487e-06 9.47514e-06 8.77318e-06
+ 8.07123e-06 7.36927e-06 6.66731e-06 5.96536e-06 5.2634e-06
+ 4.56144e-06 4.23044e-06 3.92649e-06 3.62254e-06 3.31858e-06
+ 3.01463e-06 2.71068e-06 2.40673e-06 2.23063e-06 2.12082e-06
+ 2.01102e-06 1.90121e-06 1.7914e-06 1.68159e-06 1.57178e-06
+ 1.46197e-06 1.35216e-06 1.24235e-06 1.13255e-06 1.02274e-06
+ 9.12929e-07 8.0312e-07 6.33171e-07 -1.51288e-08 -6.63428e-07
+ -1.31173e-06 -1.96003e-06 0.0437517 0.0265689 -0.0515377
+ -0.0658688 0.010727 -0.000511921 -8.36924e-05 2.13278e-05
+ 1.45207e-05 4.54862e-06 -6.14726e-06 2.0062e-06 1.02709e-06
+ 1.4152e-05 -3.08225e-05 -0.0166501 -0.0157139 -0.013957
+ 0.0107537 0.0873717 0.111302 0.0454129 -0.00530142 -0.0468336
+ -0.0790063 -0.0826944 -0.0534753 -0.0288705 -0.0149009 -0.00801592
+ -0.0046342 -0.00291835 -0.00213019 -0.00170055 -0.001352
+ -0.00110593 -0.000742655 -0.000532042 -0.000544742 -0.000479206
+ -0.000407307 -0.000403575 -0.000366209 -0.000324161 -0.000286183
+ -0.000247579 -0.000214281 -0.000203435 -0.000186896 -0.000171033
+ -0.00015779 -0.000145259 -0.000128069 -0.000122647 -9.89398e-05
+ -0.000114926 -0.000132195 -0.000107872 -8.91015e-05 -7.87996e-05
+ -8.14061e-05 -8.9098e-05 -8.83368e-05 -7.6122e-05 -6.14668e-05
+ -4.75402e-05 -3.81855e-05 -3.69696e-05 -4.78656e-05 -5.61346e-05
+ -5.35007e-05 -4.1459e-05 -3.35411e-05 -2.52374e-05 -2.37479e-05
+ -4.6406e-05 -9.41884e-05 -0.000109222 -8.52676e-05 -4.25166e-05
+ -4.10125e-05 -3.95085e-05 -3.80045e-05 -3.65004e-05 -3.49964e-05
+ -3.41627e-05 -3.3541e-05 -3.29193e-05 -3.22976e-05 -3.16758e-05
+ -3.10334e-05 -3.03653e-05 -2.96971e-05 -2.9029e-05 -2.83609e-05
+ -2.76928e-05 -2.70246e-05 -2.63565e-05 -2.56884e-05 -2.50203e-05
+ -2.43521e-05 -2.38716e-05 -2.34324e-05 -2.29932e-05 -2.25539e-05
+ -2.21147e-05 -2.16755e-05 -2.12362e-05 -2.0797e-05 -2.03578e-05
+ -1.99186e-05 -1.95079e-05 -1.9217e-05 -1.8926e-05 -1.8635e-05
+ -1.8344e-05 -1.8053e-05 -1.7762e-05 -1.74711e-05 -1.71801e-05
+
+}
+.graph element create V20 -x x -y v20
+
+v21 set {
+ 1.86175 1.73273 1.42016 1.02483 0.944013 0.274107 0.0823742
+ 0.0379366 0.020816 0.0132952 0.00955525 0.00717008 0.00592286
+ 0.00437379 0.00383557 0.00273694 -0.0037467 -0.0054191 -0.00131454
+ 0.0112179 0.0133918 0.00519747 -0.00260113 -0.00252847 -0.00181292
+ 0.000183398 -0.000667607 -0.000750747 -0.000594314 -0.000433904
+ -0.000308985 -0.000217858 -0.000152926 -0.000107454 -7.54076e-05
+ -5.2675e-05 -3.66299e-05 -2.54341e-05 -1.75095e-05 -1.18848e-05
+ -7.97289e-06 -5.30239e-06 -3.53615e-06 -2.38504e-06 -2.40158e-06
+ -3.84485e-06 -5.29435e-06 -2.57099e-06 1.95189e-06 3.55083e-06
+ 2.06179e-06 5.72753e-07 3.30469e-07 3.40296e-07 3.60221e-07
+ 4.86081e-07 6.1194e-07 7.37799e-07 8.63659e-07 9.89518e-07
+ 9.21274e-07 7.22275e-07 5.23276e-07 3.24277e-07 1.25278e-07
+ -5.59467e-08 -9.03265e-08 -1.24706e-07 -1.59086e-07 -1.93466e-07
+ -2.27846e-07 -2.62226e-07 -2.96605e-07 -3.30985e-07 -3.65365e-07
+ -3.99745e-07 -4.24266e-07 -3.82163e-07 -3.40061e-07 -2.97959e-07
+ -2.55857e-07 -2.13755e-07 -1.71652e-07 -1.2955e-07 -8.7448e-08
+ -4.53457e-08 -3.24353e-09 3.76901e-08 7.19937e-08 1.06297e-07
+ 1.40601e-07 1.74904e-07 2.09208e-07 2.43512e-07 2.77815e-07
+ 3.12119e-07 3.46422e-07 3.80726e-07 4.04507e-07 3.77191e-07
+ 3.49876e-07 3.22561e-07 2.95246e-07 2.67931e-07 2.40616e-07
+ 2.13301e-07 1.85986e-07 1.58671e-07 1.31356e-07 1.04041e-07
+ 7.67256e-08 4.94105e-08 2.20955e-08 -5.21962e-09 -3.25347e-08
+ -5.98498e-08 -8.71649e-08 -1.1448e-07 -1.41795e-07 -1.6911e-07
+ 7.87893e-06 0.0114592 -0.0245712 -0.111637 0.0961324 1.61168
+ 3.22343 4.20442 4.53535 4.83834 4.95464 4.98874 4.99746
+ 4.99883 4.99948 4.99815 4.98431 4.99298 4.99718 5.01948
+ 5.04749 5.008 4.98243 4.98985 4.99781 4.99887 4.99679 4.99616
+ 4.99743 4.99859 4.99936 4.99972 5.00058 5.00123 5.0002 4.99945
+ 4.99983 4.9998 4.99966 4.99958 4.99956 4.99956 4.99956 4.99958
+ 4.99961 4.99965 4.99969 4.99973 4.99977 4.9998 4.99983 4.99985
+ 4.99987 4.99989 4.99991 4.99992 4.99993 4.99994 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 4.99999 4.99998 4.99997 4.99996 5.01454
+ 4.99566 4.96796 4.99819 5.03232 5.00034 4.99867 4.99937
+ 4.99977 4.99992 4.99997 4.99999 5.00001 5.00021 4.99974
+ 4.98462 4.99301 4.99723 5.01936 5.04807 5.00929 4.9789 4.97876
+ 4.98244 4.9863 4.99575 5.0069 5.00863 5.00624 5.00357 5.0019
+ 5.00098 5.00048 5.00025 5.00016 5.00011 5.00013 5.00009
+ 4.99982 4.99994 5.00005 4.99994 4.99988 4.99989 4.99997
+ 5.00003 5.00005 5.00002 5.00001 5.00001 5.00001 4.99993
+ 4.99999 5 5.00021 4.99997 4.99981 5 5.00009 5.0001 5.00001
+ 4.99991 4.9999 5 5.00011 5.00017 5.00018 5.00018 5.00014
+ 5.00007 4.99999 4.9999 4.9999 5.00001 5.00016 5.00014 4.99999
+ 4.99993 4.99999 5.00009 5.00007 5.00006 5.00004 5.00003
+ 5.00001 5.00001 5 4.99999 4.99998 4.99997 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5
+}
+.graph element create V21 -x x -y v21
+
+v22 set {
+ 7.10441e-10 0.00107105 0.000637109 -0.00236346 -0.018079
+ -0.0120077 -0.00217059 0.00266679 0.00403383 0.00403836
+ 0.00356705 0.00303303 0.00244716 0.00198586 0.0016855 0.00136497
+ -3.96022e-05 -0.000367409 -3.77079e-05 0.00194085 0.00506964
+ -0.0400214 -0.0402572 0.0524434 0.286234 0.803011 1.44795
+ 2.02473 2.54768 3.02748 3.4415 3.78287 4.09667 4.35152 4.53987
+ 4.67614 4.77407 4.84319 4.89227 4.92702 4.95119 4.96764
+ 4.97846 4.98557 4.98982 4.99209 4.99371 4.99569 4.99727
+ 4.99802 4.99834 4.99867 4.99892 4.99915 4.99936 4.99939
+ 4.99943 4.99946 4.9995 4.99953 4.99957 4.9996 4.99963 4.99967
+ 4.9997 4.99973 4.99974 4.99975 4.99976 4.99977 4.99978 4.9998
+ 4.99981 4.99982 4.99983 4.99984 4.99985 4.99986 4.99986
+ 4.99986 4.99987 4.99987 4.99988 4.99988 4.99989 4.99989
+ 4.9999 4.9999 4.9999 4.9999 4.99991 4.99991 4.99991 4.99991
+ 4.99992 4.99992 4.99992 4.99992 4.99993 4.99993 4.99993
+ 4.99993 4.99993 4.99993 4.99993 4.99993 4.99994 4.99994
+ 4.99994 4.99994 4.99994 4.99994 4.99994 4.99994 4.99995
+ 4.99995 4.99995 4.99995 4.99995 4.99995 4.99995 5.00145
+ 5.00659 5.01209 5.01931 5.00279 4.99273 4.99217 4.99295
+ 4.99471 4.99594 4.99696 4.9978 4.99844 4.99891 4.99924 4.99635
+ 4.99699 4.99813 5.00068 5.00307 5.0588 4.96365 4.54012 3.6307
+ 2.35176 1.0322 0.354379 0.115986 0.0435668 0.0245112 0.020786
+ 0.0164656 0.0118409 0.00849698 0.00597078 0.0040105 0.0026076
+ 0.0016597 0.00118185 0.00121067 0.00153587 0.00174836 0.00136519
+ -0.000189116 -0.00315555 -0.00646603 -0.00898042 -0.010203
+ -0.0110896 -0.0123764 -0.00953841 -0.00225795 0.000818314
+ 0.00152252 0.00150269 0.00119025 0.000767068 0.000308852
+ -3.79272e-05 -0.00019691 -0.000186642 -9.73653e-05 -8.49784e-06
+ 2.04147e-05 -9.91086e-06 -1.55959e-05 -1.80499e-05 -1.77097e-05
+ -1.51548e-05 -1.1978e-05 -9.84916e-06 -1.29728e-05 -1.67235e-05
+ -1.74153e-05 -1.39958e-05 -5.92272e-06 -8.08216e-06 -1.53077e-05
+ -2.92531e-05 -3.91049e-05 -2.98935e-05 -7.32122e-06 3.18534e-05
+ 4.39134e-05 4.18753e-05 3.22759e-05 1.86766e-05 1.58432e-05
+ 1.30098e-05 1.01765e-05 7.34312e-06 4.50975e-06 1.67639e-06
+ -1.15697e-06 -1.23877e-06 -1.11991e-06 -1.00106e-06 -8.82208e-07
+ -7.63355e-07 -6.44502e-07 -5.2565e-07 -4.29318e-07 -3.44661e-07
+ -2.60004e-07 -1.75347e-07 -9.06904e-08 -6.03349e-09 7.86234e-08
+ 1.6328e-07 2.47937e-07 3.32594e-07 4.17251e-07 5.01908e-07
+ 5.86565e-07 6.71222e-07 7.36123e-07 6.43886e-07 5.5165e-07
+ 4.59414e-07 3.67178e-07 0.000334759 -4.60833e-05 -0.00106139
+ -0.00166624 0.000859563 0.00102606 0.00410037 0.00419931
+ 0.00518997 0.00459791 0.00503125 0.00523877 0.00452158 0.00339924
+ 0.00233399 0.000876915 0.000546439 0.000444299 0.000983968
+ 0.00119304 -0.0429422 -0.0403983 0.0534896 0.288013 0.807345
+ 1.44247 2.03448 2.57021 3.05049 3.47332 3.8131 4.1009 4.34677
+ 4.53512 4.67127 4.76531 4.82526 4.86593 4.89586 4.91904
+ 4.93806 4.95348 4.96597 4.97629 4.9843 4.98983 4.99335 4.9957
+ 4.99741 4.99864 4.99946 4.99994 5.00047 5.00073 5.00086
+ 5.00092 5.00094 5.00091 5.00087 5.00081 5.00074 5.00067
+ 5.00059 5.00052 5.00046 5.0004 5.00034 5.0003 5.00026 5.00022
+ 5.00019 5.00016 5.00014 5.00012 5.0001 5.00009 5.00007 5.00006
+ 5.00006 5.00005 5.00004 5.00004 5.00004 5.00003 5.00003
+ 5.00003 5.00002 5.00002 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00002 5.00002
+}
+.graph element create V22 -x x -y v22
+
+v23 set {
+ 5 5.00284 5.01266 5.01895 4.98936 4.99575 4.99217 4.99545
+ 4.99775 4.99894 4.99946 4.99968 4.99975 4.99977 4.99986
+ 4.9999 4.99528 4.99808 5.00039 5.00392 5.00512 4.99985 4.99863
+ 4.99942 4.99992 5.00017 4.99897 4.99803 4.99784 4.99739
+ 4.99883 5.00365 5.00298 5.00133 5.00048 5.00019 5.00008
+ 5.00005 5.00004 5.00003 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 4.99999 4.99997 4.99995 4.99996
+ 4.99998 5 5.00001 5.00001 5.00002 5.00002 5.00003 5.00003
+ 5.00002 5.00002 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00002
+ 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5.00217 5.00108 4.99547 4.99658 5.00667
+ 4.99641 4.99532 4.99938 5.00328 5.00222 5.00114 5.00052
+ 5.00024 5.00011 5.00009 4.99285 4.99591 4.99897 5.00403
+ 5.00786 5.00318 4.99942 4.9992 4.99949 5.001 5.00408 5.00319
+ 5.00063 4.99995 5.00014 4.99982 4.99832 4.99838 4.99865
+ 4.99912 4.99836 4.99735 4.99606 4.99814 5.00958 5.02973
+ 5.05293 5.06103 4.99342 4.80726 4.50744 4.07509 3.41358
+ 2.37924 1.03194 0.261552 0.142392 0.0904482 0.0555071 0.0322869
+ 0.018289 0.0113802 0.00875182 0.00757055 0.00629906 0.00523
+ 0.00403349 0.0031953 0.00280864 0.00286119 0.00250389 0.00202815
+ 0.001723 0.00147312 0.0012411 0.00104401 0.000886204 0.000758277
+ 0.000651915 0.00056348 0.000487966 0.000424048 0.000365613
+ 0.000308178 0.000258725 0.000228061 0.000207976 0.000198491
+ 0.00018518 0.000172716 0.000163197 0.000155007 0.000141734
+ 0.000128461 0.000115188 0.000101915 8.86417e-05 7.53686e-05
+ 6.20956e-05 5.69164e-05 5.23275e-05 4.77385e-05 4.31495e-05
+ 3.85605e-05 3.39716e-05 2.93826e-05 2.69449e-05 2.56224e-05
+ 2.42999e-05 2.29774e-05 2.16549e-05 2.03324e-05 1.90099e-05
+ 1.76873e-05 1.63648e-05 1.50423e-05 1.37198e-05 1.23973e-05
+ 1.10748e-05 9.75232e-06 8.48447e-06 7.65129e-06 6.81811e-06
+ 5.98494e-06 5.15176e-06 0.00056893 -0.00787906 -0.0217381
+ -0.0370066 -0.00770505 0.00659312 0.00975477 0.00949456
+ 0.00777552 0.00655645 0.00568776 0.00508782 0.00458121 0.00410187
+ 0.00365665 0.0015121 0.00160863 0.00263181 0.00638941 0.00772607
+ 0.00225583 0.0010843 0.000882939 0.000801563 0.00075632
+ 0.000554992 0.000435131 0.0003474 0.000217667 0.000491602
+ 0.0012267 0.00250446 0.000212058 -0.0174972 -0.0527527 -0.0479071
+ 0.194908 1.45838 3.40677 4.49242 4.86894 4.97215 5.01218
+ 5.04342 5.06228 5.03069 4.87169 4.57056 4.11523 3.38264
+ 2.19691 0.715839 0.172818 0.102162 0.0627162 0.0363388 0.020289
+ 0.0119414 0.00826608 0.0066417 0.00549092 0.00492505 0.00439443
+ 0.0037156 0.00306471 0.00247451 0.00195965 0.0014822 0.0010815
+ 0.000904464 0.0010514 0.00152308 0.00120752 0.000228447
+ -0.00102833 -0.00116644 -0.00042067 4.78758e-05 5.09599e-05
+ -4.45756e-05 -3.22966e-06 3.81163e-05 7.94622e-05 0.000120808
+ 0.000162154 0.000161895 0.000148481 0.000135068 0.000121654
+ 0.000108241 9.81453e-05 9.2164e-05 8.61827e-05 8.02014e-05
+ 7.42201e-05 6.82388e-05 6.22576e-05 5.62763e-05 5.0295e-05
+ 4.43137e-05 3.83324e-05 3.54323e-05 3.321e-05 3.09877e-05
+ 2.87654e-05 2.65431e-05 2.43209e-05 2.20986e-05 1.98763e-05
+ 1.7654e-05 1.54317e-05 1.34612e-05 1.25441e-05 1.1627e-05
+ 1.07099e-05 9.79276e-06 8.87564e-06 7.95851e-06 7.04139e-06
+ 6.12427e-06
+}
+.graph element create V23 -x x -y v23
+
+v24 set {
+ 5 5.01099 5.00866 4.97845 4.92369 4.9273 4.97413 4.9929
+ 4.99826 4.99958 4.99978 5.00005 4.99968 4.99959 5.00014
+ 4.99979 4.99914 4.99982 5.00023 5.00295 5.00664 4.99854
+ 4.99647 5.00438 5.01722 5.03681 5.04766 5.04799 5.04867
+ 5.04873 5.04685 5.04413 5.0367 5.02505 5.01726 5.01183 5.00806
+ 5.00549 5.00371 5.00246 5.00162 5.00105 5.00069 5.00045
+ 5.00031 5.00024 5.00019 5.00012 5.00007 5.00004 5.00001
+ 4.99998 4.99999 4.99999 5 5.00001 5.00001 5.00002 5.00002
+ 5.00003 5.00003 5.00003 5.00002 5.00002 5.00001 5.00001
+ 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5.00418 4.99953 4.99152
+ 4.99807 5.00497 5.00112 5.00055 5.00038 5.00018 5.00006
+ 5.00006 5.00007 5.00006 5.00004 5.00004 4.99853 4.99945
+ 4.99998 5.00304 5.00935 5.00742 4.99181 4.97421 4.93603
+ 4.8853 4.8927 4.93984 4.97458 4.99039 4.99614 4.99801 4.99851
+ 4.99869 4.99924 5.00108 5.00181 5.00119 5.00059 5.00031
+ 5.00022 5.00018 5.00011 5.00001 5.00006 4.99981 4.99977
+ 4.99982 5.00012 4.99993 5.00008 5.00043 5.00048 5.00024
+ 5.00008 4.99984 4.99993 5.00011 4.99996 4.9998 4.99977 4.9998
+ 4.99993 5.00008 5.00011 5.00002 4.99995 4.99989 4.99993
+ 5 5.00007 5.00009 4.99994 4.99977 4.9997 4.99975 4.99996
+ 4.99996 4.99988 4.9997 4.99952 4.9995 4.99956 4.99973 4.99988
+ 5.00005 5.00025 5.00042 5.00036 5.00031 5.00025 5.0002 5.00014
+ 5.00009 5.00003 5.00002 5.00001 5.00001 5 4.99999 4.99998
+ 4.99998 4.99997 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5.00284
+ 5.00442 5.00381 4.98997 4.99092 5.00733 5.07791 4.98237
+ 4.86434 4.76835 4.74067 4.79278 4.85094 4.90068 4.93603
+ 4.95698 4.96984 4.97856 4.98869 4.99904 5.0005 4.99524 5.00181
+ 5.01878 5.05177 5.07986 4.98917 4.56217 3.68 2.3539 1.18541
+ 0.505772 0.221044 0.115287 0.0760938 0.0589194 0.0476784
+ 0.0457213 0.0412911 0.033889 0.0259741 0.0191452 0.0139018
+ 0.0100235 0.00711788 0.00497657 0.00349368 0.00250021 0.00176179
+ 0.00121843 0.000838368 0.000582711 0.000423458 0.000294608
+ 0.000201251 0.000133748 8.6227e-05 5.44252e-05 3.30514e-05
+ 1.93926e-05 1.09814e-05 5.29857e-06 1.92247e-06 3.08708e-07
+ -3.74311e-07 -6.11121e-07 -7.27807e-07 -4.87604e-07 -4.80493e-07
+ -9.15925e-07 -2.03774e-06 -4.01128e-06 -2.46644e-06 2.10626e-06
+ 8.22422e-06 1.04922e-05 9.83047e-06 7.27106e-06 3.29654e-06
+ -2.06736e-06 -2.18019e-06 -2.29303e-06 -2.40586e-06 -2.51869e-06
+ -2.63153e-06 -2.24615e-06 -1.70325e-06 -1.16036e-06 -6.17468e-07
+ -7.45754e-08 2.45198e-07 2.88285e-07 3.31373e-07 3.7446e-07
+ 4.17548e-07 4.60635e-07 5.03723e-07 5.4681e-07 5.89898e-07
+ 6.32985e-07 6.76073e-07 6.19054e-07 5.4001e-07 4.60967e-07
+ 3.81923e-07 3.02879e-07 2.23836e-07 1.44792e-07 6.57488e-08
+ -1.32948e-08 -9.23383e-08 -1.6698e-07 -2.23206e-07 -2.79432e-07
+ -3.35658e-07 -3.91884e-07 -4.48109e-07 -5.04335e-07 -5.60561e-07
+ -6.16787e-07
+}
+.graph element create V24 -x x -y v24
+
+v25 set {
+ 1.34824 1.35838 1.36465 1.34675 1.29167 1.23161 1.2201 1.2185
+ 1.2181 1.21798 1.21793 1.21788 1.21785 1.21782 1.21779 1.21776
+ 1.21655 1.21656 1.21669 1.21871 1.22421 1.22247 1.21858
+ 1.2228 1.23803 1.27737 1.10647 0.395248 0.0600669 0.027687
+ 0.0192374 0.015425 0.0130881 0.00977445 0.00696598 0.00491122
+ 0.00341952 0.00237078 0.00162339 0.00109178 0.000726647
+ 0.000478886 0.00031568 0.000207902 0.000143494 0.000109768
+ 8.62987e-05 5.69775e-05 3.36547e-05 2.30356e-05 1.86108e-05
+ 1.41861e-05 1.08293e-05 7.68835e-06 4.79593e-06 4.51019e-06
+ 4.22444e-06 3.9387e-06 3.65295e-06 3.36721e-06 3.04559e-06
+ 2.69981e-06 2.35403e-06 2.00825e-06 1.66247e-06 1.34508e-06
+ 1.26225e-06 1.17941e-06 1.09657e-06 1.01373e-06 9.30893e-07
+ 8.48054e-07 7.65216e-07 6.82378e-07 5.9954e-07 5.16702e-07
+ 4.37489e-07 3.82774e-07 3.2806e-07 2.73346e-07 2.18632e-07
+ 1.63917e-07 1.09203e-07 5.4489e-08 -2.2523e-10 -5.49395e-08
+ -1.09654e-07 -1.52862e-07 -1.3079e-07 -1.08718e-07 -8.6646e-08
+ -6.45739e-08 -4.25019e-08 -2.04298e-08 1.64229e-09 2.37144e-08
+ 4.57864e-08 6.78585e-08 8.71693e-08 9.30725e-08 9.89758e-08
+ 1.04879e-07 1.10782e-07 1.16685e-07 1.22589e-07 1.28492e-07
+ 1.34395e-07 1.40298e-07 1.46201e-07 1.52105e-07 1.58008e-07
+ 1.63911e-07 1.69814e-07 1.75718e-07 1.81621e-07 1.87524e-07
+ 1.93427e-07 1.9933e-07 2.05234e-07 2.11137e-07 2.19788e-07
+ 0.000393944 -0.000218983 -0.00105784 0.00172403 -0.00027134
+ -0.000204147 8.79968e-06 5.93762e-05 5.83554e-05 4.13815e-05
+ 3.71369e-05 3.03372e-05 2.25336e-05 1.5986e-05 1.07284e-05
+ -7.5239e-05 5.60593e-05 6.97571e-05 0.000667617 0.000960856
+ 0.00131749 -0.00759564 -0.0217897 -0.0450321 -0.076646 -0.128569
+ -0.186391 -0.202175 -0.206953 -0.2082 -0.208416 -0.208669
+ -0.208934 -0.209111 -0.209234 -0.209329 -0.209389 -0.209416
+ -0.2094 -0.209329 -0.20926 -0.209204 -0.209208 -0.209285
+ -0.209454 -0.209641 -0.20977 -0.209811 -0.209833 -0.209887
+ -0.209653 -0.209127 -0.208893 -0.208811 -0.208777 -0.208758
+ -0.208747 -0.20874 -0.208726 -0.208697 -0.208657 -0.208611
+ -0.208565 -0.208524 -0.208488 -0.208451 -0.208412 -0.208373
+ -0.208333 -0.208294 -0.208256 -0.208219 -0.208183 -0.208145
+ -0.208107 -0.208066 -0.208029 -0.207993 -0.207959 -0.207923
+ -0.207883 -0.207838 -0.207789 -0.207747 -0.20771 -0.207675
+ -0.207642 -0.207605 -0.207568 -0.207531 -0.207494 -0.207457
+ -0.20742 -0.207383 -0.207346 -0.207308 -0.207271 -0.207233
+ -0.207196 -0.207158 -0.207121 -0.207084 -0.207046 -0.207009
+ -0.206972 -0.206935 -0.206898 -0.206861 -0.206823 -0.206786
+ -0.206749 -0.206712 -0.206675 -0.206638 -0.2066 -0.206563
+ -0.206526 -0.206489 -0.206452 -0.206415 -0.203384 -0.20015
+ -0.196872 -0.205024 -0.210727 -0.206779 -0.0685263 0.586138
+ 1.4665 2.22945 2.77554 3.076 3.24926 3.34515 3.40164 3.43006
+ 3.43713 3.43075 3.42886 3.4384 3.46567 3.49025 3.51287 3.53821
+ 3.57841 3.39846 2.80753 2.22947 1.7549 1.30429 0.707786
+ 0.303206 0.131352 0.0671706 0.0429955 0.032461 0.0257161
+ 0.0239521 0.0217397 0.0179705 0.0138745 0.0102813 0.00749643
+ 0.0054328 0.00386817 0.0027004 0.00189442 0.00135552 0.000954715
+ 0.000659981 0.000453435 0.000313993 0.000231347 0.000159665
+ 0.000108122 7.10528e-05 4.50233e-05 2.77892e-05 1.62765e-05
+ 8.9893e-06 4.5471e-06 1.54614e-06 -1.6542e-07 -8.68508e-07
+ -1.04369e-06 -9.63086e-07 -8.44294e-07 -6.57339e-07 -7.35885e-07
+ -9.80056e-07 -1.39772e-06 -2.10199e-06 -1.37474e-06 6.13269e-07
+ 3.3028e-06 4.60941e-06 4.91053e-06 4.14186e-06 2.45258e-06
+ -8.7388e-09 -3.59647e-07 -7.10554e-07 -1.06146e-06 -1.41237e-06
+ -1.76328e-06 -1.63073e-06 -1.34534e-06 -1.05995e-06 -7.74561e-07
+ -4.8917e-07 -2.95733e-07 -2.16326e-07 -1.3692e-07 -5.75135e-08
+ 2.18929e-08 1.01299e-07 1.80706e-07 2.60112e-07 3.39519e-07
+ 4.18925e-07 4.98332e-07 4.83984e-07 4.4901e-07 4.14035e-07
+ 3.79061e-07 3.44087e-07 3.09112e-07 2.74138e-07 2.39163e-07
+ 2.04189e-07 1.69215e-07 1.26002e-07 4.83213e-08 -2.9359e-08
+ -1.07039e-07 -1.8472e-07 -2.624e-07 -3.4008e-07 -4.1776e-07
+ -4.95441e-07
+}
+.graph element create V25 -x x -y v25
+
+v26 set {
+ 7.10441e-10 0.000309731 -0.000308186 -0.001694 -0.00360784
+ 8.40909e-05 0.00203175 0.0012896 0.000596548 0.000277191
+ 0.000161134 0.000120439 8.4915e-05 9.49929e-05 6.18812e-05
+ 1.65433e-05 1.89682e-05 3.97578e-05 4.95446e-05 0.000225325
+ 0.000214579 -0.00230134 -0.000451102 0.00997237 0.0341443
+ 0.0449314 0.0424411 0.0341996 0.0315315 0.0308892 0.0291614
+ 0.024365 0.0190282 0.0188976 0.017238 0.0138526 0.0105645
+ 0.00778548 0.00561753 0.0039871 0.00279554 0.00194075 0.0013468
+ 0.000934775 0.000664723 0.000498911 0.000377384 0.000254183
+ 0.000163421 0.000120773 9.65058e-05 7.22384e-05 5.60316e-05
+ 4.14549e-05 2.79516e-05 2.57096e-05 2.34677e-05 2.12257e-05
+ 1.89837e-05 1.67417e-05 1.46737e-05 1.27228e-05 1.07719e-05
+ 8.82099e-06 6.87009e-06 5.0896e-06 4.71705e-06 4.34451e-06
+ 3.97196e-06 3.59941e-06 3.22686e-06 2.85431e-06 2.48176e-06
+ 2.10921e-06 1.73666e-06 1.36411e-06 1.02855e-06 9.42931e-07
+ 8.57316e-07 7.71701e-07 6.86086e-07 6.00471e-07 5.14856e-07
+ 4.29241e-07 3.43626e-07 2.58011e-07 1.72396e-07 9.85409e-08
+ 9.14091e-08 8.42773e-08 7.71456e-08 7.00138e-08 6.2882e-08
+ 5.57503e-08 4.86185e-08 4.14867e-08 3.4355e-08 2.72232e-08
+ 2.05821e-08 1.63235e-08 1.2065e-08 7.80643e-09 3.54786e-09
+ -7.10696e-10 -4.96926e-09 -9.22782e-09 -1.34864e-08 -1.77449e-08
+ -2.20035e-08 -2.62621e-08 -3.05206e-08 -3.47792e-08 -3.90378e-08
+ -4.32963e-08 -4.75549e-08 -5.18134e-08 -5.6072e-08 -6.03306e-08
+ -6.45891e-08 -6.88477e-08 -8.76373e-06 0.000131607 -0.00021685
+ -0.000433027 0.00047234 0.000211593 -0.000189601 3.2492e-05
+ 0.000575955 7.72235e-05 -0.000285172 -0.000242061 -0.000135112
+ -3.50117e-05 -2.75868e-05 5.48974e-05 1.80604e-07 5.48911e-05
+ 3.97478e-05 0.000192909 0.000297932 0.00402253 -0.0122366
+ -0.047853 -0.0963082 -0.108071 -0.0567275 -0.0239271 -0.0178628
+ -0.0233027 -0.031853 -0.0400843 -0.0482725 -0.0576154 -0.0627218
+ -0.0511236 -0.0279524 -0.0150986 -0.00931091 -0.00652876
+ -0.00479286 -0.00344346 -0.00249578 -0.0019532 -0.00157977
+ -0.00131848 -0.00111251 -0.000939229 -0.000797445 -0.000708384
+ -0.000630452 -0.000539722 -0.000508862 -0.000480596 -0.000439484
+ -0.000407217 -0.000363866 -0.000329506 -0.000318642 -0.000307362
+ -0.000286511 -0.000266253 -0.000242943 -0.000218107 -0.000204661
+ -0.00020241 -0.000194435 -0.000185062 -0.000173042 -0.000160549
+ -0.000151407 -0.000145626 -0.000145976 -0.000147342 -0.000145288
+ -0.000137979 -0.000124481 -0.000123218 -0.000127453 -0.000139006
+ -0.000145486 -0.000129764 -9.82749e-05 -4.72596e-05 -3.08671e-05
+ -3.28834e-05 -4.52254e-05 -6.25389e-05 -6.32516e-05 -6.39643e-05
+ -6.4677e-05 -6.53897e-05 -6.61023e-05 -6.6815e-05 -6.75277e-05
+ -6.61005e-05 -6.45173e-05 -6.29341e-05 -6.13509e-05 -5.97676e-05
+ -5.81844e-05 -5.66012e-05 -5.54231e-05 -5.4455e-05 -5.3487e-05
+ -5.25189e-05 -5.15508e-05 -5.05828e-05 -4.96147e-05 -4.86466e-05
+ -4.76785e-05 -4.67105e-05 -4.57424e-05 -4.47743e-05 -4.38063e-05
+ -4.28382e-05 -4.18821e-05 -4.10211e-05 -4.016e-05 -3.9299e-05
+ -3.8438e-05 4.29885e-05 5.14113e-05 -0.000127986 -0.000611463
+ -0.000149428 0.000882394 0.00297059 -0.00405825 -0.00591067
+ -0.00546997 -0.00158744 0.00190677 0.00298403 0.00268595
+ 0.00196161 0.00130289 0.000783347 0.000520683 0.000565306
+ 0.00053419 -0.00224696 -0.000920818 0.0132755 0.0322504
+ 0.0442808 0.0638615 0.0701007 0.0539356 0.0247771 0.056244
+ 0.294266 0.831368 1.45424 2.02898 2.54559 2.9937 3.35333
+ 3.72609 4.06363 4.32789 4.52413 4.66504 4.7652 4.83637 4.88631
+ 4.92109 4.94464 4.96046 4.97218 4.98079 4.98679 4.99076
+ 4.99361 4.99555 4.99686 4.99783 4.99853 4.99902 4.99936
+ 4.99959 4.99973 4.99983 4.9999 4.99993 4.99996 4.99998 5
+ 5.00001 5 4.99999 4.99997 4.99994 4.99993 4.99994 4.99996
+ 4.99999 5.00004 5.00006 5.00005 5.00003 5.00002 5.00001
+ 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 5 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99998
+
+}
+.graph element create V26 -x x -y v26
+
+v27 set {
+ 5 4.99984 4.99796 4.99478 4.9889 4.98738 4.98896 4.99087
+ 4.99262 4.99419 4.99552 4.99659 4.99743 4.99807 4.99855
+ 4.9989 4.99894 4.99908 4.99935 5.00001 5.0007 5.00132 5.00032
+ 4.99976 5.00134 5.00339 5.00315 5.00157 5.00091 5.00058
+ 5.00012 4.99944 4.99886 4.9994 4.99934 4.99899 4.99876 4.99868
+ 4.99872 4.99883 4.99898 4.99914 4.9993 4.99944 4.99956 4.99967
+ 4.99976 4.99982 4.99986 4.9999 4.99993 4.99997 4.99997 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5.00009 5.00028 5.00015 4.99983
+ 5.00036 4.99996 4.99834 4.99783 5.00383 5.00734 5.00387
+ 5.00058 4.99893 4.99836 4.99832 4.99854 4.99873 4.99905
+ 4.99927 4.99952 4.99969 4.99834 4.99536 4.99163 4.99073
+ 5.0053 5.03631 5.03103 4.9008 4.62503 4.21887 3.70902 3.09967
+ 2.35791 1.41912 0.519675 0.210458 0.131362 0.0980819 0.0708209
+ 0.0471701 0.0323272 0.0253535 0.0199144 0.0152615 0.0117228
+ 0.00917696 0.00738117 0.00609292 0.00512664 0.00436184 0.0037961
+ 0.00331639 0.00289006 0.0025477 0.00226529 0.00202925 0.00182793
+ 0.00165474 0.00150531 0.00137529 0.00125983 0.00115603 0.00106455
+ 0.000982977 0.000911255 0.000846819 0.000790092 0.000738698
+ 0.000692816 0.00065107 0.000613595 0.000579642 0.000548935
+ 0.00052106 0.000495598 0.000472174 0.000450849 0.000431118
+ 0.000412667 0.000395868 0.000381319 0.000368487 0.000357327
+ 0.000344212 0.000330334 0.00031622 0.000303298 0.000295809
+ 0.00028832 0.000280831 0.000273342 0.000265853 0.000258364
+ 0.000250875 0.000245118 0.000239488 0.000233857 0.000228227
+ 0.000222596 0.000216966 0.000211336 0.000207047 0.000203455
+ 0.000199863 0.00019627 0.000192678 0.000189085 0.000185493
+ 0.0001819 0.000178308 0.000174716 0.000171123 0.000167531
+ 0.000163938 0.000160346 0.000156835 0.000153973 0.00015111
+ 0.000148248 0.000145385 0.000296579 -3.96718e-05 -0.000449085
+ 0.000323433 0.000750086 0.000268264 0.000149028 -0.000100249
+ 7.00956e-05 0.00012605 0.00022592 0.000193036 0.000120453
+ 8.07865e-05 7.65771e-05 -3.27828e-05 0.000116759 0.000169498
+ 0.000409804 0.000414965 0.00092323 -0.00590633 -0.0175477
+ -0.032433 -0.0559842 -0.0820373 0.0688484 0.626629 1.32929
+ 2.01657 2.60925 3.12329 3.38952 3.14128 2.38463 1.23802
+ 0.316019 0.107832 0.0694707 0.051837 0.035247 0.0209999
+ 0.0116618 0.00967674 0.00789182 0.00574566 0.00386872 0.00258612
+ 0.00167126 0.00104169 0.000641093 0.000401246 0.000277928
+ 0.000171775 0.000102266 5.89376e-05 3.29258e-05 1.80463e-05
+ 1.0057e-05 6.4571e-06 5.10093e-06 4.06791e-06 3.62716e-06
+ 3.63321e-06 3.99625e-06 4.64368e-06 5.20886e-06 4.77728e-06
+ 3.23919e-06 1.14113e-06 -1.29416e-06 -4.15607e-06 -1.88532e-06
+ 5.24411e-06 1.38678e-05 1.28823e-05 3.6758e-06 -2.52285e-06
+ -3.97133e-06 -4.03071e-06 -3.37154e-06 -2.71238e-06 -2.05321e-06
+ -1.39404e-06 -7.34872e-07 -3.73325e-07 -1.05873e-07 1.61578e-07
+ 4.2903e-07 6.96482e-07 8.18468e-07 7.60065e-07 7.01662e-07
+ 6.43258e-07 5.84855e-07 5.26452e-07 4.68049e-07 4.09646e-07
+ 3.51243e-07 2.9284e-07 2.34437e-07 1.71213e-07 1.06928e-07
+ 4.2644e-08 -2.16403e-08 -8.59247e-08 -1.50209e-07 -2.14493e-07
+ -2.78778e-07 -3.43062e-07 -4.07346e-07 -4.55065e-07 -4.3348e-07
+ -4.11896e-07 -3.90311e-07 -3.68726e-07 -3.47141e-07 -3.25556e-07
+ -3.03971e-07 -2.82386e-07
+}
+.graph element create V27 -x x -y v27
+
+v28 set {
+ 0.368163 0.361756 0.327463 0.269513 0.149476 0.0805716 0.0501146
+ 0.03403 0.0230886 0.0160474 0.0116071 0.00870013 0.00679614
+ 0.00542384 0.00432512 0.00340653 -0.00129719 -0.00399429
+ -0.00318719 0.00443085 0.0150156 0.0334147 0.0132288 -0.0189751
+ -0.0508377 -0.0252174 -0.0142489 -0.00675908 -0.0038653
+ -0.00243423 -0.00168891 -0.00120901 -0.000900426 -0.000685575
+ -0.000557595 -0.000457268 -0.000377427 -0.000315269 -0.000266613
+ -0.000228397 -0.000198283 -0.000174248 -0.000154886 -0.00013892
+ -0.000125864 -0.000115189 -0.000105841 -9.66611e-05 -8.84262e-05
+ -8.23872e-05 -7.74668e-05 -7.25463e-05 -6.79992e-05 -6.35276e-05
+ -5.92413e-05 -5.68994e-05 -5.45574e-05 -5.22154e-05 -4.98735e-05
+ -4.75315e-05 -4.54981e-05 -4.36726e-05 -4.18471e-05 -4.00216e-05
+ -3.81961e-05 -3.64559e-05 -3.54209e-05 -3.43858e-05 -3.33508e-05
+ -3.23157e-05 -3.12807e-05 -3.02456e-05 -2.92105e-05 -2.81755e-05
+ -2.71404e-05 -2.61054e-05 -2.51232e-05 -2.44984e-05 -2.38736e-05
+ -2.32487e-05 -2.26239e-05 -2.19991e-05 -2.13742e-05 -2.07494e-05
+ -2.01246e-05 -1.94998e-05 -1.88749e-05 -1.82865e-05 -1.79044e-05
+ -1.75224e-05 -1.71403e-05 -1.67582e-05 -1.63762e-05 -1.59941e-05
+ -1.56121e-05 -1.523e-05 -1.4848e-05 -1.44659e-05 -1.41138e-05
+ -1.39075e-05 -1.37011e-05 -1.34947e-05 -1.32883e-05 -1.30819e-05
+ -1.28755e-05 -1.26691e-05 -1.24627e-05 -1.22563e-05 -1.205e-05
+ -1.18436e-05 -1.16372e-05 -1.14308e-05 -1.12244e-05 -1.1018e-05
+ -1.08116e-05 -1.06052e-05 -1.03988e-05 -1.01924e-05 -9.98605e-06
+ -9.77966e-06 -2.85319e-05 0.00281092 0.00180106 -0.000981083
+ 0.00551926 -0.00119763 -0.0295069 -0.0367677 0.064749 0.119022
+ 0.0882007 0.0552062 0.03418 0.0223243 0.015545 0.011949
+ 0.00757134 0.00667655 0.00583243 0.00644443 0.00650959 -0.0302575
+ -0.0437806 -0.0355466 0.0381776 0.282109 0.674178 1.07582
+ 1.45189 1.789 2.08649 2.34663 2.57245 2.81211 3.04778 3.2523
+ 3.45877 3.65593 3.83396 3.9923 4.13368 4.25864 4.36719 4.46064
+ 4.54086 4.60962 4.66835 4.71838 4.76094 4.79716 4.82796
+ 4.85413 4.87634 4.89518 4.91116 4.92476 4.93631 4.94608
+ 4.95434 4.9613 4.96715 4.97211 4.97638 4.98001 4.98312 4.98571
+ 4.98795 4.98979 4.99138 4.99269 4.99381 4.99474 4.99551
+ 4.99615 4.99668 4.99713 4.99752 4.99783 4.99811 4.99836
+ 4.99858 4.99873 4.99884 4.99892 4.999 4.99907 4.99912 4.99916
+ 4.99921 4.99926 4.99932 4.99937 4.99942 4.99948 4.99953
+ 4.99956 4.99958 4.99961 4.99963 4.99966 4.99968 4.99971
+ 4.99972 4.99973 4.99974 4.99975 4.99976 4.99977 4.99978
+ 4.99979 4.9998 4.9998 4.99981 4.99982 4.99983 4.99984 4.99985
+ 4.99986 4.99986 4.99987 4.99987 5.00498 5.00354 4.99359
+ 4.98981 5.00498 5.00099 5.00041 5.00022 5.00015 5.00012
+ 5.0001 5.00008 5.00005 5.00003 5 4.99431 4.99459 4.99591
+ 5.00087 5.01029 5.03935 4.92784 4.51643 3.78356 2.68745
+ 1.43417 0.583128 0.205094 0.0777337 0.0391566 0.02723 0.023883
+ 0.018808 0.010165 0.00254623 -0.00377463 -0.0038097 0.00144145
+ 0.00267231 0.00193045 0.00144538 0.00121758 0.00112893 0.00109424
+ 0.0010226 0.000948072 0.000882573 0.000826996 0.000776391
+ 0.000729719 0.000686499 0.000647333 0.000610108 0.000575631
+ 0.000545069 0.000515485 0.000488514 0.000465316 0.000443215
+ 0.000422454 0.00040292 0.00038488 0.000368472 0.000353628
+ 0.000339643 0.000326197 0.000313483 0.000302884 0.000294038
+ 0.000284003 0.000270941 0.000254925 0.000246511 0.000244089
+ 0.000245538 0.000242099 0.000235728 0.000227482 0.000218001
+ 0.000207257 0.000202127 0.000196997 0.000191868 0.000186738
+ 0.000181608 0.00017758 0.000173899 0.000170219 0.000166538
+ 0.000162857 0.000159576 0.00015679 0.000154005 0.000151219
+ 0.000148433 0.000145647 0.000142861 0.000140076 0.00013729
+ 0.000134504 0.000131718 0.000129603 0.000127635 0.000125668
+ 0.0001237 0.000121732 0.000119765 0.000117797 0.000115829
+ 0.000113862 0.000111894 0.000109993 0.000108372 0.000106751
+ 0.00010513 0.000103509 0.000101887 0.000100266 9.86449e-05
+ 9.70237e-05
+}
+.graph element create V28 -x x -y v28
+
+v29 set {
+ 5 4.99899 4.99654 4.99327 4.9863 4.98954 4.99212 4.99378
+ 4.9951 4.99624 4.99715 4.99786 4.99839 4.99879 4.99909 4.99931
+ 4.99922 4.99933 4.99971 5.00064 5.00084 5.00123 4.99865
+ 4.99853 4.99983 5.00457 5.00242 5.00105 5.00062 5.00042
+ 4.99971 4.9994 4.9992 4.9996 4.99955 4.99932 4.99918 4.99915
+ 4.99919 4.99927 4.99937 4.99948 4.99957 4.99966 4.99974
+ 4.9998 4.99985 4.99989 4.99992 4.99993 4.99994 4.99994 4.99996
+ 4.99998 5 5 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5
+ 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 4.9997 4.99998 4.99954 4.99963
+ 5.00059 4.99945 4.99732 4.99957 5.00919 5.00558 5.00033
+ 4.99851 4.9983 4.99854 4.99871 4.99928 4.99914 4.99939 4.99952
+ 4.9998 4.99976 4.99744 4.99598 4.99478 4.99806 5.01911 5.04602
+ 5.05469 5.01317 4.89484 4.69655 4.42036 4.06069 3.60793
+ 3.12531 2.72975 2.45187 2.25081 2.09841 1.98509 1.90211
+ 1.84084 1.79411 1.7574 1.72763 1.70283 1.68188 1.66389 1.64823
+ 1.63438 1.62201 1.61088 1.60081 1.59163 1.58323 1.57549
+ 1.56835 1.56173 1.55558 1.54985 1.54451 1.53951 1.53479
+ 1.53035 1.52615 1.5222 1.51845 1.5149 1.51153 1.50834 1.50529
+ 1.5024 1.49964 1.497 1.49449 1.49208 1.48977 1.48755 1.48542
+ 1.48336 1.48138 1.47948 1.47765 1.4759 1.47419 1.47255 1.47096
+ 1.46949 1.46823 1.46696 1.4657 1.46444 1.46317 1.46191 1.46065
+ 1.45956 1.4585 1.45743 1.45636 1.45529 1.45422 1.45315 1.45226
+ 1.45145 1.45064 1.44983 1.44902 1.44821 1.4474 1.44659 1.44579
+ 1.44498 1.44417 1.44336 1.44255 1.44174 1.44094 1.44019
+ 1.43944 1.43868 1.43793 1.43765 1.43679 1.43515 1.43405
+ 1.43478 1.43387 1.43345 1.43184 1.43086 1.43021 1.43003
+ 1.42988 1.42944 1.42883 1.42818 1.42702 1.42642 1.42595
+ 1.42586 1.42616 1.42783 1.41733 1.38106 1.30738 1.3877 2.09819
+ 3.05285 3.58059 3.77601 3.87609 4.02557 4.24887 4.4608 4.60411
+ 4.72109 4.8255 4.90465 4.97379 5.01253 5.01532 5.01239 5.0092
+ 5.00665 5.00474 5.00333 5.00232 5.00163 5.00117 5.00082
+ 5.00057 5.00039 5.00027 5.00019 5.00013 5.00009 5.00006
+ 5.00004 5.00003 5.00002 5.00001 5.00001 5 5 5 4.99998 4.99995
+ 4.99992 4.99996 5.00005 5.00012 5.00008 4.99996 4.9999 4.99985
+ 4.99986 4.99997 5.00021 5.0003 5.00024 5.00009 5.00007 5.00005
+ 5.00003 5.00001 4.99998 4.99998 4.99998 4.99999 4.99999
+ 5 5 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00002 5.00002 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998
+}
+.graph element create V29 -x x -y v29
+v30 set {
+ 7.10441e-10 5.70385e-05 0.000226143 0.000131916 -0.000887764
+ -8.01837e-05 -3.49653e-05 9.40039e-05 0.000118663 0.000108025
+ 8.6059e-05 6.33268e-05 4.99295e-05 3.16843e-05 3.60692e-05
+ 2.07572e-05 -8.6375e-05 3.44583e-05 8.07397e-05 0.000196296
+ 0.000115615 -7.12768e-05 -0.000129812 -4.18679e-05 7.94364e-05
+ 0.000182034 -5.41226e-05 -0.000451819 -0.000713937 -0.00129863
+ -0.00262186 -0.00213417 -0.00133767 0.000775698 0.000969902
+ 0.000549281 0.000280946 0.000140321 8.6919e-05 7.22446e-05
+ 6.5631e-05 6.45263e-05 6.63087e-05 7.17391e-05 7.59042e-05
+ 7.59172e-05 7.03353e-05 6.33558e-05 5.31136e-05 4.64278e-05
+ 4.40594e-05 4.16909e-05 4.05674e-05 3.96957e-05 3.87875e-05
+ 3.74977e-05 3.62079e-05 3.49181e-05 3.36283e-05 3.23385e-05
+ 3.12427e-05 3.02775e-05 2.93124e-05 2.83472e-05 2.7382e-05
+ 2.64613e-05 2.59077e-05 2.5354e-05 2.48004e-05 2.42468e-05
+ 2.36931e-05 2.31395e-05 2.25859e-05 2.20322e-05 2.14786e-05
+ 2.0925e-05 2.03916e-05 1.9995e-05 1.95984e-05 1.92019e-05
+ 1.88053e-05 1.84087e-05 1.80122e-05 1.76156e-05 1.7219e-05
+ 1.68225e-05 1.64259e-05 1.6051e-05 1.57991e-05 1.55471e-05
+ 1.52952e-05 1.50433e-05 1.47913e-05 1.45394e-05 1.42875e-05
+ 1.40356e-05 1.37836e-05 1.35317e-05 1.32978e-05 1.31513e-05
+ 1.30048e-05 1.28583e-05 1.27118e-05 1.25653e-05 1.24188e-05
+ 1.22724e-05 1.21259e-05 1.19794e-05 1.18329e-05 1.16864e-05
+ 1.15399e-05 1.13934e-05 1.12469e-05 1.11005e-05 1.0954e-05
+ 1.08075e-05 1.0661e-05 1.05145e-05 1.0368e-05 1.02215e-05
+ 1.76447e-05 7.21516e-05 -3.59786e-05 -0.000159618 0.000156236
+ 0.000135106 -0.000336402 -0.000302283 0.000699323 0.000473866
+ -0.000156146 -0.000225625 -0.000123592 -3.78116e-05 8.47472e-06
+ 2.43387e-06 -7.44762e-05 7.80111e-05 9.43608e-05 0.000170159
+ 8.83919e-05 -0.00018802 -0.000373512 -0.000390597 0.000156875
+ 0.0032343 0.00776304 -0.000566905 -0.00760695 -0.0159226
+ -0.0245989 -0.0331402 -0.0100902 0.067837 0.266702 0.910818
+ 1.82282 2.69714 3.43247 3.98325 4.32893 4.51529 4.67087
+ 4.79288 4.87574 4.92797 4.95902 4.97655 4.98622 4.99195
+ 4.99526 4.99735 4.9991 4.99974 4.99982 4.99974 4.99961 4.9995
+ 4.99943 4.9994 4.9994 4.99942 4.99944 4.99948 4.99952 4.99956
+ 4.99961 4.99965 4.9997 4.99974 4.99977 4.99981 4.99983 4.99986
+ 4.99988 4.9999 4.99991 4.99992 4.99993 4.99994 4.99995 4.99995
+ 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999 4.99999
+ 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001 5
+ 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 5 5.00019 4.99888 4.99663 4.99457 4.99902
+ 5.00229 5.00323 5.00302 5.0023 5.0015 5.00085 5.00041 5.00013
+ 4.99993 4.99979 4.99948 4.99954 4.99983 5.00055 5.00109
+ 5.00009 4.9987 4.998 4.99755 4.99676 4.99618 5.01091 5.05272
+ 5.04156 4.80112 4.27692 3.42343 2.23953 0.967179 0.429813
+ 0.540757 1.32991 2.32147 3.14903 3.78143 4.22325 4.47978
+ 4.59448 4.69875 4.79798 4.87419 4.92339 4.95249 4.97174
+ 4.98408 4.99124 4.99478 4.99729 4.99868 4.9992 4.99941 4.99947
+ 4.99946 4.99943 4.9994 4.99939 4.9994 4.99942 4.99946 4.99951
+ 4.99956 4.99961 4.99967 4.99973 4.99977 4.9998 4.99981 4.99983
+ 4.99984 4.99987 4.99992 5.00001 5.00005 5.00001 4.99994
+ 4.99995 4.99995 4.99996 4.99996 4.99996 4.99997 4.99997
+ 4.99997 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5 5 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99998 4.99998 4.99998
+}
+.graph element create V30 -x x -y v30
+
+v31 set {
+ 1.8179e-09 -5.28841e-06 -1.44913e-05 -3.62932e-05 -9.75719e-05
+ 0.000141781 3.73396e-05 -1.65603e-05 -1.5271e-05 -6.73884e-06
+ 4.40157e-06 -4.85345e-06 -1.02964e-05 2.03126e-05 -1.89457e-05
+ -8.75564e-06 7.67422e-06 4.71103e-06 1.29798e-05 6.13469e-06
+ -1.14363e-05 -0.0394563 -0.0477298 -0.0622012 -0.0519225
+ 0.262499 0.943611 1.67052 2.31017 2.84028 3.28467 3.61582
+ 3.85887 4.13011 4.36511 4.54063 4.67013 4.76408 4.83263
+ 4.8825 4.91837 4.94373 4.96117 4.97318 4.98093 4.98562 4.98906
+ 4.99267 4.99539 4.99666 4.99731 4.99797 4.99844 4.99887
+ 4.99927 4.99933 4.99938 4.99944 4.99949 4.99955 4.9996 4.99965
+ 4.9997 4.99975 4.9998 4.99985 4.99986 4.99987 4.99989 4.9999
+ 4.99991 4.99992 4.99993 4.99995 4.99996 4.99997 4.99998
+ 4.99998 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5.00001
+ 5.00001 5.00001 5 5 5 5 5 5 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99997 5.00002 5.00004 5.0001 5.0001 4.99987 5.00009
+ 5.00021 5.00002 5.00004 4.99988 5.00013 4.99993 5.00026
+ 4.99973 5 5.00006 5.00009 5.00004 5.00004 5.04854 4.82711
+ 4.04208 2.64155 0.838902 0.19014 0.0982549 0.0723197 0.0576863
+ 0.0427644 0.0301979 0.020146 0.0135728 0.00980358 0.00774482
+ 0.00586604 0.0036687 0.00211511 0.00121906 0.000647581 0.000828436
+ 0.00190938 0.00224254 0.00199956 0.00165488 0.00135612 0.00113715
+ 0.000984181 0.000877175 0.000789973 0.000741139 0.000689338
+ 0.000625676 0.000586082 0.000550152 0.000529573 0.000505606
+ 0.000482117 0.000460574 0.000441649 0.000424674 0.000408398
+ 0.000391914 0.000376272 0.000361487 0.000348181 0.000336045
+ 0.000324466 0.000313545 0.000303046 0.000293056 0.00028356
+ 0.000274586 0.000266155 0.000258279 0.000250938 0.000243789
+ 0.000236912 0.000230244 0.000224186 0.000219291 0.000215346
+ 0.000212468 0.000207291 0.000200862 0.00019368 0.000186767
+ 0.000183515 0.000180263 0.00017701 0.000173758 0.000170506
+ 0.000167253 0.000164001 0.000161164 0.000158357 0.00015555
+ 0.000152743 0.000149936 0.000147129 0.000144322 0.000142066
+ 0.000140096 0.000138127 0.000136157 0.000134187 0.000132218
+ 0.000130248 0.000128278 0.000126308 0.000124339 0.000122369
+ 0.000120399 0.000118429 0.00011646 0.000114527 0.000112892
+ 0.000111258 0.000109623 0.000107988 0.000103598 6.86052e-05
+ 3.337e-05 7.00783e-05 0.000218764 0.000221318 0.000118593
+ -0.000113962 5.78552e-05 9.42068e-05 0.000237037 0.000171302
+ 0.0001033 6.16066e-05 5.52908e-05 6.30233e-05 7.01897e-05
+ 8.48573e-05 0.000106859 8.37213e-05 -0.0391541 -0.047722
+ -0.0618454 -0.0169804 0.345725 1.03426 1.74825 2.37152 2.88737
+ 3.32173 3.66761 3.9707 4.17762 3.98832 3.30483 2.09737 0.710892
+ 0.148159 0.0707463 0.0555808 0.045618 0.0319116 0.0199589
+ 0.0133357 0.00898528 0.00586075 0.00375478 0.00245443 0.00156038
+ 0.000962344 0.000590953 0.000375107 0.000250243 0.00015882
+ 0.000100203 6.18122e-05 3.7372e-05 2.23009e-05 1.32569e-05
+ 8.29437e-06 5.72457e-06 3.96832e-06 2.98935e-06 2.59699e-06
+ 2.75024e-06 3.38689e-06 4.0453e-06 3.50095e-06 1.64988e-06
+ -3.84371e-07 -2.03828e-06 -3.46401e-06 -1.24301e-06 4.63458e-06
+ 1.14104e-05 1.02619e-05 2.15487e-06 -2.98487e-06 -3.67221e-06
+ -2.94279e-06 -2.58649e-06 -2.23019e-06 -1.87389e-06 -1.5176e-06
+ -1.1613e-06 -7.92127e-07 -4.18889e-07 -4.56502e-08 3.27588e-07
+ 7.00827e-07 8.79539e-07 8.17025e-07 7.5451e-07 6.91996e-07
+ 6.29481e-07 5.66966e-07 5.04452e-07 4.41937e-07 3.79422e-07
+ 3.16908e-07 2.54393e-07 1.90078e-07 1.25366e-07 6.0654e-08
+ -4.05776e-09 -6.87696e-08 -1.33481e-07 -1.98193e-07 -2.62905e-07
+ -3.27617e-07 -3.92329e-07 -4.40392e-07 -4.18802e-07 -3.97213e-07
+ -3.75624e-07 -3.54035e-07 -3.32446e-07 -3.10856e-07 -2.89267e-07
+ -2.67678e-07
+}
+.graph element create V31 -x x -y v31
+v32 set {
+ 1.10294 1.10297 1.10291 1.10277 1.10259 1.10294 1.10313
+ 1.10306 1.10299 1.10296 1.10295 1.10295 1.10294 1.10294
+ 1.10294 1.10294 1.10294 1.10294 1.10294 1.10296 1.10296
+ 1.00547 0.998599 1.5201 2.49297 3.31258 3.73162 3.84757
+ 3.92505 4.02965 4.16599 4.30294 4.41541 4.52886 4.64414
+ 4.73865 4.81065 4.86391 4.90315 4.93188 4.95258 4.96726
+ 4.97738 4.98436 4.98888 4.99162 4.99363 4.99573 4.99731
+ 4.99804 4.99843 4.99881 4.99909 4.99934 4.99957 4.9996 4.99964
+ 4.99967 4.9997 4.99973 4.99977 4.9998 4.99983 4.99986 4.99988
+ 4.99991 4.99992 4.99992 4.99993 4.99994 4.99994 4.99995
+ 4.99996 4.99996 4.99997 4.99997 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 5.00028 4.99988 4.99968
+ 5.00019 4.99987 5.00021 4.99973 4.99977 4.99996 4.99997
+ 5.0002 4.99957 5.00026 4.99947 5.00074 5.00003 4.99987 4.99979
+ 5.00008 4.99997 5.08794 5.05993 4.76875 3.99197 3.10174
+ 2.5197 2.21771 2.04 1.92235 1.83874 1.77592 1.72665 1.686
+ 1.65276 1.6286 1.61299 1.60039 1.58934 1.57954 1.57083 1.56306
+ 1.55604 1.54963 1.54375 1.53832 1.53331 1.52865 1.52432
+ 1.52026 1.51645 1.51287 1.50949 1.50629 1.50327 1.50039
+ 1.49766 1.49505 1.49257 1.49019 1.48792 1.48574 1.48365
+ 1.48164 1.47971 1.47784 1.47604 1.47431 1.47264 1.47102
+ 1.46945 1.46794 1.46647 1.46505 1.46367 1.46233 1.46103
+ 1.45976 1.45853 1.45733 1.45616 1.45502 1.45392 1.45284
+ 1.45179 1.45076 1.44975 1.4488 1.44795 1.44711 1.44626 1.44541
+ 1.44457 1.44372 1.44287 1.44212 1.44138 1.44063 1.43989
+ 1.43914 1.4384 1.43766 1.43701 1.43641 1.43581 1.43522 1.43462
+ 1.43402 1.43342 1.43282 1.43223 1.43163 1.43103 1.43043
+ 1.42984 1.42924 1.42865 1.42808 1.42752 1.42695 1.42639
+ 1.42584 1.42529 1.42472 1.42412 1.42365 1.42326 1.42304
+ 1.42162 1.42082 1.42032 1.42029 1.42026 1.41995 1.41947
+ 1.41894 1.41841 1.4179 1.41742 1.41699 1.41656 1.32097 1.30963
+ 1.78765 2.64656 3.35764 3.747 3.86589 3.94217 4.04185 4.18453
+ 4.3561 4.53439 4.68621 4.74905 4.77848 4.84629 4.91261 4.97541
+ 5.01284 5.01548 5.01248 5.00924 5.00666 5.00475 5.00334
+ 5.00234 5.00164 5.00118 5.00083 5.00058 5.0004 5.00028 5.00019
+ 5.00013 5.00009 5.00007 5.00004 5.00003 5.00002 5.00001
+ 5.00001 5.00001 5 5 4.99999 4.99995 4.99992 4.99996 5.00006
+ 5.00012 5.00009 4.99997 4.9999 4.99985 4.99986 4.99997 5.00021
+ 5.00031 5.00024 5.0001 5.00007 5.00005 5.00003 5.00001 4.99998
+ 4.99998 4.99999 4.99999 4.99999 5 5 5 5 5 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5
+ 5 4.99999 4.99999 4.99999 4.99998 4.99998 4.99998
+}
+.graph element create V32 -x x -y v32
+v33 set {
+ 5 5.00012 5.00023 5.0003 4.99972 4.99988 4.99984 4.99991
+ 4.99996 4.99999 5.00008 5.00009 4.99986 5.00003 5.00007
+ 4.99995 4.9999 4.99997 5.00013 5.00014 5.00013 4.99701 4.99763
+ 4.99742 4.99998 5.02836 5.07262 4.96856 4.57267 3.85637
+ 2.79544 1.45942 0.408016 0.084885 0.0271375 0.0119294 0.00707546
+ 0.0051087 0.00373035 0.00264737 0.00186477 0.00130379 0.000915857
+ 0.000653121 0.000483893 0.000380852 0.000302362 0.000219498
+ 0.000154435 0.000121928 0.000104026 8.61242e-05 7.48526e-05
+ 6.49216e-05 5.56238e-05 5.29689e-05 5.03139e-05 4.7659e-05
+ 4.5004e-05 4.23491e-05 4.00356e-05 3.79522e-05 3.58687e-05
+ 3.37852e-05 3.17018e-05 2.97592e-05 2.89804e-05 2.82016e-05
+ 2.74228e-05 2.66441e-05 2.58653e-05 2.50865e-05 2.43077e-05
+ 2.35289e-05 2.27501e-05 2.19714e-05 2.12346e-05 2.07821e-05
+ 2.03295e-05 1.98769e-05 1.94244e-05 1.89718e-05 1.85192e-05
+ 1.80667e-05 1.76141e-05 1.71615e-05 1.6709e-05 1.62828e-05
+ 1.60061e-05 1.57294e-05 1.54527e-05 1.5176e-05 1.48993e-05
+ 1.46226e-05 1.43459e-05 1.40692e-05 1.37925e-05 1.35158e-05
+ 1.3262e-05 1.31191e-05 1.29761e-05 1.28332e-05 1.26903e-05
+ 1.25474e-05 1.24045e-05 1.22615e-05 1.21186e-05 1.19757e-05
+ 1.18328e-05 1.16898e-05 1.15469e-05 1.1404e-05 1.12611e-05
+ 1.11182e-05 1.09752e-05 1.08323e-05 1.06894e-05 1.05465e-05
+ 1.04036e-05 1.02606e-05 1.00185e-05 3.8343e-05 -3.06781e-05
+ -0.000111758 0.000111673 0.000130815 -0.000210491 -0.000231304
+ 0.000310226 0.000265303 3.0878e-05 -4.48405e-05 -1.2852e-05
+ -7.84469e-06 3.29986e-05 -1.23286e-05 -6.07871e-05 5.35082e-05
+ 7.69194e-05 0.000126221 6.57178e-05 0.00223349 -0.0148854
+ -0.0476636 -0.0491447 0.220125 1.11174 2.03988 2.90209 3.61069
+ 4.13554 4.50679 4.71501 4.83916 4.91027 4.95284 4.98086
+ 4.99151 4.98651 4.97113 4.95075 4.93102 4.93683 4.95457
+ 4.97071 4.98212 4.98948 4.99386 4.99636 4.99785 4.9987 4.99927
+ 4.99989 5.00014 5.00007 4.99988 4.99982 4.99976 4.99973
+ 4.99972 4.99972 4.99973 4.99974 4.99975 4.99977 4.99979
+ 4.99981 4.99984 4.99986 4.99988 4.99989 4.99991 4.99992
+ 4.99993 4.99994 4.99995 4.99996 4.99996 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99999 4.99999 4.99999
+ 4.99999 5 5 5 5.00001 5.00001 5.00001 5.00002 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 5 4.99999 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999 4.99999
+ 4.99999 4.99999 4.99999 5 5.00012 4.99946 4.99839 4.99733
+ 4.99948 5.00114 5.00158 5.00147 5.00113 5.00073 5.00043
+ 5.0002 5.00006 4.99995 4.99986 4.99973 4.99976 4.9999 5.00029
+ 5.00055 4.99704 4.99734 4.9972 5.00278 5.03354 5.07184 4.94057
+ 4.51936 3.75638 2.60982 1.23803 0.315016 0.0796102 0.0252894
+ 0.0165723 0.0827785 0.491298 1.40686 2.33436 3.1251 3.7691
+ 4.22201 4.49976 4.68115 4.80513 4.88509 4.93208 4.95861
+ 4.97579 4.98655 4.99268 4.99571 4.99771 4.99881 4.99929
+ 4.99954 4.99965 4.9997 4.99971 4.99971 4.99971 4.99971 4.99972
+ 4.99974 4.99976 4.99978 4.99981 4.99984 4.99987 4.99989
+ 4.99991 4.99991 4.99992 4.99992 4.99993 4.99997 5.00003
+ 5.00006 5.00004 5.00001 5 4.99999 4.99998 4.99998 4.99997
+ 4.99997 4.99997 4.99998 4.99998 4.99998 4.99999 4.99999
+ 4.99999 4.99999 5 5 5 5 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5 5 5 5 5 5 5 4.99999
+ 4.99999 4.99999 4.99999 4.99998 4.99998
+}
+.graph element create V33 -x x -y v33
+v34 set {
+ 5 5.00207 5.00813 5.01486 5.00156 5.0018 4.99861 4.99844
+ 4.99888 4.9993 4.99956 4.99971 4.99979 4.99983 4.99987 4.99989
+ 4.99671 4.9974 4.99864 5.00131 5.00377 5.0021 5.00039 4.99993
+ 5.00004 5.0009 5.00109 4.99636 4.98617 4.96778 4.92047 4.89528
+ 4.91112 4.9559 4.98286 4.99369 4.99812 4.99951 4.99994 5.00014
+ 5.00008 4.99994 4.99984 4.99989 4.99998 5.00004 5.00004
+ 5.00006 5.00005 5.00001 4.99997 4.99992 4.99993 4.99994
+ 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996
+ 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996 4.99996
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997 4.99997
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998
+ 4.99998 4.99998 4.99998 4.99998 5.00131 5.00072 4.9977 4.99811
+ 5.00325 4.99647 4.98948 4.99459 5.00262 5.00276 5.00156
+ 5.00072 5.0003 5.00013 4.99995 4.99668 4.99775 4.99917 5.00173
+ 5.00386 5.00188 4.99888 4.99757 4.99951 5.01712 5.0557 5.07088
+ 5.07704 5.07758 5.06958 5.04223 5.03331 5.0279 5.03408 5.07611
+ 5.01911 4.68594 3.99152 2.92195 1.69878 0.809 0.344091 0.154663
+ 0.0788717 0.0467212 0.0336168 0.0280514 0.0254947 0.024173
+ 0.0223567 0.0220555 0.0271514 0.0295872 0.0296052 0.0283971
+ 0.0264726 0.0241813 0.0218244 0.0195349 0.017368 0.0152495
+ 0.013295 0.0115444 0.00996982 0.00857091 0.00733891 0.00627261
+ 0.0053494 0.00456316 0.00388373 0.00331073 0.00282181 0.00240991
+ 0.00206389 0.00177187 0.00152283 0.00131167 0.00112558 0.000954373
+ 0.000805726 0.00069326 0.000600991 0.000525743 0.00047355
+ 0.00044359 0.000434815 0.000436053 0.000402511 0.000368969
+ 0.000335427 0.000301886 0.000268344 0.000234802 0.00020126
+ 0.000184967 0.000169932 0.000154896 0.000139861 0.000124825
+ 0.00010979 9.47546e-05 8.67896e-05 8.24901e-05 7.81906e-05
+ 7.38911e-05 6.95915e-05 6.5292e-05 6.09925e-05 5.66929e-05
+ 5.23934e-05 4.80939e-05 4.37943e-05 3.94948e-05 3.51953e-05
+ 3.08957e-05 2.67968e-05 2.42936e-05 2.17904e-05 1.92872e-05
+ 1.6784e-05 0.00125927 -0.00794344 -0.0305499 -0.0621697
+ -0.0463796 -0.0224608 -0.00538381 0.00546086 0.0108675 0.012883
+ 0.0131787 0.0127271 0.0119702 0.0110398 0.0100635 0.00649617
+ 0.00489388 0.00545863 0.0098351 0.0167428 0.0126563 0.00697542
+ 0.00427027 0.00330002 0.00390774 0.00408999 -0.00259143
+ -0.0160578 -0.0451849 -0.0409651 0.1301 0.597429 1.3848
+ 2.63426 3.81272 4.51373 4.8412 4.98731 4.88165 4.37165 3.40034
+ 2.17681 1.12217 0.505129 0.219703 0.104992 0.0622333 0.0448317
+ 0.0355782 0.0311867 0.0293529 0.0274615 0.0288739 0.0307845
+ 0.0304909 0.029245 0.0273602 0.0251006 0.022697 0.0202765
+ 0.0179357 0.0157106 0.0136562 0.0117951 0.0101273 0.00865784
+ 0.00739394 0.00634364 0.00551356 0.00480538 0.00415747 0.00356084
+ 0.00297585 0.00236711 0.00181853 0.00160713 0.00169822 0.00166542
+ 0.00145504 0.00120252 0.00109259 0.000982658 0.00087273
+ 0.000762802 0.000652874 0.000584068 0.000528263 0.000472458
+ 0.000416653 0.000360848 0.000321155 0.000301442 0.000281729
+ 0.000262016 0.000242303 0.00022259 0.000202877 0.000183164
+ 0.000163451 0.000143738 0.000124025 0.000114582 0.000107399
+ 0.000100216 9.30332e-05 8.58502e-05 7.86672e-05 7.14841e-05
+ 6.43011e-05 5.7118e-05 4.9935e-05 4.35378e-05 4.04281e-05
+ 3.73184e-05 3.42088e-05 3.10991e-05 2.79894e-05 2.48798e-05
+ 2.17701e-05 1.86604e-05
+}
+.graph element create V34 -x x -y v34
+v35 set {
+ 7.24585e-12 2.21843e-05 3.20014e-05 1.25076e-05 -2.44947e-05
+ 1.8425e-05 5.50546e-06 3.53025e-05 -1.07551e-05 -3.94383e-06
+ -2.27848e-06 -9.04789e-05 7.44215e-05 -2.7662e-05 0.000200038
+ -2.11998e-05 -2.09011e-05 2.37098e-05 2.18751e-05 -2.28422e-05
+ -6.23659e-05 3.58241e-05 1.76386e-05 -4.28311e-05 0.000355626
+ 0.00156903 0.00100999 -0.0085304 -0.02067 -0.0389485 -0.0651568
+ -0.128475 -0.314362 -0.406837 -0.421558 -0.421277 -0.418176
+ -0.414481 -0.410845 -0.407348 -0.403971 -0.400716 -0.397582
+ -0.394563 -0.391658 -0.388866 -0.386178 -0.383585 -0.381094
+ -0.378789 -0.376569 -0.37435 -0.372256 -0.370188 -0.36815
+ -0.366422 -0.364694 -0.362967 -0.361239 -0.359511 -0.357888
+ -0.356334 -0.354781 -0.353227 -0.351674 -0.350152 -0.348888
+ -0.347625 -0.346361 -0.345098 -0.343834 -0.342571 -0.341307
+ -0.340044 -0.33878 -0.337517 -0.336279 -0.335215 -0.334152
+ -0.333088 -0.332024 -0.330961 -0.329897 -0.328833 -0.32777
+ -0.326706 -0.325642 -0.324601 -0.323683 -0.322766 -0.321849
+ -0.320932 -0.320014 -0.319097 -0.31818 -0.317263 -0.316345
+ -0.315428 -0.314545 -0.313825 -0.313106 -0.312387 -0.311667
+ -0.310948 -0.310228 -0.309509 -0.308789 -0.30807 -0.307351
+ -0.306631 -0.305912 -0.305192 -0.304473 -0.303754 -0.303034
+ -0.302315 -0.301595 -0.300876 -0.300157 -0.299437 -0.298716
+ -0.29798 -0.297329 -0.296691 -0.295837 -0.29516 -0.294725
+ -0.294044 -0.292917 -0.292351 -0.291965 -0.291365 -0.290687
+ -0.290027 -0.289376 -0.288772 -0.288193 -0.287505 -0.286892
+ -0.28626 -0.285714 -0.284545 -0.289246 -0.298717 -0.298492
+ -0.214163 0.181451 0.0749974 0.0454707 0.0292987 0.0196837
+ 0.0124119 0.00884715 0.00527181 0.00585821 0.0296361 0.169856
+ 0.361207 0.538856 0.67469 0.685933 0.392802 0.17772 0.0813085
+ 0.0424601 0.0246654 0.0175258 0.0144256 0.0129859 0.012205
+ 0.0112846 0.010933 0.0134813 0.0147254 0.0147981 0.0142156
+ 0.0132732 0.0121355 0.0109587 0.00981238 0.00872731 0.00767007
+ 0.00669346 0.00581341 0.00502167 0.00431819 0.00369842 0.00316168
+ 0.00269663 0.00230035 0.00195801 0.00166928 0.00142286 0.00121522
+ 0.00104072 0.000893384 0.000767675 0.000661268 0.000567659
+ 0.000481766 0.000407101 0.000350044 0.000302721 0.000263424
+ 0.000236813 0.00022199 0.000218182 0.000219548 0.0002027
+ 0.000185853 0.000169006 0.000152158 0.000135311 0.000118463
+ 0.000101616 9.33782e-05 8.57685e-05 7.81588e-05 7.0549e-05
+ 6.29393e-05 5.53296e-05 4.77199e-05 4.36954e-05 4.15296e-05
+ 3.93637e-05 3.71978e-05 3.50319e-05 3.28661e-05 3.07002e-05
+ 2.85343e-05 2.63685e-05 2.42026e-05 2.20367e-05 1.98709e-05
+ 1.7705e-05 1.55391e-05 1.34772e-05 1.22416e-05 1.10061e-05
+ 9.77055e-06 8.535e-06 0.000631271 -0.00362586 -0.0146235
+ -0.0308486 -0.0237466 -0.0117522 -0.00304171 0.00251033
+ 0.00531986 0.0063897 0.00657351 0.00636494 0.00599705 0.00553442
+ 0.00505994 0.00330925 0.00246671 0.0027006 0.00473161 0.00830333
+ 0.00649147 0.00356815 0.00217448 0.00187579 0.00270447 0.00219543
+ -0.00546118 -0.0179576 -0.0445306 -0.0649309 0.0197935 0.473629
+ 0.87268 0.269542 0.0086094 0.0844602 0.606456 1.04929 0.906014
+ 0.916205 0.919425 0.872867 0.556244 0.262457 0.11838 0.0571226
+ 0.0333451 0.0237133 0.0185096 0.0159617 0.0148663 0.0138683
+ 0.0144081 0.0153797 0.0152551 0.0146487 0.0137192 0.0125973
+ 0.0113996 0.0101903 0.00901851 0.00790495 0.00687502 0.00593994
+ 0.00510092 0.00436111 0.00372439 0.0031945 0.00277537 0.00241888
+ 0.002095 0.00179943 0.00150419 0.00119264 0.00090934 0.000802394
+ 0.000852816 0.000838368 0.000730842 0.000601028 0.000546616
+ 0.000492205 0.000437793 0.000383381 0.000328969 0.00029454
+ 0.000266428 0.000238317 0.000210205 0.000182093 0.000162091
+ 0.000152145 0.000142198 0.000132252 0.000122306 0.000112359
+ 0.000102413 9.24665e-05 8.25201e-05 7.25738e-05 6.26274e-05
+ 5.78553e-05 5.42216e-05 5.05878e-05 4.69541e-05 4.33204e-05
+ 3.96867e-05 3.60529e-05 3.24192e-05 2.87855e-05 2.51518e-05
+ 2.19153e-05 2.03406e-05 1.8766e-05 1.71913e-05 1.56167e-05
+ 1.4042e-05 1.24674e-05 1.08927e-05 9.31806e-06
+}
+.graph element create V35 -x x -y v35
+v36 set {
+ 5 5.01426 5.02852 5.01923 4.77685 4.56471 4.52338 4.56813
+ 4.63122 4.693 4.74776 4.79385 4.83258 4.86358 4.88918 4.91021
+ 4.90553 4.89733 4.89554 4.91953 5.00757 5.07101 5.06318
+ 5.05241 5.05535 5.08042 5.07251 4.90973 4.56136 3.98637
+ 3.237 2.67216 2.33678 2.13529 2.00544 1.91429 1.84638 1.79461
+ 1.75338 1.71958 1.69175 1.6686 1.64918 1.63258 1.61836 1.60607
+ 1.59506 1.58483 1.57575 1.56847 1.56193 1.55538 1.54968
+ 1.54416 1.5388 1.53523 1.53165 1.52807 1.52449 1.52091 1.51771
+ 1.51477 1.51182 1.50888 1.50593 1.50309 1.50113 1.49917
+ 1.4972 1.49524 1.49328 1.49132 1.48935 1.48739 1.48543 1.48346
+ 1.48157 1.48012 1.47868 1.47724 1.47579 1.47435 1.47291
+ 1.47146 1.47002 1.46857 1.46713 1.46574 1.46462 1.4635 1.46238
+ 1.46126 1.46014 1.45902 1.4579 1.45678 1.45567 1.45455 1.45349
+ 1.45275 1.45201 1.45127 1.45053 1.44979 1.44905 1.44831
+ 1.44757 1.44683 1.44609 1.44535 1.44461 1.44387 1.44313
+ 1.44239 1.44165 1.44091 1.44017 1.43943 1.43869 1.43795
+ 1.43721 1.43874 1.43976 1.43619 1.43182 1.43726 1.43084
+ 1.42587 1.42383 1.42642 1.42728 1.42736 1.4271 1.42669 1.42621
+ 1.42569 1.41703 1.41244 1.41019 1.41199 1.41833 1.42502
+ 1.41504 1.37535 1.28381 1.44779 2.33713 3.25835 3.67554
+ 3.84975 4.01125 4.2253 4.45433 4.62215 4.74478 4.82998 4.8868
+ 4.92396 4.94768 4.96498 4.98537 5.0128 5.04467 5.06722 5.06535
+ 5.01475 4.91956 4.80647 4.7242 4.7059 4.73552 4.76379 4.81684
+ 4.87376 4.92276 4.96112 4.9884 5.0045 5.00999 5.00933 5.00619
+ 5.00384 5.00342 5.00373 5.00362 5.00309 5.00272 5.00239
+ 5.00204 5.00172 5.00146 5.00124 5.00105 5.00089 5.00076
+ 5.00065 5.00057 5.00048 5.00041 5.00034 5.00028 5.00023
+ 5.00019 5.00015 5.00015 5.00016 5.0002 5.00023 5.00021 5.00019
+ 5.00017 5.00015 5.00012 5.0001 5.00008 5.00007 5.00006 5.00005
+ 5.00004 5.00003 5.00002 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00002 5.00002 5.00002 5.00002
+ 5.00002 5.00002 5.00002 5.00002 5.00002 5.00002 5.00001
+ 5.00001 5.00001 5.00062 4.99506 4.9835 4.96726 4.9728 4.97877
+ 4.98675 4.9966 5.00406 5.00679 5.00629 5.00561 5.00487 5.00429
+ 5.00384 5.002 5.00164 5.00229 5.00484 5.00769 5.00019 5.00242
+ 5.01319 5.0335 5.07265 5.10129 5.11485 5.12551 5.13953 5.16048
+ 5.18862 5.22811 5.25656 5.25627 5.19975 4.9139 4.24745 3.43732
+ 2.8202 2.43224 2.17409 2.01333 1.93951 1.94622 1.98861 2.02217
+ 2.05383 2.08376 2.11184 2.13793 2.16191 2.18267 2.20502
+ 2.22837 2.24958 2.26901 2.28648 2.302 2.31582 2.32802 2.33869
+ 2.34795 2.35596 2.36282 2.3687 2.37371 2.37797 2.38161 2.38476
+ 2.38743 2.3897 2.39168 2.39329 2.39463 2.39575 2.39671 2.39756
+ 2.39835 2.39907 2.39968 2.39999 2.4003 2.40061 2.40091 2.40122
+ 2.40142 2.40159 2.40176 2.40193 2.4021 2.40222 2.40228 2.40234
+ 2.4024 2.40247 2.40253 2.40259 2.40265 2.40271 2.40277 2.40284
+ 2.40287 2.40289 2.40291 2.40294 2.40296 2.40298 2.40301
+ 2.40303 2.40305 2.40308 2.4031 2.40311 2.40312 2.40313 2.40314
+ 2.40315 2.40316 2.40317 2.40318
+}
+.graph element create V36 -x x -y v36
+v37 set {
+ 5 5.01732 5.03181 5.05944 5.12686 5.20725 5.28103 5.31254
+ 5.32901 5.33709 5.3408 5.34257 5.34311 5.34347 5.34386 5.34411
+ 5.3406 5.33484 5.32942 5.32904 5.33644 5.34869 5.35001 5.34882
+ 5.34758 5.34672 5.34599 5.34496 5.34364 5.34165 5.33712
+ 5.33502 5.3366 5.34067 5.34306 5.34398 5.34434 5.34442 5.34443
+ 5.34443 5.34441 5.34439 5.34437 5.34437 5.34438 5.34438
+ 5.34438 5.34438 5.34438 5.34437 5.34437 5.34436 5.34436
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437 5.34437
+ 5.34437 5.34437 5.34437 5.34437 5.34437 5.35377 5.35451
+ 5.34265 5.34488 5.35861 5.28622 4.90033 4.75027 4.89731
+ 4.97098 4.99293 4.99832 4.99909 4.99956 4.99858 4.99829
+ 4.9998 5.00035 5.0038 5.00989 5.00251 4.99438 4.9953 4.99761
+ 4.99985 5.00152 5.0011 5.00046 4.99996 4.99925 4.99862 4.99919
+ 4.99961 5.00048 5.00234 4.99654 4.98235 4.95936 4.83738
+ 4.53021 4.21004 4.00593 3.91207 3.88059 3.87822 3.89117
+ 3.91278 3.94044 3.97376 4.01152 4.05052 4.10679 4.17908
+ 4.25673 4.33414 4.40875 4.47879 4.54342 4.60258 4.65595
+ 4.70291 4.74414 4.78018 4.81185 4.83915 4.86291 4.88301
+ 4.90048 4.91528 4.92802 4.9387 4.94777 4.95539 4.9618 4.96725
+ 4.97195 4.97588 4.97932 4.98247 4.98512 4.98697 4.98831
+ 4.98919 4.99015 4.99101 4.99169 4.99222 4.99282 4.99341
+ 4.994 4.9946 4.99519 4.99578 4.99638 4.99667 4.99693 4.9972
+ 4.99747 4.99773 4.998 4.99827 4.99841 4.99849 4.99856 4.99864
+ 4.99872 4.9988 4.99888 4.99896 4.99904 4.99911 4.99919 4.99927
+ 4.99935 4.99943 4.9995 4.99955 4.9996 4.99965 4.9997 5.00736
+ 4.98252 4.87516 4.66727 4.49142 4.43103 4.4301 4.4571 4.49729
+ 4.5407 4.5835 4.62363 4.66114 4.69577 4.72738 4.74632 4.75971
+ 4.77576 4.80671 4.87073 4.91665 4.93252 4.94418 4.95331
+ 4.96094 4.96727 4.97148 4.97471 4.97612 4.98276 5.00247
+ 5.04086 5.08628 5.10673 5.08887 5.0564 5.02767 5.01336 4.99685
+ 4.97422 4.90866 4.67035 4.33117 4.07888 3.94432 3.89105
+ 3.88174 3.89292 3.91442 3.94564 3.98708 4.0355 4.09134 4.16315
+ 4.24088 4.31918 4.39527 4.46693 4.53337 4.59405 4.6486 4.69693
+ 4.73938 4.77617 4.80809 4.83551 4.85895 4.87894 4.89596
+ 4.91081 4.92417 4.93651 4.94552 4.95198 4.9565 4.96096 4.96523
+ 4.96972 4.97428 4.97868 4.98064 4.9826 4.98455 4.98651 4.98847
+ 4.98967 4.99064 4.9916 4.99257 4.99353 4.99422 4.99457 4.99493
+ 4.99528 4.99563 4.99598 4.99633 4.99668 4.99703 4.99738
+ 4.99773 4.9979 4.99804 4.99817 4.9983 4.99843 4.99856 4.99869
+ 4.99883 4.99896 4.99909 4.99921 4.99926 4.99931 4.99937
+ 4.99942 4.99948 4.99953 4.99959 4.99964
+}
+.graph element create V37 -x x -y v37
+v38 set {
+ 4.49849 4.53282 4.58329 4.66625 4.83345 4.97823 5.0207 5.01816
+ 5.01116 5.00595 5.00296 5.00148 5.00073 5.00062 5.00033
+ 5.0003 4.99864 4.99661 4.99652 4.99928 5.00361 5.12573 5.17251
+ 5.22612 5.33479 5.44503 5.44432 5.44379 5.44334 5.443 5.44276
+ 5.44258 5.44246 5.44238 5.44232 5.44228 5.44225 5.44223
+ 5.44221 5.4422 5.44219 5.44219 5.44218 5.44218 5.44218 5.44218
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217
+ 5.44217 5.44217 5.44217 5.44217 5.44217 5.44217 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216 5.44216
+ 5.44216 5.44216 5.44216 5.44216 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215 5.44215
+ 5.44214 5.44214 5.44214 5.44214 5.44214 5.44214 5.44214
+ 5.44214 5.44214 5.44214 5.44214 5.44212 5.45159 5.45236
+ 5.44064 5.44307 5.45616 5.38122 4.77163 3.53297 2.74466
+ 2.34448 2.11802 1.9783 1.88656 1.82001 1.77389 1.72955 1.69632
+ 1.66971 1.6526 1.65236 1.56034 1.53764 1.97139 2.75096 3.39212
+ 3.74042 3.82345 3.85696 3.88547 3.91862 3.9585 4.00467 4.05903
+ 4.1254 4.19533 4.26791 4.34517 4.42112 4.49238 4.55807 4.6179
+ 4.6713 4.71815 4.75889 4.79418 4.82456 4.85062 4.87291 4.89196
+ 4.90823 4.92209 4.93388 4.9439 4.95242 4.95968 4.96585 4.97108
+ 4.9755 4.97923 4.98237 4.98503 4.98732 4.98927 4.99094 4.99233
+ 4.99353 4.99452 4.99538 4.99608 4.99668 4.99718 4.9976 4.99794
+ 4.99822 4.99847 4.99867 4.99884 4.99899 4.99913 4.99924
+ 4.99932 4.99938 4.99943 4.99947 4.99951 4.99953 4.99955
+ 4.99958 4.99961 4.99964 4.99967 4.99969 4.99972 4.99975
+ 4.99977 4.99978 4.99979 4.99981 4.99982 4.99983 4.99985
+ 4.99986 4.99986 4.99987 4.99987 4.99988 4.99988 4.99988
+ 4.99989 4.99989 4.9999 4.9999 4.99991 4.99991 4.99992 4.99992
+ 4.99993 4.99993 4.99993 4.99994 5.00381 5.00064 4.99246
+ 4.99823 5.00349 5.00076 5.00033 5.00015 5.00009 5.00007
+ 5.00005 5.00004 5.00003 5.00002 4.99988 4.99732 4.99728
+ 4.9978 5.00187 5.00927 5.08712 5.07654 4.92855 4.4863 3.76162
+ 3.00049 2.49834 2.20883 2.03492 1.92384 1.84676 1.79021
+ 1.74716 1.7132 1.68576 1.66309 1.64406 1.62785 1.61383 1.60162
+ 1.59081 1.58117 1.57253 1.56473 1.55765 1.55117 1.54527
+ 1.53988 1.53485 1.53012 1.5257 1.5216 1.51773 1.51411 1.51071
+ 1.50746 1.50438 1.50146 1.49868 1.49603 1.4935 1.49109 1.48878
+ 1.48657 1.48445 1.48242 1.48046 1.47858 1.47677 1.47502
+ 1.47333 1.4717 1.47012 1.46859 1.46711 1.46568 1.46428 1.46292
+ 1.4616 1.46034 1.45923 1.45812 1.45701 1.4559 1.45479 1.45378
+ 1.45279 1.45181 1.45082 1.44983 1.44893 1.44813 1.44732
+ 1.44652 1.44571 1.44491 1.4441 1.4433 1.44249 1.44169 1.44089
+ 1.44019 1.43951 1.43883 1.43815 1.43747 1.4368 1.43612 1.43544
+ 1.43476 1.43408 1.43342 1.43283 1.43223 1.43163 1.43104
+ 1.43044 1.42984 1.42924 1.42865
+}
+.graph element create V38 -x x -y v38
+v39 set {
+ 5 5.01048 5.01221 4.98887 4.76261 4.54943 4.51564 4.56249
+ 4.62621 4.68843 4.74374 4.79044 4.82972 4.86127 4.88724
+ 4.90862 4.90791 4.89858 4.89589 4.91767 5.00405 5.16956
+ 5.12391 4.7557 3.87953 3.01124 2.48482 2.20424 2.03812 1.92679
+ 1.84956 1.79256 1.74907 1.71487 1.68724 1.6644 1.64513 1.6287
+ 1.61446 1.60197 1.59095 1.58117 1.57245 1.5646 1.55752 1.55109
+ 1.54516 1.53958 1.53444 1.53008 1.52606 1.52205 1.51843
+ 1.5149 1.51146 1.50893 1.50639 1.50387 1.50133 1.4988 1.49651
+ 1.49436 1.49222 1.49007 1.48793 1.48585 1.48433 1.4828 1.48128
+ 1.47975 1.47823 1.4767 1.47518 1.47365 1.47213 1.4706 1.46912
+ 1.46795 1.46678 1.46561 1.46444 1.46327 1.4621 1.46093 1.45976
+ 1.45859 1.45741 1.45628 1.45534 1.45441 1.45347 1.45254
+ 1.4516 1.45067 1.44973 1.4488 1.44786 1.44693 1.44604 1.44539
+ 1.44475 1.4441 1.44345 1.44281 1.44216 1.44151 1.44086 1.44022
+ 1.43957 1.43892 1.43828 1.43763 1.43698 1.43633 1.43569
+ 1.43504 1.43439 1.43375 1.4331 1.43245 1.4318 1.43157 1.43089
+ 1.43001 1.43042 1.42899 1.42439 1.42216 1.43447 1.44048
+ 1.43705 1.43314 1.43039 1.42861 1.42739 1.42651 1.42548
+ 1.42488 1.4243 1.42392 1.4235 1.32443 1.31149 1.78169 2.64844
+ 3.43211 3.95252 4.20231 4.3746 4.49948 4.58929 4.65742 4.71183
+ 4.77057 4.83196 4.88354 4.92894 4.96625 4.99235 5.00651
+ 5.00941 5.00813 5.00689 5.00588 5.00504 5.00431 5.00368
+ 5.00314 5.00268 5.00228 5.00194 5.00165 5.0014 5.00118 5.001
+ 5.00085 5.00072 5.00061 5.00052 5.00044 5.00037 5.00031
+ 5.00027 5.00022 5.00019 5.00016 5.00013 5.00011 5.00009
+ 5.00008 5.00007 5.00006 5.00005 5.00004 5.00003 5.00003
+ 5.00003 5.00002 5.00002 5.00002 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001 5.00001
+ 5.00001 5.00001 5 5 5 5 5 4.99999 4.99999 4.99999 4.99998
+ 4.99998 4.99998 4.99998 4.99998 4.99998 4.99998 4.99999
+ 4.99999 4.99999 4.99999 4.99999 4.99999 5 5 5 5 5.00001
+ 5.00002 5.00003 5.00004 5.00022 4.99974 4.99942 4.99997
+ 5.00063 5.00002 5.00003 4.99994 4.99998 4.99999 5 5 5 5
+ 5 4.99981 4.99998 5.00004 5.00036 5.00049 5.12012 5.16315
+ 5.19712 5.21835 4.87874 4.10151 3.31555 2.74207 2.38075
+ 2.15872 2.01614 1.91886 1.84852 1.79401 1.75052 1.71508
+ 1.68672 1.66467 1.64602 1.62985 1.61576 1.60343 1.59256
+ 1.58287 1.57418 1.56632 1.55922 1.55282 1.54687 1.54132
+ 1.53618 1.53143 1.52698 1.52282 1.51895 1.51527 1.5118 1.50851
+ 1.5054 1.50244 1.49963 1.49695 1.4944 1.49196 1.48963 1.4874
+ 1.48527 1.48322 1.48124 1.47934 1.47751 1.47574 1.47403
+ 1.47239 1.4708 1.46926 1.46777 1.46632 1.46491 1.46355 1.46237
+ 1.4612 1.46002 1.45884 1.45766 1.45659 1.45555 1.45451 1.45346
+ 1.45242 1.45147 1.45062 1.44978 1.44894 1.44809 1.44725
+ 1.4464 1.44556 1.44472 1.44387 1.44303 1.4423 1.44159 1.44088
+ 1.44017 1.43947 1.43876 1.43805 1.43734 1.43664 1.43593
+ 1.43524 1.43462 1.434 1.43338 1.43276 1.43213 1.43151 1.43089
+ 1.43027
+}
+.graph element create V39 -x x -y v39
+toplevel .top
+.graph legend configure -position .top.legend -columns 1
+pack .top.legend -fill both -expand yes
+
+button .quit -text "quit" -bg "red" -command "exit"
+table . \
+ .graph 0,0 -fill both \
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ClosestPoint $graph
+Blt_PrintKey $graph
+
+$graph legend bind all <ButtonRelease-1> { HighlightTrace %W }
+$graph legend bind all <ButtonRelease-3> {
+ %W legend deactivate *
+ eval %W element deactivate [%W element activate]
+}
+
+proc HighlightTrace { graph } {
+ set entry [$graph legend get current]
+ set active [$graph legend activate]
+ if { [lsearch $active $entry] < 0 } {
+ $graph legend activate $entry
+ $graph element activate $entry
+ } else {
+ $graph legend deactivate $entry
+ $graph element deactivate $entry
+ }
+}
+
+set lastRow 0
+set logicPlots {}
+set leftMargin 0
+set rightMargin 0
+
+proc LogicPlot { from graph signal args } {
+ if { ![winfo exists $graph] } {
+ global rightMargin leftMargin
+ graph $graph -title "" -topmargin 1 -bottommargin 1 -height 0.75i \
+ -plotpadx 4 -plotpady 8 -bd 0 \
+ -leftmargin $leftMargin -rightmargin $rightMargin
+ $graph grid off
+ set xMin [$from axis cget x -min]
+ set xMax [$from axis cget x -max]
+ set yLim [$from axis limits y]
+ set yMin [lindex $yLim 0]
+ set yMax [lindex $yLim 1]
+ $graph axis configure x -title "" -hide yes -min $xMin -max $xMax
+ $graph axis configure y -title $signal -min $yMin -max $yMax
+ $graph legend configure -anchor nw
+ global lastRow
+ incr lastRow
+ table . $graph $lastRow,0 -fill both
+ global logicPlots
+ lappend logicPlots $graph
+ }
+ set list [linsert $args 0 $signal ]
+ foreach i [$graph element names] {
+ if { [lsearch $list $i] < 0 } {
+ $graph element delete $i
+ }
+ }
+ foreach i $list {
+ if { ![$graph element exists $i] } {
+ $graph element create $i
+ }
+ set pen [$from element cget $i -pen]
+ set xData [$from element cget $i -x]
+ set yData [$from element cget $i -y]
+ $graph element configure $i -x $xData -y $yData -pen $pen
+ }
+}
+
+set changePending "no"
+proc EventuallyChangePlots { p1 p2 how } {
+ global changePending
+ if { $changePending == "no" } {
+ after idle ChangePlots
+ }
+ set changePending "yes"
+}
+
+proc ChangePlots { } {
+ global changePending
+ global logicPlots
+ global leftMargin rightMargin
+ set from .graph
+ set xMin [$from axis cget x -min]
+ set xMax [$from axis cget x -max]
+ set yLim [$from axis limits y]
+ set yMin [lindex $yLim 0]
+ set yMax [lindex $yLim 1]
+ foreach g ".graph .g2 .g3" {
+ $g configure -leftmargin $leftMargin -rightmargin $rightMargin
+ $g axis configure x -min $xMin -max $xMax
+ #$g axis configure y -min $yMin -max $yMax
+ }
+ set changePending "no"
+}
+
+#LogicPlot .graph .g1 V1
+#LogicPlot .graph .g2 V5
+#LogicPlot .graph .g3 V9
+# LogicPlot .graph .g4 V13
+# LogicPlot .graph .g5 V17
+# LogicPlot .graph .g6 V22
+# LogicPlot .graph .g7 V26
+
+#.g1 configure -leftvariable leftMargin -rightvariable rightMargin
+trace variable leftMargin w EventuallyChangePlots
+trace variable rightMargin w EventuallyChangePlots
+
diff --git a/blt/demos/graph7.tcl b/blt/demos/graph7.tcl
new file mode 100755
index 00000000000..60171eabef3
--- /dev/null
+++ b/blt/demos/graph7.tcl
@@ -0,0 +1,95 @@
+#!../src/bltwish
+
+set blt_library ../library
+package require BLT
+set blt_library ../library
+set auto_path [linsert $auto_path 0 ../library]
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+image create photo bgTexture -file ./images/buckskin.gif
+
+option add *Graph.Tile bgTexture
+option add *Label.Tile bgTexture
+option add *Frame.Tile bgTexture
+option add *Htext.Tile bgTexture
+option add *TileOffset 0
+option add *HighlightThickness 0
+option add *Element.ScaleSymbols no
+option add *Element.Smooth linear
+option add *activeLine.Color yellow4
+option add *activeLine.Fill yellow
+option add *activeLine.LineWidth 0
+option add *Element.Pixels 3
+option add *Graph.halo 7i
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" } {
+ option add *print.background yellow
+ option add *quit.background red
+}
+
+proc FormatLabel { w value } {
+ return $value
+}
+
+set graph .graph
+
+set length 250000
+graph $graph -title "Scatter Plot\n$length points"
+$graph xaxis configure \
+ -loose no \
+ -title "X Axis Label"
+$graph yaxis configure \
+ -title "Y Axis Label"
+$graph legend configure \
+ -activerelief sunken \
+ -background ""
+
+$graph element create line3 -symbol square -color green4 -fill green2 \
+ -linewidth 0 -outlinewidth 1 -pixels 4
+table . .graph 0,0 -fill both
+update
+
+vector x($length) y($length)
+x expr random(x)
+y expr random(y)
+x sort y
+$graph element configure line3 -x x -y y
+
+wm min . 0 0
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+
+busy hold $graph
+update
+busy release $graph
+
diff --git a/blt/demos/hierbox1.tcl b/blt/demos/hierbox1.tcl
new file mode 100755
index 00000000000..fe79c2d4c58
--- /dev/null
+++ b/blt/demos/hierbox1.tcl
@@ -0,0 +1,133 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set saved [pwd]
+
+#blt::bltdebug 100
+
+image create photo bgTexture -file ./images/rain.gif
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+#option add *Hierbox.Tile bgTexture
+option add *Hierbox.ScrollTile yes
+
+option add *xHierbox.openCommand {
+ set path /home/gah/src/blt/%P
+ if { [file isdirectory $path] } {
+ cd $path
+ set files [glob -nocomplain * */. ]
+ if { $files != "" } {
+ eval %W insert -at %n end $files
+ }
+ }
+}
+
+option add *xHierbox.closeCommand {
+ eval %W delete %n 0 end
+}
+
+image create photo openFolder -file images/open.gif
+image create photo closeFolder -file images/close.gif
+
+option add *Hierbox.icons "closeFolder openFolder"
+
+image create photo openFolder2 -file images/open2.gif
+image create photo closeFolder2 -file images/close2.gif
+
+option add *Hierbox.activeIcons "closeFolder2 openFolder2"
+
+hierbox .h \
+ -activebackground blue \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set }
+
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x
+
+table configure . c1 r1 -resize none
+
+proc DoFind { dir path } {
+ global fileList
+ set saved [pwd]
+
+ cd $dir
+ lappend fileList $path
+ foreach f [lsort [glob -nocomplain *]] {
+ set entry [file join $path $f]
+ lappend fileList $entry
+ if { [file isdirectory $f] } {
+ DoFind $f $entry
+ }
+ }
+ cd $saved
+}
+
+proc Find { dir } {
+ global fileList
+ set fileList {}
+ DoFind $dir $dir
+ return $fileList
+}
+set top ..
+set trim "$top"
+
+.h configure -separator "/" -autocreate yes
+
+proc GetAbsolutePath { dir } {
+ set saved [pwd]
+ cd $dir
+ set path [pwd]
+ cd $saved
+ return $path
+}
+.h entry configure root -label [file tail [GetAbsolutePath $top]]
+.h configure -bg grey90
+update
+regsub -all {\.\./*} [Find $top] {} fileList
+eval .h insert end $fileList
+.h configure -bg white
+
+.h find -glob -name *.gif -exec {
+ %W entry configure %n -image [image create photo -file $top/%P]
+}
+
+focus .h
+
+set nodes [.h find -glob -name *.c]
+eval .h entry configure $nodes -labelcolor red
+
+cd $saved
+
diff --git a/blt/demos/hierbox2.tcl b/blt/demos/hierbox2.tcl
new file mode 100755
index 00000000000..dad7dd1e7f6
--- /dev/null
+++ b/blt/demos/hierbox2.tcl
@@ -0,0 +1,100 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+proc AddDirEntries { w dir } {
+ if { [file isdirectory $dir] } {
+ set files [glob -nocomplain $dir/*]
+ eval $w insert end [lsort $files]
+ set subdirs [glob -nocomplain $dir/*/]
+ eval $w entry configure [lsort $subdirs] -button yes
+ }
+}
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+set top ../
+
+#option add *Hierbox.Tile bgTexture
+option add *Hierbox.TileOffset yes
+
+option add *forceGadgets no
+option add *Hierbox.openCommand {
+ AddDirEntries %W "$top/%P"
+}
+option add *Hierbox.closeCommand {
+ eval %W delete %n 0 end
+}
+
+image create photo openFolder -file images/open.gif
+image create photo closeFolder -file images/close.gif
+
+option add *Hierbox.icons "closeFolder openFolder"
+
+#option add *Hierbox.Button.activeForeground red
+#option add *Hierbox.bindTags "Label all"
+
+hierbox .h \
+ -selectmode multiple \
+ -hideroot yes \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set }
+
+.h button configure -activebackground grey92
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+button .test -text Test -command {
+ set index [.h curselection]
+ set names [eval .h get -full $index]
+ puts "selected names are $names"
+}
+
+button .quit -text Quit -command { exit 0 }
+
+table . \
+ 0,0 .h -fill both \
+ 2,0 .quit \
+ 0,1 .vs -fill y 1,0 .hs -fill x \
+ 3,0 .test
+
+table configure . c1 r1 r2 r3 -resize none
+
+.h configure -separator "/" -trim $top \
+ -allowduplicates no
+
+#.h entry configure 0 -label [file tail $top]
+
+AddDirEntries .h $top
+focus .h
+set nodes [.h find -glob -name *.c]
+eval .h entry configure $nodes -labelcolor red
+
+wm protocol . WM_DELETE_WINDOW { destroy . }
+#blt::bltdebug 100 \ No newline at end of file
diff --git a/blt/demos/hierbox3.tcl b/blt/demos/hierbox3.tcl
new file mode 100755
index 00000000000..98e86eb33bf
--- /dev/null
+++ b/blt/demos/hierbox3.tcl
@@ -0,0 +1,80 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+proc AddDirEntries { w dir } {
+ if { [file isdirectory $dir] } {
+ set files [glob -nocomplain $dir/*]
+ eval $w insert end [lsort $files]
+ set subdirs [glob -nocomplain $dir/*/]
+ eval $w entry configure [lsort $subdirs] -button yes
+ }
+}
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+image create photo openFolder -file images/open.gif
+image create photo closeFolder -file images/close.gif
+option add *Hierbox.icons "closeFolder openFolder"
+
+#option add *Hierbox.openCommand { AddDirEntries %W "$top/%P" }
+#option add *Hierbox.closeCommand { eval %W delete %n 0 end }
+
+hierbox .h \
+ -allowduplicates no \
+ -hideroot yes \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set }
+
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+button .test -text Test -command {
+ set index [.h curselection]
+ set names [eval .h get -full $index]
+ puts "selected names are $names"
+}
+
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x \
+
+table configure . c1 r1 r2 -resize none
+
+set top ../
+.h configure -separator "/" -trim $top -autocreate yes
+#.h entry configure 0 -label [file tail $top]
+
+catch { exec du $top } files
+foreach f [split $files \n ] {
+ .h insert end [lindex $f 1] -text [lindex $f 0] -button auto
+}
+
+focus .h
diff --git a/blt/demos/hierbox4.tcl b/blt/demos/hierbox4.tcl
new file mode 100755
index 00000000000..6d507b64561
--- /dev/null
+++ b/blt/demos/hierbox4.tcl
@@ -0,0 +1,78 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+proc AddDirEntries { w dir } {
+ if { [file isdirectory $dir] } {
+ set files [glob -nocomplain $dir/*]
+ eval $w insert end [lsort $files]
+ set subdirs [glob -nocomplain $dir/*/]
+ eval $w entry configure [lsort $subdirs] -gadget yes
+ }
+}
+
+#blt::bltdebug 100
+
+image create photo openFolder -file images/open.gif
+image create photo closeFolder -file images/close.gif
+
+option add *Hierbox.icons "closeFolder openFolder"
+
+option add *Hierbox.cursor crosshair
+option add *Hierbox.Button.Relief solid
+option add *Hierbox.Button.ActiveBackground white
+option add *Hierbox.Button.Background white
+
+hierbox .h \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set } \
+ -activebackground lightskyblue1 \
+ -selectbackground lightskyblue2
+
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+button .test -text Test -command {
+ set index [.h curselection]
+ set names [eval .h get -full $index]
+ puts "selected names are $names"
+}
+
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x \
+ 2,0 .test
+
+table configure . c1 r1 -resize none
+
+.h configure -autocreate yes -font { Helvetica 34 }
+focus .h
+.h insert end { The Quick Brown Fox Jumped Over the }
+.h entry configure root -label {[Root]}
+.h insert end { The\nQuick\nBrown\nFox\nJumped\nOver\nthe }
+
+.h configure -focusedit yes
diff --git a/blt/demos/hiertable1.tcl b/blt/demos/hiertable1.tcl
new file mode 100755
index 00000000000..9dd36f870ee
--- /dev/null
+++ b/blt/demos/hiertable1.tcl
@@ -0,0 +1,236 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set saved [pwd]
+
+#blt::bltdebug 100
+
+image create photo bgTexture -file ./images/rain.gif
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+#option add *Hiertable.Tile bgTexture
+#option add *Hiertable.Column.background grey90
+option add *Hiertable.ScrollTile yes
+option add *Hiertable.titleShadow { grey80 }
+option add *Hiertable.titleFont {*-helvetica-bold-r-*-*-11-*-*-*-*-*-*-*}
+
+option add *xHiertable.openCommand {
+ set path /home/gah/src/blt/%P
+ if { [file isdirectory $path] } {
+ cd $path
+ set files [glob -nocomplain * */. ]
+ if { $files != "" } {
+ eval %W insert -at %n end $files
+ }
+ }
+}
+
+option add *xHiertable.closeCommand {
+ eval %W delete %n 0 end
+}
+
+hiertable .h -hideroot no -width 0 \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set } \
+ -selectmode single -hideleaves false
+
+
+.h column configure treeView -text View
+.h column insert 0 mtime atime gid
+.h column insert end nlink mode type ctime uid ino size dev
+.h column configure uid -background \#eaeaff -font fixed -relief raised -bd 1
+.h column configure mtime -hide no -bg \#ffeaea -relief raised -bd 1
+.h column configure size gid nlink uid ino dev -justify right -edit yes
+.h column configure treeView -hide no -edit no
+.h text configure -selectborderwidth 0
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x
+
+proc FormatSize { size } {
+ set string ""
+ while { $size > 0 } {
+ set rem [expr $size % 1000]
+ set size [expr $size / 1000]
+ if { $size > 0 } {
+ set rem [format "%03d" $rem]
+ }
+ if { $string != "" } {
+ set string "$rem,$string"
+ } else {
+ set string "$rem"
+ }
+ }
+ return $string
+}
+
+array set modes {
+ 0 ---
+ 1 --x
+ 2 -w-
+ 3 -wx
+ 4 r--
+ 5 r-x
+ 6 rw-
+ 7 rwx
+}
+
+proc FormatMode { mode } {
+ global modes
+
+ set mode [format %o [expr $mode & 07777]]
+ set owner $modes([string index $mode 0])
+ set group $modes([string index $mode 1])
+ set world $modes([string index $mode 2])
+
+ return "${owner}${group}${world}"
+}
+
+table configure . c1 r1 -resize none
+image create photo fileImage -file images/stopsign.gif
+proc DoFind { dir path } {
+ global fileList count
+ set saved [pwd]
+
+ cd $dir
+ foreach f [lsort [glob -nocomplain *]] {
+ set entry [file join $path $f]
+ if { [catch { file stat $entry info }] != 0 } {
+ lappend fileList $entry
+ } else {
+ if 0 {
+ if { $info(type) == "file" } {
+ set info(type) @fileImage
+ } else {
+ set info(type) ""
+ }
+ }
+ set info(mtime) [clock format $info(mtime) -format "%b %d, %Y"]
+ set info(atime) [clock format $info(atime) -format "%b %d, %Y"]
+ set info(ctime) [clock format $info(ctime) -format "%b %d, %Y"]
+ set info(size) [FormatSize $info(size)]
+ set info(mode) [FormatMode $info(mode)]
+ lappend fileList $entry -data [array get info]
+ }
+ incr count
+ if { [file type $f] == "directory" } {
+ DoFind $f $entry
+ }
+ }
+ cd $saved
+}
+
+proc Find { dir } {
+ global fileList count
+ set fileList {}
+ catch { file stat $dir info }
+ incr count
+ lappend fileList $dir -data [array get info]
+ DoFind $dir $dir
+ return $fileList
+}
+
+proc GetAbsolutePath { dir } {
+ set saved [pwd]
+ cd $dir
+ set path [pwd]
+ cd $saved
+ return $path
+}
+
+set top [GetAbsolutePath ..]
+set trim "$top"
+
+.h configure -separator "/" -trim $trim
+
+set count 0
+.h entry configure root -label [file tail [GetAbsolutePath $top]]
+.h configure -bg grey90
+regsub -all {\.\./*} [Find $top] {} fileList
+puts "$count entries"
+eval .h insert end $fileList
+.h configure -bg white
+
+focus .h
+
+set nodes [.h find -glob -name *.c]
+eval .h entry configure $nodes -foreground green4
+set nodes [.h find -glob -name *.h]
+eval .h entry configure $nodes -foreground cyan4
+set nodes [.h find -glob -name *.o]
+eval .h entry configure $nodes -foreground red4
+
+cd $saved
+#bltdebug 100
+
+toplevel .top
+hiertable .top.h2 -tree .h -yscrollcommand { .top.sbar set }
+scrollbar .top.sbar -command { .top.h2 yview }
+pack .top.h2 -side left -expand yes -fill both
+pack .top.sbar -side right -fill y
+
+.h column bind all <ButtonRelease-3> {
+ %W configure -flat no
+}
+
+proc SortColumn { column } {
+ set old [.h sort cget -column]
+ set decreasing 0
+ if { "$old" == "$column" } {
+ set decreasing [.h sort cget -decreasing]
+ set decreasing [expr !$decreasing]
+ }
+ .h sort configure -decreasing $decreasing -column $column -mode integer
+ .h configure -flat yes
+ .h sort auto yes
+
+ blt::busy hold .h
+ update
+ blt::busy release .h
+}
+
+foreach column [.h column names] {
+ .h column configure $column -command [list SortColumn $column]
+}
+
+scale .s -from 0 -to 300 -orient horizontal -length 300
+table . \
+ 3,0 .s
+update
+.s set 20
+if 1 {
+ .s configure -command { .h entry configure 0 -height }
+}
+
diff --git a/blt/demos/hiertable2.tcl b/blt/demos/hiertable2.tcl
new file mode 100755
index 00000000000..f45cf9e31f9
--- /dev/null
+++ b/blt/demos/hiertable2.tcl
@@ -0,0 +1,221 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set saved [pwd]
+
+#blt::bltdebug 100
+
+image create photo bgTexture -file ./images/rain.gif
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+#option add *Hiertable.Tile bgTexture
+option add *Hiertable.ScrollTile yes
+#option add *Hiertable.Column.background grey90
+option add *Hiertable.titleShadow { grey80 }
+option add *Hiertable.titleFont {*-helvetica-bold-r-*-*-11-*-*-*-*-*-*-*}
+
+option add *xHiertable.openCommand {
+ set path /home/gah/src/blt/%P
+ if { [file isdirectory $path] } {
+ cd $path
+ set files [glob -nocomplain * */. ]
+ if { $files != "" } {
+ eval %W insert -at %n end $files
+ }
+ }
+}
+
+option add *xHiertable.closeCommand {
+ eval %W delete %n 0 end
+}
+
+hiertable .h -width 0\
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set } \
+ -selectmode multiple
+
+
+.h column configure treeView -text "View"
+.h column insert 0 mtime atime gid
+.h column insert end nlink mode type ctime uid ino size dev
+.h column configure uid -background \#eaeaff -font fixed
+.h column configure mtime -hide no -bg \#ffeaea
+.h column configure size gid nlink uid ino dev -justify right
+.h column configure treeView -hide no -edit no
+
+.h text configure -selectborderwidth 0
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x
+
+proc FormatSize { size } {
+ set string ""
+ while { $size > 0 } {
+ set rem [expr $size % 1000]
+ set size [expr $size / 1000]
+ if { $size > 0 } {
+ set rem [format "%03d" $rem]
+ }
+ if { $string != "" } {
+ set string "$rem,$string"
+ } else {
+ set string "$rem"
+ }
+ }
+ return $string
+}
+
+array set modes {
+ 0 ---
+ 1 --x
+ 2 -w-
+ 3 -wx
+ 4 r--
+ 5 r-x
+ 6 rw-
+ 7 rwx
+}
+
+proc FormatMode { mode } {
+ global modes
+
+ set mode [format %o [expr $mode & 07777]]
+ set owner $modes([string index $mode 0])
+ set group $modes([string index $mode 1])
+ set world $modes([string index $mode 2])
+
+ return "${owner}${group}${world}"
+}
+
+table configure . c1 r1 -resize none
+image create photo fileImage -file images/stopsign.gif
+proc DoFind { dir path } {
+ global fileList count
+ set saved [pwd]
+
+ cd $dir
+ foreach f [lsort [glob -nocomplain *]] {
+ set entry [file join $path $f]
+ if { [catch { file stat $entry info }] != 0 } {
+ lappend fileList $entry
+ } else {
+ if 0 {
+ if { $info(type) == "file" } {
+ set info(type) @fileImage
+ } else {
+ set info(type) ""
+ }
+ }
+ set info(mtime) [clock format $info(mtime) -format "%b %d, %Y"]
+ set info(atime) [clock format $info(atime) -format "%b %d, %Y"]
+ set info(ctime) [clock format $info(ctime) -format "%b %d, %Y"]
+ set info(size) [FormatSize $info(size)]
+ set info(mode) [FormatMode $info(mode)]
+ lappend fileList $entry -data [array get info]
+ }
+ incr count
+ if { [file isdirectory $f] } {
+ DoFind $f $entry
+ }
+ }
+ cd $saved
+}
+
+proc Find { dir } {
+ global fileList count
+ set fileList {}
+ catch { file stat $dir info }
+ incr count
+ lappend fileList $dir -data [array get info]
+ DoFind $dir $dir
+ return $fileList
+}
+
+proc GetAbsolutePath { dir } {
+ set saved [pwd]
+ cd $dir
+ set path [pwd]
+ cd $saved
+ return $path
+}
+
+set top [GetAbsolutePath ..]
+set trim "$top"
+
+.h configure -separator "/" -trim $trim
+
+set count 0
+#.h entry configure root -label [file tail [GetAbsolutePath $top]]
+.h configure -bg grey90
+regsub -all {\.\./*} [Find $top] {} fileList
+puts "$count entries"
+eval .h insert end $fileList
+.h configure -bg white
+
+focus .h
+
+set nodes [.h find -glob -name *.c]
+eval .h entry configure $nodes -foreground green4
+set nodes [.h find -glob -name *.h]
+eval .h entry configure $nodes -foreground cyan4
+set nodes [.h find -glob -name *.o]
+eval .h entry configure $nodes -foreground red4
+
+cd $saved
+#bltdebug 100
+bind .h <ButtonPress-3> { .h text get %x %y ; focus .h.edit }
+
+.h column bind all <ButtonRelease-3> {
+ %W configure -flat no
+}
+
+proc SortColumn { column } {
+ set old [.h sort cget -column]
+ set decreasing 0
+ if { "$old" == "$column" } {
+ set decreasing [.h sort cget -decreasing]
+ set decreasing [expr !$decreasing]
+ }
+ .h sort configure -decreasing $decreasing -column $column
+ .h configure -flat yes
+ .h sort auto yes
+ blt::busy hold .h
+ update
+ blt::busy release .h
+}
+
+foreach column [.h column names] {
+ .h column configure $column -command [list SortColumn $column]
+}
diff --git a/blt/demos/hiertable3.tcl b/blt/demos/hiertable3.tcl
new file mode 100755
index 00000000000..c4f501083cc
--- /dev/null
+++ b/blt/demos/hiertable3.tcl
@@ -0,0 +1,201 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set saved [pwd]
+
+#blt::bltdebug 100
+
+image create photo bgTexture -file ./images/rain.gif
+
+set imageList {}
+foreach f [glob ./images/mini-*.gif] {
+ lappend imageList [image create photo -file $f]
+}
+
+#option add *Hiertable.Tile bgTexture
+option add *Hiertable.ScrollTile yes
+#option add *Hiertable.Column.background grey90
+option add *Hiertable.titleShadow { grey80 }
+option add *Hiertable.titleFont {*-helvetica-bold-r-*-*-11-*-*-*-*-*-*-*}
+
+hiertable .h -width 0\
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set } \
+ -selectmode multiple \
+ -hideroot yes
+
+#.h configure -icons "" -activeicons ""
+
+.h column configure treeView -text "View"
+.h column insert 0 mtime atime gid
+.h column insert end nlink mode type ctime uid ino size dev
+.h column configure uid -background \#eaeaff -font fixed
+.h column configure mtime -hide no -bg \#ffeaea
+.h column configure size gid nlink uid ino dev -justify right
+.h column configure treeView -hide no -edit no
+
+.h text configure -selectborderwidth 0
+scrollbar .vs -orient vertical -command { .h yview }
+scrollbar .hs -orient horizontal -command { .h xview }
+table . \
+ 0,0 .h -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x
+
+proc FormatSize { size } {
+ set string ""
+ while { $size > 0 } {
+ set rem [expr $size % 1000]
+ set size [expr $size / 1000]
+ if { $size > 0 } {
+ set rem [format "%03d" $rem]
+ }
+ if { $string != "" } {
+ set string "$rem,$string"
+ } else {
+ set string "$rem"
+ }
+ }
+ return $string
+}
+
+array set modes {
+ 0 ---
+ 1 --x
+ 2 -w-
+ 3 -wx
+ 4 r--
+ 5 r-x
+ 6 rw-
+ 7 rwx
+}
+
+proc FormatMode { mode } {
+ global modes
+
+ set mode [format %o [expr $mode & 07777]]
+ set owner $modes([string index $mode 0])
+ set group $modes([string index $mode 1])
+ set world $modes([string index $mode 2])
+
+ return "${owner}${group}${world}"
+}
+
+table configure . c1 r1 -resize none
+image create photo fileImage -file images/stopsign.gif
+
+proc DoFind { dir parent } {
+ global count
+ set saved [pwd]
+
+ cd $dir
+ foreach f [lsort [glob -nocomplain *]] {
+ set node [tree0 insert $parent -label $f]
+ if { [catch { file stat $f info }] == 0 } {
+ if 0 {
+ if { $info(type) == "file" } {
+ set info(type) @fileImage
+ } else {
+ set info(type) ""
+ }
+ }
+ set info(mtime) [clock format $info(mtime) -format "%b %d, %Y"]
+ set info(atime) [clock format $info(atime) -format "%b %d, %Y"]
+ set info(ctime) [clock format $info(ctime) -format "%b %d, %Y"]
+ set info(size) [FormatSize $info(size)]
+ set info(mode) [FormatMode $info(mode)]
+ eval tree0 set $node [array get info]
+ }
+ incr count
+ if { [file isdirectory $f] } {
+ DoFind $f $node
+ }
+ }
+ cd $saved
+}
+
+proc Find { dir } {
+ global count
+ set count 0
+ catch { file stat $dir info }
+ incr count
+ tree create tree0
+ tree0 label root [file tail $dir]
+ eval tree0 set root [array get info]
+ DoFind $dir root
+ puts "$count entries"
+}
+
+proc GetAbsolutePath { dir } {
+ set saved [pwd]
+ cd $dir
+ set path [pwd]
+ cd $saved
+ return $path
+}
+
+set top [GetAbsolutePath ..]
+Find $top
+
+focus .h
+
+.h configure -tree tree0 -separator /
+
+set nodes [.h find -glob -name *.c]
+eval .h entry configure $nodes -foreground green4
+set nodes [.h find -glob -name *.h]
+eval .h entry configure $nodes -foreground cyan4
+set nodes [.h find -glob -name *.o]
+eval .h entry configure $nodes -foreground red4
+
+cd $saved
+
+bind .h <ButtonPress-3> { .h text get %x %y ; focus .h.edit }
+
+.h column bind all <ButtonRelease-3> {
+ %W configure -flat no
+}
+
+proc SortColumn { column } {
+ set old [.h sort cget -column]
+ set decreasing 0
+ if { "$old" == "$column" } {
+ set decreasing [.h sort cget -decreasing]
+ set decreasing [expr !$decreasing]
+ }
+ .h sort configure -decreasing $decreasing -column $column
+ .h configure -flat yes
+ .h sort auto yes
+ blt::busy hold .h
+ update
+ blt::busy release .h
+}
+
+foreach column [.h column names] {
+ .h column configure $column -command [list SortColumn $column]
+}
diff --git a/blt/demos/htext.txt b/blt/demos/htext.txt
new file mode 100644
index 00000000000..7c476144b08
--- /dev/null
+++ b/blt/demos/htext.txt
@@ -0,0 +1,615 @@
+
+ This is a (for lack of a better name) hypertext widget.
+
+This widget combines text and other Tk widgets in the same window.
+It is sort of a cross between a read-only text widget and the pack command.
+Any widget can be attached to the hypertext window by the %%
+set this $htext(widget)
+label $this.lab -text "append " -relief sunken \
+ -font *-Courier-Bold-R-Normal-*-12-120-*
+$this append $this.lab
+%% command.
+For example,
+%% message $this.msg -relief sunken -bd 2 -aspect 10000 -font \
+ *-Courier-Medium-R-Normal-*-12-* -text {set w $htext(widget)
+label $w.face -bitmap @bitmaps/face.xbm \
+ -relief sunken -borderwidth 2
+$w append $w.face -padx 2 -pady 0.25i}
+$this append $this.msg \
+ -fill both %% added this %%
+global tk_library
+label $this.face \
+ -bitmap @bitmaps/face.xbm \
+ -relief sunken -borderwidth 2
+$this append $this.face -padx 2 -pady 0.25i
+%%.
+There can be many types of widgets in the same document. For example,
+this is a simple %%
+button $this.but -bg pink -text { button } \
+ -command { puts stderr { a stupid message } }
+$this append $this.but
+%%. If you click on the button, it prints a stupid message.
+Any Tk widget can be used, including %%
+set whichTile 0
+proc ChangeTile { w } {
+ global whichTile
+
+ if { $whichTile } {
+ $w configure -tile bgTexture2
+ } else {
+ $w configure -tile bgTexture1
+ }
+}
+checkbutton $this.ckbut -bg lightblue -text { check buttons } \
+ -variable whichTile -command "ChangeTile $this"
+$this append $this.ckbut -justify top
+%%, %%
+radiobutton $this.rdbut -bg mediumseagreen -text { radio buttons } \
+ -command { puts stderr { radio button pressed } }
+$this append $this.rdbut -justify bottom
+%%,
+and scales %%
+# -sliderforeground
+scale $this.sc -showvalue true \
+ -length 100 \
+ -foreground powderblue \
+ -sliderlength 10 \
+ -orient horizontal
+$this append $this.sc
+%%.
+
+ Widget trees can be also be included. The following example is
+*borrowed* from the widget demo. It is a couple of frames surrounding a
+listbox, a message, and a button widget.
+%%
+ set w $this.frame
+ frame $w
+ message $w.msg -font *times-medium-r-normal--*-12-120-* -aspect 300 \
+ -text "A listbox containing the 50 states is displayed below, along with a scrollbar. You can scan the list either using the scrollbar or by dragging in the listbox window with button 3 pressed. Click the \"OK\" button when you've seen enough." -bg lightsteelblue -relief sunken
+ frame $w.frame -borderwidth 10
+ pack append $w.frame \
+ [scrollbar $w.frame.scroll -relief sunken \
+ -command "$w.frame.list yview"] {right expand filly frame w} \
+ [listbox $w.frame.list -yscroll "$w.frame.scroll set" -relief sunken] \
+ {left expand filly frame e}
+ $w.frame.list insert 0 Alabama Alaska Arizona Arkansas California \
+ Colorado Connecticut Delaware Florida Georgia Hawaii Idaho Illinois \
+ Indiana Iowa Kansas Kentucky Louisiana Maine Maryland \
+ Massachusetts Michigan Minnesota Mississippi Missouri \
+ Montana Nebraska Nevada "New Hampshire" "New Jersey" "New Mexico" \
+ "New York" "North Carolina" "North Dakota" \
+ Ohio Oklahoma Oregon Pennsylvania "Rhode Island" \
+ "South Carolina" "South Dakota" \
+ Tennessee Texas Utah Vermont Virginia Washington \
+ "West Virginia" Wisconsin Wyoming
+ button $w.ok -text OK -command "puts stderr $w; destroy $w"
+
+ pack append $w $w.msg {top fill} $w.frame {top expand fill} \
+ $w.ok {bottom fill}
+ $w config -bg lightsteelblue -relief sunken
+
+$this append $w -pady 0.25i
+%%
+
+You can add you own home-grown widgets. Here's the graph widget.
+Beside it is the "color" demo. Moving the scales, adjusts the background
+color of the graph.
+%%
+#
+# Simple script to change colors of a window.
+#
+global xlabel ylabel red green blue graph
+set red 255
+set green 215
+set blue 0
+
+option add *Scale.sliderForeground "#cdb79e"
+option add *Scale.activeForeground "#ffe4c4"
+set w $this.colorFrame
+frame $w
+scale $w.red -command "color red" -label "Red Intensity" \
+ -from 0 -to 255 -orient horizontal -bg "#ffaeb9" -length 250
+scale $w.green -command "color green" -label "Green Intensity" \
+ -from 0 -to 255 -orient horizontal -bg "#43cd80"
+scale $w.blue -command "color blue" -label "Blue Intensity" \
+ -from 0 -to 255 -orient horizontal -bg "#7ec0ee"
+
+$w.blue set $blue
+$w.green set $green
+$w.red set $red
+
+pack append $w $w.red {top expand fill}
+pack append $w $w.green {top expand fill}
+pack append $w $w.blue {top expand fill}
+
+proc color {which intensity} {
+ global red green blue graph xlabel ylabel
+ set $which $intensity
+ set rgb [format #%02x%02x%02x $red $green $blue]
+ $graph config -bg $rgb
+ $xlabel config -bg $rgb
+ $ylabel config -bg $rgb
+}
+
+$this append $w
+
+%%
+%%
+proc makeplot { widget } {
+
+ graph $widget
+ set X {
+ 2.00000e-01 4.00000e-01 6.00000e-01 8.00000e-01 1.00000e+00
+ 1.20000e+00 1.40000e+00 1.60000e+00 1.80000e+00 2.00000e+00
+ 2.20000e+00 2.40000e+00 2.60000e+00 2.80000e+00 3.00000e+00
+ 3.20000e+00 3.40000e+00 3.60000e+00 3.80000e+00 4.00000e+00
+ 4.20000e+00 4.40000e+00 4.60000e+00 4.80000e+00 5.00000e+00
+ }
+
+ $widget element create Y1 -x $X -y {
+ 1.14471e+01 2.09373e+01 2.84608e+01 3.40080e+01 3.75691e+01
+ 3.91345e+01 3.92706e+01 3.93474e+01 3.94242e+01 3.95010e+01
+ 3.95778e+01 3.96545e+01 3.97313e+01 3.98081e+01 3.98849e+01
+ 3.99617e+01 4.00384e+01 4.01152e+01 4.01920e+01 4.02688e+01
+ 4.03455e+01 4.04223e+01 4.04990e+01 4.05758e+01 4.06526e+01
+ } -symbol circle -label VGS=2.0 -color blue4 -fill blue
+
+ $widget element create Y2 -x $X -y {
+ 2.61825e+01 5.04696e+01 7.28517e+01 9.33192e+01 1.11863e+02
+ 1.28473e+02 1.43140e+02 1.55854e+02 1.66606e+02 1.75386e+02
+ 1.82185e+02 1.86994e+02 1.89802e+02 1.90683e+02 1.91047e+02
+ 1.91411e+02 1.91775e+02 1.92139e+02 1.92503e+02 1.92867e+02
+ 1.93231e+02 1.93595e+02 1.93958e+02 1.94322e+02 1.94686e+02
+ } -symbol diamond -label VGS=3.5 -color green4 -fill green
+
+ $widget element create Y3 -x $X -y {
+ 4.07008e+01 7.95658e+01 1.16585e+02 1.51750e+02 1.85051e+02
+ 2.16479e+02 2.46024e+02 2.73676e+02 2.99427e+02 3.23267e+02
+ 3.45187e+02 3.65177e+02 3.83228e+02 3.99331e+02 4.13476e+02
+ 4.25655e+02 4.35856e+02 4.44073e+02 4.50294e+02 4.54512e+02
+ 4.56716e+02 4.57596e+02 4.58448e+02 4.59299e+02 4.60151e+02
+ } -symbol triangle -label VGS=5.0 -color red4 -fill red
+
+}
+
+option add *graph.title "Plot Title"
+option add *graph.xTitle "X Axis Label"
+option add *graph.yTitle "Y Axis Label"
+#option add *graph.legendMapped false
+option add *graph.elemPixels 8
+option add *graph.relief ridge
+option add *graph.borderWidth 2
+
+set graph $this.graph
+set xlabel $this.xlab
+set ylabel $this.ylab
+makeplot $graph
+$this append $graph -padx 0.25i -pady 0.25i
+
+%%
+If you click on any button in the graph, you will get the coordinate
+values at the pointer location.
+
+The current coordinate values are %%
+label $xlabel -text { ??? ??? } -relief sunken
+label $ylabel -text { ??? ??? } -relief sunken
+bind $graph <ButtonPress> {labelxy [ %W invtransform %x %y ]}
+
+proc labelxy { values } {
+ global xlabel ylabel
+ scan $values "%e %e" x y
+ $xlabel config -text $x
+ $ylabel config -text $y
+}
+$this append $this.xlab -width 100 -fill x
+%% and %%
+$this append $this.ylab -width 100 -fill x
+%%.
+
+
+There are four global variables automatically created when a hypertext
+file is read. They are:
+
+%%
+button $this.l1 -text " \$htext(widget) " \
+ -command "puts $this" -bg orange
+$this append $this.l1 -width 200 -pady 4
+%%the pathname of the hypertext widget.
+%%
+button $this.l2 -text " \$htext(file) " \
+ -command "puts $htext(file)" -bg orange
+$this append $this.l2 -width 200 -pady 4
+%%the file being read.
+%%
+button $this.l3 -text " \$htext(line) " \
+ -command "puts $htext(line)" -bg orange
+$this append $this.l3 -width 200 -pady 4
+%%the current line number.
+%%
+button $this.l4 -text " \$htext(index) " \
+ -command "puts $htext(index)" -bg orange
+$this append $this.l4 -width 200 -pady 4
+%%the current index in the text.
+
+Click on any button and the current value is printed on standard output.
+
+The hypertext widget works with plain text too. If you don't want
+to read it, click on the %%
+button $this.goto -text button -fg purple -bg white \
+ -command "global endOfText; $this gotoline \$endOfText"
+$this append $this.goto
+%% to jump to the end of the plain text.
+
+ ------------------------------------------------------
+
+[This is a pre-release version of BLT. It's basically the latest
+
+snapshot of BLT, as it moves towards a full release. What this means
+is that the documentation and demos still need work. Let me know
+about any configuration/compiler/installation goofs so I make sure
+they're fixed for the next release.]
+
+This is version 2.4 of the BLT library. It's an extension to the
+Tcl/Tk toolkit. You simply compile and link with the Tcl/Tk
+libraries. It does not require the Tcl or Tk source files.
+
+BLT is available from
+
+ ftp.tcltk.com
+
+in the "pub/blt" directory. The URL is
+
+ ftp://ftp.tcltk.com/pub/blt/BLT2.4.tar.gz
+
+This release has been compiled and tested with versions:
+
+ Tcl 7.5 / Tk 4.1
+ Tcl 7.6 / Tk 4.2
+ Tcl/Tk 8.0
+ Tcl/Tk 8.1a2
+
+What is BLT?
+
+ BLT is an extension to Tk. It adds plotting widgets (X-Y graph,
+ barchart, stripchart), a powerful geometry manager, a new canvas
+ item, and several new commands to Tk.
+
+ Plotting widgets:
+
+ graph, barchart, stripchart
+ BLT has X-Y graph, barchart, and stripchart widgets that are
+ both easy to use and customize. All the widgets work with
+ BLT vector data objects, which makes it easy to manage data.
+
+ Hierarchical list box:
+
+ hierbox Displays a general ordered tree which may be built
+ on-the-fly or all at once.
+
+ Tab set:
+
+ tabset Can be used either as a tab notebook or simple tabset.
+ Multi-tiered and/or scrolled tabsets are available.
+ Notebook pages can be torn-off into separate windows and
+ later put back.
+
+ Geometry Manager:
+
+ table A table-based geometry manager. Lets you specify widget
+ layouts by row and column positions in the table. Unlike the
+ packer or grid, you can finely control and constrain window
+ sizes.
+
+ Vector Data Object:
+
+ vector Lets you manage a vector of floating point values in a
+ high-level fashion. Vectors inter-operate seamlessly with
+ the plotting widgets. The graphs will automatically redraw
+ themselves when the vector data changes. Vector's components
+ can be managed through a Tcl array variable, a Tcl command,
+ or the using its own C API.
+
+ Background Program Execution:
+
+ bgexec Like Tcl's "exec ... &", but collects the output, error, and
+ status of the detached UNIX subprocesses. Sets a Tcl variable
+ upon completion.
+
+ Busy Command:
+
+ busy For preventing user-interactions when the application is
+ busy. Manages an invisible "busy" window which prevents
+ further user interactions (keyboard, mouse, button, etc.).
+ Also you can provide a busy cursor that temporarily
+ overrides those of the Tk widgets.
+
+ New Canvas Item:
+
+ eps An new item is added to the Tk canvas for handling
+ encapsulated PostScript. It lets you embed an EPS file into
+ the canvas displaying either an EPS preview image found in
+ the file, or a Tk image that you provide. When you print
+ the canvas the EPS item will automatically include the EPS
+ file, translating and scaling the PostScript. For example,
+ you could use "eps" items to tile several PostScript pages
+ into single page.
+
+ The "eps" item can also be used as a replacement for "image"
+ canvas items. Unlike "image" canvas items, the image of an
+ eps item can be printed and scaled arbitrarily.
+
+ Drag & Drop Facility:
+
+ drag&drop Adds drag-n-drop capabilities to Tk. It uses "send"-style
+ communication between drag-drop sources and targets. The
+ result is a much more powerful drag-and-drop mechanism than
+ is available with OpenLook or Motif.
+
+ Bitmap Command:
+
+ bitmap Lets you read and write bitmaps from Tcl. You can define
+ bitmaps from ordinary text strings. Bitmaps can also be
+ scaled and rotated. For example, you can create a button
+ with rotated text by defining a bitmap from a text string
+ and rotating it. You can then use the bitmap in the button
+ widget.
+
+ Miscellaneous Commands:
+
+ winop Basic window operations. You can raise, lower, map, or,
+ unmap windows. Other operations let you move the pointer
+ or take photo image snapshots of Tk widgets.
+
+ bltdebug Lets you trace the execution of Tcl commands and procedures.
+ Prints out each Tcl command before it's executed.
+
+ watch Lets you specify Tcl procedures to be run before and/or
+ after every Tcl command. May be used for logging, tracing,
+ profiling, or debugging or Tcl code.
+
+ spline Computes a spline fitting a set of data points (x and y
+ vectors) and produces a vector of the interpolated images
+ (y-coordinates) at a given set of x-coordinates.
+
+ htext A simple hypertext widget. Allows text and Tk widgets to
+ be combined in a scroll-able text window. Any Tk widget
+ can be embedded and used to form hyper-links. Other
+ options allow for selections and text searches.
+
+What's new in 2.4?
+
+ 1. "eps" canvas item.
+
+ An encapsulated PostScript canvas item lets you embed an EPS file into
+ the canvas. The "eps" item displays either a EPS preview image found
+ in the file, or a Tk image that you provide.
+
+ 2. "hierbox" widget.
+
+ Hierarchical listbox widget. Displays a general ordered tree which
+ may be built on-the-fly or all at once.
+
+ 3. "tabset" widget.
+
+ Can be used either as a tab notebook or simple tabset. Tabs can
+ be arranged in a variety of ways: multi-tiered, scrolled, and
+ attached to any of the four sides. Tab labels can contain both
+ images and text (text can be arbitrarily rotated). Notebook pages
+ can be torn-off into separate windows and replaced later.
+
+ 4. Changes to vectors.
+
+ New features:
+
+ o Vector expressions. The vector now has an "expr" operation
+ that lets you perform math (including math library
+ functions) on vectors. There are several new functions
+ (such as "max", "min", "mean" "median", "q1", "q3", "prod",
+ "sum", "adev", "sdev", "skew", ...)
+
+ vector expr { sin(x)^2 + cos(x)^2 }
+ y expr { log(x) * $value }
+
+ o New syntax to create and destroy vectors:
+
+ vector create x
+ vector destroy x
+
+ The old syntax for creating vectors still works.
+
+ vector x
+
+ o Vectors are *not* automatically deleted when their Tcl
+ variable is unset anymore. This means that you can
+ temporarily map vectors to variables and use them as you
+ would an ordinary Tcl array (kind of like "upvar").
+
+ proc AddValue { vecName value } {
+ $vecName variable x
+
+ set x(++end) $value
+ }
+
+ There's an "-watchunset" flag to restore the old
+ behavior if you need it.
+
+ vector create x -watchunset yes
+
+ o Vectors still automatically create Tcl variables by
+ default. I'd like to change this, but it silently
+ breaks lots of code, so it will stay.
+
+ Bug fixes:
+
+ o Vector reallocation failed when shrinking the vector.
+
+ o Vector "destroy" callback made after vector was
+ already freed.
+
+ 5. Changes to Graph, Barchart, Stripchart widgets.
+
+ New features:
+
+ o Drop shadows for text (titles, markers, etc). Drop
+ shadows improve contrast when displaying text over a
+ background with similar color intensities.
+
+ o Postscript "-preview" option to generate a EPS
+ PostScript preview image that can be read and
+ displayed by the EPS canvas item.
+
+ o New "-topvariable", "-bottomvariable",
+ "-leftvariable", and "-rightvariable" options. They
+ specify variables to contain the current margin
+ sizes. These variables are updated whenever the
+ graph is redrawn.
+
+ o New "-aspect" option. Let's you maintain a particular aspect
+ ratio for the the graph.
+
+ o Image markers can now be stretched and zoomed like
+ bitmap markers.
+
+ o Bind operation for legend entries, markers, and elements.
+
+ Much thanks to Julian Loaring <bigj@bigj.demon.co.uk>
+ for the suggestions.
+
+ o New "-xor" option for line markers, lets you draw the line
+ by rubberbanded by XOR-ing without requiring the graph to
+ be redrawn. This can be used, for example, to select regions
+ like in zooming.
+
+ Thanks to Johannes Zellner (joze@krisal.physik.uni-karlsruhe.de)
+ for the suggestion.
+
+ Bug fixes:
+
+ o Closest line (point) broken when using pens styles.
+
+ o Marker elastic coordinates were wrong.
+
+ o PostScript bounding box included the border of the page.
+
+ o Bad PostScript generated for barchart symbols with stipples.
+
+ o Wrong dimensions computed with postscript " -maxpect" option.
+
+ o Text markers fixed.
+
+ Thanks to De Clarke for the bug report and fix.
+
+
+ o Renamed axis configuration from "-range" to "-autorange" to
+ match the documentation.
+
+ Thanks to Brian Smith for the correction.
+
+ o Fixed polygon marker pick routine.
+
+ o Fixed active tab labels overlapping the selected tab.
+
+
+What's incompatible with releases prior to BLT 2.4?
+
+ 1. Vector names must start with a letter and contain letters, digits,
+ or underscores.
+
+ Namespace Issues: Vector names are still global. If Tcl provides
+ an API, vectors may in the future be created on
+ a per-namespace basis. Right now, there's no
+ mechanism for detecting when a namespace has been
+ destroyed. Which is why you can't currently
+ prefix a vector name with a namespace qualifier.
+
+ [Ok, there is... Thanks to Michael McLennan for
+ pointing this out to me. So maybe soon there
+ will be vectors on a per namespace basis.]
+
+ 2. The "-mapped" options throughout the graph have been replaced
+ by the "-hide" option. The many usages of the word "map" was
+ getting confusing.
+
+ # No longer works.
+ .graph legend configure -mapped no
+
+ # Instead use this.
+ .graph legend configure -hide yes
+
+
+How to compile and test BLT?
+
+ See the file "INSTALL" for instructions.
+
+When will the so-called "official" BLT work with Windows?
+
+ It currently compiles and runs with MS VC++ and EGCS 1.1 under
+ Windows 95/NT (loadable binary versions will be forthcoming).
+ Everything pretty much works: graphs, bgexec, busy, drag&drop etc.
+
+When will...?
+
+ In general, I can't answer the "When will" questions, mostly out of
+ embarrassment. My estimates of when new features and releases will
+ occur usually turn out to be way way off.
+
+What does BLT stand for?
+
+ Whatever you want it to.
+
+--gah
+
+%%
+global endOfText
+set endOfText [expr $htext(line)-1 ]
+
+global updateInterval count barchart
+global Red Green Blue
+set updateInterval 200
+set count 0
+set Red bb
+set Green 00
+set Blue 33
+
+option add *barchart.title "Bar Chart"
+option add *barchart.x.title "X"
+option add *barchart.y.title "Y"
+option add *barchart.y2.title "Y"
+option add *barchart.Axis.subTicks 0
+option add *barchart.x.stepSize 0
+option add *barchart.x.Ticks 0
+option add *barchart.legend.hide yes
+option add *barchart.Axis.Font *-Courier-Bold-R-Normal-*-8-80-*
+option add *barchart.y2.hide yes
+
+set barchart $this.barchart
+barchart $barchart -bd 2 -relief raised -tile bgTexture2
+$barchart y2axis use y
+$this append $barchart -fill both -padx 10 -pady 10 -relwidth 0.8
+
+proc AnimateBarchart { } {
+ global updateInterval
+ global barchart count Red Blue Green
+
+ if { [info commands $barchart] != $barchart } {
+ return
+ }
+ incr count
+ if { $count > 100 } {
+ $barchart element delete [lindex [$barchart element show] end]
+ }
+ set color [format "%x" [expr $count%16]]
+ set Green ${color}${color}
+ $barchart element create $count -data { $count sin($count*0.1)} \
+ -fg #${Red}${Green}${Blue} -bg brown
+ after $updateInterval AnimateBarchart
+}
+AnimateBarchart
+
+%%
+
+ Press %%
+button $this.quit -command { exit } -text {Quit} -bg pink
+$this append $this.quit
+%% to remove the window.
+
diff --git a/blt/demos/htext1.tcl b/blt/demos/htext1.tcl
new file mode 100755
index 00000000000..19f642710ce
--- /dev/null
+++ b/blt/demos/htext1.tcl
@@ -0,0 +1,186 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set visual [winfo screenvisual .]
+if { $visual == "staticgray" || $visual == "grayscale" } {
+ set activeBg black
+ set normalBg white
+ set bitmapFg black
+ set bitmapBg white
+ option add *top.background white
+} else {
+ option add *htext.foreground navyblue
+ if { $tk_version >= 4.0 } {
+ set file1 ./images/clouds.gif
+ set file2 ./images/chalk.gif
+ image create photo bgTexture1 -file $file1
+ image create photo bgTexture2 -file $file2
+# option add *htext.tile bgTexture1
+ option add *htext.foreground black
+ option add *htext.background white
+ option add *htext.selectBackground gold1
+ }
+}
+option add *highlightThickness 0
+
+proc Blt_FindPattern { htext } {
+ toplevel .search
+ wm title .search "Text search"
+ label .search.label1 -text "Enter Pattern"
+ entry .search.entry -relief sunken
+ button .search.clear -text "Clear" \
+ -command ".search.entry delete 0 end"
+ button .search.cancel -text "Cancel" \
+ -command "destroy .search; focus $htext"
+ button .search.search -text "Search" -command "Blt_Search&Move $htext"
+ bind .search.entry <Return> "Blt_Search&Move $htext"
+ table .search \
+ .search.label1 0,0 -padx 4 \
+ .search.entry 0,1 -cspan 2 -pady 4 -padx 4 -reqwidth 3i \
+ .search.search 3,0 -reqwidth .75i -anchor w -padx 10 -pady 5 \
+ .search.clear 3,1 -reqwidth .75i -anchor center -padx 10 -pady 5 \
+ .search.cancel 3,2 -reqwidth .75i -anchor e -padx 10 -pady 5
+ focus .search.entry
+ bind .search <Visibility> { raise .search }
+}
+
+set last 0
+set lastPattern {}
+
+proc Blt_Search&Move { h } {
+ global last
+ global lastPattern
+
+
+ set pattern [.search.entry get]
+ if { [string compare $pattern $lastPattern] != 0 } {
+ set last 0
+ set lastPattern $pattern
+ }
+ if { $pattern == "" } {
+ return
+ }
+
+ set indices [$h search $pattern $last end]
+ if { $indices == "" } {
+ bell
+ } else {
+ set first [lindex $indices 0]
+ set last [lindex $indices 1]
+ $h selection range $first $last
+ $h gotoline $first
+ incr last
+ }
+}
+
+# Create horizonatal and vertical scrollbars
+scrollbar .vscroll -command { .htext yview } -orient vertical
+scrollbar .hscroll -command { .htext xview } -orient horizontal
+
+# Create the hypertext widget
+htext .htext -file ./htext.txt \
+ -yscrollcommand { .vscroll set } \
+ -xscrollcommand { .hscroll set } \
+ -yscrollunits 10m -xscrollunits .25i \
+ -height 6i
+
+
+table . \
+ .htext 0,0 -fill both \
+ .vscroll 0,1 -fill y \
+ .hscroll 1,0 -fill x
+
+table configure . r1 c1 -resize none
+
+bind .htext <B1-Motion> {
+ %W select to @%x,%y
+}
+bind .htext <1> {
+ %W select from @%x,%y
+ %W select to @%x,%y
+}
+
+bind .htext <Shift-1> {
+ %W select word @%x,%y
+}
+bind .htext <Meta-1> {
+ %W select line @%x,%y
+}
+bind .htext <Control-1> {
+ puts stderr [%W select index @%x,%y]
+}
+
+bind .htext <B2-Motion> {
+ %W scan dragto @%x,%y
+}
+bind .htext <2> {
+ %W scan mark @%x,%y
+}
+
+bind .htext <3> {
+ %W select adjust @%x,%y
+}
+
+bind .htext <Control-p> {
+ set line [%W gotoline]
+ if { $line == 0 } {
+ bell
+ } else {
+ set line [expr $line-1]
+ %W gotoline $line.0
+ }
+}
+bind .htext <Control-n> {
+ set line [%W gotoline]
+ incr line
+ if { [%W gotoline $line.0] != $line } {
+ bell
+ }
+}
+
+bind .htext <Control-v> {
+ %W yview [expr [%W yview]+10]
+}
+
+bind .htext <Meta-v> {
+ %W yview [expr [%W yview]-10]
+}
+
+bind .htext <Alt-v> {
+ %W yview [expr [%W yview]-10]
+}
+
+bind .htext <Any-q> {
+ exit 0
+}
+bind .htext <Control-s> {
+ Blt_FindPattern %W
+}
+
+wm min . 0 0
+focus .htext
diff --git a/blt/demos/images/blt98.gif b/blt/demos/images/blt98.gif
new file mode 100644
index 00000000000..5d8c9645fe9
--- /dev/null
+++ b/blt/demos/images/blt98.gif
Binary files differ
diff --git a/blt/demos/images/buckskin.gif b/blt/demos/images/buckskin.gif
new file mode 100644
index 00000000000..e2d7be9d628
--- /dev/null
+++ b/blt/demos/images/buckskin.gif
Binary files differ
diff --git a/blt/demos/images/chalk.gif b/blt/demos/images/chalk.gif
new file mode 100644
index 00000000000..30d29a72216
--- /dev/null
+++ b/blt/demos/images/chalk.gif
Binary files differ
diff --git a/blt/demos/images/close.gif b/blt/demos/images/close.gif
new file mode 100644
index 00000000000..02f83638cf9
--- /dev/null
+++ b/blt/demos/images/close.gif
Binary files differ
diff --git a/blt/demos/images/close2.gif b/blt/demos/images/close2.gif
new file mode 100644
index 00000000000..09cbdffc929
--- /dev/null
+++ b/blt/demos/images/close2.gif
Binary files differ
diff --git a/blt/demos/images/clouds.gif b/blt/demos/images/clouds.gif
new file mode 100644
index 00000000000..49d15c26195
--- /dev/null
+++ b/blt/demos/images/clouds.gif
Binary files differ
diff --git a/blt/demos/images/corrugated_metal.gif b/blt/demos/images/corrugated_metal.gif
new file mode 100644
index 00000000000..f212bd76741
--- /dev/null
+++ b/blt/demos/images/corrugated_metal.gif
Binary files differ
diff --git a/blt/demos/images/folder.gif b/blt/demos/images/folder.gif
new file mode 100644
index 00000000000..86eb798c7f4
--- /dev/null
+++ b/blt/demos/images/folder.gif
Binary files differ
diff --git a/blt/demos/images/jan25_palm3x_L.jpg b/blt/demos/images/jan25_palm3x_L.jpg
new file mode 100644
index 00000000000..de30779045b
--- /dev/null
+++ b/blt/demos/images/jan25_palm3x_L.jpg
Binary files differ
diff --git a/blt/demos/images/mini-book1.gif b/blt/demos/images/mini-book1.gif
new file mode 100644
index 00000000000..b8868dcd4da
--- /dev/null
+++ b/blt/demos/images/mini-book1.gif
Binary files differ
diff --git a/blt/demos/images/mini-book2.gif b/blt/demos/images/mini-book2.gif
new file mode 100644
index 00000000000..266251a078e
--- /dev/null
+++ b/blt/demos/images/mini-book2.gif
Binary files differ
diff --git a/blt/demos/images/mini-display.gif b/blt/demos/images/mini-display.gif
new file mode 100644
index 00000000000..a2416fbe31b
--- /dev/null
+++ b/blt/demos/images/mini-display.gif
Binary files differ
diff --git a/blt/demos/images/mini-doc.gif b/blt/demos/images/mini-doc.gif
new file mode 100644
index 00000000000..30343205dc3
--- /dev/null
+++ b/blt/demos/images/mini-doc.gif
Binary files differ
diff --git a/blt/demos/images/mini-filemgr.gif b/blt/demos/images/mini-filemgr.gif
new file mode 100644
index 00000000000..cd16bc2624f
--- /dev/null
+++ b/blt/demos/images/mini-filemgr.gif
Binary files differ
diff --git a/blt/demos/images/mini-ofolder.gif b/blt/demos/images/mini-ofolder.gif
new file mode 100644
index 00000000000..78d7436e342
--- /dev/null
+++ b/blt/demos/images/mini-ofolder.gif
Binary files differ
diff --git a/blt/demos/images/mini-windows.gif b/blt/demos/images/mini-windows.gif
new file mode 100644
index 00000000000..5a504fc4a1b
--- /dev/null
+++ b/blt/demos/images/mini-windows.gif
Binary files differ
diff --git a/blt/demos/images/ofolder.gif b/blt/demos/images/ofolder.gif
new file mode 100644
index 00000000000..1238fa2b435
--- /dev/null
+++ b/blt/demos/images/ofolder.gif
Binary files differ
diff --git a/blt/demos/images/open.gif b/blt/demos/images/open.gif
new file mode 100644
index 00000000000..ea826a9aa2d
--- /dev/null
+++ b/blt/demos/images/open.gif
Binary files differ
diff --git a/blt/demos/images/open2.gif b/blt/demos/images/open2.gif
new file mode 100644
index 00000000000..6db5fb74546
--- /dev/null
+++ b/blt/demos/images/open2.gif
Binary files differ
diff --git a/blt/demos/images/out.ps b/blt/demos/images/out.ps
new file mode 100644
index 00000000000..1536442a308
--- /dev/null
+++ b/blt/demos/images/out.ps
@@ -0,0 +1,11662 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%BoundingBox: 72 104 539 687
+%%Pages: 0
+%%Creator: (BLT 2.4 Graph)
+%%CreationDate: (Sun May 16 22:17:22 1999)
+%%Title: (out.ps)
+%%%%DocumentData: Clean7Bit
+%Orientation: Landscape
+%%DocumentNeededResources: font Helvetica Courier
+%%EndComments
+%%BeginPreview: 599 480 8 9584
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000000000FF
+% 101018111818181818181818181818181818181818181818181811181010
+% 1218181818181A181A181818161010101010101010101010101817181818
+% 161210101210121012101216181818181818181818181010121018181818
+% 181818101012101018111818181818181818181818181818181818181818
+% 1818111810101218181818181A181A181818161010101010101010101010
+% 101817181818161210101210121012101216181818181818181818181010
+% 121018181818181818101012101018111818181818181818181818181818
+% 1818181818181818111810101218181818181A181A181818161010101010
+% 101010101010101817181818161210101210121012101216181818181818
+% 181818181010121018181818181818101012101018111818181818181818
+% 1818181818181818181818181818111810101218181818181A181A181818
+% 161010101010101010101010101817181818161210101210121012101216
+% 181818181818181818181010121018181818181818101012101018111818
+% 181818181818181818181818181818181818181811181010121818181818
+% 1A181A181818161010101010101010101010101817181818161210101210
+% 121012101216181818181818181818181010121018181818181818101012
+% 101018111818181818181818181818181818181818181818181811181010
+% 1218181818181A181A181818161010101010101010101010101817181818
+% 161210101210121012101216181818181818181818181010121018181818
+% 18181810101210101811181818181818181818181818181818181800FF12
+% 1810181018181018181818181A1818181818181A18181818161818131810
+% 181818181A18181818181818181012101010101010101010181818181818
+% 18181012161816121610181218161818161818121012101012101818181A
+% 1818101010121810181018181018181818181A1818181818181A18181818
+% 161818131810181818181A18181818181818181012101010101010101010
+% 181818181818181810121618161216101812181618181618181210121010
+% 12101818181A1818101010121810181018181018181818181A1818181818
+% 181A18181818161818131810181818181A18181818181818181012101010
+% 101010101010181818181818181810121618161216101812181618181618
+% 18121012101012101818181A181810101012181018101818101818181818
+% 1A1818181818181A18181818161818131810181818181A18181818181818
+% 181012101010101010101010181818181818181810121618161216101812
+% 18161818161818121012101012101818181A181810101012181018101818
+% 1018181818181A1818181818181A18181818161818131810181818181A18
+% 181818181818181012101010101010101010181818181818181810121618
+% 16121610181218161818161818121012101012101818181A181810101012
+% 1810181018181018181818181A1818181818181A18181818161818131810
+% 181818181A18181818181818181012101010101010101010181818181818
+% 18181012161816121610181218161818161818121012101012101818181A
+% 1818101010121810181018181018181818181A1818181818181A00FF1810
+% 121812161218101818181818181818181A18181818181818181810181018
+% 181818181818181818181818101810121010101010101012121012181818
+% 181012181818181212101210181218181810101810181010121218181818
+% 181810121810121812161218101818181818181818181A18181818181818
+% 181810181018181818181818181818181818101810121010101010101012
+% 121012181818181012181818181212101210181218181810101810181010
+% 121218181818181810121810121812161218101818181818181818181A18
+% 181818181818181810181018181818181818181818181818101810121010
+% 101010101012121012181818181012181818181212101210181218181810
+% 101810181010121218181818181810121810121812161218101818181818
+% 181818181A18181818181818181810181018181818181818181818181818
+% 101810121010101010101012121012181818181012181818181212101210
+% 181218181810101810181010121218181818181810121810121812161218
+% 101818181818181818181A18181818181818181810181018181818181818
+% 181818181818101810121010101010101012121012181818181012181818
+% 181212101210181218181810101810181010121218181818181810121810
+% 121812161218101818181818181818181A18181818181818181810181018
+% 181818181818181818181818101810121010101010101012121012181818
+% 181012181818181212101210181218181810101810181010121218181818
+% 181810121810121812161218101818181818181818181A181800FF171816
+% 12161012161210181818181818181A181818181818181818181810181818
+% 18181A181818101812181810121018101210121010101010101010121818
+% 121618181818161010121012101216121818181818181010101012101818
+% 16181817181612161012161210181818181818181A181818181818181818
+% 18181018181818181A181818101812181810121018101210121010101010
+% 101010121818121618181818161010121012101216121818181818181010
+% 10101210181816181817181612161012161210181818181818181A181818
+% 18181818181818181018181818181A181818101812181810121018101210
+% 121010101010101010121818121618181818161010121012101216121818
+% 181818181010101012101818161818171816121610121612101818181818
+% 18181A18181818181818181818181018181818181A181818101812181810
+% 121018101210121010101010101010121818121618181818161010121012
+% 101216121818181818181010101012101818161818171816121610121612
+% 10181818181818181A18181818181818181818181018181818181A181818
+% 101812181810121018101210121010101010101010121818121618181818
+% 161010121012101216121818181818181010101012101818161818171816
+% 12161012161210181818181818181A181818181818181818181810181818
+% 18181A181818101812181810121018101210121010101010101010121818
+% 121618181818161010121012101216121818181818181010101012101818
+% 16181817181612161012161210181818181818181A18181800FF18181818
+% 121010101010121618181818181818181A1818181A181818181818181818
+% 181818181818181010101012101818181010101010101010101010181810
+% 121810181318131010101012101210101210181818181818121012101018
+% 181818181818121010101010121618181818181818181A1818181A181818
+% 181818181818181818181818181010101012101818181010101010101010
+% 101010181810121810181318131010101012101210101210181818181818
+% 121012101018181818181818121010101010121618181818181818181A18
+% 18181A181818181818181818181818181818181010101012101818181010
+% 101010101010101010181810121810181318131010101012101210101210
+% 181818181818121012101018181818181818121010101010121618181818
+% 181818181A1818181A181818181818181818181818181818181010101012
+% 101818181010101010101010101010181810121810181318131010101012
+% 101210101210181818181818121012101018181818181818121010101010
+% 121618181818181818181A1818181A181818181818181818181818181818
+% 181010101012101818181010101010101010101010181810121810181318
+% 131010101012101210101210181818181818121012101018181818181818
+% 121010101010121618181818181818181A1818181A181818181818181818
+% 181818181818181010101012101818181010101010101010101010181810
+% 121810181318131010101012101210101210181818181818121012101018
+% 181818181818121010101010121618181818181818181A00FF1012181010
+% 1010101010101012181818181818181818181A181A181818181818181818
+% 181818181818101210101010181210181012101010101012101218181817
+% 181618161816121012101012101012101210121818181818101012121218
+% 1210121810101010101010101012181818181818181818181A181A181818
+% 181818181818181818181818101210101010181210181012101010101012
+% 101218181817181618161816121012101012101012101210121818181818
+% 101012121218121012181010101010101010101218181818181818181818
+% 1A181A181818181818181818181818181818101210101010181210181012
+% 101010101012101218181817181618161816121012101012101012101210
+% 121818181818101012121218121012181010101010101010101218181818
+% 1818181818181A181A181818181818181818181818181818101210101010
+% 181210181012101010101012101218181817181618161816121012101012
+% 101012101210121818181818101012121218121012181010101010101010
+% 1012181818181818181818181A181A181818181818181818181818181818
+% 101210101010181210181012101010101012101218181817181618161816
+% 121012101012101012101210121818181818101012121218121012181010
+% 1010101010101012181818181818181818181A181A181818181818181818
+% 181818181818101210101010181210181012101010101012101218181817
+% 181618161816121012101012101012101210121818181818101012121218
+% 1210121810101010101010101012181818181818181800FF121010101010
+% 0810101010101010101218161818181A181A181818181817181818181818
+% 181818181818101010101010101210121010181010101010121818181818
+% 181818181818181810181010121010121010101210181818181810101010
+% 1210101010100810101010101010101218161818181A181A181818181817
+% 181818181818181818181818101010101010101210121010181010101010
+% 121818181818181818181818181810181010121010121010101210181818
+% 1818101010101210101010100810101010101010101218161818181A181A
+% 181818181817181818181818181818181818101010101010101210121010
+% 181010101010121818181818181818181818181810181010121010121010
+% 101210181818181810101010121010101010081010101010101010121816
+% 1818181A181A181818181817181818181818181818181818101010101010
+% 101210121010181010101010121818181818181818181818181810181010
+% 121010121010101210181818181810101010121010101010081010101010
+% 1010101218161818181A181A181818181817181818181818181818181818
+% 101010101010101210121010181010101010121818181818181818181818
+% 181810181010121010121010101210181818181810101010121010101010
+% 0810101010101010101218161818181A181A181818181817181818181818
+% 181818181818101010101010101210121010181010101010121818181818
+% 181818181818181810181010121010121010101210181818181810101010
+% 12101010101008101010101010101012181618181800FF10101210101010
+% 0A0F0A10101010101010121618181820181A181818181818121618181818
+% 1818181818121010101010101010101818181817121018161818181A1818
+% 181818181818181812101210101210121012101012101816181612101010
+% 1012101010100A0F0A10101010101010121618181820181A181818181818
+% 121618181818181818181812101010101010101010181818181712101816
+% 1818181A1818181818181818181812101210101210121012101012101816
+% 1816121010101012101010100A0F0A10101010101010121618181820181A
+% 181818181818121618181818181818181812101010101010101010181818
+% 1817121018161818181A1818181818181818181812101210101210121012
+% 1010121018161816121010101012101010100A0F0A101010101010101216
+% 18181820181A181818181818121618181818181818181812101010101010
+% 1010101818181817121018161818181A1818181818181818181812101210
+% 1012101210121010121018161816121010101012101010100A0F0A101010
+% 10101010121618181820181A181818181818121618181818181818181812
+% 1010101010101010101818181817121018161818181A1818181818181818
+% 181812101210101210121012101012101816181612101010101210101010
+% 0A0F0A10101010101010121618181820181A181818181818121618181818
+% 1818181818121010101010101010101818181817121018161818181A1818
+% 181818181818181812101210101210121012101012101816181612101010
+% 1012101010100A0F0A101010101010101216181800FF1012101010101010
+% 0A1010080A10081010101818181A18181818181818101810181217181818
+% 181818181010101010101010101218181818181818181818181818181818
+% 181618181818181612161012101010101010101010121818181818181012
+% 1010101010100A1010080A10081010101818181A18181818181818101810
+% 181217181818181818181010101010101010101218181818181818181818
+% 181818181818181618181818181612161012101010101010101010121818
+% 1818181810121010101010100A1010080A10081010101818181A18181818
+% 181818101810181217181818181818181010101010101010101218181818
+% 181818181818181818181818181618181818181612161012101010101010
+% 1010101218181818181810121010101010100A1010080A10081010101818
+% 181A18181818181818101810181217181818181818181010101010101010
+% 101218181818181818181818181818181818181618181818181612161012
+% 1010101010101010101218181818181810121010101010100A1010080A10
+% 081010101818181A18181818181818101810181217181818181818181010
+% 101010101010101218181818181818181818181818181818181618181818
+% 181612161012101010101010101010121818181818181012101010101010
+% 0A1010080A10081010101818181A18181818181818101810181217181818
+% 181818181010101010101010101218181818181818181818181818181818
+% 181618181818181612161012101010101010101010121818181818181012
+% 1010101010100A1010080A100810101018181800FF181216121818181010
+% 10101008100810101012181818181A181818181818101210101818121818
+% 12161216121010100A10101012181818181818181818181A181818181818
+% 121018181818181210121018101812121612101010181818181818181216
+% 12181818101010101008100810101012181818181A181818181818101210
+% 10181812181812161216121010100A10101012181818181818181818181A
+% 181818181818121018181818181210121018101812121612101010181818
+% 18181818121612181818101010101008100810101012181818181A181818
+% 18181810121010181812181812161216121010100A101010121818181818
+% 18181818181A181818181818121018181818181210121018101812121612
+% 101010181818181818181216121818181010101010081008101010121818
+% 18181A18181818181810121010181812181812161216121010100A101010
+% 12181818181818181818181A181818181818121018181818181210121018
+% 101812121612101010181818181818181216121818181010101010081008
+% 10101012181818181A181818181818101210101818121818121612161210
+% 10100A10101012181818181818181818181A181818181818121018181818
+% 181210121018101812121612101010181818181818181216121818181010
+% 10101008100810101012181818181A181818181818101210101818121818
+% 12161216121010100A10101012181818181818181818181A181818181818
+% 121018181818181210121018101812121612101010181818181818181216
+% 12181818101010101008100810101012181800FF18161210181618101210
+% 101010080A10101010101818181818181817121012161218101210101010
+% 121018121012101010101012181818181A18181818181818181818181010
+% 121818171818101810181018181818181818181210121218161818161210
+% 181618101210101010080A10101010101818181818181817121012161218
+% 101210101010121018121012101010101012181818181A18181818181818
+% 181818181010121818171818101810181018181818181818181210121218
+% 161818161210181618101210101010080A10101010101818181818181817
+% 121012161218101210101010121018121012101010101012181818181A18
+% 181818181818181818181010121818171818101810181018181818181818
+% 181210121218161818161210181618101210101010080A10101010101818
+% 181818181817121012161218101210101010121018121012101010101012
+% 181818181A18181818181818181818181010121818171818101810181018
+% 181818181818181210121218161818161210181618101210101010080A10
+% 101010101818181818181817121012161218101210101010121018121012
+% 101010101012181818181A18181818181818181818181010121818171818
+% 101810181018181818181818181210121218161818161210181618101210
+% 101010080A10101010101818181818181817121012161218101210101010
+% 121018121012101010101012181818181A18181818181818181818181010
+% 121818171818101810181018181818181818181210121218161818161210
+% 181618101210101010080A10101010101800FF1012101012181818181012
+% 101010101010101218181818181818181812101218121012101210121010
+% 18121612101010101010101818181A181A18181A181A1818181810121010
+% 10181218101218181818181818181A181818181210101012181012101012
+% 181818181012101010101010101218181818181818181812101218121012
+% 10121012101018121612101010101010101818181A181A18181A181A1818
+% 18181012101010181218101218181818181818181A181818181210101012
+% 181012101012181818181012101010101010101218181818181818181812
+% 10121812101210121012101018121612101010101010101818181A181A18
+% 181A181A181818181012101010181218101218181818181818181A181818
+% 1812101010121810121010121818181810121010101010FF1012181818FF
+% 181818181812101218121012101210121010181216121010101010101018
+% 18181A181A18181A181A1818181810121010101812181012181818181818
+% 18181A181818181210101012181012101012181818181012101010101010
+% 101218181818181818181812101218121012101210121010181216121010
+% 10101010101818181A181A18181A181A1818181810121010101812181012
+% 18181818181818181A181818181210101012181012101012181818181012
+% 101010101010101218181818181818181812101218121012101210121010
+% 18121612101010101010101818181A181A18181A181A1818181810121010
+% 10181218101218181818181818181A181818181210101012181012101012
+% 1818181810121010101010101012181800FF181012101210101012101210
+% 100A08101010101012101218101810121010101010181818181010101010
+% 101210121810181012121818181A18181818181818181818101010101010
+% 101010121010101012101818181818181818181818181018181012101210
+% 101012101210100A08101010101012101218101810121010101010181818
+% 181010101010101210121810181012121818181A18181818181818181818
+% 101010101010101010121010101012101818181818181818181818181018
+% 181012101210101012101210100A08101010101012101218101810121010
+% 101010181818181010101010101210121810181012121818181A18181818
+% 181818181818101010101010101010121010101012101818181818181818
+% 181818181018181012101210101012101210100A081010FF101012FF1218
+% 101810121010101010181818181010101010101210121810181012121818
+% 181A18181818181818181818101010101010101010121010101012101818
+% 181818181818181818181018181012101210101012101210100A08101010
+% 101012101218101810121010101010181818181010101010101210121810
+% 181012121818181A18181818181818181818101010101010101010121010
+% 101012101818181818181818181818181018181012101210101012101210
+% 100A08101010101012101218101810121010101010181818181010101010
+% 101210121810181012121818181A18181818181818181818101010101010
+% 101010121010101012101818181818181818181818181018181012101210
+% 101012101210100A0810101010101200FF18181810101010121012161010
+% 101010101010101010121012121010101010101018181818181810181010
+% 101012101816121816181818181818181818181818181810101010101010
+% 101012101210121018181718181618181818181818181818181810101010
+% 121012161010101010101010101010121012121010101010101018181818
+% 181810181010101012101816121816181818181818181818181818181810
+% 101010101010101012101210121018181718181618181818181818181818
+% 181810101010121012161010101010101010101010121012121010101010
+% 101018181818181810181010101012101816121816181818181818181818
+% 181818181810101010101010101012101210121018181718181618181818
+% 18181818181818181010101012101216101010101010FF101010FF121012
+% 121010101010101018181818181810181010101012101816121816181818
+% 181818181818181818181810101010101010101012101210121018181718
+% 181618181818181818181818181810101010121012161010101010101010
+% 101010121012121010101010101018181818181810181010101012101816
+% 121816181818181818181818181818181810101010101010101012101210
+% 121018181718181618181818181818181818181810101010121012161010
+% 101010101010101010121012121010101010101018181818181810181010
+% 101012101816121816181818181818181818181818181810101010101010
+% 101012101210121018181718181618181818181818181818181810101010
+% 121012161010101010101010101000FF1818171818181810101210121010
+% 101010101010101010101010101010081010101218181818181818101010
+% 101010181212161818181A16181818181618181818101210101010101210
+% 181818101810181018121818181818181818181818181818171818181810
+% 101210121010101010101010101010101010101010081010101218181818
+% 181818101010101010181212161818181A16181818181618181818101210
+% 101010101210181818101810181018121818181818181818181818181818
+% 171818181810101210121010101010101010101010101010101010081010
+% 101218181818181818101010101010181212161818181A16181818181618
+% 181818101210101010101210181818101810181018121818181818181818
+% 18181818181817181818181010121012101010101010FF10FF1010101010
+% 101010081010101218181818181818101010101010181212161818181A16
+% 181818181618181818101210101010101210181818101810181018121818
+% 181818181818181818181818171818181810101210121010101010101010
+% 101010101010101010081010101218181818181818101010101010181212
+% 161818181A16181818181618181818101210101010101210181818101810
+% 181018121818181818181818181818181818171818181810101210121010
+% 101010101010101010101010101010081010101218181818181818101010
+% 101010181212161818181A16181818181618181818101210101010101210
+% 181818101810181018121818181818181818181818181818171818181810
+% 1012101210101010101010101000FF121818181818181012101810181816
+% 12181818121010101210101010100A081010181818181818181818121010
+% 101012101012121818181818161818181818161310101010101012161818
+% 181818181210121012101810181818181818181818121818181818181012
+% 10181018181612181818121010101210101010100A081010181818181818
+% 181818121010101012101012121818181818161818181818161310101010
+% 101012161818181818181210121012101810181818181818181818121818
+% 18181818101210181018181612181818121010101210101010100A081010
+% 181818181818181818121010101012101012121818181818161818181818
+% 161310101010101012161818181818181210121012101810181818181818
+% 18181812181818181818101210181018181612181818FF10101012101010
+% 10100A081010181818181818181818121010101012101012121818181818
+% 161818181818161310101010101012161818181818181210121012101810
+% 181818181818181818121818181818181012101810181816121818181210
+% 10101210101010100A081010181818181818181818121010101012101012
+% 121818181818161818181818161310101010101012161818181818181210
+% 121012101810181818181818181818121818181818181012101810181816
+% 12181818121010101210101010100A081010181818181818181818121010
+% 101012101012121818181818161818181818161310101010101012161818
+% 181818181210121012101810181818181818181818121818181818181012
+% 10181018181612181818121000FF10121012101212101012121618181818
+% 181818181818101818131610101010101210181818181818181010101010
+% 101010101010121018181818181818181818101210101010101818181818
+% 1A1818181010101010121012101812161818181010121012101212101012
+% 121618181818181818181818101818131610101010101210181818181818
+% 181010101010101010101010121018181818181818181818101210101010
+% 1018181818181A1818181010101010121012101812161818181010121012
+% 101212101012121618181818181818181818101818131610101010101210
+% 181818181818181010101010101010101010121018181818181818181818
+% 1012101010101018181818181A1818181010101010121012101812161818
+% 181010121012101212101012121618181818181818FF1818101818131610
+% 101010101210181818181818181010101010101010101010121018181818
+% 1818181818181012101010101018181818181A1818181010101010121012
+% 101812161818181010121012101212101012121618181818181818181818
+% 101818131610101010101210181818181818181010101010101010101010
+% 1210181818181818181818181012101010101018181818181A1818181010
+% 101010121012101812161818181010121012101212101012121618181818
+% 181818181818101818131610101010101210181818181818181010101010
+% 101010101010121018181818181818181818101210101010101818181818
+% 1A1818181010101010121012101812161818181010121012101212101012
+% 121618181818181818181800FF1210101210101010181010121818181818
+% 181A181A1818181718181218181810101810181218181012101210101818
+% 1010101010101212181818181818181818181012101010101818181A1818
+% 181818121010101010121010101012101818101210101210101010181010
+% 121818181818181A181A1818181718181218181810101810181218181012
+% 101210101818101010101010121218181818181818181818101210101010
+% 1818181A1818181818121010101010121010101012101818101210101210
+% 101010181010121818181818181A181A1818181718181218181810101810
+% 181218181012101210101818101010101010121218181818181818181818
+% 1012101010101818181A1818181818121010101010121010101012101818
+% 10121010121010101018101012181818181818FF18FF1818181718181218
+% 181810101810181218181012101210101818101010101010121218181818
+% 1818181818181012101010101818181A1818181818121010101010121010
+% 101012101818101210101210101010181010121818181818181A181A1818
+% 181718181218181810101810181218181012101210101818101010101010
+% 1212181818181818181818181012101010101818181A1818181818121010
+% 101010121010101012101818101210101210101010181010121818181818
+% 181A181A1818181718181218181810101810181218181012101210101818
+% 1010101010101212181818181818181818181012101010101818181A1818
+% 181818121010101010121010101012101818101210101210101010181010
+% 121818181818181A181A00FF181812101012101212101210101818161818
+% 1A1818181818181818171818181612121012101010181810101212181018
+% 181010101210101818181A18181818181816121012121818181818181A18
+% 181810100B0E10101010100E101010121818181812101012101212101210
+% 1018181618181A1818181818181818171818181612121012101010181810
+% 101212181018181010101210101818181A18181818181816121012121818
+% 181818181A18181810100B0E10101010100E101010121818181812101012
+% 1012121012101018181618181A1818181818181818171818181612121012
+% 101010181810101212181018181010101210101818181A18181818181816
+% 121012121818181818181A18181810100B0E10101010100E101010121818
+% 1818121010121012121012101018181618FF1A1818FF1818181818171818
+% 181612121012101010181810101212181018181010101210101818181A18
+% 181818181816121012121818181818181A18181810100B0E10101010100E
+% 1010101218181818121010121012121012101018181618181A1818181818
+% 181818171818181612121012101010181810101212181018181010101210
+% 101818181A18181818181816121012121818181818181A18181810100B0E
+% 10101010100E101010121818181812101012101212101210101818161818
+% 1A1818181818181818171818181612121012101010181810101212181018
+% 181010101210101818181A18181818181816121012121818181818181A18
+% 181810100B0E10101010100E101010121818181812101012101212101210
+% 1018181618181A181800FF18181818121010181010101012181810101818
+% 181818121012101818181818181810101010101018181012101010181818
+% 18181612161218181818181818181818181018131618181818181A181A18
+% 1812101010101010100A100A101012161818181818121010181010101012
+% 181810101818181818121012101818181818181810101010101018181012
+% 101010181818181816121612181818181818181818181810181316181818
+% 18181A181A181812101010101010100A100A101012161818181818121010
+% 181010101012181810101818181818121012101818181818181810101010
+% 101018181012101010181818181816121612181818181818181818181810
+% 18131618181818181A181A181812101010101010100A100A101012161818
+% 18181812101018101010101218181010FF181818FF121012101818181818
+% 181810101010101018181012101010181818181816121612181818181818
+% 18181818181018131618181818181A181A181812101010101010100A100A
+% 101012161818181818121010181010101012181810101818181818121012
+% 101818181818181810101010101018181012101010181818181816121612
+% 18181818181818181818181018131618181818181A181A18181210101010
+% 1010100A100A101012161818181818121010181010101012181810101818
+% 181818121012101818181818181810101010101018181012101010181818
+% 18181612161218181818181818181818181018131618181818181A181A18
+% 1812101010101010100A100A101012161818181818121010181010101012
+% 181810101818181800FF1618181818181018181810121010101818161818
+% 18161810101018131618181A181818121010101810181210181813181818
+% 18181818181818181818181818181018101216181818181A181A18181818
+% 1818181810100A1010101010101012181618181818181018181810121010
+% 10181816181818161810101018131618181A181818121010101810181210
+% 18181318181818181818181818181818181818181018101216181818181A
+% 181A181818181818181810100A1010101010101012181618181818181018
+% 18181012101010181816181818161810101018131618181A181818121010
+% 101810181210181813181818181818181818181818181818181810181012
+% 16181818181A181A181818181818181810100A1010101010101012181618
+% 1818181810181818101210101018FF1618181816FF10101018131618181A
+% 181818121010101810181210181813181818181818181818181818181818
+% 18181018101216181818181A181A181818181818181810100A1010101010
+% 101012181618181818181018181810121010101818161818181618101010
+% 18131618181A181818121010101810181210181813181818181818181818
+% 18181818181818181018101216181818181A181A18181818181818181010
+% 0A1010101010101012181618181818181018181810121010101818161818
+% 18161810101018131618181A181818121010101810181210181813181818
+% 18181818181818181818181818181018101216181818181A181A18181818
+% 1818181810100A1010101010101012181618181818181018181810121010
+% 1018181618181800FF121818181818181818171210101010121012181018
+% 121818181812161812161818181818101810121012161818101818181818
+% 1818181818181818181A1818181818181818181818181818181818181216
+% 181812101010121010101010101012121818181818181818171210101010
+% 121012181018121818181812161812161818181818101810121012161818
+% 1018181818181818181818181818181A1818181818181818181818181818
+% 181818181216181812101010121010101010101012121818181818181818
+% 171210101010121012181018121818181812161812161818181818101810
+% 1210121618181018181818181818181818181818181A1818181818181818
+% 181818181818181818181216181812101010121010101010101012121818
+% 181818181818171210101010121012181018121818181812161812161818
+% 1818181018101210121618181018181818181818181818181818181A1818
+% 181818181818181818181818181818181216181812101010121010101010
+% 101012121818181818181818171210101010121012181018121818181812
+% 161812161818181818101810121012161818101818181818181818181818
+% 1818181A1818181818181818181818181818181818181216181812101010
+% 121010101010101012121818181818181818171210101010121012181018
+% 121818181812161812161818181818101810121012161818101818181818
+% 1818181818181818181A1818181818181818181818181818181818181216
+% 181812101010121010101010101012121818181818181818171210101010
+% 12101218101800FF10121612181610181818121612101012101210121618
+% 181818181818121018101216181618181810181012101210181618181818
+% 18181A181A18181818181818181818181818181018181818181812101012
+% 161818121018101010101010101010121612181610181818121612101012
+% 101210121618181818181818121018101216181618181810181012101210
+% 18161818181818181A181A18181818181818181818181818181018181818
+% 181812101012161818121018101010101010101010121612181610181818
+% 121612101012101210121618181818181818121018101216181618181810
+% 18101210121018161818181818181A181A18181818181818181818181818
+% 181018181818181812101012161818121018101010101010101010121612
+% 181610181818121612101012101210121618181818181818121018101216
+% 18161818181018101210121018161818181818181A181A18181818181818
+% 181818181818181018181818181812101012161818121018101010101010
+% 101010121612181610181818121612101012101210121618181818181818
+% 12101810121618161818181018101210121018161818181818181A181A18
+% 181818181818181818181818181018181818181812101012161818121018
+% 101010101010101010121612181610181818121612101012101210121618
+% 181818181818121018101216181618181810181012101210181618181818
+% 18181A181A18181818181818181818181818181018181818181812101012
+% 161818121018101010101010101010121612181610181818121612101012
+% 101210121600FF1010121012181218161810121012101010101010121216
+% 181818161810181010121012181818181612101810121012121018181818
+% 18181818181A18181A1E181A181818181818181818181818181810101210
+% 181216181810121010121010101010121012181218161810121012101010
+% 101010121216181818161810181010121012181818181612101810121012
+% 12101818181818181818181A18181A1E181A181818181818181818181818
+% 181810101210181216181810121010121010101010121012181218161810
+% 121012101010101010121216181818161810181010121012181818181612
+% 10181012101212101818181818181818181A18181A1E181A181818181818
+% 181818181818181810101210181216181810121010121010101010121012
+% 181218161810121012101010101010121216181818161810181010121012
+% 18181818161210181012101212101818181818181818181A18181A1E181A
+% 181818181818181818181818181810101210181216181810121010121010
+% 101010121012181218161810121012101010101010121216181818161810
+% 18101012101218181818161210181012101212101818181818181818181A
+% 18181A1E181A181818181818181818181818181810101210181216181810
+% 121010121010101010121012181218161810121012101010101010121216
+% 181818161810181010121012181818181612101810121012121018181818
+% 18181818181A18181A1E181A181818181818181818181818181810101210
+% 181216181810121010121010101010121012181218161810121012101010
+% 1010101200FF101010101012161218101812161210101012101210101210
+% 181218181818181810181810121210121810121010101010121012181818
+% 1818181818181A181A18181818181818181818181A181818181010121012
+% 101210121810121010101210101010101012161218101812161210101012
+% 101210101210181218181818181810181810121210121810121010101010
+% 1210121818181818181818181A181A18181818181818181818181A181818
+% 181010121012101210121810121010101210101010101012161218101812
+% 161210101012101210101210181218181818181810181810121210121810
+% 1210101010101210121818181818181818181A181A181818181818181818
+% 18181A181818181010121012101210121810121010101210101010101012
+% 161218101812161210101012101210101210181218181818181810181810
+% 1212101218101210101010101210121818181818181818181A181A181818
+% 18181818181818181A181818181010121012101210121810121010101210
+% 101010101012161218101812161210101012101210101210181218181818
+% 181810181810121210121810121010101010121012181818181818181818
+% 1A181A18181818181818181818181A181818181010121012101210121810
+% 121010101210101010101012161218101812161210101012101210101210
+% 181218181818181810181810121210121810121010101010121012181818
+% 1818181818181A181A18181818181818181818181A181818181010121012
+% 101210121810121010101210101010101012161218101812161210101012
+% 10121000FF10101012181818161818181018161810101010101012101210
+% 101218161818181818101210101018161210121010101010101210121612
+% 181818181A18201E1A181A18181818181818181818181818181812101010
+% 101018101818181812101010101012181818161818181018161810101010
+% 101012101210101218161818181818101210101018161210121010101010
+% 101210121612181818181A18201E1A181A18181818181818181818181818
+% 181812101010101018101818181812101010101012181818161818181018
+% 161810101010101012101210101218161818181818101210101018161210
+% 121010101010101210121612181818181A18201E1A181A18181818181818
+% 181818181818181812101010101018101818181812101010101012181818
+% 161818181018161810101010101012101210101218161818181818101210
+% 101018161210121010101010101210121612181818181A18201E1A181A18
+% 181818181818181818181818181812101010101018101818181812101010
+% 101012181818161818181018161810101010101012101210101218161818
+% 181818101210101018161210121010101010101210121612181818181A18
+% 201E1A181A18181818181818181818181818181812101010101018101818
+% 181812101010101012181818161818181018161810101010101012101210
+% 101218161818181818101210101018161210121010101010101210121612
+% 181818181A18201E1A181A18181818181818181818181818181812101010
+% 101018101818181812101010101012181818161818181018161810101010
+% 101000FF1810121018181818121810181818181810121012101010101012
+% 101012121018181018101010101812101010101010101010101010101018
+% 18181818181A181A18181818181613181818181818181818121010101010
+% 101012101818181818181810121018181818121810181818181810121012
+% 101010101012101012121018181018101010101812101010101010101010
+% 10101010101818181818181A181A18181818181613181818181818181818
+% 121010101010101012101818181818181810121018181818121810181818
+% 181810121012101010101012101012121018181018101010101812101010
+% 10101010101010101010101818181818181A181A18181818181613181818
+% 181818181818121010101010101012101818181818181810121018181818
+% 121810181818181810121012101010101012101012121018181018101010
+% 10181210101010101010101010101010101818181818181A181A18181818
+% 181613181818181818181818121010101010101012101818181818181810
+% 121018181818121810181818181810121012101010101012101012121018
+% 18101810101010181210101010101010101010101010101818181818181A
+% 181A18181818181613181818181818181818121010101010101012101818
+% 181818181810121018181818121810181818181810121012101010101012
+% 101012121018181018101010101812101010101010101010101010101018
+% 18181818181A181A18181818181613181818181818181818121010101010
+% 101012101818181818181810121018181818121810181818181810121012
+% 1000FF1A1818181618181816181818181618181818101010101012121012
+% 101010101218101210121010181016121010101010101010101010101018
+% 18181818181820181A181818121612101818181012161216101010101012
+% 1010181818181818181A1818181618181816181818181618181818101010
+% 101012121012101010101218101210121010181016121010101010101010
+% 10101010101818181818181820181A181818121612101818181012161216
+% 1010101010121010181818181818181A1818181618181816181818181618
+% 181818101010101012121012101010101218101210121010181016121010
+% 10101010101010101010101818181818181820181A181818121612101818
+% 1810121612161010101010121010181818181818181A1818181618181816
+% 181818181618181818101010101012121012101010101218101210121010
+% 18101612101010101010101010101010101818181818181820181A181818
+% 1216121018181810121612161010101010121010181818181818181A1818
+% 181618181816181818181618181818101010101012121012101010101218
+% 101210121010181016121010101010101010101010101018181818181818
+% 20181A181818121612101818181012161216101010101012101018181818
+% 1818181A1818181618181816181818181618181818101010101012121012
+% 101010101218101210121010181016121010101010101010101010101018
+% 18181818181820181A181818121612101818181012161216101010101012
+% 1010181818181818181A1818181618181816181818181618181818101010
+% 00FF181818181A1818181810181018181816181216121018101010121010
+% 101010101012101216181812181818181810101010101010101010101210
+% 18181A20211818181818101612101210101210181018181310100A080810
+% 10101216181A1818181818181A1818181810181018181816181216121018
+% 101010121010101010101012101216181812181818181810101010101010
+% 10101010121018181A202118181818181016121012101012101810181813
+% 10100A08081010101216181A1818181818181A1818181810181018181816
+% 181216121018101010121010101010101012101216181812181818181810
+% 10101010101010101010121018181A202118181818181016121012101012
+% 10181018181310100A08081010101216181A1818181818181A1818181810
+% 181018181816181216121018101010121010101010101012101216181812
+% 18181818181010101010101010101010121018181A202118181818181016
+% 12101210101210181018181310100A08081010101216181A181818181818
+% 1A1818181810181018181816181216121018101010121010101010101012
+% 10121618181218181818181010101010101010101010121018181A202118
+% 18181818101612101210101210181018181310100A08081010101216181A
+% 1818181818181A1818181810181018181816181216121018101010121010
+% 101010101012101216181812181818181810101010101010101010101210
+% 18181A20211818181818101612101210101210181018181310100A080810
+% 10101216181A1818181818181A1818181810181018181816181216121000
+% FF1818181818181A18101812121212181818181012101210101216181818
+% 181210121018101812181612181618181818101010101010101010101218
+% 1818201A1818181212181218101810121018101218161818101010081010
+% 10181818181A181818181818181A18101812121212181818181012101210
+% 101216181818181210121018101812181612181618181818101010101010
+% 1010101012181818201A1818181212181218101810121018101218161818
+% 10101008101010181818181A181818181818181A18101812121212181818
+% 181012101210101216181818181210121018101812181612181618181818
+% 1010101010101010101012181818201A1818181212181218101810121018
+% 10121816181810101008101010181818181A181818181818181A18101812
+% 121212181818181012101210101216181818181210121018101812181612
+% 1816181818181010101010101010101012181818201A1818181212181218
+% 10181012101810121816181810101008101010181818181A181818181818
+% 181A18101812121212181818181012101210101216181818181210121018
+% 1018121816121816181818181010101010101010101012181818201A1818
+% 18121218121810181012101810121816181810101008101010181818181A
+% 181818181818181A18101812121212181818181012101210101216181818
+% 181210121018101812181612181618181818101010101010101010101218
+% 1818201A1818181212181218101810121018101218161818101010081010
+% 10181818181A181818181818181A181018121212121818181810121000FF
+% 1A181A181A18181818121010101010181210101012101216121818181818
+% 1818181818121618121012101212161818101010121010100E1010101218
+% 181818181810101012101818181818101210121018181818121010101012
+% 1818181A18181A181A181A18181818121010101010181210101012101216
+% 121818181818181818181812161812101210121216181810101012101010
+% 0E1010101218181818181810101012101818181818101210121018181818
+% 1210101010121818181A18181A181A181A18181818121010101010181210
+% 101012101216121818181818181818181812161812101210121216181810
+% 1010121010100E1010101218181818181810101012101818181818101210
+% 1210181818181210101010121818181A18181A181A181A18181818121010
+% 101010181210101012101216121818181818181818181812161812101210
+% 1212161818101010121010100E1010101218181818181810101012101818
+% 1818181012101210181818181210101010121818181A18181A181A181A18
+% 181818121010101010181210101012101216121818181818181818181812
+% 1618121012101212161818101010121010100E1010101218181818181810
+% 1010121018181818181012101210181818181210101010121818181A1818
+% 1A181A181A18181818121010101010181210101012101216121818181818
+% 1818181818121618121012101212161818101010121010100E1010101218
+% 181818181810101012101818181818101210121018181818121010101012
+% 1818181A18181A181A181A1818181812101010101018121010101200FF18
+% 181818181818181810101010101018181012101010121012161818181818
+% 181818181818131612101010101212181012101010101010101010101216
+% 181812101210121012161818181818121010101012181818181210121018
+% 18181A181818181818181818181810101010101018181012101010121012
+% 161818181818181818181818131612101010101212181012101010101010
+% 101010101216181812101210121012161818181818121010101012181818
+% 18121012101818181A181818181818181818181810101010101018181012
+% 101010121012161818181818181818181818131612101010101212181012
+% 101010101010101010101216181812101210121012161818181818121010
+% 10101218181818121012101818181A181818181818181818181810101010
+% 101018181012101010121012161818181818181818181818131612101010
+% 101212181012101010101010101010101216181812101210121012161818
+% 18181812101010101218181818121012101818181A181818181818181818
+% 181810101010101018181012101010121012161818181818181818181818
+% 131612101010101212181012101010101010101010101216181812101210
+% 12101216181818181812101010101218181818121012101818181A181818
+% 181818181818181810101010101018181012101010121012161818181818
+% 181818181818131612101010101212181012101010101010101010101216
+% 181812101210121012161818181818121010101012181818181210121018
+% 18181A181818181818181818181810101010101018181012101000FF1818
+% 181818181818121010101010101818101010101218101012181818181818
+% 181818181816181818121010101012161218101012101010101010121012
+% 101010101010101212181818181818101210101010101216181216181818
+% 18181A181818181818181818121010101010101818101010101218101012
+% 181818181818181818181816181818121010101012161218101012101010
+% 101010121012101010101010101212181818181818101210101010101216
+% 18121618181818181A181818181818181818121010101010101818101010
+% 101218101012181818181818181818181816181818121010101012161218
+% 101012101010101010121012101010101010101212181818181818101210
+% 10101010121618121618181818181A181818181818181818121010101010
+% 101818101010101218101012181818181818181818181816181818121010
+% 101012161218101012101010101010121012101010101010101212181818
+% 18181810121010101010121618121618181818181A181818181818181818
+% 121010101010101818101010101218101012181818181818181818181816
+% 181818121010101012161218101012101010101010121012101010101010
+% 10121218181818181810121010101010121618121618181818181A181818
+% 181818181818121010101010101818101010101218101012181818181818
+% 181818181816181818121010101012161218101012101010101010121012
+% 101010101010101212181818181818101210101010101216181216181818
+% 18181A1818181818181818181210101010101018181010101000FF181818
+% 181818181810121010101010181618181216121010101210181810121818
+% 181818181818181818161210101018121612101010101010121010121010
+% 12101010101010101818181A181812101010100808101012101812171818
+% 181818181818181818181810121010101010181618181216121010101210
+% 181810121818181818181818181818161210101018121612101010101010
+% 12101012101012101010101010101818181A181812101010100808101012
+% 101812171818181818181818181818181810121010101010181618181216
+% 121010101210181810121818181818181818181818161210101018121612
+% 10101010101012101012101012101010101010101818181A181812101010
+% 100808101012101812171818181818181818181818181810121010101010
+% 181618181216121010101210181810121818181818181818181818161210
+% 10101812161210101010101012101012101012101010101010101818181A
+% 181812101010100808101012101812171818181818181818181818181810
+% 121010101010181618181216121010101210181810121818181818181818
+% 181818161210101018121612101010101010121010121010121010101010
+% 10101818181A181812101010100808101012101812171818181818181818
+% 181818181810121010101010181618181216121010101210181810121818
+% 181818181818181818161210101018121612101010101010121010121010
+% 12101010101010101818181A181812101010100808101012101812171818
+% 18181818181818181818181012101010101018161818121600FF12161818
+% 101810181210121010101212181818181812101010121613101210181618
+% 181818181818181818181010101216121012101216181810181012101216
+% 12101010FFFFFFFF18181818FFFFFFFF1010100AFFFFFFFF121612181818
+% 181812161818101810181210121010101212181818181812101010121613
+% FFFFFFFFFFFF18181818FFFF1818181810FFFFFFFF121012101216181810
+% 181012101216121010101010101818181818181818101010100A10FFFFFF
+% FFFFFF181818FFFFFFFF18181018FFFFFFFF121010101212181818181812
+% 1010101216131012101816181818181818181818181810101012161210FF
+% FFFFFF1818101810FFFFFFFF121010101010101818181818181818101010
+% 100A10101010121612181818181812161818101810181210121010FFFFFF
+% FF1818181812101010121613101210181618181818181818181818181010
+% 10121612101210121618181018101210121612FFFFFFFF1010181818FFFF
+% FFFF18101010100A10101010121612181818181812161818101810181210
+% 12101010121218181818181210FFFFFFFFFFFF121018FFFFFFFF18181818
+% FFFFFFFF1010101216121012101216181810181012101216121010101010
+% 10181818181818181810FFFFFFFFFFFF10101216FFFF1818181812FFFFFF
+% FF1810181210121010101212181818181812101010121613101210181618
+% 181818181818181818FFFFFFFF12161210FFFFFFFF18181018FFFFFFFF16
+% 121010101010101818181818181818101010100A10101010121612181818
+% 181812161818101810181210121010101212181818181800FF1012101012
+% 161210101010121012161818181A18181210181218181818101218121216
+% 181818181218181818181012101810181018101218181818181216121812
+% 1010FF101010FFFF181AFFFF1818FFFF1010FFFF100AFFFF101818181818
+% 121012101012161210101010121012161818181A18181210181218181818
+% FFFF18121216181818FFFF18181818FFFF1210FFFF181018101218181818
+% 1812161218121010101010101818181A20181818101210101010100AFFFF
+% 10181818FFFF1210FFFF1012FFFF1010FFFF121012161818181A18181210
+% 181218181818101218121216181818181218181818181012101810181018
+% 10FFFF181818FFFF1612FFFF1010101010101818181A2018181810121010
+% 1010100A101210181818181812101210101216121010101012FFFF1618FF
+% FF1A18181210181218181818101218121216181818181218181818181012
+% 101810181018101218181818181216121812101010FFFF101818FFFF2018
+% FFFF101210101010100A1012101818181818121012101012161210101010
+% 121012161818181A181812101812FFFF18181012FFFF1216FFFF1818FFFF
+% 1818FFFF1012101810181018101218181818181216121812101010101010
+% 1818181A201818181012FFFF1010100A101210FFFF18181812FFFF1010FF
+% FF1210101010121012161818181A18181210181218181818101218121216
+% 18181818121818FF181810FFFF1810FFFF1810FFFF1818FFFF1216FFFF12
+% 1010101010101818181A20181818101210101010100A1012101818181818
+% 121012101012161210101010121012161818181A181800FF181818181810
+% 1216121612161218181818181A1818181010101010121018101010101218
+% 18181810121010101018181012101812181816181A181818181816181818
+% 1818101010FFFF1216FFFF1818FFFF1210FFFF1010FFFF10101012101818
+% 1818181818101216121612161218181818181A1818181010101010121018
+% FFFF10101218181818FFFF101010FFFF1810FFFF1812181816181A181818
+% 181816181818181810101010101216181818181810121010101010FFFF10
+% 101012FFFF1818FFFF1818FFFF1612FFFF161218181818181A1818181010
+% 101010121018101010101218181818101210101010181810121018121818
+% 16FFFF1818FFFF1816FFFF18181810101010101216181818181810121010
+% 101010101010101012101818181818181810121612161216FFFF1818FFFF
+% 1A1818181010101010121018101010101218181818101210101010181810
+% 12101812181816181A181818181816181818181810FFFF1010FFFF1818FF
+% FF1810121010101010101010101012101818181818181810121612161216
+% 1218181818181A181818101010FFFF12101810FFFF1012FFFF1818FFFF10
+% 10FFFF18181012101812181816181A181818181816181818181810101010
+% 10121618181818181012FFFF10101010101010FFFF101818FFFF1818FFFF
+% 1216121612161218181818181A1818181010101010121018101010101218
+% 18181810121010101018FFFF1210FFFF1818FFFF1A18FFFF1818FFFF1818
+% 181810101010101216181818181810121010101010101010101012101818
+% 1818181818101216121612161218181818181A181800FF1A181818101210
+% 181012101216181818181818181818181010101010101010101010101216
+% 1818181010121012181810101216121618181818181AFFFFFFFFFFFF1818
+% 16181818FFFF1012FFFF1810FFFF1010FFFF1210FFFF101210121618181A
+% 18181810121018101210121618181818181818FFFFFFFFFFFF1010101010
+% FFFF101012161818FFFF101210FFFF1810FFFF16121618181818181A1818
+% 18181818181816181818101010121810FFFFFFFFFFFF10101210FFFF1012
+% 1012FFFF181AFFFF1810FFFF1810FFFF1216181818181818181818181010
+% 101010101010101010101216181818101012FFFFFFFFFFFF121612FFFFFF
+% FFFF181AFFFF1818FFFF1818161818181010101218101810181210101010
+% 12101010101210121618181A1818181012101810121012FFFF1818FFFF18
+% 181818181010101010101010101010101216181818101012101218181010
+% 1216121618181818181A18181818181818FFFFFFFFFF1010FFFF1810FFFF
+% 18121010101012101010101210121618181A181818101210181012101216
+% 181818181818181818181010FFFF10101010FFFF1010FFFF1818FFFF1012
+% FFFF181810101216121618181818181A1818181818181818161818181010
+% 10121810181018121010FFFF121010101012FFFF161818FFFF1818FFFF10
+% 181012101216181818181818181818181010101010101010101010101216
+% 181818101012101218FFFF1012FFFF1618FFFF1818FFFF1818FFFF181818
+% 16181818101010121810181018121010101012101010101210121618181A
+% 181818101210181012101216181818181818181800FF1818181818111813
+% 161216121818181818181A18181818101010101010100A10101010101218
+% 181818181012161218101210181318181818181818181818181012161318
+% 18FFFFFF101012FFFFFFFFFF101012FFFF1618FFFF12161818FFFF181818
+% 181818111813161216121818181818181A18181818101010101010100A10
+% FFFF1010121818FFFF181012FFFF1810FFFF18131818FFFF181818181818
+% 18101216131818181812101012101012101810101210121618FFFF121618
+% 1818FFFFFFFF181818FFFF1316FFFF12181818FFFF181A18181818101010
+% 101010100A1010101010121818181818101216121810121018FFFF1818FF
+% FF1818FFFF1818FFFF16131818FFFF121010121010121018101012101216
+% 18161812161818181818181818181811181316121612FFFF1818FFFF1A18
+% 1818FFFF1010101010100A10101010101218181818181012161218101210
+% 181318181818181818181818181012FFFF1818FFFF1210FFFF1010FFFF18
+% 101012FFFF16181618121618181818181818181818111813161216121818
+% 181818181A181818181010FFFF1010100A10FFFFFFFF121818FFFF1810FF
+% FF12181012FFFF1318181818181818181818181012161318181818121010
+% 12101012101810101210FFFF1816181216FFFF181818FFFF1818FFFF1813
+% 1612FFFF1818181818181A18181818101010101010100A10101010101218
+% 181818181012FFFFFF101210FFFFFFFFFF181818FFFF1818FFFF12161318
+% FFFF18121010121010121018101012101216181618121618181818181818
+% 181818111813161216121818181818181A181800FF1A1818181818161818
+% 1210121618181A181A181818181810101010101010101010081010101818
+% 181818181018161216121810181018101818181818181810101012181818
+% 1818FFFF1010FFFF121012101210FFFF1818FFFF181818FF1818FF1A1818
+% 1818181618181210121618181A181A18181818181010101010FFFF1010FF
+% FF101010181818FFFF1810FFFF1216FFFF101810FF1018FF181818181810
+% 101012181818181818181010101012101210121012161818FFFF18181818
+% FFFF181AFFFF1818FFFF1818FFFF121618FF1A18FF181818181810101010
+% 101010101010081010101818181818181018161216121810FFFF1810FFFF
+% 1818FFFF1810FFFF121818FF1818FF181010101012101210121012161818
+% 1818181818181818181A1818181818161818121012FFFF181AFFFF181818
+% FF1810FF1010101010101010081010101818181818181018161216121810
+% 1810181018181818181818101010FFFF1818FFFF1818FFFF1010FFFF1210
+% 12FF1216FF181818181818181818181A1818181818161818121012161818
+% 1A181A18181818181010FFFF10101010FFFF0810FFFF1818FFFF1818FFFF
+% 161216FF1810FF1018101818181818181810101012181818181818181010
+% 1010121012FFFF1012FFFF181818181818FFFF1818FFFF1818FFFF161818
+% FF1012FF18181A181A181818181810101010101010101010081010101818
+% 18181818101816FFFF1218FFFF101810181818FFFF1818FFFF101218FF18
+% 18FF181810101010121012101210121618181818181818181818181A1818
+% 1818181618181210121618181A181A18181800FF18181818181818181712
+% 16181818181818181A181818181118121012101010101010080E10101818
+% 181818181210121018101810121812101818181817181218181816FF1818
+% 18FFFF181810FFFF1810181012FFFF1818FFFF181818FF1818FF18181818
+% 18181818171216181818181818181A181818181118121012FFFF1010FFFF
+% 080EFF101818FFFF1818FFFF1210FFFF181012FF1210FF18181817181218
+% 181816181818181818181810121018101810121018FFFFFFFF18181818FF
+% FF1818FFFF1818FFFF1817FFFF181818FF1818FF1A181818181118121012
+% 101010101010080E101018181818181812101210181018FFFF1812FFFF18
+% 18FFFF1812FFFF181618FF1818FF18181810121018101810121018181818
+% 1818181818181818181818181818181817121618FFFF1818FFFF1A1818FF
+% 1811FF121012101010101010080E10101818181818181210121018101810
+% 12181210181818181718121818FFFF1818FFFF1818FFFF1012FFFF101810
+% FF1018FF1818181818181818181818181818181818181712161818181818
+% 18181A18181818FFFFFFFF12101010FFFF1008FFFF1018FFFF1818FFFF10
+% 1210FF1018FF121812101818181817181218181816181818181818181810
+% 12101810FFFF1210FFFF1818FF181818FFFF1818FFFF1818FFFF181817FF
+% 1618FF18181818181A181818181118121012101010101010080E10101818
+% 1818FF181210FFFF181018FFFF1812101818FFFF1718FFFF181816FF1818
+% FF1818181810121018101810121018181818181818181818181818181818
+% 18181818171216181818181818181A181800FF18181818181A1818181812
+% 181818181818181818181618181612161810121012101010101010101218
+% 161818181018181316131810121010121612181812161818181818FFFFFF
+% FF1818181718FFFFFFFF101210FFFFFFFF1818181818FFFF181818181818
+% 1A1818181812181818181818181818181618181612161810FFFFFFFF1010
+% 10FFFFFFFFFFFF181818FFFFFFFF1613181012FFFF121612181812161818
+% 18181810181218181818171818181210101210121218FFFF1818181818FF
+% FFFFFF1818181AFFFFFFFF1218181818FFFF181818181618181612161810
+% 1210121010101010101012181618181810181813161318FFFFFFFF121612
+% 18FFFFFFFF1818181810FFFF181818181718181812101012101212181818
+% 1818181818181818181818181A18181818121818FFFFFFFF1818181816FF
+% FF1612161810121012101010101010101218161818181018181316131810
+% 12101012161218181216181818FFFFFFFF12181818FFFFFFFF1812101012
+% FFFF121818181818181818181818181818181A1818181812181818181818
+% 1818181816181816FFFF1810121012FFFFFFFF10101012FFFFFFFF181018
+% 1813FFFF1810121010121612181812161818181818101812181818181718
+% 18181210FFFFFFFF121818FFFFFFFFFFFF181818FFFFFFFF1A18181818FF
+% FF1818181818181818181618181612161810121012101010101010101218
+% 1618FFFFFFFF1813161318FFFFFFFF121612FFFFFFFF1818181818FFFF12
+% 181818181718181812101012101212181818181818181818181818181818
+% 1A18181818121818181818181818181800FF18181A181818181818181718
+% 181818121818181818121210121012101216121012101210101010101012
+% 181613101216181812161210121010121618181018181818181816121618
+% 18181818181612161210101010121018181818181A18181818181A181818
+% 181818181718181818121818181818121210121012101216121012101210
+% 101010101012181613101216181812161210121010121618181018181818
+% 18181612161818181818181612161210101010121018181818181A181818
+% 18181A181818181818181718181818121818181818121210121012101216
+% 121012101210101010101012181613101216181812161210121010121618
+% 181018181818181816121618181818181816121612101010101210181818
+% 18181A18181818181A181818181818181718181818121818181818121210
+% 121012101216121012101210101010101012181613101216181812161210
+% 121010121618181018181818181816121618181818181816121612101010
+% 10121018181818181A18181818181A181818181818181718181818121818
+% 181818121210121012101216121012101210101010101012181613101216
+% 181812161210121010121618181018181818181816121618181818181816
+% 12161210101010121018181818181A18181818181A181818181818181718
+% 181818121818181818121210121012101216121012101210101010101012
+% 181613101216181812161210121010121618181018181818181816121618
+% 18181818181612161210101010121018181818181A18181818181A181818
+% 18181818171818181812181818181800FF1818181818181A181818181818
+% 181818161818161810101010101010121012181612101010100A10101210
+% 181810121818181018101010101010121810181018181618181818181018
+% 1818181612101810121010101010181612181818181A181818181818181A
+% 181818181818181818161818161810101010101010121012181612101010
+% 100A10101210181810121818181018101010101010121810181018181618
+% 1818181810181818181612101810121010101010181612181818181A1818
+% 18181818181A181818181818181818161818161810101010101010121012
+% 181612101010100A10101210181810121818181018101010101010121810
+% 181018181618181818181018181818161210181012101010101018161218
+% 1818181A181818181818181A181818181818181818161818161810101010
+% 101010121012181612101010100A10101210181810121818181018101010
+% 101010121810181018181618181818181018181818161210181012101010
+% 1010181612181818181A181818181818181A181818181818181818161818
+% 161810101010101010121012181612101010100A10101210181810121818
+% 181018101010101010121810181018181618181818181018181818161210
+% 1810121010101010181612181818181A181818181818181A181818181818
+% 181818161818161810101010101010121012181612101010100A10101210
+% 181810121818181018101010101010121810181018181618181818181018
+% 1818181612101810121010101010181612181818181A181818181818181A
+% 181818181818181818161818161800FF181818181A181818181818181818
+% 181818181818181818101012101010101018121810181010101012161818
+% 181818161817181818181810101010101218181012121818181812101210
+% 12101210121816121010101010101010101216181810181818181A181818
+% 181818181818181818181818181818101012101010101018121810181010
+% 101012161818181818161817181818181810101010101218181012121818
+% 181812101210121012101218161210101010101010101012161818101818
+% 18181A181818181818181818181818181818181818101012101010101018
+% 121810181010101012161818181818161817181818181810101010101218
+% 181012121818181812101210121012101218161210101010101010101012
+% 16181810181818181A181818181818181818181818181818181818101012
+% 101010101018121810181010101012161818181818161817181818181810
+% 101010101218181012121818181812101210121012101218161210101010
+% 10101010101216181810181818181A181818181818181818181818181818
+% 181818101012101010101018121810181010101012161818181818161817
+% 181818181810101010101218181012121818181812101210121012101218
+% 16121010101010101010101216181810181818181A181818181818181818
+% 181818181818181818101012101010101018121810181010101012161818
+% 181818161817181818181810101010101218181012121818181812101210
+% 12101210121816121010101010101010101216181810181818181A181818
+% 1818181818181818181818181800FF1818181A1E181A1818181612101218
+% 181818181818101810121010101010101218171818101210101018181818
+% 181818181818171818161012101210121010101010101010121612161216
+% 1218101810121010101010101010101010121810181818181A1E181A1818
+% 181612101218181818181818101810121010101010101218171818101210
+% 101018181818181818181818171818161012101210121010101010101010
+% 121612161216121810181012101010101010101010101012181018181818
+% 1A1E181A1818181612101218181818181818101810121010101010101218
+% 171818101210101018181818181818181818171818161012101210121010
+% 101010101010121612161216121810181012101010101010101010101012
+% 1810181818181A1E181A1818181612101218181818181818101810121010
+% 101010101218171818101210101018181818181818181818171818161012
+% 101210121010101010101010121612161216121810181012101010101010
+% 1010101010121810181818181A1E181A1818181612101218181818181818
+% 101810121010101010101218171818101210101018181818181818181818
+% 171818161012101210121010101010101010121612161216121810181012
+% 1010101010101010101010121810181818181A1E181A1818181612101218
+% 181818181818101810121010101010101218171818101210101018181818
+% 181818181818171818161012101210121010101010101010121612161216
+% 1218101810121010101010101010101010121810181818181A1E181A1818
+% 18161210121818181818181800FF1818181A181A18181812101210101012
+% 161218101818181010101010101010121612181818181812181818181818
+% 181818181818181818181816121010121012101010101010121012101210
+% 101012101210101210100810100F1010101012181818181A181A18181812
+% 101210101012161218101818181010101010101010121612181818181812
+% 181818181818181818181818181818181816121010121012101010101010
+% 121012101210101012101210101210100810100F1010101012181818181A
+% 181A18181812101210101012161218101818181010101010101010121612
+% 181818181812181818181818181818181818181818181816121010121012
+% 101010101010121012101210101012101210101210100810100F10101010
+% 12181818181A181A18181812101210101012161218101818181010101010
+% 101010121612181818181812181818181818181818181818181818181816
+% 121010121012101010101010121012101210101012101210101210100810
+% 100F1010101012181818181A181A18181812101210101012161218101818
+% 181010101010101010121612181818181812181818181818181818181818
+% 181818181816121010121012101010101010121012101210101012101210
+% 101210100810100F1010101012181818181A181A18181812101210101012
+% 161218101818181010101010101010121612181818181812181818181818
+% 181818181818181818181816121010121012101010101010121012101210
+% 101012101210101210100810100F1010101012181818181A181A18181812
+% 101210101012161218101800FF1818181A18181A18181810101010121012
+% 101212161218101010100A1010101012181818181818181818181A181818
+% 181818161812181818181818101210101810101010101010121613181018
+% 1218161818181010100A100B101010101010181818181A18181A18181810
+% 101010121012101212161218101010100A10101010121818181818181818
+% 18181A181818181818161812181818181818101210101810101010101010
+% 1216131810181218161818181010100A100B101010101010181818181A18
+% 181A18181810101010121012101212161218101010100A10101010121818
+% 18181818181818181A181818181818161812181818181818101210101810
+% 1010101010101216131810181218161818181010100A100B101010101010
+% 181818181A18181A18181810101010121012101212161218101010100A10
+% 10101012181818181818181818181A181818181818161812181818181818
+% 1012101018101010101010101216131810181218161818181010100A100B
+% 101010101010181818181A18181A18181810101010121012101212161218
+% 101010100A1010101012181818181818181818181A181818181818161812
+% 181818181818101210101810101010101010121613181018121816181818
+% 1010100A100B101010101010181818181A18181A18181810101010121012
+% 101212161218101010100A1010101012181818181818181818181A181818
+% 181818161812181818181818101210101810101010101010121613181018
+% 1218161818181010100A100B101010101010181818181A18181A18181810
+% 1010101210121012121600FF1818181A181A181818181210121010121010
+% 10101210121818101010100810121618181A181A181818181A1818181818
+% 181012121816181212181018161010181818181810101012161216101316
+% 101818181818181010100E101010101010181818181A181A181818181210
+% 12101012101010101210121818101010100810121618181A181A18181818
+% 1A1818181818181012121816181212181018161010181818181810101012
+% 161216101316101818181818181010100E101010101010181818181A181A
+% 18181818121012101012101010101210121818101010100810121618181A
+% 181A181818181A1818181818181012121816181212181018161010181818
+% 181810101012161216101316101818181818181010100E10101010101018
+% 1818181A181A181818181210121010121010101012101218181010101008
+% 10121618181A181A181818181A1818181818181012121816181212181018
+% 161010181818181810101012161216101316101818181818181010100E10
+% 1010101010181818181A181A181818181210121010121010101012101218
+% 18101010100810121618181A181A181818181A1818181818181012121816
+% 181212181018161010181818181810101012161216101316101818181818
+% 181010100E101010101010181818181A181A181818181210121010121010
+% 10101210121818101010100810121618181A181A181818181A1818181818
+% 181012121816181212181018161010181818181810101012161216101316
+% 101818181818181010100E101010101010181818181A181A181818181210
+% 12101012101010101200FF10181818181818181818161216121810181018
+% 1012101018181610101010081012161818181818181A1818181818181810
+% 1210101012181010101210121818181818181A1818121818121818181218
+% 181818181818161210101010101010101210181818181818181818161216
+% 1218101810181012101018181610101010081012161818181818181A1818
+% 1818181818101210101012181010101210121818181818181A1818121818
+% 121818181218181818181818161210101010101010101210181818181818
+% 181818161216121810181018101210101818161010101008101216181818
+% 1818181A1818181818181810121010101218101010121012181818181818
+% 1A1818121818121818181218181818181818161210101010101010101210
+% 181818181818181818161216121810181018101210101818161010101008
+% 1012161818181818181A1818181818181810121010101218101010121012
+% 1818181818181A1818121818121818181218181818181818161210101010
+% 101010101210181818181818181818161216121810181018101210101818
+% 1610101010081012161818181818181A1818181818181810121010101218
+% 1010101210121818181818181A1818121818121818181218181818181818
+% 161210101010101010101210181818181818181818161216121810181018
+% 1012101018181610101010081012161818181818181A1818181818181810
+% 1210101012181010101210121818181818181A1818121818121818181218
+% 181818181818161210101010101010101210181818181818181818161216
+% 121810181018101200FF1012161818181818101210121018181818181312
+% 101010101212181810121012101818181818181818181A18181818101210
+% 10101012161010121012101210181818181818181810181618101818FF12
+% 101010121818181010101010101010101012161818181818101210121018
+% 18181818131210101010121218181012101210181818181818FF18181A18
+% 181818101210101010121610101210121012101818181818181818101816
+% 18101818161210101012181818101010101010101010FF12161818181818
+% 101210121018181818181312101010101212181810121012101818181818
+% 181818181A181818181012101010101216101012FF121012101818181818
+% 181818101816181018181612101010121818181010101010101010101012
+% 1618181818181012101210181818181813FF101010101212181810121012
+% 101818181818181818181A18181818101210101010121610101210121012
+% 1018181818181818181018161810FF181612101010121818181010101010
+% 101010101012161818181818101210121018181818181312101010101212
+% 181810121012101818181818FF1818181A18181818101210101010121610
+% 101210121012101818181818181818101816181018181612101010121818
+% 181010101010101010FF1012161818181818101210121018181818181312
+% 101010101212181810121012101818181818181818181A18181818101210
+% 10101012161010FF10121012101818181818181818101816181018181612
+% 101010121818181010101010101010101012161818181818101210121018
+% 1818181813121000FF101218161818101010101010101818181818181010
+% 101010101618181816101018181818181818181818181818181012101010
+% 101010121010101210101210181812161218181A18181218181810FF1010
+% 101010101218101010121010101010101218161818101010101010101818
+% 181818181010101010101618181816101018181818181818FF1818181818
+% 181012101010101010121010101210101210181812161218181A18181218
+% 181810181010101010101218101010121010101010FF1218161818101010
+% 101010101818181818181010101010101618181816101018181818181818
+% 18181818181818101210101010101012101010FF10101210181812161218
+% 181A18181218181810181010101010101218101010121010101010101218
+% 16181810101010101010181818181818FF10101010101618181816101018
+% 181818181818181818181818181012101010101010121010101210101210
+% 181812161218181A1818121818FF10181010101010101218101010121010
+% 101010101218161818101010101010101818181818181010101010101618
+% 1818161010181818181818FF181818181818181012101010101010121010
+% 101210101210181812161218181A18181218181810181010101010101218
+% 1010101210101010FF101218161818101010101010101818181818181010
+% 101010101618181816101018181818181818181818181818181012101010
+% 101010121010FF1210101210181812161218181A18181218181810181010
+% 101010101218101010121010101010101218161818101010101010101818
+% 18181818101000FF10101012101012101010101012181818181818181010
+% 080A10101012181818181818181818181218181818171812181010101010
+% 101012181810101012161818161816181618171A181818101218FF181812
+% 101010101018121610101210101010101012101012101010101012181818
+% 181818181010080A101010121818181818181818181812FF181818171812
+% 181010101010101012181810101012161818161816181618171A18181810
+% 1218181818121010101010181216101012101010FF101012101012101010
+% 101012181818181818181010080A10101012181818181818181818181218
+% 181818171812181010101010101012181810FF1012161818161816181618
+% 171A18181810121818181812101010101018121610101210101010101012
+% 101012101010101012181818181818FF1010080A10101012181818181818
+% 181818181218181818171812181010101010101012181810101012161818
+% 161816181618171A18181810FF1818181812101010101018121610101210
+% 101010101012101012101010101012181818181818181010080A10101012
+% 18181818181818181818FF18181818171812181010101010101012181810
+% 101012161818161816181618171A18181810121818181812101010101018
+% 12161010121010FF10101012101012101010101012181818181818181010
+% 080A10101012181818181818181818181218181818171812181010101010
+% 1010121818FF101012161818161816181618171A18181810121818181812
+% 101010101018121610101210101010101012101012101010101012181818
+% 181818181000FF1210121018101810101010101012161818181A18181010
+% 101010101010101018181818181818161818181818101810121010101010
+% 12181818181612101210181818181818181818181818181618FF18181710
+% 101210121612101210101818101210121018101810101010101012161818
+% 181A1818101010101010101010101818181818181816FF18181818101810
+% 121010101010121818181816121012101818181818181818181818181816
+% 18181818171010121012161210121010181810FF10121018101810101010
+% 101012161818181A18181010101010101010101018181818181818161818
+% 1818181018101210101010101218181818FF121012101818181818181818
+% 181818181816181818181710101210121612101210101818101210121018
+% 101810101010101012161818181AFF181010101010101010101018181818
+% 181818161818181818101810121010101010121818181816121012101818
+% 1818181818181818181818FF181818181710101210121612101210101818
+% 101210121018101810101010101012161818181A18181010101010101010
+% 101018181818181818FF1818181818101810121010101010121818181816
+% 121012101818181818181818181818181816181818181710101210121612
+% 101210101818FF1210121018101810101010101012161818181A18181010
+% 101010101010101018181818181818161818181818101810121010101010
+% 12181818FF16121012101818181818181818181818181816181818181710
+% 101210121612101210101818101210121018101810101010101012161818
+% 181A181800FF181818181818181810121210121012181818181818181010
+% 101010101010101012161818181818181718121612101210101210121818
+% 181818181818101210181818181818181818181818181811FF1218181818
+% 161210181018101818181818181818181818181810121210121012181818
+% 181818181010101010101010101012161818181818FF1718121612101210
+% 101210121818181818181818101210181818181818181818181818181811
+% 181218181818161210181018101818181818FF1818181818181810121210
+% 121012181818181818181010101010101010101012161818181818181718
+% 12161210121010121012181818181818FF18101210181818181818181818
+% 181818181811181218181818161210181018101818181818181818181818
+% 18181012121012101218181818FF18181010101010101010101012161818
+% 181818181718121612101210101210121818181818181818101210181818
+% 18181818181818181818FF11181218181818161210181018101818181818
+% 181818181818181810121210121012181818181818181010101010101010
+% 1010121618181818FF181718121612101210101210121818181818181818
+% 101210181818181818181818181818181811181218181818161210181018
+% 1018181818FF181818181818181810121210121012181818181818181010
+% 101010101010101012161818181818181718121612101210101210121818
+% 181818FF1818101210181818181818181818181818181811181218181818
+% 161210181018101818181818181818181818181810121210121012181818
+% 18181800FF1A181818181818181818101010101012181818181818101810
+% 121010101010101210181218181818181612181012101010101010181818
+% 18181A1818101010121012161216121012101818181818FF101210181818
+% 1012101210121818181A181A181818181818181818101010101012181818
+% 1818181018101210101010101012101812181818FF181612181012101010
+% 10101018181818181A181810101012101216121612101210181818181810
+% 1012101818181012101210121818181A18FF181818181818181818101010
+% 101012181818181818101810121010101010101210181218181818181612
+% 18101210101010101018181818181AFF1810101012101216121612101210
+% 1818181818101012101818181012101210121818181A181A181818181818
+% 181818101010101012181818FF1818101810121010101010101210181218
+% 18181818161218101210101010101018181818181A181810101012101216
+% 121612101210181818FF18101012101818181012101210121818181A181A
+% 181818181818181818101010101012181818181818101810121010101010
+% 10121018121818FF1818161218101210101010101018181818181A181810
+% 101012101216121612101210181818181810101210181818101210121012
+% 1818181AFF1A181818181818181818101010101012181818181818101810
+% 121010101010101210181218181818181612181012101010101010181818
+% 1818FF181810101012101216121612101210181818181810101210181818
+% 1012101210121818181A181A181818181818181818101010101012181818
+% 181800FF1818181818181818181818121010101210121018101812101216
+% 181018121610101210181718181818181810181012101010121818181A18
+% 18181818181010101010101010101010101216181810FF10101018121210
+% 101810121018181818181818181818181818181818121010101210121018
+% 10181210121618101812161010121018171818FF18181810181012101010
+% 121818181A18181818181810101010101010101010101012161818101210
+% 10101812121010181012101818181818FF18181818181818181818121010
+% 101210121018101812101216181018121610101210181718181818181810
+% 181012101010121818181A181818FF181810101010101010101010101012
+% 161818101210101018121210101810121018181818181818181818181818
+% 1818181210101012101210FF101812101216181018121610101210181718
+% 181818181810181012101010121818181A18181818181810101010101010
+% 1010101010121618FF101210101018121210101810121018181818181818
+% 181818181818181818121010101210121018101812101216181018121610
+% 101210181718FF1818181810181012101010121818181A18181818181810
+% 101010101010101010101012161818101210101018121210101810121018
+% 181818FF1818181818181818181818121010101210121018101812101216
+% 181018121610101210181718181818181810181012101010121818181A18
+% 18FF18181810101010101010101010101012161818101210101018121210
+% 101810121018181818181818181818181818181818121010101210121018
+% 1000FF1A1818181818181818181818181210101010101012101018101218
+% 1818181A181012101818181818181818181817181012101216181818181A
+% 181818121010101010101010101010101012181618FF1012101210101010
+% 1210181018161818181A1818181818181818181818181210101010101012
+% 1010181012181818181A1810121018181818FF1818181818171810121012
+% 16181818181A181818121010101010101010101010101012181618181012
+% 101210101010121018101816181818FF1818181818181818181818181210
+% 1010101010121010181012181818181A1810121018181818181818181818
+% 17181012101216181818181A18FF18121010101010101010101010101012
+% 1816181810121012101010101210181018161818181A1818181818181818
+% 18181818121010101010FF121010181012181818181A1810121018181818
+% 18181818181817181012101216181818181A181818121010101010101010
+% 10101010101218FF181810121012101010101210181018161818181A1818
+% 1818181818181818181812101010101010121010181012181818181A1810
+% 1210181818FF18181818181817181012101216181818181A181818121010
+% 101010101010101010101012181618181012101210101010121018101816
+% 1818FF1A1818181818181818181818181210101010101012101018101218
+% 1818181A181012101818181818181818181817181012101216181818181A
+% FF1818121010101010101010101010101012181618181012101210101010
+% 1210181018161818181A1818181818181818181818181210101010101012
+% 00FF18181818181012101817181818181810121010121010121012161818
+% 18181818181810181818181818181818181818181816181818181A181818
+% 1816181818101010101010101010101010101218FF181818181612161216
+% 121012101218181818181818181012101817181818181810121010121010
+% 1210121618181818181818181018181818FF181818181818181818161818
+% 18181A181818181618181810101010101010101010101010121818181818
+% 1816121612161210121012181818FF181818181012101817181818181810
+% 121010121010121012161818181818181818101818181818181818181818
+% 18181816181818181A181818FF1618181810101010101010101010101010
+% 121818181818181612161216121012101218181818181818181012101817
+% 181818181810121010FF1010121012161818181818181818101818181818
+% 18181818181818181816181818181A181818181618181810101010101010
+% 101010101010FF1818181818181612161216121012101218181818181818
+% 181012101817181818181810121010121010121012161818181818181818
+% 10181818FF1818181818181818181816181818181A181818181618181810
+% 101010101010101010101010121818181818181612161216121012101218
+% 18FF18181818181012101817181818181810121010121010121012161818
+% 18181818181810181818181818181818181818181816181818181A1818FF
+% 181618181810101010101010101010101010121818181818181612161216
+% 121012101218181818181818181012101817181818181810121010121000
+% FF1818111210101010121818181818181818181210101210101210121810
+% 121618181818181818181A1818181818181818181818181818181A181818
+% 18181818101210121010121010101010101818FF20191818181818181818
+% 101010121012181818111210101010121818181818181818181210101210
+% 10121012181012161818181818181818FF18181818181818181818181818
+% 18181A181818181818181012101210101210101010101018181820191818
+% 18181818181810101012101218FF18111210101010121818181818181818
+% 181210101210101210121810121618181818181818181A18181818181818
+% 18181818181818181A1818FF181818181012101210101210101010101018
+% 181820191818181818181818101010121012181818111210101010121818
+% 1818181818181812FF101210101210121810121618181818181818181A18
+% 18181818181818181818181818181A181818181818181012101210101210
+% 1010101010FF181820191818181818181818101010121012181818111210
+% 101010121818181818181818181210101210101210121810121618181818
+% 181818FF1A1818181818181818181818181818181A181818181818181012
+% 101210101210101010101018181820191818181818181818101010121012
+% FF1818111210101010121818181818181818181210101210101210121810
+% 121618181818181818181A1818181818181818181818181818181A18FF18
+% 181818181012101210101210101010101018181820191818181818181818
+% 1010101210121818181112101010101218181818181818181812101000FF
+% 181818161812181612101212181818181018101813181818181612161818
+% 181818181818181A181818181A1818181812101316181818181818181812
+% 101210121012161818181010101010101018FF1818181818181810181818
+% 181012101018181818161812181612101212181818181018101813181818
+% 181612161818181818181818181A18FF18181A1818181812101316181818
+% 181818181812101210121012161818181010101010101018181818181818
+% 181810181818181012101018FF1818161812181612101212181818181018
+% 101813181818181612161818181818181818181A181818181A1818181812
+% 10131618181818181818FF12101210121012161818181010101010101018
+% 181818181818181810181818181012101018181818161812181612101212
+% 18181818101810FF13181818181612161818181818181818181A18181818
+% 1A1818181812101316181818181818181812101210121012161818181010
+% 10101010FF18181818181818181810181818181012101018181818161812
+% 181612101212181818181018101813181818181612161818181818181818
+% 181AFF1818181A1818181812101316181818181818181812101210121012
+% 1618181810101010101010181818181818181818101818181810121010FF
+% 181818161812181612101212181818181018101813181818181612161818
+% 181818181818181A181818181A1818181812101316181818181818FF1812
+% 101210121012161818181010101010101018181818181818181810181818
+% 18101210101818181816181218161210121218181818101810181300FF10
+% 181018101818181818101618181818181818181816181212101210121818
+% 181818181818181818181818181A18181618181818181818181818181610
+% 1012101012101810181210121010101210FF101217181818101818181810
+% 121010101810181018101818181818101618181818181818181816181212
+% 1012101218181818181818181818FF181818181A18181618181818181818
+% 181818181610101210101210181018121012101010121012101217181818
+% 1018181818101210101018FF181018101818181818101618181818181818
+% 181816181212101210121818181818181818181818181818181A18181618
+% 181818181818181818FF1610101210101210181018121012101010121012
+% 101217181818101818181810121010101810181018101818181818101618
+% 181818181818FF1816181212101210121818181818181818181818181818
+% 181A18181618181818181818181818181610101210101210181018121012
+% 101010FF1012101217181818101818181810121010101810181018101818
+% 181818101618181818181818181816181212101210121818181818181818
+% 18FF18181818181A18181618181818181818181818181610101210101210
+% 18101812101210101012101210121718181810181818181012101010FF10
+% 181018101818181818101618181818181818181816181212101210121818
+% 181818181818181818181818181A181816181818181818181818FF181610
+% 101210101210181018121012101010121012101217181818101818181810
+% 121010101810181018101818181818101618181818181818181800FF1012
+% 121212101018181818181210121018121612161210101010101010101018
+% 181818181818181818181A18181818181818181818181818181818181813
+% 10101210121012181010101010101010FFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18181813
+% 101012101210121810101010101010101012181812181618101210121010
+% 1010101210121212121010181818181812101210181216121600FF121010
+% 101010121810181818161010121010101210121612101010101010101012
+% 10161818181818181A1820181A1818181818181818181818181818181618
+% 101010101012101812121012101012101012101817121210181618101210
+% 101010121010101010121810181818161010121010101210121612101010
+% 10101010101210161818181818181A1820181A1818181818181818181818
+% 181818181618101010101012101812121012101012101012101817121210
+% 181618101210101010121010101010121810181818161010121010101210
+% 12161210101010101010101210161818181818181A1820181A1818181818
+% 181818181818181818181618101010101012101812121012101012101012
+% 101817121210181618101210101010121010101010121810181818161010
+% 12101010121012161210101010101010101210161818181818181A182018
+% 1A1818181818181818181818181818181618101010101012101812121012
+% 101012101012101817121210181618101210101010121010101010121810
+% 181818161010121010101210121612101010101010101012101618181818
+% 18181A1820181A1818181818181818181818181818181618101010101012
+% 101812121012101012101012101817121210181618101210101010121010
+% 101010121810181818161010121010101210121612101010101010101012
+% 10161818181818181A1820181A1818181818181818181818181818181618
+% 101010101012101812121012101012101012101817121210181618101210
+% 10101012101010101012181018181816101012101010121000FF10101012
+% 101210101218181810121012161218121612101010101008100E10101010
+% 12121618181818181A181818181818181818181818181818181818181810
+% 101010101012101817181612101010121618181818101618181816181618
+% 181010101012101210101218181810121012161218121612101010101008
+% 100E1010101012121618181818181A181818181818181818181818181818
+% 181818181810101010101012101817181612101010121618181818101618
+% 181816181618181010101012101210101218181810121012161218121612
+% 101010101008100E1010101012121618181818181A181818181818181818
+% 181818181818181818181810101010101012101817181612101010121618
+% 181818101618181816181618181010101012101210101218181810121012
+% 161218121612101010101008100E1010101012121618181818181A181818
+% 181818181818181818181818181818181810101010101012101817181612
+% 101010121618181818101618181816181618181010101012101210101218
+% 181810121012161218121612101010101008100E10101010121216181818
+% 18181A181818181818181818181818181818181818181810101010101012
+% 101817181612101010121618181818101618181816181618181010101012
+% 101210101218181810121012161218121612101010101008100E10101010
+% 12121618181818181A181818181818181818181818181818181818181810
+% 101010101012101817181612101010121618181818101618181816181618
+% 181010101012101210101218181810121012161218121600FF1012101010
+% 101010101012181818181012101010101210121010101010101010101010
+% 1012181818181A18181A1818181818171818121618181818181618181010
+% 101008101018181818181818181613181818181818121818181818181818
+% 101012101010101010101012181818181012101010101210121010101010
+% 1010101010101012181818181A18181A1818181818171818121618181818
+% 181618181010101008101018181818181818181613181818181818121818
+% 181818181818101012101010101010101012181818181012101010101210
+% 1210101010101010101010101012181818181A18181A1818181818171818
+% 121618181818181618181010101008101018181818181818181613181818
+% 181818121818181818181818101012101010101010101012181818181012
+% 1010101012101210101010101010101010101012181818181A18181A1818
+% 181818171818121618181818181618181010101008101018181818181818
+% 181613181818181818121818181818181818101012101010101010101012
+% 181818181012101010101210121010101010101010101010101218181818
+% 1A18181A1818181818171818121618181818181618181010101008101018
+% 181818181818181613181818181818121818181818181818101012101010
+% 101010101012181818181012101010101210121010101010101010101010
+% 1012181818181A18181A1818181818171818121618181818181618181010
+% 101008101018181818181818181613181818181818121818181818181818
+% 1010121010101010101010121818181810121010101000FF121010121012
+% 101210121010121612161216121813181718101210101010101010101010
+% 101816181818181A18181818181812101210121812171818181218101010
+% 08101010101818181A18181A181818181A18181818181818181818181810
+% 121010121012101210121010121612161216121813181718101210101010
+% 101010101010101816181818181A18181818181812101210121812171818
+% 18121810101008101010101818181A18181A181818181A18181818181818
+% 181818181810121010121012101210121010121612161216121813181718
+% 101210101010101010101010101816181818181A18181818181812101210
+% 12181217181818121810101008101010101818181A18181A181818181A18
+% 181818181818181818181810121010121012101210121010121612161216
+% 121813181718101210101010101010101010101816181818181A18181818
+% 18181210121012181217181818121810101008101010101818181A18181A
+% 181818181A18181818181818181818181810121010121012101210121010
+% 121612161216121813181718101210101010101010101010101816181818
+% 181A18181818181812101210121812171818181218101010081010101018
+% 18181A18181A181818181A18181818181818181818181810121010121012
+% 101210121010121612161216121813181718101210101010101010101010
+% 101816181818181A18181818181812101210121812171818181218101010
+% 08101010101818181A18181A181818181A18181818181818181818181810
+% 12101012101210121012101012161216121612181300FF10101210101210
+% 1812101210101210181818181816181818181610101010100A0E10101212
+% 181218181818181818181818101012101010121618121612181610101008
+% 0A0000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000008
+% 0A1010121818181820181818181818181818181812101210181216181010
+% 101210101210181210121010121018181818181600FF1218101818181818
+% 1A1818181810101818161216121012101218181818121010101010101010
+% 101818181818181818101018121816121612101210121010121818101010
+% FF2121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121001010
+% 1010101816181A181A181A18181A181A1816121010121012101210181218
+% 1018181818181A18181818101018181612161200FF101012101818181818
+% 181A18181218181818181812101810101218181010101010101010101210
+% AF16181818181612101210181018181818101810121012101216121010FF
+% 7D0000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000002100101010
+% 101018181818181A18181A181A1818181812101210121012101812101012
+% 101818181818181A1818121818181818181200FF10121010121012181818
+% 1818181818181818181817181310101010101210101010101010101012AF
+% 10121212101210101012101218181818181810121010101010121818FF7D
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 007F7F7F0000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000000000FF
+% FFFF00000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000007F7F7F000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000FFFFFF000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000210018181813
+% 16181818181A181818181818181818181010101010101012101010121010
+% 121012181818181818181818181818181700FF1810101210101818181818
+% 18181818181818181818181610101210101010101010101010121010AF10
+% 121010121010101010101216181818181818101010101010101018FF7D00
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000007F7F7F
+% 1313137F7F7F000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000FFFFFFFF5353
+% 53FFFFFF0000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000007F7F7F1313137F7F7F00000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000FFFFFF535353FFFFFF00000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000021001018181612
+% 181818181818181818181818181810101010101010121010181810101210
+% 1018181818181818181818181818181800FF181818101612181018181818
+% 181818181818181818181818181010121018181010101210101012AF1010
+% 10121010121010101010121818181818181012101010100A1010FF7D0000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000007F7F7F131313
+% 7F7F1313137F7F0000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000FFFF53FF535353FFFF
+% 535353FFFF00000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000007F7F7F1313137F7F1313137F7F000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000000000FFFFFF535353FFFF535353FFFF000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000002100101010121818
+% 1818181818181817181818181210121010C5C5C5C5181818181818101612
+% 18101818181818181818181818181800FF1A181812121210181012121018
+% 1818181818181812161818181012101818181010181018181818AF121012
+% 10101216101010101012181818181818101210101010100810FF7D000000
+% 00000000000000000000000000000000AFAFAFAFAFAF0000000000000000
+% 000000FFFFFFFF0000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000007F13137F13137F13
+% 13131313137F7F7F00000000000000000000000000000000000000000000
+% 00000000000000000000000000000000000000FF535353FF5353FF535353
+% 5353FFFFFF00000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000007F7F13137F13137F13131313137F7F7F000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000FF5353FF5353FF535353535353FFFFFF0000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000210008101010101818
+% 171818181818181818181612101010C5C51012C5C518181A181812121210
+% 181012121018181818181818181200FF1818121010101012101010121012
+% 1818181718181618121810121210121218181818181818181AAF18181010
+% 181818101210101010101818181818161217121010101010FF7D00000000
+% 0000000000000000000000000000000000AFAF0000000000000000000000
+% 00FFFF0000FFFF0000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000007F7F7F137F1313137F1313
+% 13137F7F1313137F7F000000000000000000000000000000000000000000
+% 00000000000000000000000000000000FFFFFF5353FF535353FF535353FF
+% FF535353FFFF000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000007F7F137F137F1313137F1313137F7F1313137F7F0000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000FFFFFF53FF535353FF53535353FFFF535353FFFF00000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000021001010101010101218
+% 1818181818181118131810101010C5C51218C5C5181A1818121010101012
+% 1010101210121818181718181600FF181810101010101210101010101216
+% 181818181012101210101010101010101818181818181818181818181818
+% 18181810101010AFAFAFAFAFAFAFAFAFAFAFAFAF181210FF7D0000000000
+% 0000003A3A3A3A3A3A3A3A3A3A3A3A3AAFAF3A3A3A3A3A3A3A3A3A3A3A3A
+% FFFF3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F13137F13137F13131313
+% 7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53FF535353FF5353FF535353FF5353
+% 5353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A7F13137F13137F13137F1313137F131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3AFF53FF5353FF5353FF53535353FF53535353535353FF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A000000000000210012101010C5C5C5C5C5
+% C5C5C5C5C5C5C5C51012101010C5C51618C5C51A18181810101010101210
+% 10101010121618181818101200FF1818181010080810101010100B0E0A10
+% 101618181818181810181316121612161210121618181818181818161218
+% 101818181818161010101012101010121010AF121818FF7D000000000000
+% 00003A3A3AAFAFAFAFAFAF3A3A3A3AAFAF3A3A3A3A3A3A3A3A3A3A3A3AFF
+% FF3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F13137F13137F131313137F
+% 13131313137F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3AFF53FF535353FF5353FF535353FF535353
+% 5353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F13137F13137F13137F1313137F13131313137F7F7F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3AFF53FF5353FF5353FF53535353FF5353535353FFFFFF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A000000000000210018181618C51010181618
+% 181A18181813101018101810C5C51818C5C5181818181810100808101010
+% 10100B0E0A10101618181800FF1818181612101010101010101010101010
+% 12181818181816101810161217121810121012AF1818181818AF18101810
+% 1818181818181818101210101210101010AF161818FF7D00000000000000
+% 003A3A3A3A3A3A3A3A3A3A3A3A3AAFAF3A3A3A3A3A3A3A3A3A3A3A3AFFFF
+% 3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F13137F1313137F137F1313137F1313
+% 13137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFFFF5353FF53535353FF53FF5353FF53535353FF
+% FF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F7F1313137F1313137F137F13137F131313137F7F1313137F7F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FFFF5353FF535353FF53FF535353FF53535353FFFF535353FFFF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A000000000000210018181818C5181818181818
+% 1818181818101210121816C5C51818C5C51A181818181612101010101010
+% 1010101010101218181800FF181618181818181012101010101010101010
+% 121618181818121812181218121012101210AF1018181816AF1810181210
+% 12101818181818121010121012101210AF121818FF7D0000000000000000
+% 3A3A3A3A3A3A3A3A3A3A3A3A3AAFAF3A3A3A3A3A3A3A3A3A3A3A3AFFFF3A
+% 3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F137F1313137F13137F13137F13137F131313
+% 7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF53FF535353FF535353FF5353FF53FF535353FF5353
+% 5353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 7F137F131313137F13137F13137F137F1313137F131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53
+% FF535353FF5353FF5353FF5353FF535353FF53535353535353FF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A000000000000210018181818C518181818181818
+% 18181818181316181012C5C51818C5C51818181618181818181012101010
+% 10101010101012161800FF1812121618181812161210101010100A0F1010
+% 12181818161817181618161810121010AFAFAFAFAFAFAFAF161218101010
+% 181818181818121612181018101810AF101216FF7D00000000000000003A
+% 3A9D3A3A9D3A3A9D3A3AAFAFAFAF9D3A3A3A3A3A3A3A3A3A3A3AFFFF3A3A
+% FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F137F1313137F1313137F7F7F13137F1313137F
+% 131313131313137F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF53FF535353FF53535353FFFFFF53FF535353FF535353
+% 53535353FF3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F
+% 137F131313137F1313137F7F7F137F1313137F131313131313137F3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF53FF
+% 535353FF535353FFFFFF5353FF535353FF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A000000000000210012161818C518181A1818181A18
+% 18181818181818181816C5C5C5C518181818121216181818121612101010
+% 10100A0F1010121800FF1810101012101216121810121010121010101010
+% 121618181818181818181810101010AFAFAFAFAFAFAFAF12181018101818
+% 1818181818161218101810181210AF101212FF7D0000000000000000FF3A
+% 9D3A3A9D3A3A9D3A3A3A3AAFAF9D3A3A3A3A3A3A3A3A3A3A3A3AFFFFFFFF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A7F1313137F1313137F7F1313137F7F7F137F137F1313
+% 131313137F7F7F7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF535353FF535353FFFF535353FFFFFFFFFF53FF5353535353
+% 53FFFFFFFF3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313
+% 137F131313137F7F1313137F7F7F7F137F131313131313137F7F7F3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFF535353FF
+% 535353FFFF535353FFFFFF53FF53FF535353535353FFFFFFFF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3AFF3A3A3A3AFF0000000000210012121818C518181818181818181A
+% 181818181818181818181818181818181810101012101216121810121010
+% 1210101010101200FF181612101010101210121612101216101010101210
+% 121818181818181818181612101010101210121012AF1216121818181818
+% 18161210121012101210121010AF101010FF7D0000000000000000FF3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A3A3A9D3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A7F7F1313137F13131313137F7F7F3A3A3A7F7F137F131313
+% 137F7F1313137F7F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3AFFFF535353FF5353535353FFFFFF3A3A3A3AFF53FF53535353FFFF
+% 535353FFFF3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A
+% 3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F131313
+% 7F1313131313137F7F7F3A3A3A7F137F13131313137F7F1313137F7F3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF535353FF53
+% 53535353FFFFFF3A3A3AFFFF53FF53535353FFFF535353FFFF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF3A3A3A3A3A00FF00000000210010101218C518181818181818181818
+% 181A18181818181818181818181818181612101010101210121612101216
+% 10101010121000FF18181816101010101012101210181216121010101012
+% 1618181818181818181218101010101010101210AF101810181818181818
+% 181210101210121012101210AF100F10FF7D0000000000000000FF3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9D
+% 3A3A3A9D7F137F131313137F7F1313137F7F3A3A3A3A3A3A7F7F1313137F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF53FF53535353FFFF535353FFFF3A9D3A3A3A3AFFFF535353FF535353
+% 53535353FF3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F13131313
+% 7F7F1313137F7F9D3A3A3A3A3A7F7F131313137F131313131313137F3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF5353FF53535353FFFF
+% 535353FFFF3A3A3A3A3A3AFFFF535353FF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% FF3A3AFF3A3A00FF0000000021000F101010C5101818181818181A18181A
+% 181A18181818181818181818181818181816101010101012101210181216
+% 121010101000FF1818181818121010101010101818181810101010101012
+% 181818181818181818181012101010121010101012101818101818181818
+% 1816121010101210101010AF100A10FF7D0000000000000000FF3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9D3A
+% 3A3A9D7F13137F13131313137F7F7F3A3A3A3A3A3A3A3A9D3A7F13137F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF5353FF5353535353FFFFFF3A3A3A9D3A3A3A3A3A3AFF5353FF53535353
+% 535353FF3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F13131313
+% 137F7F7F3A3A9D3A3A3A3A3A3A3A7F1313137F131313131313137F3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF535353FF5353535353FF
+% FFFF3A3A3A3A3A3A3A3A3A3AFF5353FF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3AFF
+% 3A3AFF3A3A00FF0000000021000A101010C510121018181818181A181A18
+% 201818181818181818181818181818181818121010101010101818181810
+% 1010101000FF181818181010101010101218181618181818181818181818
+% 1A1818181810101816181818181216101210121018101210101018181810
+% 12101012101010121010AF100A08FF7D0000000000000000FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A7F1313137F131313131313137F3A3A3A3A3A3A3A3A3A3A7F137F131313
+% 1313131313137F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3AFF53
+% 5353FF53535353535353FF3A3A3A3A3A3A3A3A3A3AFF53FF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313137F1313131313
+% 13137F3A3A3A3A9D3A3A3A3A3A7F13137F1313131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353FF53535353535353
+% FF3A3A3A3A3A3A3A3A3A3AFF53FF535353535353535353FF3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF
+% FFFFFFFFFFFF0000000021000A081010C510101012161818181818181A18
+% 181818101012101810181618181818181010101010101218181618181818
+% 18181800FF16181818101012081010101012181818181818181818181A18
+% 1A1818161212181818181818161210121010121216181010121010101212
+% 161218101210101012AF101010FF7D0000000000000000FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 7F131313137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A7F7F13131313
+% 13137F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353
+% 5353FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3AFFFF53535353535353
+% FFFFFF3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313137F7F1313137F
+% 7F3A3A3A3A3A3A3A3A3A3A3A9D7F7F7F1313131313137F7F7F7F3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3AFF5353535353FFFF535353FFFF3A
+% 9D3A3A3A3A3A3A3A3A3A3AFFFF535353535353FFFFFFFF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF
+% 3AFFFFFF0000000000210010101010C5101010101218181818181A181818
+% 181810121012101210121216181818101012081010101012181818181818
+% 181800FF121018121612100E0A081010101818181A18181A181818181818
+% 181812101018181818181818181010181318161818121010101010101018
+% 1816181810181216AF101010FF7D00000000000000003A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F
+% 1313131313137F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313137F
+% 7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353
+% 535353FFFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353535353FFFF53
+% 5353FFFF3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313131313137F7F7F7F3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F131313137F7F1313137F7F3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A3AFFFF53535353535353FFFFFF3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3AFF53535353FFFF535353FFFF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A000000000000210010101010C5100E1010121018181818181A181818
+% 18101216121018101210121018121612100E0A081010101818181A18181A
+% 1800FF101210181818181010100A1010181818181818181818181A181818
+% 181010121012101818181810181218161818181816181010101010121818
+% 18181618121618AF121010FF7D00000000000000003A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313137F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F1313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353FF535353
+% 53535353FF3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF535353FF53535353
+% 535353FF3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F13137F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F131313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF5353FF53535353535353FF9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF5353FF53535353535353FF3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A000000000000210010101010C510101010101212101818181818181818
+% 181810181112101610101210181818181010100A10101818181818181818
+% 00FF1010101012101818181010101218181818181818181818181A181818
+% 101210101010121012101018101818181818181818181818181010181218
+% 101212101218AF101810FF7D0000000000FF00003A3AFF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313137F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353FF53535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353FF5353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F131313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF5353FF53535353535353FF3A3A3A3A3A3A3A
+% 9D3A3A9D3A9D3A3A9D3AFF5353FF53535353535353FF3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFFFFFFFF
+% FF0000000000210018101210C51010101010101012181818181818181018
+% 101812181018121010101010121018181810101012181818181818181800
+% FF1012101010101218181810121618181818181818181818181818181012
+% 1010101010101012101212101810181818181818181A1818181216121612
+% 1010101012AF181012FF7D00000000FF0000003A3A3AFF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313137F7F
+% 1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353FFFF535353
+% FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313137F7F1313137F7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3AFF53535353FFFF535353FFFF3A3A3A3A3A3A3A3A9D
+% 3A3A9D3A9D3A3A9D3A3AFFFF535353535353535353FF3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFFFFFFFFFFFF
+% FF00000000210010121010C5101010101012101012121818161210101212
+% 1612161818181210121010101012181818101216181818181818181800FF
+% 1810121010100E1816121810181618161818161818181818181818121012
+% 101010101218181812101012121612181618181818181818181818181810
+% 18121010AF101216FF7D00000000FF0000FF3A3A3AFF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353FFFFFFFF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF5353535353535353
+% 53FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F1313131313137F7F7F7F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF535353535353FFFFFFFF3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF3AFF3A3A00FF
+% 00000000210012161210C510101010101012101210121012101010101012
+% 1818181818181810121010100E181612181018161816181816181800FF18
+% 1810100A100A101010101812121218181818181818181818181810101010
+% 101216181818181810121010121612181812101010181818181818181818
+% 18180AAF101216FF7D00000000FF0000FF3A3A3AFF3A3A3A3A9D3A9D3A3A
+% 9D3A3A9D3A3A3A9D9D3A9D3A9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D9D3A9D
+% 9D3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F131313131313131313
+% 7F9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D7F1313131313137F
+% 7F7F7F3A9D9D3A9D9D3A9D3A9D3A3A9D3A9DFF535353535353535353FF3A
+% 9D3A9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A3A3A3AFF535353535353FFFFFF
+% FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313137F7F7F7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3AFF535353535353FFFFFFFF3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3AFF3A3AFF3A3A00FF00
+% 000000210012161210C51010101010101010101010121010101010101210
+% 18181A181A181810100A100A101010101812121218181818181800FF1818
+% 18101010100B101012101818181818181818181010181216121012101010
+% 101218181818181A18181216121818121018181818181818181612181618
+% 1818AF101210FF7D00000000FFFFFFFFFFFFFFFF3A3A3A3A9D3A9D3A3A9D
+% 3A3A9D3A3A3A9D9D3A9D3A9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D9D3A9D9D
+% 3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F7F7F131313131313137F9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D7F1313137F7F1313
+% 137F7F9D9D3A9D9D3A9D3A9D3A3A9D3AFFFFFF53535353535353FF9D3A9D
+% 3A9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A3A3A3A3AFF535353FFFF535353FF
+% FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A7F7F7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F1313137F7F1313137F7F3A3A3A3A3A3A3A
+% 3A3A3A3A3AFFFFFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3AFF535353FFFF535353FFFF3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3AFF3A3AFFFFFFFFFF0000
+% 0000210012101818C5101216121818181810101210101210101010101218
+% 18181818181818101010100B1010121018181818181818181800FF181818
+% 101210101010101012101210181818181818181211181012101012101010
+% 101818181818181812101210181810121012181018181818181210121210
+% 18AF181012FF7D0000000000FFFF00FFFFFF3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13137F131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A3A7F13137F1313131313
+% 13137F3A9D3A3A9D9D3A9D3A3A9DFF5353FF53535353535353FF3A9D9D3A
+% 9D9D9D9D3A9D3A9D3A9D9D9D3A9D9D9D9D9D3AFF5353FF53535353535353
+% FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D
+% 9D3A9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D7F13137F131313131313137F9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D7F13137F131313131313137F9D9D9D9D9D9D9D
+% 9D9D9DFF5353FF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9DFF5353FF53535353535353FF9D9D9D3A3A3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF3A3A3AFFFFFF00000000
+% 00210010121612C518181818181818181218161812101010101010101012
+% 18181818181810121010101010101210121018181818181800FF16181216
+% 121012101010121010101018181818181818181812161216121012101218
+% 181818181A18181010101010101010101010121012101210101010101018
+% AF121012FF7D00000000000000003A3A3A3A9D3A3A3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F1313137F7F1313137F7F9D3A9D9D
+% 9D9D3A9D9D3A9D3A9D3A9D3A9D3A9D9D3A9D3A3A9D7F7F7F131313131313
+% 137F9D3A9D9D3A3A9D3A9D3A3AFF535353FFFF535353FFFF3A3A3A9D3A9D
+% 3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3AFFFFFF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F1313137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A3AFF535353FFFF535353FFFF3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFFFFFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A000000000000
+% 210010121018C51316181818181818181818181618181818101010101010
+% 181816181216121012101010121010101018181818181800FF1812161218
+% 10101210121010101010101218181818181818101812101210101217181A
+% 18181A1818181810101010101010101010101010101818121810101012AF
+% 121010FF7D00000000000000003A3A3A3A9D3A3A3A3A3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D7F1313131313137F7F7F7F9D9D3A9D9D9D
+% 9D3A9D9D3A9D3A9D3A9D3A9D3A9D9D3A9D3A3A9D3A7F1313131313131313
+% 137F3A9D9D3A3A9D3A9D3AFF535353535353FFFFFFFF3A3A3A3A9D3A9D3A
+% 3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3AFF535353535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F1313131313137F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A
+% FF535353535353FFFFFFFF3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A00000000000021
+% 0010101216C518181818181818181A181818121818181818121010101012
+% 1818121612181010121012101010101010121818181800FF101018111818
+% 181818181818181818181818181818181818181811181010121818181818
+% 1A181A18181816101010101010101010101010181718181816121010AF10
+% 1210FF7D000000000000FFFFFFFFFF3A3A3A3A3A9D3A3A9D9D9D3A9D9D9D
+% 9D3A9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D9D9D9D
+% 9D3A9D9D9D9D3A9D9D3A9D9D9D3A9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D3A
+% 9D9D3A9D9D9D9D9D9D9D3A9D7F1313131313131313137F9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F131313131313131313
+% 7F9D9D3A9D9D9D3A9D3AFF535353535353535353FF9D9D9D9D3A9D3A3A9D
+% 9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9DFF535353535353535353FF9D
+% 9D9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D
+% 9D3A9D9D3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D3A9D9D3A9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D3A3A9D
+% 3A9D7F1313131313131313137F3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D3A3A
+% 3A3A9D3A9D3A3A9D3A3A7F1313131313131313137F3A3A9D3A3A3A3A3AFF
+% 535353535353535353FF3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF535353535353535353FF3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFFFFFFFFFFFF00000000002100
+% 12101210C516181818181818181818181010121018181818181818101012
+% 10101811181818181818181818181818181818181800FF12181018101818
+% 1018181818181A1818181818181A18181818161818131810181818181A18
+% 181818181818181012101010101010101010181818181818181810AF1618
+% 16FF7D0000000000FFFFFFFFFFFFFF3A3A3A3A9D3A9D9D9D3A9D9D3A9D9D
+% 9D9D3A9D9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D3A3A3A9D9D3A9D9D9D3A9D
+% 9D3A9D9D9D9D9D3A9D9D3A3A9D9D9D3A9D3A9D9D3A9D9D3A9D9D3A9D9D9D
+% 9D3A9D9D9D9D3A3A9D9D9D7F1313131313131313137F9D9D3A9D3A3A9D9D
+% 9D9D9D9D3A9D3A9D9D9D3A3A3A9D3A3A9D3A3A7F1313131313131313137F
+% 9D9D9D9D3A9D9D9D3AFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9DFF535353535353535353FF9D9D
+% 9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A
+% 9D7F1313131313131313137F3A9D9D9D3A3A9D9D9D9D9D9D3A9D3A9D9D3A
+% 9D9D9D9D3A9D9D9D3A7F1313131313131313137F9D9D9D9D3A9D9D9DFF53
+% 5353535353535353FF3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D9DFF535353535353535353FF3A9D3A9D9D9D9D9D9D9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D3A3A3A3A3A3AFFFFFFFFFFFFFFFF00000000210018
+% 161216C5181218161818161818121012101012101818181A181810101012
+% 1810181018181018181818181A1818181818181A00FF1810121812161218
+% 101818181818181818181A18181818181818181810181018181818181818
+% 1818181818181018101210101010101010121210121818181810AF181818
+% FF7D00000000FFFF00FF3A3A3AFF3A3A3A3A9D3A9D9D9D3A9D9D3A9D9D9D
+% 9D3A9D9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D3A3A3A9D9D3A9D9D9D3A9D9D
+% 3A9D9D9D9D9D3A9D9D3A3A9D9D9D3A9D3A9D9D3A9D9D3A9D9D3A9D9D9D9D
+% 3A9D9D9D9D3A3A9D9D9D3A7F131313131313137F9D9D9D3A9D3A3A9D9D9D
+% 9D9D9D3A9D3A9D9D9D3A3A3A9D3A3A9D3A3A3A7F131313131313137F3A9D
+% 9D9D9D3A9D9D9D3A9DFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9DFF53535353535353FF9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D
+% 9D7F131313131313137F3A3A9D9D9D3A3A9D9D9D9D9D9D3A9D3A9D9D3A9D
+% 9D9D9D3A9D9D9D3A3A7F131313131313137F9D9D9D9D9D3A9D9D9D9DFF53
+% 535353535353FF9D3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D9D9D9D9DFF53535353535353FF9D3A9D3A9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D3A3A3A3A3A3AFF3A3A3A3A3A00FF0000000021001818
+% 1812C5101210181218181810101810181010121218181818181810121810
+% 121812161218101818181818181818181A181800FF171816121610121612
+% 10181818181818181A18181818181818181818181018181818181A181818
+% 10181218181012101810121012101010101010101012181812AF181818FF
+% 7D00000000FF0000FF3A3A3AFF3A3A3A3A9D3A9D9D9D9D3A9D9D9D3A9D9D
+% 9D3A9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D3A9D3A9D9D9D
+% 3A9D9D9D3A9D9D9D9D9D3A3A9D9D3A9D3A9D9D9D9D9D9D9D9D3A9D3A9D9D
+% 9D9D9D9D9D9D3A9D9D9D7F131313131313137F3A9D9D9D9D3A9D9D3A9D3A
+% 9D3A9D9D9D9D3A9D9D3A9D3A3A9D9D3A9D3A7F1313131313137F7F7F3A9D
+% 9D9D9D9D9D3A3AFFFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF5353535353FFFFFF9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D7F
+% 7F131313131313137F9D3A3A3A9D9D3A3A9D9D3A9D3A9D9D9D3A3A3A9D9D
+% 9D3A9D3A3A9D9D9D7F13131313137F7F7F9D9D9D9D3A9D9D9D9D9DFF5353
+% 5353535353FF3A9D3A9D9D3A9D9D3A3A9D9D3A9D3A9D3A9D9D9D9D9D9D9D
+% 3A9D9D9D9DFF5353535353FFFFFF3A9D3A3A3A3A3A9D9D3A9D9D9D9D3A9D
+% 9D3A3A9D3A9D9D3A9D3A3A3A3AFF3A3A3A3A3A00FF000000002100181818
+% 16C510121012101216121818181818181010101012101818161818171816
+% 12161012161210181818181818181A18181800FF18181818121010101010
+% 121618181818181818181A1818181A181818181818181818181818181818
+% 181010101012101818181010101010101010101010181810AF181018FF7D
+% 00000000FF0000FFFFFFFFFF3A3A3A3A9D3A9D9D3A3A9D9D3A9D3A9D3A9D
+% 9D9D3A3A9D9D3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A9D9D3A9D9D3A9D9D
+% 3A9D3A9D9D9D9D9D3A9D3A9D3A9D3A9D3A9D3A9D9D3A9D3A9D9D3A9D9D3A
+% 9D9D9D9D3A9D9D7F7F137F7F1313137F7F3A3A9D9D9D3A9D3A9D9D9D9D3A
+% 3A9D9D9D3A9D9D3A9D9D3A9D3A3A3A3A3A3A7F7F13137F7F1313137F7F9D
+% 9D9D9D3AFFFF5353FFFF535353FFFF3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9DFFFF53FFFF535353FFFF9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A7F7F1313
+% 7F7F1313137F7F9D3A3A9D9D3A9D9D3A3A9D9D9D3A9D9D9D9D3A9D9D9D9D
+% 3A9D9D9D9D9D3A9D7F7F137F7F1313137F7F9D9D3A9D9D9DFFFF53FFFF53
+% 5353FFFF3A9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D9D3A9D3A9D9D9D3A3A9D
+% 9D9D9D3A3AFFFF53FFFF535353FFFF3A9D3A3A3A3A9D9D9D3A9D9D9D9D3A
+% 9D9D3A9D9D9D3A9D3A3A3A9DFFFFFFFFFFFFFFFF00000000210010181318
+% C51010101012101210101210181818181818121012101018181818181818
+% 121010101010121618181818181818181A00FF1012181010101010101010
+% 1012181818181818181818181A181A181818181818181818181818181818
+% 1012101010101812101810121010101010121012181818AF181618FF7D00
+% 000000FF000000FFFFFF3A3A3A3A3A9D3A9D9D3A3A9D9D3A9D3A9D3A9D9D
+% 9D3A3A9D9D3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A9D9D3A9D9D3A9D9D3A
+% 9D3A9D9D9D9D9D3A9D3A9D3A9D3A9D3A9D3A9D9D3A9D3A9D9D3A9D9D3A9D
+% 9D9D9D3A9D7F13131313137F7F7F9D9D3A3A9D9D9D3A9D3A9D9D9D9D3A3A
+% 9D9D9D3A9D9D3A9D9D3A9D3A3A3A3A3A3A9D9D7F7F131313131313137F9D
+% 9D9DFF535353535353FFFFFF3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF53535353535353FF9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A7F1313131313
+% 137F7F7F9D3A9D3A3A9D9D3A9D9D3A3A9D9D9D3A9D9D9D9D3A9D9D9D9D3A
+% 9D9D9D9D9D3A9D3A9D7F131313131313137F9D3A9D9DFF5353535353FFFF
+% FF9D3A3A9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D9D3A9D3A9D9D9D3A3A9D9D
+% 9D9D3A3A3A3AFF53535353535353FF9D3A3A3A3A9D9D9D3A9D9D9D9D3A9D
+% 9D3A9D9D9D3A9D3A3A3A9D3AFFFFFFFFFFFF0000000000210016181618C5
+% 121012101012101012101210121818181818101012121218121012181010
+% 1010101010101012181818181818181800FF121010101010081010101010
+% 1010101218161818181A181A181818181817181818181818181818181818
+% 10101010101010121012101018101010101012181818AF181818FF7D0000
+% 0000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A
+% 9D3A3A3A3A3A3A9D9D9D9D9D3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D3A
+% 9D9D9D9D3A9D9D9D3A9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A
+% 9D9D9D9D7F131313131313137F3A9D9D3A9D3A9D9D3A9D9D3A9D3A9D3A9D
+% 9D9D9D9D9D3A3A3A9D9D9D3A9D3A9D3A9D9D3A7F131313131313137F9D9D
+% 9DFF53535353535353FF3A9D9D9D3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D3A
+% 9D3A3A3A3A3A3A9D9D3A3A3A3A9D3A3AFF53535353535353FF3A9D9D9D9D
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D9D9D9D9D9D3A3A3A3A3A9D
+% 3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A9D3A3A3A9D3A9D9D7F131313131313
+% 137F9D9D3A9D3A9D9D3A9D3A3A3A9D9D3A9D9D9D9D3A9D9D3A9D9D3A9D3A
+% 9D9D9D9D3A9D9D9D7F131313131313137F9D3A9D9DFF53535353535353FF
+% 9D9D9D9D9D9D3A3A9D3A9D3A9D3A9D9D9D9D3A9D9D9D9D3A9D9D9D3A9D3A
+% 3A3A9D3A9DFF53535353535353FF3A9D3A3A3A3A3A9D9D9D3A9D9D9D9D3A
+% 9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A000000000000210018181818C518
+% 181810181010121010121010101210181818181810101010121010101010
+% 08101010101010101012181618181800FF101012101010100A0F0A101010
+% 10101010121618181820181A181818181818121618181818181818181812
+% 101010101010101010181818181712101816181818AF181818FF7D000000
+% 00000000003A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D3A9D9D9D9D3A9D3A
+% 3A9D3A3A9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D9D3A9D9D9D9D9D9D3A9D3A
+% 9D9D9D9D9D9D9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D3A3A9D9D9D9D9D3A
+% 9D9D7F1313131313131313137F9D3A9D9D9D9D9D9D3A3A9D3A9D3A9D9D9D
+% 9D9D3A9D9D3A3A9D9D9D3A3A9D3A9D9D9D7F1313131313131313137F9DFF
+% 535353535353535353FF9D9D9D3A3A3A3A3A9D9D9D9D3A3A3A9D3A9D9D3A
+% 3A3A3A3A3A3A9D9D9D3A3A3A3A3AFF535353535353535353FF9D9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D3A
+% 3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D9D3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D3A9D9D7F1313131313131313
+% 137F9D3A3A9D3A3A3A9D9D3A3A3A9D9D3A9D9D9D9D9D3A3A9D9D9D3A9D3A
+% 9D9D9D9D9D3A7F1313131313131313137F3A9DFF535353535353535353FF
+% 3A9D9D9D3A9D9D3A9D9D3A3A9D9D9D3A9D9D3A9D9D9D3A9D3A9D3A9D3A3A
+% 9D3A3AFF535353535353535353FF3A9D3A9D3A3A3A9D9D9D9D9D9D9D9D3A
+% 9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181818C51818
+% 181812101210101210121012101012101816181612101010101210101010
+% 0A0F0A101010101010101216181800FF10121010101010100A1010080A10
+% 081010101818181A18181818181818101810181217181818181818181010
+% 1010101010101012181818181818181818181818AF181818FF7D00000000
+% 00FFFFFFFFFFFF3A3A3A3A3A3A3A9D3A3A9D3A9D9D3A9D9D9D9D3A9D3A3A
+% 9D3A3A9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D9D3A9D9D9D9D9D9D3A9D3A9D
+% 9D9D9D9D9D9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D3A3A9D9D9D9D9D3A9D
+% 9D7F1313131313131313137F9D3A9D9D9D9D9D9D3A3A9D3A9D3A9D9D9D9D
+% 9D3A9D9D3A3A9D9D9D3A3A9D3A9D9D9D7F1313131313131313137F9DFF53
+% 5353535353535353FF9D9D9D3A3A3A3A3A9D9D9D9D3A3A3A9D3A9D9D3A3A
+% 3A3A3A3A3A9D9D9D3A3A3A3A3AFF535353535353535353FF9D9D9D3A9D3A
+% 3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D3A3A
+% 3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D3A9D9D7F131313131313131313
+% 7F9D3A3A9D3A3A3A9D9D3A3A3A9D9D3A9D9D9D9D9D3A3A9D9D9D3A9D3A9D
+% 9D9D9D9D3A7F1313131313131313137F3A9DFF535353535353535353FF3A
+% 9D9D9D3A9D9D3A9D9D3A3A9D9D9D3A9D9D3A9D9D9D3A9D3A9D3A9D3A3A9D
+% 3A3AFF535353535353535353FF3A9D3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D
+% 9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181816C5181818
+% 181612161012101010101010101010121818181818181012101010101010
+% 0A1010080A100810101018181800FF181216121818181010101010081008
+% 10101012181818181A181818181818101210101818121818121612161210
+% 10100A10101012181818181818181818181A18AF181818FF7D00000000FF
+% FFFFFFFFFFFFFF3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D3A3A9D3A9D3A3A
+% 9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A9D3A9D9D3A9D9D9D3A9D9D3A
+% 9D9D9D3A9D3A9D3A3A9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D9D3A9D9D3A9D
+% 7F1313131313131313137F9D9D9D3A9D9D3A9D9D3A9D9D3A9D3A9D3A9D9D
+% 9D3A9D3A9D3A9D3A9D9D3A3A3A9D9D7F1313131313131313137F9DFF5353
+% 53535353535353FF9D9D9D9D3A3A9D3A3A3A9D9D3A3A3A9D9D9D3A3A3A3A
+% 3A3A3A3A9D9D9D3A3A3A3A3AFF535353535353535353FF9D9D9D9D3A9D9D
+% 3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A
+% 3A3A3A3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A9D9D3A3A3A3A3A3A
+% 9D9D9D9D3A3A3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A
+% 9D3A3A3A9D3A3A9D3A3A3A3A3A9D9D9D3A9D3A7F1313131313131313137F
+% 9D3A9D3A9D9D9D9D3A3A3A9D9D9D3A9D9D9D9D9D9D3A9D3A3A9D9D9D9D3A
+% 9D9D3A9D7F1313131313131313137F9D9DFF535353535353535353FF9D9D
+% 9D9D9D9D3A9D3A9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A
+% 9DFF535353535353535353FF9D9D3A9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181812C518181818
+% 181210121018101812121612101010181818181818181216121818181010
+% 10101008100810101012181800FF18161210181618101210101010080A10
+% 101010101818181818181817121012161218101210101010121018121012
+% 101010101012181818181A18181818181818AF181818FF7D00000000FF00
+% 00003A3A3AFF3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D3A3A9D9D3A3A3A9D9D
+% 9D3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D3A9D9D9D9D3A3A9D9D9D9D
+% 9D9D9D3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D3A
+% 7F131313131313137F3A9D3A9D9D9D9D3A9D9D3A9D3A9D3A9D9D9D3A9D9D
+% 9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A7F131313131313137F9D9D9DFF5353
+% 5353535353FF3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A3A3A3A3A3A
+% 3A3A3A9D9D9D3A3A3A3A3A3AFF53535353535353FF3A9D9D3A9D3A9D3A3A
+% 9D3A3A3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A3A3A9D3A3A9D9D9D3A3A3A
+% 3A3A3A3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D9D3A3A3A3A3A3A3A
+% 9D9D9D3A3A3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A
+% 3A3A9D9D3A3A3A3A3A3A3A9D9D9D9D3A9D3A9D7F131313131313137F9D9D
+% 3A3A9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D9D9D3A3A9D9D9D3A9D9D9D3A
+% 9D9D3A9D7F131313131313137F9D3A9D9DFF53535353535353FF9D9D9D9D
+% 9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A3A9D
+% 9DFF53535353535353FF3A3A9D9D3A3A3A9D3A3A9D9D9D9D9D3A3A9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181010C51818171818
+% 101810181018181818181818181210121218161818161210181618101210
+% 101010080A10101010101800FF1012101012181818181012101010101010
+% 101218181818181818181812101218121012101210121010181216121010
+% 10101010101818181A181A18181A181A18AF181810FF7D00000000FF0000
+% 003A3A3AFF3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D3A3A9D9D3A3A3A9D9D9D
+% 3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D3A9D9D9D9D3A3A9D9D9D9D9D
+% 9D9D3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D3A7F
+% 131313131313137F3A9D3A9D9D9D9D3A9D9D3A9D3A9D3A9D9D9D3A9D9D9D
+% 9D9D9D9D9D3A9D9D3A9D3A9D9D3A7F13131313137F7F7F9D9D9DFF535353
+% 53535353FF3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A3A3A3A3A3A3A
+% 3A3A9D9D9D3A3A3A3A3A3AFF5353535353FFFFFF3A9D9D3A9D3A9D3A3A9D
+% 3A3A3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A3A3A9D3A3A9D9D9D3A3A3A3A
+% 3A3A3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D9D3A3A3A3A3A3A3A9D
+% 9D9D3A3A3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A
+% 3A9D9D3A3A3A3A3A3A3A9D9D9D9D3A9D3A9D7F131313131313137F9D9D3A
+% 3A9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D9D9D3A3A9D9D9D3A9D9D9D3A9D
+% 9D3A9D7F13131313137F7F7F9D3A9D9DFF53535353535353FF9D9D9D9D9D
+% 9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A3A9D9D
+% FF535353535353FFFFFF3A9D9D3A3A3A9D3A3A9D9D9D9D9D3A3A9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A000000000000210018101210C5C5C5C5C5C5C5
+% 18181818181818181A181818181210101012181012101012181818181012
+% 1010101010101012181800FF181012101210101012101210100A08101010
+% 101012101218101810121010101010181818181010101010101210121810
+% 181012121818181A1818181818181818AF181010FF7D00000000FFFFFFFF
+% FFFFFFFF3A3A3A3A9D3A9D9D9D3A9D3A3A9D9D3A9D3A3A3A9D9D9D3A3A3A
+% 9D9D3A9D3A9D9D3A3A3A3A9D9D3A3A3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D7F7F137F
+% 7F1313137F7F9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D9D3A9D
+% 9D9D3A9D9D9D9D9D3A9D3A9D9D9D7F7F137F7F1313137FFFFF53FFFF5353
+% 53FFFF3A3A9D9D9D3A3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A9D3A3A3A3A3A
+% 3A9D9D3A3A3A3A3A3A3A3AFFFF53FFFF535353FFFF9D9D9D3A9D3A9D3A3A
+% 9D3A3A3A9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A
+% 3A3A3A9D9D3A3A3A9D3A3A3A9D9D9D3A9D3A3A9D9D3A3A3A3A3A3A9D9D9D
+% 9D9D9D3A3A9D9D3A9D9D9D3A3A3A3A9D3A3A3A9D9D9D9D3A3A3A3A3A3A9D
+% 3A9D3A3A3A3A3A3A3A9D9D9D9D3A9D7F7F137F7F1313137F7F9D9D9D3A9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D3A9D9D9D9D9D
+% 9D3A9D7F7F137F7F1313137F7FFFFF53FFFF535353FFFF9D3A9D9D9D9D9D
+% 9D9D3A9D3A3A9D3A9D9D9D9D9D9D3A9D9D3A9D9D3A3A9D3A9D9D9D3A3A3A
+% FFFF5353FFFF535353FFFF3A9D9D9D3A9D3A3A9D9D3A9D3A9D9D9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A000000000000210010101010C510101010121010
+% 101012101818181818181818181818181018181012101210101012101210
+% 100A0810101010101200FF18181810101010121012161010101010101010
+% 101010121012121010101010101018181818181810181010101012101816
+% 121816181818181818181818181818AF181010FF7D0000000000FFFFFFFF
+% FFFF3A3A3A3A3A9D3A9D9D9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A3A3A9D9D
+% 9D9D3A9D3A9D9D9D3A3A3A9D9D3A3A3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F1313131313
+% 7F7F7F9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D7F1313131313FF5353535353FFFFFF
+% 3A3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D3A3A9D3A3A3A3A3A9DFF53535353535353FF3A9D9D3A9D3A3A3A3A
+% 3A3A3A9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A
+% 3A9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A9D9D3A3A3A3A3A3A9D9D3A9D
+% 9D3A9D3A9D9D3A3A9D9D3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D9D9D9D9D9D7F13131313137F7F7F9D9D9D9D3A3A3A9D
+% 9D9D3A9D3A3A9D9D3A9D9D9D9D9D3A9D9D3A3A3A3A9D9D3A9D3A9D9D9D3A
+% 3A9D3A9D7F131313131313FF5353535353FFFFFF3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D9D3A3A3A9D
+% 9DFFFF53535353535353FF9D9D3A3A3A9D3A3A9D9D9D9D3A9D9D9D3A9D3A
+% 3A3A3A3A3A3A3A3A3A000000000000210010101010C51010101012101210
+% 121018181718181618181818181818181818181810101010121012161010
+% 101010101010101000FF1818171818181810101210121010101010101010
+% 101010101010101010081010101218181818181818101010101010181212
+% 161818181A161818181816181818AF101210FF7D00000000000000003A3A
+% 3A3A3A3A3A3A9D3A9D9D9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A3A3A9D9D9D
+% 9D3A9D3A9D9D9D3A3A3A9D9D3A3A3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F131313131313
+% 137F9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A9D9D9D9D7F1313131313FF53535353535353FF3A
+% 3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A9D
+% 9D9D3A3A9D3A3A3A3A3A9DFF53535353535353FF3A9D9D3A9D3A3A3A3A3A
+% 3A3A9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A
+% 9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A9D9D3A3A3A3A3A3A9D9D3A9D9D
+% 3A9D3A9D9D3A3A9D9D3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A9D9D9D9D9D9D7F131313131313137F9D9D9D9D3A3A3A9D9D
+% 9D3A9D3A3A9D9D3A9D9D9D9D9D3A9D9D3A3A3A3A9D9D3A9D3A9D9D9D3A3A
+% 9D3A9D7F131313131313FF53535353535353FF3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D9D3A3A3A9D9D
+% 9DFF53535353535353FF9D9D3A3A3A9D3A3A9D9D9D9D3A9D9D9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A000000000000210012101010C5101210181818101810
+% 181018121818181818181818181818181818171818181810101210121010
+% 1010101010101000FF121818181818181012101810181816121818181210
+% 10101210101010100A081010181818181818181818121010101012101012
+% 12181818181816181818181816AF101010FF7D00000000000000003A3A3A
+% 3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D3A3A3A9D9D3A3A9D3A9D3A9D3A
+% 9D9D3A9D9D9D9D3A3A3A9D9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313
+% 137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313FF535353535353535353FF3A
+% 3A3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A9D9D3A9D9D
+% 9D3A3A3A3A3A3A3A3AFF535353535353535353FF9D3A9D3A3A3A3A3A3A3A
+% 3A9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A9D9D3A9D9D3A3A9D3A3A3A3A3A3A3A9D9D9D9D3A9D
+% 3A3A3A3A9D3A9D9D3A3A3A3A3A3A3A9D9D9D9D3A9D3A3A3A3A3A3A9D9D3A
+% 3A3A3A3A3A3A9D9D9D9D7F1313131313131313137F9D3A3A3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D7F131313131313FF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D9D9D3A3A3A9D3A9D9DFF
+% 535353535353535353FF9D9D9D3A3A9D3A9D9D3A9D9D9D3A9D3A9D3A3A3A
+% 3A3A9D3A3A3A3A000000000000210010101010C512161818181818181210
+% 121012101810181818181818181818121818181818181012101810181816
+% 12181818121000FF10121012101212101012121618181818181818181818
+% 101818131610101010101210181818181818181010101010101010101010
+% 121018181818181818181818AF121010FF7D00000000000000003A3A3A3A
+% 9D3A3A3A9D3A9D9D9D9D9D3A9D3A3A9D3A9D9D9D3A3A3A9D9D3A9D3A9D9D
+% 9D3A9D3A3A9D9D9D3A3A9D9D3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D3A9D9D7F131313131313131313
+% 7F3A9D3A9D9D3A3A9D9D9D9D3A9D9D9D3A9D9D3A9D9D9D9D9D3A9D9D9D9D
+% 9D3A9D9D3A9D3A9D9D3A9D7F1313131313FF535353535353535353FF3A3A
+% 3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D
+% 3A3A3A3A3A3A3A3AFF535353535353535353FF9D3A9D3A3A3A3A3A3A3A9D
+% 9D9D9D3A3A3A9D3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A9D
+% 9D3A3A3A3A3A3A3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D
+% 9D9D9D3A9D3A3A3A3A9D3A3A3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A9D3A3A3A9D9D7F1313131313131313137F3A9D3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 7F131313131313FF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A3A9D3A9D3A9DFF53
+% 5353535353535353FF3A3A9D9D9D3A3A3A9D3A3A9D9D9D9D3A3A3A3A3A3A
+% 3AFF3A3A3A3A00FF00000000210010101010C518181818181A1818181010
+% 101010121012101812161818181010121012101212101012121618181818
+% 181818181800FF1210101210101010181010121818181818181A181A1818
+% 181718181218181810101810181218181012101210101818101010101010
+% 1212181818181818181818AF101210FF7D00000000000000003A3A3A3A9D
+% 3A3A3A9D3A9D9D9D9D9D3A9D3A3A9D3A9D9D9D3A3A3A9D9D3A9D3A9D9D9D
+% 3A9D3A3A9D9D9D3A3A9D9D3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D3A9D9D7F1313131313131313137F
+% 3A9D3A9D9D3A3A9D9D9D9D3A9D9D9D3A9D9D3A9D9D9D9D9D3A9D9D9D9D9D
+% 3A9D9D3A9D3A9D9D3A9D7F1313131313FF535353535353535353FF3A3A3A
+% 9D9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D3A
+% 3A3A3A3A3A3A3AFF535353535353535353FF9D3A9D3A3A3A3A3A3A3A9D9D
+% 9D9D3A3A3A9D3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A9D9D
+% 3A3A3A3A3A3A3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D9D
+% 9D9D3A9D3A3A3A3A9D3A3A3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A9D3A3A3A9D9D7F1313131313131313137F3A9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F
+% 131313131313FF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A3A9D3A9D3A9DFF5353
+% 53535353535353FF3A3A9D9D9D3A3A3A9D3A3A9D9D9D9D3A3A3A3A3A3A3A
+% FF3A3A3A3A00FF00000000210012101010C51818181A1818181818121010
+% 101010121010101012101818101210101210101010181010121818181818
+% 181A181A00FF1818121010121012121012101018181618181A1818181818
+% 181818171818181612121012101010181810101212181018181010101210
+% 101818181A1818181818AF161210FF7D00000000000000003A3A3A3A3A3A
+% 3A3A9D3A9D9D3A9D9D9D9D3A9D3A3A3A9D3A3A9D9D9D3A3A9D9D3A9D9D3A
+% 9D3A9D9D9D3A3A3A9D9D3A9D3A3A9D9D3A9D9D3A3A9D3A9D9D3A9D3A3A3A
+% 3A3A9D9D3A3A3A3A9D3A3A3A9D3A9D9D3A9D3A7F131313131313137F3A9D
+% 9D9D9D3A9D3A9D9D3A9D3A9D9D3A9D3A9D3A9D3A3A9D3A9D3A3A9D9D3A9D
+% 9D9D3A9D3A9D3A9D3A3A7F1313131313FF53535353535353FF3A3A3A3A9D
+% 9D9D9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D9D3A3A
+% 3A3A3A3A3A3A9DFF53535353535353FF9D3A9D3A9D3A3A9D3A3A3A3A9D9D
+% 9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A9D9D3A
+% 3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A3A3A3A3A3A9D9D3A3A3A
+% 3A3A3A3A9D9D9D9D7F131313131313137F9D3A9D9D3A9D9D3A9D9D9D9D9D
+% 3A9D9D3A9D3A9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D7F
+% 131313131313FF53535353535353FF3A9D3A9D3A9D3A9D9D3A3A9D3A9D3A
+% 9D9D9D9D3A9D3A3A9D3A9D3A9D9D3A9D9D9D3A9D9D9D9D3A3A9D9DFF5353
+% 5353535353FF9D3A3A9D9D3A3A9D3A3A9D9D3A9D9D9D3A9D3A3A3A3AFFFF
+% FFFFFFFFFFFF00000000210012101212C518181818181A18181810100B0E
+% 10101010100E101010121818181812101012101212101210101818161818
+% 1A181800FF18181818121010181010101012181810101818181818121012
+% 101818181818181810101010101018181012101010181818181816121612
+% 181818AFAFAFAFAFAFAF181018FF7D00000000000000003A3A3A3A3A3A3A
+% 3A9D3A9D9D3A3A9D9D3A3A9D9D3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A3A3A9D3A9D9D3A3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D
+% 3A9D9D3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D7F131313131313137F9D3A3A
+% 9D3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A3A9D3A9D3A9D3A9D9D3A9D3A3A9D
+% 3A9D3A9D3A3A9D3A9D7F1313131313FF53535353535353FF3A3A3A3A9D9D
+% 9D3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3AFF53535353535353FF3A3A9D9D3A3A3A3A3A3A3A3A9D9D3A
+% 3A3A3A3A9D9D9D9D3A9D3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A
+% 3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D
+% 9D9D3A3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A9D9D9D3A7F131313131313137F3A3A9D3A3A9D3A9D3A3A9D3A9D3A
+% 9D3A9D9D9D3A9D3A9D9D3A9D3A9D3A9D3A9D3A3A9D3A3A3A3A3A9D9D7F13
+% 1313131313FF53535353535353FF9D3A3A9D9D9D9D3A9D9D9D9D9D3A3A9D
+% 3A9D3A9D3A9D9D3A9D3A3A9D3A3A3A9D3A3A3A9D9D3A9D9D9D9DFF535353
+% 53535353FF3A9D3A3A9D9D9D3A9D3A9D3A9D3A9D9D3A9D3A3A3A3AFFFFFF
+% FFFFFFFFFF00000000210010181316C5181818181A181A18181210101010
+% 1010100A100A101012161818181818121010181010101012181810101818
+% 181800FF1618181818181018181810121010101818161818181618101010
+% 18131618181A181818121010101810181210181813181818181818181818
+% 1818181818181818AF181012FF7D00000000000000003A3A3A3A3A3A3A3A
+% 9D3A9D9D3A3A9D9D3A3A9D9D3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A3A3A9D3A9D9D3A3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D3A
+% 9D9D3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D7F7F7F1313137F7F9D9D3A3A9D
+% 3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A3A9D3A9D3A9D3A9D9D3A9D3A3A9D3A
+% 9D3A9D3A3A9D3A9D3A7F7F131313FFFFFF535353FFFF3A3A3A3A3A9D9D9D
+% 3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3AFFFF535353FFFFFF3A3A9D9D3A3A3A3A3A3A3A3A9D9D3A3A
+% 3A3A3A9D9D9D9D3A9D3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A3A
+% 3A3A3A3A3A9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D
+% 9D3A3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A9D9D9D3A7F7F7F1313137F7F9D3A3A9D3A3A9D3A9D3A3A9D3A9D3A9D
+% 3A9D9D9D3A9D3A9D9D3A9D3A9D3A9D3A9D3A3A9D3A3A3A3A3A9D9D3A7F7F
+% 131313FFFFFFFF535353FFFF3A9D3A3A9D9D9D9D3A9D9D9D9D9D3A3A9D3A
+% 9D3A9D3A9D9D3A9D3A3A9D3A3A3A9D3A3A3A9D9D3A9D9D9D9D9DFFFF5353
+% 53FFFFFF3A9D3A3A9D9D9D3A9D3A9D3A9D3A9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A00FF00000000210010121618C518181A181A18181818181818181010
+% 0A1010101010101012181618181818181018181810121010101818161818
+% 1800FF121818181818181818171210101010121012181018121818181812
+% 161812161818181818101810121012161818101818181818181818181818
+% 1818181A181818AF181818FF7D00000000000000003A3A3A3A3A3A3A3A3A
+% 3A9D9D9D9D9D9D3A9D3A3A9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D3A9D9D3A9D3A9D3A9D3A9D9D3A3A9D9D9D3A3A9D9D3A9D9D3A
+% 9D3A9D9D3A9D9D9D3A9D9D9D3A3A7F7F1313137F7F7F9D9D9D3A9D3A9D9D
+% 9D3A3A9D9D3A9D3A9D3A3A9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A9D9D3A9D
+% 3A3A9D3A9D3A9D3A9D9D7FFFFF535353FFFFFF9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFFFFFF535353FFFF9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D3A7F7F1313137F7F7F3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A9D3A9D
+% 3A9D9D3A9D3A9D3A9D3A3A3A3A9D3A9D3A9D3A3A9D3A3A9D3A3A9D3A9D7F
+% FFFF535353FFFFFFFF3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D3A9D3A3A9D3A
+% 3A9D3A9D9D3A9D3A3A9D3A9D9D3A3A9D9D3A9D9D9D9D9D9D9D9D9DFFFFFF
+% 535353FFFF9D3A3A9D3A9D9D3A9D9D9D9D9D9D3A9D3A3A9D3A3A3A3A3A3A
+% 3A00FF00000000210018181818C518181818181818181216181812101010
+% 121010101010101012121818181818181818171210101010121012181018
+% 00FF10121612181610181818121612101012101210121618181818181818
+% 12101810121618161818181018101210121018161818181818181A181A18
+% 181818181818AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A
+% 9D3A9D9D9D9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 9D3A9D3A3A3A9D9D3A9D3A9D3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D3A9D3A3A3AFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9DFF53535353535353FF9D3A9D9D9D3A9D9D9D9D9D9D3A9D3A
+% 9D9D3A9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D7F131313131313137F3A9D3A3A3A9D9D3A3A3A9D9D3A3A3A3A3A3A9D3A
+% 9D3A9D9D3A9D3A9D3A9D9D3A3A3A9D9D3A3A9D9D3A9D9D9D9D3A9D9DFF53
+% 535353535353FF3A9D3A9D3A3A3A9D3A9D9D3A9D3A3A9D9D9D3A9D9D9D3A
+% 3A3A9D3A9D3A9D3A3A9D3A9D3A9D3A9D9D9D3A3A3A3A3A3A3A3AFF535353
+% 53535353FF3A3A9D9D9D3A9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 000000000000210018181818C51018181818181812101012161818121018
+% 101010101010101010121612181610181818121612101012101210121600
+% FF1010121012181218161810121012101010101010121216181818161810
+% 18101012101218181818161210181012101212101818181818181818181A
+% 18181A1E18AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D
+% 3A9D9D9D9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A9D3A3A3A9D9D3A9D3A9D3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A9D3A3A3AFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9DFF53535353535353FF9D3A9D9D9D3A9D9D9D9D9D9D3A9D3A9D
+% 9D3A9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 7F131313131313137F3A9D3A3A3A9D9D3A3A3A9D9D3A3A3A3A3A3A9D3A9D
+% 3A9D9D3A9D3A9D3A9D9D3A3A3A9D9D3A3A9D9D3A9D9D9D9D3A9D9DFF5353
+% 5353535353FF3A9D3A9D3A3A3A9D3A9D9D3A9D3A3A9D9D9D3A9D9D9D3A3A
+% 3A9D3A9D3A9D3A3A9D3A9D3A9D3A9D9D9D3A3A3A3A3A3A3A3AFF53535353
+% 535353FF3A3A9D9D9D3A9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00
+% 0000000000210018181818C5181818181818181810101210181216181810
+% 1210101210101010101210121812181618101210121010101010101200FF
+% 101010101012161218101812161210101012101210101210181218181818
+% 181810181810121210121810121010101010121012181818181818181818
+% 1A181A18AF181818FF7D0000000000FFFFFFFFFFFF3A3A3A3A3A9D3A9D9D
+% 9D3A9D9D9D3A9D3A9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A3A3A9D3A9D9D3A
+% 9D9D9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A9D3A3AFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9DFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D
+% 3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F13
+% 13131313131313137F3A9D3A3A9D3A9D9D3A3A3A3A9D3A3A3A9D3A3A3A3A
+% 9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3AFF53535353
+% 5353535353FF3A3A3A9D3A3A3A9D9D3A9D3A9D9D3A3A3A9D3A3A3A3A9D3A
+% 9D3A3A3A9D3A3A3A9D9D3A9D9D9D9D3A9D9D3A3A3A9D3AFF535353535353
+% 535353FF3A9D9D3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A0000
+% 00000000210018181818C51818181A181818181010121012101210121810
+% 12101010121010101010101216121810181216121010101210121000FF10
+% 101012181818161818181018161810101010101012101210101218161818
+% 181818101210101018161210121010101010101210121612181818181A18
+% 201E1AAF1A1818FF7D00000000FFFFFFFFFFFFFFFF3A3A3A3A9D3A9D9D9D
+% 9D9D3A9D9D3A9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D3A9D9D9D
+% 3A9D3A3A9D9D9D3A9D9D3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A9D3A3A3A9D7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 9D9D3A3A9DFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9DFF535353535353535353FF3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D
+% 9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F1313
+% 131313131313137F9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A9D3A9D3A9D9D3A3A9D9D3A3A9D9D3A3A3A3A3A3AFF53535353535353
+% 5353FF3A3A9D9D9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000
+% 000000210018181818C51818181818181818181812101010101018101818
+% 181812101010101012181818161818181018161810101010101000FF1810
+% 121018181818121810181818181810121012101010101012101012121018
+% 18101810101010181210101010101010101010101010101818181818181A
+% 181AAF181818FF7D00000000FF0000003A3A3AFF3A3A3A3A9D3A9D9D9D9D
+% 9D3A9D9D3A9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D3A9D9D9D3A
+% 9D3A3A9D9D9D3A9D9D3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D3A
+% 3A3A9D3A3A3A9D7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D3A3A9DFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9DFF535353535353535353FF3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D
+% 3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F131313
+% 1313131313137F9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A9D3A9D3A9D9D3A3A9D9D3A3A9D9D3A3A3A3A3A3AFF5353535353535353
+% 53FF3A3A9D9D9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000
+% 0000210018181816C5181818181818181818121010101010101012101818
+% 1818181818101210181818181218101818181818101210121000FF1A1818
+% 181618181816181818181618181818101010101012121012101010101218
+% 101210121010181016121010101010101010101010101018181818181818
+% 20AF1A1818FF7D00000000FF0000003A3A3AFF3A3A3A3A9D3A9D3A9D3A9D
+% 3A9D9D3A9D3A3A9D3A9D9D9D9D3A3A3A3A3A9D3A3A9D9D3A9D9D9D3A9D9D
+% 3A3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D7F131313131313137F9D3A9D9D3A9D3A9D3A3A3A3A3A9D
+% 9D3A3A3A9D3A3A3A3A3A9D3A3A9D3A3A9D3A3A3A9D3A9D3A3A3A9D3A3A3A
+% 9D9D3A9DFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9DFF53535353535353FF9D3A9D3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D3A9D9D3A
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F131313
+% 131313137F3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 53FF3A9D3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A9D3A
+% 9D3A9D3A9D9D3A3A9D9D9D9D9D9D3A9D3A3A3A3A3AFF53535353535353FF
+% 9D3A3A9D9D3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000
+% 00210018181812C512101818181012161216101010101012101018181818
+% 1818181A181818161818181618181818161818181810101000FF18181818
+% 1A1818181810181018181816181216121018101010121010101010101012
+% 10121618181218181818181010101010101010101010121018181A202118
+% AF181818FF7D00000000FFFFFFFFFFFFFFFF3A3A3A3A3A3A9D9D3A9D3A3A
+% 9D9D3A9D3A9D9D9D9D3A9D9D9D3A3A3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D
+% 3A9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A9D3A7F131313131313137F3A3A3A3A9D3A9D3A9D3A3A9D9D3A3A
+% 9D3A9D3A9D3A3A3A9D9D9D9D9D3A3A3A3A9D9D3A3A9D9D9D3A3A3A3A3A9D
+% 3A3A9DFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF53535353535353FF9D3A3A9D3A3A3A3A9D3A3A3A3A9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D9D3A3A3A3A3A9D3A3A3A9D
+% 3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A7F13131313
+% 1313137F9D3A9D3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D3A9D3A3A3A3A9D3A
+% 9D3A3A3A9D3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A9D9DFF53535353535353
+% FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D
+% 3A9D3A3A3A3A9D9D3A3A9D9D9D3A3A3A3A3A3A9DFF53535353535353FF9D
+% 9D3A9D9D3A9D3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000
+% 210018181016C5101210101210181018181310100A08081010101216181A
+% 1818181818181A1818181810181018181816181216121000FF1818181818
+% 181A18101812121212181818181012101210101216181818181210121018
+% 1018121816121816181818181010101010101010101012181818201A18AF
+% 181212FF7D0000000000FFFFFFFFFFFF3A3A3A3A3A3A3A9D9D3A9D3A3A9D
+% 9D3A9D3A9D9D9D9D3A9D9D9D3A3A3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D3A
+% 9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A9D3A9D7F7F1313137F7F9D3A3A3A3A9D3A9D3A9D3A3A9D9D3A3A9D
+% 3A9D3A9D3A3A3A9D9D9D9D9D3A3A3A3A9D9D3A3A9D9D9D3A3A3A3A3A9D3A
+% 3A9D3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FFFF535353FFFF3A9D3A3A9D3A3A3A3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D9D3A3A3A3A3A9D3A3A3A9D3A
+% 9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A7F7F131313
+% 7F7F3A9D3A9D3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D3A9D3A3A3A3A9D3A9D
+% 3A3A3A9D3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A9D9D3AFFFF535353FFFF3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D3A
+% 9D3A3A3A3A9D9D3A3A9D9D9D3A3A3A3A3A3A9D3AFFFF535353FFFF3A9D9D
+% 3A9D9D3A9D3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021
+% 0012121812C510181012101810121816181810101008101010181818181A
+% 181818181818181A181018121212121818181810121000FF1A181A181A18
+% 181818121010101010181210101012101216121818181818181818181812
+% 1618121012101212161818101010121010100E101010121818181818AF10
+% 1010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D3A9D9D3A3A9D3A
+% 3A3A3A9D9D3A3A9D9D9D3A3A3A3A3A3A3A3A9D9D9D3A3A9D9D3A9D9D3A3A
+% 9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A7F7F7F7F7F7F7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3AFFFFFFFFFFFF7F7F7F9D3A9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D
+% 9D3A9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D3A3A
+% 9DFFFFFFFFFFFFFF9D9D9D3A9D3A9D3A9D9D3A9D3A3A9D9D9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A9D3A3A9D3A9D3A9D9D9D9D9D3A
+% 3A9D9D3A3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D3A9D9D
+% 9D3A9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D7F7F7F7F7F7F9D
+% 3A3A3A9D9D9D3A3A3A3A3A3A3A3A9D3A9D3A3A9D9D3A9D9D9D9D9D3A3A9D
+% 3A3A3A9D9D9D3A9D3A3A9D3A9D3A9D9D3A9D3A3AFFFFFFFFFFFF7F7F7F7F
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A9D3A3A9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A9D9DFFFFFFFFFFFF9D9D3A
+% 3A9D9D9D3A9D9D9D3A9D3A9D3A3A9D3A3A3A3A3A3A3A0000000000002100
+% 10101210C5181818181012101210181818181210101010121818181A1818
+% 1A181A181A1818181812101010101018121010101200FF18181818181818
+% 181810101010101018181012101010121012161818181818181818181818
+% 131612101010101212181012101010101010101010101216181812AF1210
+% 12FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D3A9D9D9D3A
+% 3A3A9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D9D9D3A9D
+% 9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D
+% 7F7F1313137F7F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A9D3AFFFF
+% 535353FFFF7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 9D3AFFFF535353FFFF9D9D9D9D3A3A3A3A9D3A9D3A3A3A9D3A9D3A9D3A9D
+% 3A3A9D3A9D9D9D3A3A3A3A3A9D3A9D3A3A9D9D3A9D9D3A9D9D3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A9D3A9D3A9D3A3A9D
+% 3A3A9D3A9D3A9D3A9D3A9D9D3A3A9D3A9D3A9D9D7F7F1313137F7F9D3A9D
+% 9D3A9D3A9D3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D9D3A3A3A3A3A9D3A3A3A3A3A3A9DFFFF535353FFFF7F7F1313137F
+% 7F3A3A9D3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D9D9D9D9D3A3A9D3A3A9D3A
+% 9D3A3A9D9D9D3A9D3A9D9D3A3A3A9D3A3A3A9D9D9DFFFF535353FFFF3A9D
+% 9D3A9D3A9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A000000000000210010
+% 121012C5181818181812101010101218181818121012101818181A181818
+% 181818181818181810101010101018181012101000FF1818181818181818
+% 121010101010101818101010101218101012181818181818181818181816
+% 1818181210101010121612181010121010101010101210121010AF101010
+% FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D3A9D9D9D3A3A
+% 3A9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D9D9D3A9D9D
+% 3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A7F13
+% 1313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A9DFF535353
+% 53535353FF13131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% FF53535353535353FF9D9D9D3A3A3A3A9D3A9D3A3A3A9D3A9D3A9D3A9D3A
+% 3A9D3A9D9D9D3A3A3A3A3A9D3A9D3A3A9D9D3A9D9D3A9D9D3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A9D3A9D3A9D3A3A9D3A
+% 3A9D3A9D3A9D3A9D3A9D9D3A3A9D3A9D3A9D7F131313131313137F3A9D9D
+% 3A9D3A9D3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D3A3A3A3A3A9D3A3A3A3A3A3AFF53535353535353FF131313131313
+% 7F3A9D3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D9D9D9D9D3A3A9D3A3A9D3A9D
+% 3A3A9D9D9D3A9D3A9D9D3A3A3A9D3A3A3A9D9DFF53535353535353FF9D9D
+% 3A9D3A9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A00000000000021001010
+% 1012C518181818181810121010101010121618121618181818181A181818
+% 1818181818181210101010101018181010101000FF181818181818181810
+% 121010101010181618181216121010101210181810121818181818181818
+% 18181816121010101812161210101010101012101012101012AF101010FF
+% 7D00000000000000003A3A9D3A3A3A3A3A9D3A9D9D9D9D3A9D3A9D3A3A9D
+% 9D9D9D3A9D9D3A9D3A3A3A3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D3A3A9D9D
+% 9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D3A3A3A3A7F1313
+% 13131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3AFF53535353
+% 535353FF13131313137F3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A9D9D9D9D
+% 9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D3A9D3A3A3AFF
+% 53535353535353FF3A9D9D9D9D9D9D9D9D3A3A9D9D9D3A9D3A3A3A3A3A9D
+% 9D3A9D9D9D9D9D3A3A3A9D9D9D3A3A9D3A3A9D3A9D3A9D3A9D9D9D9D3A3A
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D3A9D3A9D9D3A9D3A
+% 9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D3A7F131313131313137F9D9D9D3A
+% 9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A9D9D3A3A3A9D3AFF53535353535353FF1313131313137F
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D3A9D3A9D9D3A3A3A3A9D3A9D9D3A
+% 3A9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A9D9DFF53535353535353FF3A9D9D
+% 9D9D3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100101010
+% 10C51818181A181812101010100808101012101812171818181818181818
+% 18181818181012101010101018161818121600FF12161818101810181210
+% 121010101212181818181812101010121613101210181618181818181818
+% 181818181010101216121012101216181810181012101216AF101010FF7D
+% 00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D3A9D3A9D9D
+% 3A9D9D9D9D3A9D9D3A3A3A3A3A3A9D9D3A9D9D3A9D9D3A9D9D3A9D9D9D9D
+% 3A3A3A3A9D3A3A3A3A3A9D3A3A3A9D9D9D3A3A3A3A3A3A3A3A7F13131313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFF535353535353
+% 535353FF13131313137F9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D
+% 9D9D9D9D3A9D9D9D9D3A9D3A3A9D9D3A9D9D3A9D9D3A9D3A9D9D3AFF5353
+% 53535353535353FF9D9D3A9D9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D9D3A3A3A3A3A9D3A9D3A9D3A9D3A9D9D9D9D9D3A3A9D9D
+% 3A9D9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A7F1313131313131313137F9D9D9D9D
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3AFF535353535353535353FF1313131313137F
+% 3A3A3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A9D3A3A
+% 9D9D9D9D9D3A3A9D9D3A3A3A3A3A3A9DFF535353535353535353FF9D3A9D
+% 3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A000000000000210010101010
+% C51818181818181818101010100A10101010121612181818181812161818
+% 101810181210121010101212181818181800FF1012101012161210101010
+% 121012161818181A18181210181218181818101218121216181818181218
+% 1818181810121018101810181012181818181812161218AF101010FF7D00
+% 000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D3A9D3A9D9D3A
+% 9D9D9D9D3A9D9D3A3A3A3A3A3A9D9D3A9D9D3A9D9D3A9D9D3A9D9D9D9D3A
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A9D9D9D3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFF53535353535353
+% 5353FF13131313137F9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D
+% 9D9D9D3A9D9D9D9D3A9D3A3A9D9D3A9D9D3A9D9D3A9D3A9D9D3AFF535353
+% 535353535353FF9D9D3A9D9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D9D3A3A3A3A3A9D3A9D3A9D3A9D3A9D9D9D9D9D3A3A9D9D3A
+% 9D9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A
+% 9D3A3A3A3A3A9D3A3A3A3A9D3A3A7F1313131313131313137F9D9D9D9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3AFF535353535353535353FF1313131313137F3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A9D3A3A9D
+% 9D9D9D9D3A3A9D9D3A3A3A3A3A3A9DFF535353535353535353FF9D3A9D3A
+% 3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A000000000000210010101010C5
+% 1818181A20181818101210101010100A1012101818181818121012101012
+% 161210101010121012161818181A181800FF181818181810121612161216
+% 1218181818181A1818181010101010121018101010101218181818101210
+% 10101018181012101812181816181A18181818181618AF181818FF7D0000
+% 0000000000003A3A3A3A9D3A3A3A3A3A9D9D9D9D3A9D3A9D3A3A9D9D9D9D
+% 9D9D9D3A9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D3A3A9D3A9D9D3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353535353535353
+% 53FF13131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A9D9D3A3A3A3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D3A9D3A3AFF53535353
+% 5353535353FF9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D3A9D9D3A
+% 9D3A9D9D3A3A9D3A3A3A9D3A9D3A3A9D3A9D3A9D3A9D9D3A3A3A9D3A3A9D
+% 9D9D9D3A9D9D9D9D9D3A9D3A9D3A9D3A9D3A9D9D9D9D3A3A9D3A9D3A9D3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A9D3A9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3AFF535353535353535353FF1313131313137F3A3A
+% 3A3A3A9D3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D
+% 3A9D9D3A9D3A3A3A3A3A3A9D3A9DFF535353535353535353FF9D9D9D3A3A
+% 9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181010C510
+% 101216181818181810121010101010101010101012101818181818181810
+% 1216121612161218181818181A181800FF1A181818101210181012101216
+% 181818181818181818181010101010101010101010101216181818101012
+% 1012181810101216121618181818181A1818181818AF181816FF7D000000
+% 00000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A3A9D9D9D3A3A9D9D3A3A9D
+% 9D9D3A9D9D3A3A9D3A3A3A9D9D9D9D9D3A9D9D9D3A9D3A3A9D9D9D3A3A3A
+% 3A9D3A3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A9D3A3A3A3A7F131313131313
+% 137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF53535353535353FF
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D3A9D3A3A9D3A
+% 3A3A3A9D3A9D3A9D9D3A9D9D9D3A3A9D9D3A9D9D3A9D3A3A9DFF53535353
+% 535353FF9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D3A9D9D
+% 3A9D9D3A9D3A3A3A9D3A9D9D3A3A9D3A9D9D9D3A9D9D9D3A3A9D3A3A9D9D
+% 3A9D3A9D3A3A9D9D9D3A9D3A9D3A9D3A9D9D9D3A3A3A9D9D3A9D3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A3A9D3A3A7F131313131313137F9D9D3A9D3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3AFF53535353535353FF1313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A9D9D3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D9D9D
+% 9D9D3A9D9D9D3A3A3A3A3A3A9D9DFF53535353535353FF3A9D3A9D9D3A9D
+% 9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018161818C51010
+% 10121810181018121010101012101010101210121618181A181818101210
+% 181012101216181818181818181800FF1818181818111813161216121818
+% 181818181A18181818101010101010100A10101010101218181818181012
+% 1612181012101813181818181818181818181810AF161318FF7D00000000
+% 000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A3A9D9D9D3A3A9D9D3A3A9D9D
+% 9D3A9D9D3A3A9D3A3A3A9D9D9D9D9D3A9D9D9D3A9D3A3A9D9D9D3A3A3A3A
+% 9D3A3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A9D3A3A3A3A7F13131313131313
+% 7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF53535353535353FF13
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D3A9D3A3A9D3A3A
+% 3A3A9D3A9D3A9D9D3A9D9D9D3A3A9D9D3A9D9D3A9D3A3A9DFF5353535353
+% 5353FF9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D3A9D9D3A
+% 9D9D3A9D3A3A3A9D3A9D9D3A3A9D3A9D9D9D3A9D9D9D3A3A9D3A3A9D9D3A
+% 9D3A9D3A3A9D9D9D3A9D3A9D3A9D3A9D9D9D3A3A3A9D9D3A9D3A9D3A3A3A
+% 3A9D3A3A3A3A3A3A3A9D3A3A7F131313131313137F9D9D3A9D3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3AFF53535353535353FF1313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A9D9D3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D9D9D9D
+% 9D3A9D9D9D3A3A3A3A3A3A9D9DFF53535353535353FF3A9D3A9D9D3A9D9D
+% 9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210013181818C5121010
+% 121010121018101012101216181618121618181818181818181818111813
+% 161216121818181818181A181800FF1A1818181818161818121012161818
+% 1A181A181818181810101010101010101010081010101818181818181018
+% 16121612181018101810181818181818181010AF121818FF7D0000000000
+% 0000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D3A9D9D3A9D3A9D9D9D9D3A
+% 9D9D3A3A3A3A3A9D3A3A9D9D9D3A3A9D9D3A9D9D3A3A9D9D3A9D3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A9D3A3A3A7F7F1313137F7F3A
+% 9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF7F1313
+% 137F7F3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D3A9D3A9D9D3A9D3A9D
+% 9D9D3A3A9D3A9D9D9D3A3A9D9D9D9D9D9D3A9D3A3A9D9D9DFFFF535353FF
+% FF9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A
+% 3A9D9D3A9D3A9D3A9D9D3A9D3A3A9D3A9D9D3A9D9D9D3A3A9D9D3A3A9D9D
+% 9D9D9D9D9D3A9D9D9D3A3A9D3A3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D9D3A
+% 3A3A3A3A3A3A3A9D3A9D3A3A7F7F1313137F7F3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9DFFFF535353FFFF7F7F1313137F7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A9D9D9D9D9D3A3A3A9D3A9D3A3A9D3A9D9D9D3A9D9D
+% 3A9D3A9D3A3A3A3A3A3A9D9D9DFFFF535353FFFF9D3A9D9D9D3A3A9D3A9D
+% 9D3A3A3A3A9D3A3A3A3A3A3A3A000000000000210018181818C518181010
+% 1010121012101210121618181818C5C5C5C51818181A18C5C518181618C5
+% C5C5C5C5C5181A181A18181800FF18181818181818181712161818181818
+% 18181A181818181118121012101010101010080E10101818181818181210
+% 121018101810121812101818181817181218AF181618FF7D000000000000
+% 00003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D3A3A3A9D9D9D3A9D9D9D3A
+% 9D9D3A3A3A3A3A3A9D9D3A9D9D3A9D9D9D9D9D3A3A9D9D9D9D3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A7F7F7F3A3A9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A7F7F7F
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A9D3A3A9D9D3A9D3A
+% 9D9D9D9D3A9D3A3A3A9D9D9D3A9D9D3A3A3A3A9D9D9D9D9D9DFFFFFF9D9D
+% 9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D3A3A
+% 3A3A9D3A3A3A3A3A3A9D9D3A9D3A9D3A9D9D3A9D9D9D3A3A9D3A3A9D9D3A
+% 9D9D3A9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D3A9D3A3A3A3A3A9D3A3A9D3A
+% 3A3A3A3A3A9D9D9D9D3A3A3A3A7F7F7F3A9D3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A9D3A9D3A3A9D3A9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D9D9D9D
+% 9D3A9D3A3A3A9D3A3A9D9D9D9D9DFFFFFF9D9D9D3A9D3A9D9D3A9D9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A000000000000210016181818C51818181810
+% 121018101810121018181818C5C51818C5C518181818C5C51818181817C5
+% C5181818181818181A181800FF18181818181A1818181812181818181818
+% 181818181618181612161810121012101010101010101218161818181018
+% 1813161318101210101216121818121618AF181818FF7D00000000000000
+% 003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D3A3A3A9D9D9D3A9D9D9D3A9D
+% 9D3A3A3A3A3A3A9D9D3A9D9D3A9D9D9D9D9D3A3A9D9D9D9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A9D9D9D9D9D3A3A3A3A3A3A7F7F7F3A3A3A3A3A9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A7F
+% 7F7F3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A9D3A3A9D9D3A9D3A9D
+% 9D9D9D3A9D3A3A3A9D9D9D3A9D9D3A3A3A3A9D9D9D9D9D9D9D9D9DFFFFFF
+% 9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D3A3A3A
+% 3A9D3A3A3A3A3A3A9D9D3A9D3A9D3A9D9D3A9D9D9D3A3A9D3A3A9D9D3A9D
+% 9D3A9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D3A9D3A3A3A3A3A9D3A3A9D3A3A
+% 3A3A3A3A9D9D9D9D3A7F7F7F9D3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A
+% 9D3A9D3A9D3A3A9D3A9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D9D9D9D9D
+% 3A9D3A3A3A9D3A3A9D9D9D9D9D9D9D3AFFFFFF3A9D3A9D9D3A9D9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A000000000000210018181018C5181818181718
+% 1818121010121012121818C5C51818C5C5181818181818181A18181818C5
+% C518181818181818181800FF18181A181818181818181718181818121818
+% 181818121210121012101216121012101210101010101012181613101216
+% 18181216121012101012161818101818AF181818FF7D0000000000000000
+% 3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D3A9D3A9D9D3A9D9D9D9D9D3A9D
+% 3A3A3A3A9D3A3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A9D3A3A9D9D9D3A3A7F7F1313137F7F3A3A3A3A9D3A3A
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3AFFFF535353FFFF3A3A3A3A3A7F7F1313
+% 137F7F3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D3A3A9D9D9D3A9D
+% 3A3A9D3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A9DFFFF535353FF
+% FF9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D3A3A9D9D3A9D3A3A3A9D3A
+% 9D3A3A3A9D3A3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D9D
+% 9D9D3A3A9D3A9D9D9D3A9D9D3A9D3A3A3A3A9D9D3A9D9D9D3A9D3A3A3A9D
+% 3A3A3A3A9D9D7F7F1313137F7F3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A9D9D9D3A3A9D3A3A9D9D3A9D9D9D3A9D9D9D3A3A9D3A9D9D3A3A
+% 3A9D3AFFFF535353FFFF9D9D3A9D3A3A7F7F1313137F7F9D9D9D3A3A3A3A
+% 3A3A3A3A9D3A9D3A3A9D3A9D3A3A9D3A3A3A3A9D3A3A9D3A3A9D9D3A9D3A
+% 9D3A3A3A3A3A3A9D9D3A9D3A9DFFFF535353FFFF9D3A3A3A9D9D9D9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A000000000000210018181612C5C5C5C5C5C5C5C5
+% C5C5C5C5C51010121018C5C51818C5C5181818181A1818181818181817C5
+% C5181812181818181800FF1818181818181A181818181818181818161818
+% 161810101010101010121012181612101010100A10101210181810121818
+% 181018101010101010121810181018AF161818FF7D00000000000000003A
+% 3A3A3A3A9D3A3A9D3A9D9D9D9D3A3A9D9D3A3A3A9D3A9D3A9D9D3A9D3A3A
+% 3A9D3A3A3A9D9D3A9D3A3A9D3A9D9D3A3A9D3A9D9D9D3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A3A3A3A3A9D3A9D9D3A7F131313131313137F3A3A9D9D9D9D3A
+% 9D9D3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D3A3A3A3A9D
+% 9D9D9D3A3A3A9D3A3A3A3A3AFF53535353535353FF9D3A3A7F1313131313
+% 13137F9D3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A9D9D9D3A9D3A3A9D9D9D9D
+% 9D9D3A3A9D9D9D3A9D9D9D3A9D3A3A9D9D9D9D3A9D9DFF53535353535353
+% FF3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A9D3A3A9D9D3A3A3A
+% 9D3A3A3A3A3A3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D3A9D9D3A3A3A
+% 9D3A9D3A9D9D9D3A9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A9D9D3A3A9D3A9D9D3A9D3A9D9D3A9D3A9D3A3A9D3A9D9D3A3A3A3A3A
+% 9DFF53535353535353FF3A9D3A9D7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A9D9D9D9D3A3A9D9D9D9D3A9D9D3A3A
+% 3A3A3A3A3A3A9D9D9D3A3AFF53535353535353FF3A3A9D9D9D9D9D3A9D3A
+% 3A9D3A3A3A3A3A3A3A000000000000210018181818C51018181818161210
+% 181012101010101018C5C51818C5C51A181818181818181A1818181818C5
+% C51818161818161800FF181818181A181818181818181818181818181818
+% 18181810101210101010101812181018101010AF12161818181818161817
+% 1818181818101010101012181810AF121818FF7D00000000000000003A3A
+% 3A3A3A9D3A3A9D3A9D9D9D9D3A3A9D9D3A3A3A9D3A9D3A9D9D3A9D3A3A3A
+% 9D3A3A3A9D9D3A9D3A3A9D3A9D9D3A3A9D3A9D9D9D3A3A3A3A3A9D3A3A3A
+% 9D3A3A3A3A3A3A3A9D3A9D9D3A7F131313131313137F3A3A9D9D9D9D3A9D
+% 9D3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D3A3A3A3A9D9D
+% 9D9D3A3A3A9D3A3A3A3A3AFF53535353535353FF9D3A3A7F131313131313
+% 137F9D3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A9D9D9D3A9D3A3A9D9D9D9D9D
+% 9D3A3A9D9D9D3A9D9D9D3A9D3A3A9D9D9D9D3A9D9DFF53535353535353FF
+% 3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A9D3A3A9D9D3A3A3A9D
+% 3A3A3A3A3A3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D3A9D9D3A3A3A9D
+% 3A9D3A9D9D9D3A9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A9D9D3A3A9D3A9D9D3A9D3A9D9D3A9D3A9D3A3A9D3A9D9D3A3A3A3A3A9D
+% FF53535353535353FF3A9D3A9D7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A9D3A9D3A9D3A9D3A3A9D9D9D9D3A3A9D9D9D9D3A9D9D3A3A3A
+% 3A3A3A3A3A9D9D9D3A3AFF53535353535353FF3A3A9D9D9D9D9D3A9D3A3A
+% 9D3A3A3A3A3A3A3A000000000000210018181818C5101210121012101218
+% 1612101010101010C5C51012C5C51810181818181A181818C5C51818C5C5
+% 1818181818181800FF1818181A1E181A1818181612101218181818181818
+% 101810121010101010101218171818101210AF1018181818181818181818
+% 17181816101210121012101010AF101010FF7D00000000000000003A3A3A
+% 3A3A3A3A3A9D3A9D9D3A9D3A3A9D9D3A3A3A9D3A9D3A9D9D3A9D3A3A3A3A
+% 3A3A3A9D9D9D9D9D9D9D9D3A9D3A3A9D9D3A9D9D3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A9D9D7F1313131313131313137F9D3A9D3A3A3A9D3A
+% 9D3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D9D3A9D9D9D3A3A9D3A
+% 9D3A3A3A3A9D9D3A3AFF535353535353535353FF9D7F1313131313131313
+% 137F9D3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A9D3A9D9D9D9D9D9D9D3A9D3A
+% 3A9D9D9D3A9D9D9D3A9D3A3A3A9D9D9D9D9D9DFF535353535353535353FF
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D3A9D3A9D9D3A9D3A9D3A
+% 3A3A3A3A9D9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D3A3A3A9D9D9D9D3A9D3A
+% 3A3A3A9D9D9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A3A9D3A3A3A3A3A3A3A9D
+% 9D7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D3A9D9D3A3A9D3A9D3A9D9D3A3A9D9D9D9D3A3A3A3AFF53
+% 5353535353535353FF3A3A7F1313131313131313137F3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A9D3A3A9D3A3A3A3A3A9D9D3A3A3A9D9D3A9D9D9D3A9D9D3A3A
+% 3A3A3A3A9D9D9D3AFF535353535353535353FF3A9D3A9D9D9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A000000000000210010101012C512161216121810181012
+% 10101010101010C5C51010C5C510181818181A1E181A18C5C51612C5C518
+% 18181818181800FF1818181A181A18181812101210101012161218101818
+% 1810101010101010101216121818181818AF181818181818181818181818
+% 181818181816121010121012AF101010FF7D00000000000000003A3A3A3A
+% 3A3A3A3A9D3A9D9D9D9D3A3A9D9D9D9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A
+% 3A3A3A9D9D9D3A9D9D3A9D9D3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A3A3A9D9D9D7F1313131313131313137F9D3A9D3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A3A9D3A3A9D9D3A3A3A9D3A3A3A9D3A3A9D9D9D9D3A3A
+% 9D3A3A3A3A9D9D3AFF535353535353535353FF3A7F131313131313131313
+% 7F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A9D9D9D3A9D3A9D3A3A
+% 3A3A9D9D9D9D3A3A9D3A3A9D9D9D9D9D9D9DFF535353535353535353FF9D
+% 9D9D3A9D9D9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A3A9D3A9D
+% 3A3A3A3A3A9D9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A3A3A9D9D9D9D3A9D9D
+% 9D3A3A3A3A9D9D3A9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D9D
+% 7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9DFF5353
+% 53535353535353FF3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A
+% 9D3A9D9D9D9D9D9D3A3A3A9D3A9D3A9D3A3A3A9D9D3A9D9D3A9D3A3A3A3A
+% 3A3A3A9D9D9D9DFF535353535353535353FF3A9D9D9D9D9D3A9D3A3A9D3A
+% 3A3A3A3A3A3A000000000000210010101010C51012101210101012101210
+% 10121010081010C5C5C5C51012181818181A181A181818C5C5C5C5101012
+% 161218101800FF1818181A18181A18181810101010121012101212161218
+% 101010100A1010101012181818181818AF1818181A181818181818161812
+% 1818181818181012101018AF101010FF7D00000000000000003A3A3A3A3A
+% 3A3A3A9D3A9D9D9D9D3A3A9D9D9D9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A3A
+% 3A3A9D9D9D3A9D9D3A9D9D3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D3A3A3A9D9D9D7F1313131313131313137F9D3A9D3A3A3A3A3A9D3A
+% 3A3A3A3A9D3A3A3A9D3A3A9D9D3A3A3A9D3A3A3A9D3A3A9D9D9D9D3A3A9D
+% 3A3A3A3A9D9D3AFF535353535353535353FF3A7F1313131313131313137F
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A9D9D9D3A9D3A9D3A3A3A
+% 3A9D9D9D9D3A3A9D3A3A9D9D9D9D9D9D9DFF535353535353535353FF9D9D
+% 9D3A9D9D9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A3A9D3A9D3A
+% 3A3A3A3A9D9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A3A3A9D9D9D9D3A9D9D9D
+% 3A3A3A3A9D9D3A9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D9D7F
+% 1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9DFF535353
+% 535353535353FF3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A9D
+% 3A9D9D9D9D9D9D3A3A3A9D3A9D3A9D3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A
+% 3A3A9D9D9D9DFF535353535353535353FF3A9D9D9D9D9D3A9D3A3A9D3A3A
+% 3A3A3A3A3A000000000000210010101010C5121613181018121816181818
+% 1010100A100B101010101010181818181A18181A18181810101010121012
+% 1012121600FF1818181A181A181818181210121010121010101012101218
+% 18101010100810121618181A181A18AF18181A1818181818181012121816
+% 18121218101816101018AF181818FF7D00000000000000003A3A3A3A3A3A
+% 3A3A9D3A9D9D9D9D3A3A9D9D3A3A3A3A9D3A9D3A9D9D3A9D3A3A3A9D3A3A
+% 9D9D3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 9D9D9D9D9D9D9D3A3A7F131313131313137F3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A9D3A3A
+% 9D3A3A3A3A9D3AFF53535353535353FF3A3A3A7F131313131313137F3A3A
+% 3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D9D9D9D9D9D9D9D9D3A3A3A3A9D
+% 9D9D9D9D3A3A9D3A9D9D3A9D9D9D3A9D9DFF53535353535353FF9D9D9D9D
+% 9D9D3A9D3A9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D3A9D3A3A3A9D3A3A9D3A
+% 3A3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D3A3A3A3A9D9D9D9D9D3A9D9D
+% 3A3A9D9D9D9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D7F
+% 131313131313137F3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9DFF535353
+% 53535353FF3A3A9D3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D9D3A9D3A3A3A9D3A9D9D9D3A3A9D9D3A9D9D9D9D3A9D3A3A3A9D3A
+% 3A9D9D9D3A9DFF53535353535353FF3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A000000000000210018181010C512161216101316101818181818
+% 181010100E101010101010181818181A181A181818181210121010121010
+% 10101200FF10181818181818181818161216121810181018101210101818
+% 1610101010081012161818181818AF1A1818181818181810121010101218
+% 101010121012181818AF18181AFF7D00000000000000003A3A3A3A3A3A3A
+% 3A9D3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A9D
+% 9D9D3A9D3A9D9D9D3A3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A9D9D3A9D3A3A3A7F131313131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D9DFF53535353535353FF3A3A3A7F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A9D9D3A9D3A9D3A9D3A9D3A9D3A3A3A9D9D9D9D9D
+% 9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9DFF53535353535353FF9D3A9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D3A3A3A3A9D
+% 3A3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D9D9D3A9D9D9D9D3A
+% 9D9D9D3A9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A7F13
+% 1313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9DFF53535353
+% 535353FF3A3A9D9D7F131313131313137F3A3A3A9D3A3A3A3A3A3A9D3A9D
+% 3A9D9D3A3A3A3A3A9D3A9D9D3A9D3A3A9D9D9D3A9D3A9D3A3A3A3A3A3A3A
+% 9D9D9D9D3AFF53535353535353FF9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A0000000000002100181A1818C51818121818181218181818181818
+% 161210101010101010101210181818181818181818161216121810181018
+% 101200FF1012161818181818101210121018181818181312101010101212
+% 181810121012101818181818181818181A18181818101210101010121610
+% 1012101210121018AF181818FF7D00000000000000003A3A3A3A3A3A3A3A
+% 9D3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A9D9D
+% 9D3A9D3A9D9D9D3A3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 9D9D3A9D3A3A3A3A7F7F1313137F7F9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D9D3AFFFF535353FFFF3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A9D9D3A9D3A9D3A9D3A9D3A9D3A3A3A9D9D9D9D9D9D
+% 9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9DFFFF535353FFFF9D9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D3A3A3A3A9D3A
+% 3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D9D9D3A9D9D9D9D3A9D
+% 9D9D3A9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A7F7F
+% 1313137F7F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3AFFFF535353
+% FFFF3A3A3A9D9D3A7F7F1313137F7F3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A
+% 9D9D3A3A3A3A3A9D3A9D9D3A9D3A3A9D9D9D3A9D3A9D3A3A3A3A3A3A3A9D
+% 9D9D9D3A9DFFFF535353FFFF9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A000000000000210018181818C5101816181018181612101010121818
+% 181010101010101010101012161818181818101210121018181818181312
+% 1000FF101218161818101010101010101818181818181010101010101618
+% 181816101018181818181818181818181818181012101010101010121010
+% 10121010121018AF121612FF7D00000000000000003A3A3A3A3A3A3A3A9D
+% 3A9D9D9D3A9D3A9D9D9D9D3A3A9D3A9D9D9D9D9D3A3A3A3A3A3A3A9D9D3A
+% 9D9D9D9D9D3A3A3A9D3A9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A7F7F7F3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A
+% 3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D9D9D9DFFFFFFFF3A9D3A3A3A3A3A9D9D7F7F7F7F3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D9D3A3A9D3A9D9D9D9D9D3A9D9D3A
+% 3A9D9D3A9D9D3A9D9D9D3A9D9D3A9D9D9DFFFFFF9D9D3A9D9D9D9D9D9D3A
+% 9D9D3A9D9D9D9D9D3A9D3A9D9D9D3A9D9D3A9D9D3A3A3A3A9D3A3A3A9D3A
+% 9D3A9D9D9D3A3A9D9D3A9D9D9D9D9D9D3A9D3A3A9D9D3A9D3A9D3A9D3A9D
+% 9D3A9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F
+% 7F7F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9DFFFFFF3A
+% 3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A9D
+% 3A9D3A3A3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A9D9D
+% 3A9D3A9D3A9DFFFFFF9D9D9D9D3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A000000000000210016121818C518181218181810181010101010101218
+% 101010121010101010101218161818101010101010101818181818181010
+% 00FF10101012101012101010101012181818181818181010080A10101012
+% 1818181818181818AFAFAFAFAFAF18171812181010101010101012181810
+% 101012161818AF181618FF7D00000000000000003A3A3A3A3A3A3A3A9D3A
+% 9D3A9D9D9D3A3A9D9D3A9D3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A9D9D9D
+% 9D3A9D3A9D3A3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A7F3A3A3A3A3A3A9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A3AFF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D3A9D3A9D
+% 3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9DFF3A9D9D9D9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D3A9D3A3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D
+% 3A9D3A9D3A9D3A9D9D3A3A9D3A3A9D3A3A3A9D3A9D3A3A9D3A9D9D7F9D3A
+% 3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A9D3A7F3A3A3A9D3A9D3A3A3A3A3A9D3A3A3A9D3A3A9D
+% 3A3A3A3A3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D9D
+% 9D9D9D3A9D3A3AFF9D9D3A9D3A9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 000000000000210016181618C51A18181810121818181812101010101018
+% 121610101210101010101012101012101010101012181818181818181000
+% FF1210121018101810101010101012161818181A18181010101010101010
+% 101018181818AFAFAFAFAFAFAFAF18101810121010101010121818181816
+% 1210121018AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D
+% 3A9D9D9D3A3A9D9D3A9D3A3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A9D9D9D9D
+% 3A9D3A9D3A3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A7F7F7F3A3A3A3A3A3A9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3AFFFFFFFF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F7F7F7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D3A9D3A9D3A
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9DFFFFFF9D9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D3A9D3A3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D3A
+% 9D3A9D3A9D3A9D9D3A3A9D3A3A9D3A3A3A9D3A9D3A3A9D3A7F7F7F9D3A3A
+% 3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3AFFFFFF3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A9D3A3A7F7F7F9D3A9D3A3A3A3A3A9D3A3A3A9D3A3A9D3A
+% 3A3A3A3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D9D9D
+% 9D9D3A9D3A3A9DFFFFFF9D3A9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00
+% 0000000000210018181818C5181818181816181818181710101210121612
+% 101210101818101210121018101810101010101012161818181A181800FF
+% 181818181818181810121210121012181818181818181010101010101010
+% 1010121618AF181818181718AF1612101210101210121818181818181818
+% 10121018AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D
+% 9D3A9D3A3A9D9D9D9D3A3A9D9D3A9D3A9D3A3A3A3A3A3A3A9D9D9D3A9D3A
+% 9D9D3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A7F7F1313137F7F9D3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3AFFFF
+% 535353FFFF3A3A3A3A3A3A3A9D3A3A3A3A3A7F7F1313137F7F3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A3A9D9D9D3A9D3A3A
+% 9D3A9D9D9D9D9D9D9D9D9D9D9D9D9DFFFF535353FFFF9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D3A9D3A3A9D9D3A3A3A3A3A3A9D3A3A9D3A9D3A3A9D3A9D3A
+% 9D9D3A9D9D3A9D9D9D9D9D9D9D3A3A9D3A3A9D9D3A9D9D9D3A3A9D9D9D9D
+% 3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A7F7F1313137F7F3A9D
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF9D3A3A3A3A
+% 3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D
+% 3A3A3A3A9D3A9D9D9D9D3A3A9D9D3A3A9D9D3A3A3A3A3A3A3A9D9D9D3A9D
+% 3A9D9D3AFFFF535353FFFF9D3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A0000
+% 00000000210018181818C518181818181811181218181818161210181018
+% 10181818181818181818181818181012121012101218181818181800FF1A
+% 181818181818181818101010101012181818181818101810121010101010
+% 10121018AF181818181816AF18101210101010101018181818181A181810
+% 101012AF121612FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A
+% 9D9D9D3A3A9D9D9D9D3A3A9D3A3A9D3A9D9D3A3A3A3A3A3A9D9D9D9D3A9D
+% 9D3A9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 53535353FF3A9D3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A3A3A9D9D3A9D9D9D3A3A9D3A3A9D9D
+% 9D9D3A9D9D9D9D9D3A9D3A9D9DFF53535353535353FF9D9D3A3A9D9D9D9D
+% 9D9D9D3A3A9D9D3A9D3A9D3A9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A9D3A9D
+% 9D3A9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A9D3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A9D9D9D9D7F131313131313137F9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A7F131313131313137F9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A9D
+% 3A3A3A3A3A9D9D9D3A9D3A3A3A9D9D9D3A9D3A3A3A3A3A3A9D9D3A9D9D9D
+% 9D3AFF53535353535353FF3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000
+% 000000210016121612C51210181818181810101210181818101210121012
+% 1818181A181A181818181818181818101010101012181818181800FF1818
+% 181818181818181818121010101210121018101812101216181018121610
+% 101210AFAFAFAFAFAFAFAF10181012101010121818181A18181818181810
+% 1010AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D
+% 9D9D3A3A9D9D9D9D3A3A9D3A3A9D3A9D9D3A3A3A3A3A3A9D9D9D9D3A9D9D
+% 3A9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353
+% 535353FF3A9D3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A9D3A3A3A9D9D3A9D9D9D3A3A9D3A3A9D9D9D
+% 9D3A9D9D9D9D9D3A9D3A9D9DFF53535353535353FF9D9D3A3A9D9D9D9D9D
+% 9D9D3A3A9D9D3A9D3A9D3A9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A9D3A9D9D
+% 3A9D9D9D9D3A9D9D9D9D3A9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A9D3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A9D3A9D9D9D9D7F131313131313137F9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A7F131313131313137F9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A9D3A
+% 3A3A3A3A9D9D9D3A9D3A3A3A9D9D9D3A9D3A3A3A3A3A3A9D9D3A9D9D9D9D
+% 3AFF53535353535353FF3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000
+% 0000210010101010C5101012161818101210101018121210101810121018
+% 1818181818181818181818181818181210101012101210181000FF1A1818
+% 1818181818181818181812101010101010121010181012181818181A1810
+% 121018AFAFAFAFAFAF18181817181012101216181818181A181818121010
+% 10AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D3A9D9D9D
+% 3A3A3A9D9D9D9D3A3A9D9D9D9D9D3A3A9D3A3A3A3A3A9D3A9D3A9D3A3A3A
+% 9D3A9D9D9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D3A9D3A3A9D3A3A3A3A9D9D9D9D9D
+% 9D9D3A9D3A3A9D9D9D9DFF535353535353535353FF3A9D9D9D9D9D9D9D9D
+% 3A9D3A3A3A3A9D3A9D9D3A9D3A9D9D3A9D3A9D3A3A9D3A9D3A9D9D9D3A3A
+% 9D9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A9D9D3A9D9D3A9D9D3A9D3A9D3A9D
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353535353535353FF3A3A3A3A3A3A
+% 3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A
+% 3A3A3A9D9D9D9D9D3A3A9D9D9D3A9D9D3A3A3A3A3A3A9D3A9D3A9D9D3AFF
+% 535353535353535353FF3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000
+% 00210010101010C510101012181618181012101210101010121018101816
+% 1818181A181818181818181818181818121010101010101200FF18181818
+% 181012101817181818181810121010121010121012161818181818181818
+% 101818181818181818181818181818161818AFAFAFAFAFAFAFAFAFAFAFAF
+% AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D
+% 9D3A3A3A3A3A9D3A3A3A9D3A3A3A9D9D9D9D9D9D3A9D3A3A9D3A3A3A9D9D
+% 9D9D9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A9D9D3A9D3A3A9D9D9D9D9D3A
+% 9D9D9D9D9D9D3A9D3AFF535353535353535353FF9D9D9D3A9D9D9D9D9D3A
+% 3A3A9D3A3A9D9D9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A9D9D
+% 3A9D9D9D9D9D9D3A9D9D9D3A3A9D3A9D9D9D9D9D3A3A9D9D3A3A9D9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A9D
+% 3A7F1313131313131313137F3A3A3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A
+% 3A3A3A9D9D9D3A9D3A3A9D9D3A3A9D9D9D9D9D9D9D3A9D9D3A9D9D3AFF53
+% 5353535353535353FF3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A000000000000
+% 210010101010C51010101010121818181818181612161216121012101218
+% 181818181818181012101817181818181810121010121000FF1818111210
+% 101010121818181818181818181210101210101210121810121618181818
+% 181818181A1818181818181818181818181818181A1818181818181810AF
+% 101210FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D
+% 3A3A3A3A3A9D3A3A3A9D3A3A3A9D9D9D9D9D9D3A9D3A3A9D3A3A3A9D9D9D
+% 9D9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353535353535353
+% 53FF3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A9D9D3A9D3A3A9D9D9D9D9D3A9D
+% 9D9D9D9D9D3A9D3AFF535353535353535353FF9D9D9D3A9D9D9D9D9D3A3A
+% 3A9D3A3A9D9D9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A9D9D3A
+% 9D9D9D9D9D9D3A9D9D9D3A3A9D3A9D9D9D9D9D3A3A9D9D3A3A9D9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A9D3A
+% 7F1313131313131313137F3A3A3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 3A3A9D9D9D3A9D3A3A9D9D3A3A9D9D9D9D9D9D9D3A9D9D3A9D9D3AFF5353
+% 53535353535353FF3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A00000000000021
+% 0012101012C5101010101018181820191818181818181818101010121012
+% 1818181112101010101218181818181818181812101000FF181818161812
+% 181612101212181818181018101813181818181612161818181818181818
+% 181A181818181A181818181210131618181818181818181210121012AF12
+% 1618FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D
+% 3A9D9D3A9D3A3A3A9D9D9D3A9D3A9D3A9D3A9D9D3A9D3A3A3A9D3A9D9D9D
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A7F1313131313
+% 13137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A9D3A9D3A3A9D3A9D3A9D9D3A3A9D9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9DFF53535353535353FF9D9D9D3A9D9D3A9D9D9D9D3A9D
+% 3A3A3A9D3A9D3A3A9D3A3A9D3A9D3A3A9D3A9D9D3A9D9D9D9D3A9D9D9D3A
+% 9D3A9D9D9D9D3A9D9D3A3A9D9D3A9D9D3A9D3A3A9D9D3A3A9D3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A9D3AFF53535353535353FF3A3A3A9D3A3A3A3A3A9D
+% 7F131313131313137F9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A
+% 3A3A9D9D9D9D9D3A3A9D9D3A3A9D9D9D3A9D9D9D9D3A9D3A3A9D9DFF5353
+% 5353535353FF9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100
+% 16181818C510101010101018181818181818181810181818181012101018
+% 18181816181218161210121218181818101810181300FF10181018101818
+% 181818101618181818181818181816181212101210121818181818181818
+% 181818181818181A18181618181818181818181818181610101210AF1210
+% 18FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A3A
+% 3A9D9D9D9D9D3A3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D3A3A
+% 9D9D3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A9D7F131313131313
+% 137F3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D3AFF53535353535353FF3A
+% 3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D
+% 9D9D3A9D9D9D9DFF53535353535353FF9D3A9D9D9D9D9D9D9D9D9D3A3A9D
+% 3A3A9D9D3A3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A9D9D
+% 9D3A9D3A9D9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF53535353535353FF9D3A3A3A3A3A3A3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A9D9D9D9D9D3A3A3A3A9D9D9D3A3A9D3A3A3A9D9D3A3A3A9D9DFF535353
+% 53535353FF9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010
+% 181018C51012101010121012101217181818101818181810121010101810
+% 181018101818181818101618181818181818181800FF1012121212101018
+% 181818181210121018121612161210101010101010101018181818181818
+% 181818AFAF181818181818181818181818181818181818131010AF101210
+% FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A3A3A
+% 9D9D9D9D9D3A3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D3A3A9D
+% 9D3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A7F7F1313137F7F
+% 3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A
+% 9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D3A3AFFFF535353FFFF3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D9D
+% 9D3A9D9D9D9D9DFFFF535353FFFF9D9D3A9D9D9D9D9D9D9D9D9D3A3A9D3A
+% 3A9D9D3A3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A9D9D9D
+% 3A9D3A9D9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F9D3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A9D3A3A3A3A3A3A3A3A3A3A7F
+% 7F1313137F7F3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 9D9D9D9D9D3A3A3A3A9D9D9D3A3A9D3A3A3A9D9D3A3A3A9D9D9DFFFF5353
+% 53FFFF3A9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021001210
+% 1218C5101010101010101012181812181618101210121010101010121012
+% 1212121010181818181812101210181216121600FF121010101010121810
+% 181818161010121010101210121612101010101010101012101618181818
+% 1818AFAF20181A181818181818181818181818181818161810AF101010FF
+% 7D00000000000000003A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D3A9D3A9D9D
+% 9D9D9D9D9D3A3A3A9D3A9D3A9D3A9D3A9D3A3A3A3A9D9D9D9D9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D7F7F7F3A3A9D
+% 9D9D9D9D3A9D3A3A3A9D9D9D9D9D3A3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D
+% 9D9D3A3A3A3A9D9D3A9D9D3A3A3A9D9D9D9D9D3A3AFFFFFF9D9D3A9D9D3A
+% 3A3A9D9D9D9D3A3A3A3A3A7F7F7F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A9D3A9D9D9D3A3A9D3A3A3A9D3A9D9D9D9D9D9D3A3A9D9D9D9D3A
+% 9D9D9D9D9D9D9D9DFFFFFF9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D9D3A3A
+% 9D3A3A3A3A9D3A3A3A9D3A3A3A9D9D9D3A9D9D9D9D9D9D3A9D3A9D3A9D9D
+% 9D9D9D9D3A9D3A9D3A3A3A3A9D3A3A3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A9D3A7F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A9D9D3A
+% 9D9D9D9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D3AFFFFFF
+% FF3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A0000000000002100101012
+% 10C512121012101012101012101817121210181618101210101010121010
+% 10101012181018181816101012101010121000FF10101012101210101218
+% 181810121012161218121612101010101008100E10101010121216181818
+% 18181A181818181818181818181818181818181818181810AF101010FF7D
+% 00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D3A3A9D3A
+% 9D9D9D3A9D9D3A3A9D9D9D9D3A9D9D3A3A3A3A3A9D9D9D9D3A3A9D3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D3A9D3A7F9D3A3A3A9D3A9D
+% 9D3A3A9D9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A
+% 3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A9D9DFF3A3A9D9D3A3A9D9D3A3A
+% 3A9D9D3A9D9D3A3A3A3A3A9D7F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A9D9D9D3A3A9D3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D3A9DFF9D9D9D9D9D3A9D3A9D9D9D9D3A3A3A9D3A3A9D9D
+% 9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D
+% 9D9D3A9D9D9D9D3A3A9D3A9D3A9D9D3A9D3A9D3A9D3A9D9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A9D3A3A3A3A3A7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3AFF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D7F3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A
+% 9D9D9D3A3A9D9D3A3A9D9D9D9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D3AFF
+% 9D3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010101012
+% C51817181612101010121618181818101618181816181618181010101012
+% 101210101218181810121012161218121600FF1012101010101010101012
+% 181818181012101010101210121010101010101010101010101218181818
+% 1A18181A18181818181718181216181818181816181810AF101008FF7D00
+% 000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D3A3A9D3A9D
+% 9D9D3A9D9D3A3A9D9D9D9D3A9D9D3A3A3A3A3A9D9D9D9D3A3A9D3A3A9D3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D3A7F7F7F9D3A3A3A9D3A9D9D
+% 3A3A9D9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A3A
+% 3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3AFFFFFF3A3A9D9D3A3A9D9D3A3A3A
+% 9D9D3A9D9D3A3A3A3A3A9D3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A9D9D9D3A3A9D3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D3A9D9DFFFFFF9D9D3A9D3A9D9D9D9D3A3A3A9D3A3A9D9D9D
+% 3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D
+% 9D3A9D9D9D9D3A3A9D3A9D3A9D9D3A9D3A9D3A9D3A9D9D3A3A3A3A3A3A3A
+% 9D3A3A3A3A9D3A3A7F7F7F7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3AFFFFFF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A7F7F7F3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D
+% 9D9D3A3A9D9D3A3A9D9D9D9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D3AFFFF
+% FFFF9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010081010C5
+% 181818181818181613181818181818121818181818181818101012101010
+% 1010101010121818181810121010101000FF121010121012101210121010
+% 121612161216121813181718101210101010101010101010101816181818
+% 181A1818181818181210121012181217181818121810AF100810FF7D0000
+% 0000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D3A9D9D3A9D3A9D9D3A
+% 3A9D9D3A9D3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3A3A9D3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D7F7F1313137F7F3A3A3A3A3A3A3A
+% 3A3A9D3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A3AFFFF535353FFFF3A3A3A3A9D9D3A3A3A9D
+% 3A3A3A3A9D3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A9D9D9D9D3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D
+% 9D9D3A9D9D9DFFFF535353FFFF9D9D9D9D9D9D3A3A3A9D3A3A9D3A9D3A3A
+% 9D3A3A3A3A9D3A3A3A3A9D3A9D3A9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D
+% 9D3A9D3A9D9D3A9D9D3A9D3A9D3A9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F7F1313137F7F9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F
+% 1313137F7F3A9D3A9D3A3A3A9D3A3A3A3A3A9D3A9D3A3A3A3A9D3A3A3A9D
+% 9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A3A3A9D3AFFFF5353
+% 53FFFF9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210008101010C518
+% 18181A18181A181818181A18181818181818181818181810121010121012
+% 10121012101012161216121612181300FF10101210101210181210121010
+% 1210181818181816181818181610101010100A0E10101212181218181818
+% 181818181818101012101010121618121612181610AF10080AFF7D000000
+% 00000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D3A3A3A9D3A9D3A9D
+% 9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A9D3A3A3A9D3A3A3A9D9D3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A
+% 3A9D3A3A3A3A9D9D3A3A3AFF53535353535353FF3A3A3A9D9D3A3A3A9D9D
+% 3A3A3A3A3A9D3A7F131313131313137F3A9D3A3A3A3A3A3A3A9D3A3A3A3A
+% 9D9D9D9D3A9D9D3A3A9D9D3A3A9D9D9D3A9D9D9D3A9D9D3A9D9D9D3A3A9D
+% 9D9D9D3AFF53535353535353FF3A9D9D9D9D9D3A9D3A9D3A9D3A3A3A9D3A
+% 3A3A3A9D3A3A9D3A3A3A3A9D9D9D9D9D9D3A9D3A3A9D9D9D3A9D9D9D9D3A
+% 9D9D9D9D9D3A3A9D3A9D3A3A3A3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F131313131313137F3A9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF53535353535353FF3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A9D7F131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A9D9D3A9D9D
+% 9D3A3A9D9D9D3A9D3A3A3A3A9D9D9D9D3A3A9D3A3A3A9D9DFF5353535353
+% 5353FF3A9D3A3A3A9D3A3A3A3A3A3A0000000000002100080A1010C51818
+% 181820181818181818181818181812101210181216181010101210101210
+% 181210121010121018181818181600FF12181018181818181A1818181810
+% 101818161216121012101218181818121010101010101010101818AF1818
+% 1818181010181218161216121012101210101218AF101010FF7D00000000
+% 000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D3A3A3A9D3A9D3A9D9D
+% 9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A9D9D3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A
+% 9D3A3A3A3A9D9D3A3A3AFF53535353535353FF3A3A3A9D9D3A3A3A9D9D3A
+% 3A3A3A3A9D3A7F131313131313137F3A9D3A3A3A3A3A3A3A9D3A3A3A3A9D
+% 9D9D9D3A9D9D3A3A9D9D3A3A9D9D9D3A9D9D9D3A9D9D3A9D9D9D3A3A9D9D
+% 9D9D3AFF53535353535353FF3A9D9D9D9D9D3A9D3A9D3A9D3A3A3A9D3A3A
+% 3A3A9D3A3A9D3A3A3A3A9D9D9D9D9D9D3A9D3A3A9D9D9D3A9D9D9D9D3A9D
+% 9D9D9D9D3A3A9D3A9D3A3A3A3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F131313131313137F3A9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF53535353535353FF3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A9D7F13131313
+% 1313137F3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A9D9D3A9D9D9D
+% 3A3A9D9D9D3A9D3A3A3A3A9D9D9D9D3A3A9D3A3A3A9D9DFF535353535353
+% 53FF3A9D3A3A3A9D3A3A3A3A3A3A000000000000210010101010C5181618
+% 1A181A181A18181A181A1816121010121012101210181218101818181818
+% 1A18181818101018181612161200FF101012101818181818181A18181218
+% 1818181818121018101012181810101010101010AFAFAFAF121618AF1818
+% 16121012101810181818181018101210121012AF121010FF7D0000000000
+% 0000003A3A3A3A9D3A3A3A9D3A9D9D9D9D9D3A9D3A9D9D3A9D9D3A9D3A9D
+% 9D3A3A9D9D9D9D3A3A3A9D9D9D3A3A3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A9D9D9D3A
+% 3A3A3A9D9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A
+% 9D9D9D3A9D3A3A3AFF535353535353535353FF3A9D9D3A3A3A3A9D9D3A3A
+% 9D3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D
+% 9D3A3A9D3A3A3A9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9DFF535353535353535353FF9D9D9D3A3A3A3A3A9D3A3A9D9D3A9D3A3A9D
+% 3A3A3A3A3A3A3A9D9D3A9D9D9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A9D3A9D
+% 9D9D3A9D3A9D3A9D9D3A3A9D9D9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 7F1313131313131313137F3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3AFF53
+% 5353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 9D9D3A9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A9D3A9DFF5353535353535353
+% 53FF9D3A3A3A3A3A3A3A3A3A3A000000000000210010101010C518181818
+% 181A18181A181A1818181812101210121012101812101012101818181818
+% 181A1818121818181818181200FF10121010121012181818181818181818
+% 18181818171813101010101012101010101010AFAFAF12101012AF121012
+% 101010121012181818181818101210101010AF121818FF7D000000000000
+% 00003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A9D
+% 9D3A9D9D3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A9D3A3A3A7F1313131313131313137F3A3A3A3A3A9D9D3A3A
+% 3A3A9D3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D
+% 9D3A9D9D3A3A3AFF535353535353535353FF3A3A9D3A3A3A3A3A9D9D9D3A
+% 3A3A3A7F1313131313131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 3A3A9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A3A9D
+% FF535353535353535353FF9D9D3A9D3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A
+% 3A9D3A3A9D3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A3A9D3A9D9D9D9D9D9D
+% 9D9D9D3A3A9D9D9D3A3A3A9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A7F
+% 1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3AFF5353
+% 53535353535353FF3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F13131313131313
+% 13137F3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A
+% 9D3A9D9D3A9D3A9D9D3A9D3A3A3A9D9D9D3A9D9DFF535353535353535353
+% FF3A3A3A3A3A3A3A3A3A3A3A000000000000210018181813C5181818181A
+% 181818181818181818181010101010101012101010121010121012181818
+% 181818181818181818181700FF1810101210101818181818181818181818
+% 181818181816101012101010101010101010AF12AF10121012AF10121010
+% 1010101012161818181818181010101010AF101018FF7D00000000000000
+% 003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A9D9D
+% 3A9D9D3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A9D3A3A3A7F1313131313131313137F3A3A3A3A3A9D9D3A3A3A
+% 3A9D3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D
+% 3A9D9D3A3A3AFF535353535353535353FF3A3A9D3A3A3A3A3A9D9D9D3A3A
+% 3A3A7F1313131313131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A
+% 3A9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A3A9DFF
+% 535353535353535353FF9D9D3A9D3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A
+% 9D3A3A9D3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A3A9D3A9D9D9D9D9D9D9D
+% 9D9D3A3A9D9D9D3A3A3A9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A7F13
+% 13131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3AFF535353
+% 535353535353FF3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F1313131313131313
+% 137F3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D
+% 3A9D9D3A9D3A9D9D3A9D3A3A3A9D9D9D3A9D9DFF535353535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A000000000000210010181816C5181818181818
+% 181818181818181810101010101010121010181810101210101818181818
+% 1818181818181818181800FF181818101612181018181818181818181818
+% 1818181818181810101210181810101012AF10AFAFAFAFAFAF1210101210
+% 10101010121818181818181012101010AF0A1010FF7D0000000000000000
+% 3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D3A9D9D9D9D3A9D3A3A3A9D3A3A3A
+% 9D9D9D9D3A9D3A3A3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F131313131313137F9D3A3A3A3A3A3A9D9D3A3A3A
+% 9D9D3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A9DFF53535353535353FF3A3A3A9D9D3A3A3A9D3A3A3A9D3A3A
+% 3A3A7F131313131313137F3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D3A3A
+% 9D9D9D3A3A3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9DFF
+% 53535353535353FF9D9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A
+% 9D9D3A9D3A9D9D9D9D3A9D3A9D9D3A3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A3A9D3A9D9D3A9D9D9D9D9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A7F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A
+% 3A9D9D9D3A9D9D9D3A9D3A3A9D3A9D9D9D3A9DFF53535353535353FF3A9D
+% 3A3A3A3A3A3A3A3A3A3A000000000000210010101012C518181818181818
+% 181718181818121012101010121010181818181818101612181018181818
+% 18181818181818181800FF1A181812121210181012121018181818181818
+% 18121618181810121018181810101810AF1818AFAFAFAF12101012161010
+% 101010121818181818181012101010AF100810FF7D00000000000000003A
+% 3A3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D3A9D3A9D3A
+% 9D3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A7F131313131313137F3A3A3A9D9D3A3A3A9D3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A
+% 9D3A3A3A9DFF53535353535353FF9D3A3A9D9D3A3A3A9D9D3A3A3A3A3A3A
+% 3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D
+% 3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9DFF53
+% 535353535353FF9D9D3A3A3A3A9D3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A9D
+% 3A3A9D3A9D9D9D9D3A9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A3A3A9D9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313
+% 13131313137F3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353
+% 535353FF9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A7F131313131313137F9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D
+% 9D3A3A3A3A3A3A9D3A9D3A9D9D9D9D3A9D9DFF53535353535353FF3A9D3A
+% 3A3A3A3A3A3A3A3A3A000000000000210008101010C5C5C5C5C5C5C51818
+% 18181818161210101010101012181818181A181812121210181012121018
+% 181818181818181200FF1818121010101012101010121012181818171818
+% 1618121810121210121218181818181818181A1818181010181818101210
+% 1010101018181818181612171210AF101010FF7D00000000000000003A3A
+% 3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D9D3A9D3A9D3A9D
+% 3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A9D7F7F1313137F7F3A3A3A3A9D9D3A3A3A9D3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A9D
+% 3A3A3A9D3AFFFF535353FFFF3A9D3A3A9D9D3A3A3A9D9D3A3A3A3A3A3A3A
+% 3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A
+% 9D3A3A3A9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9DFFFF
+% 535353FFFF9D9D9D3A3A3A3A9D3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A9D3A
+% 3A9D3A9D9D9D9D3A9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 3A3A3A9D9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A7F7F13
+% 13137F7F3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353
+% FFFF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F7F1313137F7F3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D
+% 3A3A3A3A3A3A9D3A9D3A9D9D9D9D3A9D9D9DFFFF535353FFFF9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A000000000000210010101010C5101218181818181818
+% 1118131810101010101012181818181A1818121010101012101010121012
+% 1818181718181600FF181810101010101210101010101216181818181012
+% 101210101010101010101818181818181818181818181818181818101010
+% 10101218181818181818181810AF181210FF7D00000000000000003A3A3A
+% 3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A7F7F7F9D3A3A3A3A3A9D3A3A3A3A9D3A3A3A9D9D3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D3A3A3A3A9D3A3A9D9D3A
+% 3A3A9D3A3A3AFFFFFF3A3A3A9D3A3A9D9D3A3A3A9D9D3A3A9D3A3A3A3A9D
+% 3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D
+% 3A3A3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9DFF
+% FFFF9D9D9D9D3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D3A3A9D3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F
+% 7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3AFFFFFF3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D
+% 3A3A9D3A3A9D3A9D9D9D3A9D3A9D9D9D9D9D3AFFFFFF3A9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A000000000000210012101010C510101818181818181818
+% 181810121010101012161818181A18181810101010101210101010101216
+% 18181818101200FF1818181010080810101010100B0E0A10101618181818
+% 181810181316121612161210121618181818181818161218101818181818
+% 161010101012101010121010AF121818FF7D00000000000000003A3A3A3A
+% 3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A3A3A
+% 3A3A9D9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A9D7F9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A9D9D9D9D
+% 9D3A3A3A3A9D3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A
+% 3A9D9D9DFF3A9D9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A3A
+% 3A9D3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D3A9D3A3A3A
+% 3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D
+% FF9D9D9D9D3A9D3A9D3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D
+% 3A9D9D9D9D3A9D3A3A9D9D9D9D9D3A9D9D9D9D3A9D9D9D3A9D3A9D9D3A3A
+% 9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A3AFF9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A000000000000210018181618C51010181618181A18181813
+% 10101810181012181818181A18181818181010080810101010100B0E0A10
+% 101618181800FF1818181612101010101010101010101010121818181818
+% 161018101612171218101210121818181818181818101810181818181818
+% 1818101210101210101010AF161818FF7D00000000000000003A3A3A3A3A
+% 3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A3A3A3A
+% 3A9D9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A7F7F7F9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A9D9D9D9D9D
+% 3A3A3A3A9D3A9D3A3A3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D3A3A3A3A
+% 9DFFFFFF3A9D9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A3A3A
+% 9D3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D3A9D3A3A3A3A
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D
+% FFFFFF9D3A9D3A9D3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D3A
+% 9D9D9D9D3A9D3A3A9D9D9D9D9D3A9D9D9D9D3A9D9D9D3A9D3A9D9D3A3A9D
+% 3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A3A9DFFFFFF3A9D3A3A3A3A3A
+% 3A3A3A3A3A000000000000210018181818C5181818181818181818181810
+% 12101218161216181818181A181818181612101010101010101010101010
+% 1218181800FF181618181818181012101010101010101010121618181818
+% 121812181218121012101210121018181816181810181210121018181818
+% 18121010121012101210AF121818FF7D00000000000000003A3A3A3A3A3A
+% 3A3A9D3A9D9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D9D9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D9D9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A9D3A3A9D3AFFFF
+% 535353FFFF3A3A3A3A9D3A9D3A3A3A3A3A9D3A9D9D3A3A3A3A3A3A3A3A9D
+% 7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A3A9D9D9D9D9DFFFF53
+% 5353FFFF9D3A9D3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D3A9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D3A3A3A
+% 9D3A9D3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F7F1313137F7F3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A9D
+% 9D9D9D3A3A9D9D3A9D9D9D9D9D9D9D9D9DFFFF535353FFFF3A3A3A3A3A3A
+% 3A3A3A3A000000000000210018181818C518181818181818181818181813
+% 1618101218181818181A1818181618181818181012101010101010101010
+% 12161800FF1812121618181812161210101010100A0F1010121818181618
+% 171816181618101210101010121018181812161218101010181818181818
+% 121612181018101810AF101216FF7D00000000000000003A3A3A3A3A3A3A
+% 3A9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D3A3A9D3A
+% 3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F
+% 131313131313137F3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 53535353FF9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A7F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A3A9D9D9D
+% 9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9DFF53535353
+% 535353FF3A9D3A9D3A9D3A3A3A3A9D3A9D3A3A3A3A9D3A9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F131313131313137F9D3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A9D9D3A9D9D9D9D9D3A3A9D9D3AFF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A
+% 9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A9D
+% 9D9D9D9D3A9D9D9D3A9D9D3A9D9D9DFF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A000000000000210012161818C518181A1818181A18181818181818
+% 18181816181818181818181812121618181812161210101010100A0F1010
+% 121800FF1810101012101216121810121010121010101010121618181818
+% 181818181810101010121010101210121612181018101818181818181816
+% 1218101810181210AF101212FF7D00000000000000003A3A3A3A3A3A3A3A
+% 9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D3A3A9D3A3A
+% 3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F13
+% 1313131313137F3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353
+% 535353FF9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A7F1313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A3A9D9D9D9D
+% 3A9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9DFF5353535353
+% 5353FF3A9D3A9D3A9D3A3A3A3A9D3A9D3A3A3A3A9D3A9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A
+% 3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F131313131313137F9D3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A9D9D3A9D9D9D9D9D3A3A9D9D3AFF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D3A3A9D9D
+% 9D9D9D3A9D9D9D3A9D9D3A9D9D9DFF53535353535353FF3A3A3A3A3A3A3A
+% 3A3A000000000000210012121818C518181818181818181A181818181818
+% 181818181818181818181810101012101216121810121010121010101010
+% 1200FF181612101010101210121612101216101010101210121818181818
+% 181818181612101010101210121012101216121818181818181612101210
+% 12101210121010AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D
+% 3A9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F131313
+% 1313131313137F3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A7F13131313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A9D3A3A9D9D9D9D9D
+% 9D9D3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9DFF53535353535353
+% 5353FF3A9D9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A9D9D3A9D9D9D3A
+% 9D9D3A9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A3A9D9D3A3A
+% 9D3A3A3A9D9D9D9D9D9D3A9DFF535353535353535353FF3A3A3A9D3A3A3A
+% 3A000000000000210010101218C518181818181818181818181A18181818
+% 181818181818181818181612101010101210121612101216101010101210
+% 00FF18181816101010101012101210181216121010101012161818181818
+% 181818121810101010101010121012101810181818181818181210101210
+% 121012101210AF100F10FF7D00000000000000003A3A3A3A3A9D3A3A9D3A
+% 9D9D9D3A3A9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F13131313
+% 13131313137F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D3A9D9D9D9DFF5353535353535353
+% 53FF3A9D3A3A9D3A9D3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D9D3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F3A3A3A9D3A
+% 3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A9D3A9D3A9D3A9D9D9D9D9D9D3A
+% 9D3A9D3A9D3A9D3A3A3A9D3AFF535353535353535353FF3A9D3A9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A7F1313131313131313137F9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D3A9D9D3A
+% 9D3A9D3A9D9D9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A3A3A
+% 00000000000021000F101010C5101818181818181A18181A181A18181818
+% 181818181818181818181816101010101012101210181216121010101000
+% FF1818181818121010101010101818181810101010101012181818181818
+% 181818181012101010121010101012101818101818181818181612101010
+% 1210101010AF100A10FF7D00000000000000003A3A3A3A3A9D3A3A9D3A9D
+% 9D9D3A3A9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF5353535353535353
+% 53FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D3A9D9D9D9DFF535353535353535353
+% FF3A9D3A3A9D3A9D3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D9D9D9D3A9D9D
+% 9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D9D3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F3A3A3A9D3A3A
+% 3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A9D3A9D3A9D3A9D9D9D9D9D9D3A9D
+% 3A9D3A9D3A9D3A3A3A9D3AFF535353535353535353FF3A9D3A9D3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A7F1313131313131313137F9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D3A9D9D3A9D
+% 3A9D3A9D9D9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A3A3A00
+% 000000000021000A101010C510121018181818181A181A18201818181818
+% 1818181818181818181818181210101010101018181818101010101000FF
+% 1818181810101010101012181816181818181818181818181A1818181810
+% 101816181818181216101210121018101210101018181810121010121010
+% 10121010AF100A08FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D
+% 9D9D9D3A9D9D3A3A9D9D9D9D3A9D9D9D9D9D3A9D9D9D3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 13137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 137F3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D3A9DFF53535353535353FF9D
+% 9D9D9D9D9D3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D3A9D9D3A
+% 9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A3A9D3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A9D3A9D3A3A3A3A9D3A3A9D3A9D3A9D9D9D3A3A9D9D9D3A
+% 9D9D9D9D3A9D3A9D9D9D3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A9D3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D
+% 9D9D3A9D9D9D3A9D9D9DFF53535353535353FF3A3A3A3A3A3A3A3A3A0000
+% 0000000021000A081010C510101012161818181818181A18181818101012
+% 10181018161818181818101010101010121818161818181818181800FF16
+% 181818101012081010101012181818181818181818181A181A1818161212
+% 181818181818161210121010121216181010121010101212161218101210
+% 101012AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D
+% 3A9D3A9D9D3A9D3A3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A7F131313131313
+% 137F3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D3A9D
+% 9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D9DFF53535353535353FF3A9D
+% 9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D9D9D3A3A9D9D9D9D
+% 3A9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D
+% 9D3A9D9D9D3A9D9D9D3AFF53535353535353FF3A3A3A3A3A9D3A3A9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A9D9D9D3A3A
+% 3A9D3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D9D9D9D
+% 9D3A9D9D9D9D9D9D9DFF53535353535353FF3A3A9D3A3A3A3A3A3A000000
+% 000000210010101010C5101010101218181818181A181818181810121012
+% 101210121216181818101012081010101012181818181818181800FF1210
+% 18121612100E0A081010101818181A18181A181818181818181812101018
+% 181818181818181010181318161818121010101010101018181618181018
+% 1216AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A
+% 9D3A9D9D3A9D3A3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A7F7F1313137F7F
+% 3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D3A9D9D
+% 9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9DFFFF535353FFFF3A3A9D9D
+% 3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D9D9D3A3A9D9D9D9D3A
+% 9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D9D
+% 3A9D9D9D3A9D9D9D3A3AFFFF535353FFFF9D3A3A3A3A3A9D3A3A9D9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A9D9D9D3A3A3A
+% 9D3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D9D9D9D9D
+% 3A9D9D9D9D9D9D9D9DFFFF535353FFFF3A3A3A9D3A3A3A3A3A3A00000000
+% 0000210010101010C5100E1010121018181818181A181818181012161210
+% 18101210121018121612100E0A081010101818181A18181A1800FF101210
+% 181818181010100A1010181818181818181818181A181818181010121012
+% 101818181810181218161818181816181010101010121818181816181216
+% 18AF121010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D
+% 3A9D3A3A9D9D3A9D3A9D3A9D9D3A9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F7F7F7F3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F7F7F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9DFFFFFF9D3A9D9D3A9D9D
+% 9D9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D9D3A9D9D9D9D
+% 3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D9D9D3A9D9D3A3A3A
+% 9D3A3A3A9D3A9D9D9D3AFFFFFFFF3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F7F3A3A3A9D3A3A3A9D3A3A3A9D3A9D
+% 3A9D3A9D3A9D3A9D3A3A3A9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D3A9D9D9D3A3A3A9DFFFFFF3A9D3A3A3A3A3A3A3A3A3A3A0000000000
+% 00210010101010C510101010101212101818181818181818181810181112
+% 101610101210181818181010100A1010181818181818181800FF10101010
+% 12101818181010101218181818181818181818181A181818101210101010
+% 121012101018101818181818181818181818181010181218101212101218
+% AF101810FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D
+% 3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F9D3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A9D3A
+% 9D9D3A9D3A9D3A9D9D3A3A9D3A9D9D9D9D9D9D9D9DFF9D9D9D3A9D3A9D3A
+% 9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A9D3A3A9D9D9D3A9D9D9D9D9D3A9D9D
+% 9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D9DFF3A9D9D9D3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A9D3A7F3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A
+% 9D3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A9D3A9D9D
+% 9D9D9D3A9D9D3A9D9D9D3AFF3A9D3A3A3A9D3A3A3A3A3A3A000000000000
+% 210018101210C51010101010101012181818181818181018101812181018
+% 121010101010121018181810101012181818181818181800FF1012101010
+% 101218181810121618181818181818181818181818181012101010101010
+% 1012101212101810181818181818181A18181812161216121010101012AF
+% 181012FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F9D3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3AFF3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A9D3A9D
+% 9D3A9D3A9D3A9D9D3A3A9D3A9D9D9D9D9D9D9D9D9DFF9D9D3A9D3A9D3A9D
+% 9D3A9D9D3A9D3A3A3A3A3A3A3A3A9D3A3A9D9D9D3A9D9D9D9D9D3A9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9DFF3A9D9D9D3A9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A9D3A7F3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D
+% 3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A9D3A9D9D9D
+% 9D9D3A9D9D3A9D9D9D3A9DFF9D3A3A3A9D3A3A3A3A3A3A00000000000021
+% 0010121010C5101010101012101012121818161210101212161216181818
+% 1210121010101012181818101216181818181818181800FF181012101010
+% 0E1816121810181618161818161818181818181818121012101010101218
+% 18181210101212161218161818181818181818181818181018121010AF10
+% 1216FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D3A
+% 3A9D9D3A3A9D3A3A3A9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A9D
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A7F7F7F7F3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A9D3A3A7F7F7F9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFFFF9D9D9D9D9D9D9D
+% 9D9D9D9D9D3A9D3A3A3A9D3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A7F7F7F3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D3A9D9D3A9D9D3A3A3A9D3A9D3A9D
+% 3A9D9D9DFFFFFFFF3A9D3A9D3A9D3A3A3A3A3A9D3A3A9D9D3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A7F7F7F7F9D3A3A9D9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D3A9D9D3A9D3A9D
+% 3A9D9D3A9D9D9D9D9D9DFFFFFF3A3A3A3A3A3A3A3A3A0000000000002100
+% 12161210C510101010101012101210121012101010101012181818181818
+% 1810121010100E181612181018161816181816181800FF181810100A100A
+% 101010101812121218181818181818181818181810101010101216181818
+% 18181012101012161218181210101018181818181818181818180AAF1012
+% 16FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D3A3A3A
+% 9D9D9D3A9D3A9D3A3A9D3A9D9D9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F7F1313137F7F3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFF535353FFFF3A9D3A9D9D9D
+% 3A9D3A9D9D3A9D3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F7F1313137F7F3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A9D9D3A9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D3A
+% 3AFFFF535353FFFF9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D
+% 9D3A9D3A9D3A9DFFFF535353FFFF3A3A3A3A3A3A3A000000000000210012
+% 161210C5101010101010101010101012101010101010121018181A181A18
+% 1810100A100A101010101812121218181818181800FF181818101010100B
+% 101012101818181818181818181010181216121012101010101218181818
+% 181A181812161218181210181818181818181816121816181818AF101210
+% FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D3A3A3A9D
+% 9D9D3A9D3A9D3A3A9D3A9D9D9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A7F131313131313137F3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3AFF53535353535353FF3A3A3A3A9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF53535353535353FF9D3A9D9D9D3A
+% 9D3A9D9D3A9D3A3A3A3A3A9D3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F131313131313137F3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A3A3A3A9D9D3A9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D3AFF
+% 53535353535353FF9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A7F131313131313137F3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D9D
+% 3A9D3A9D3AFF53535353535353FF3A3A3A3A3A3A00000000000021001210
+% 1818C5101216121818181810101210101210101010101218181818181818
+% 18101010100B1010121018181818181818181800FF181818101210101010
+% 101012101210181818181818181211181012101012101010101818181818
+% 18181210121018181012101218101818181818121012121018AF181012FF
+% 7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D3A3A9D3A3A3A3A
+% 9D9D3A3A3A3A9D3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D9D3A3A9DFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A9D
+% 3A9D3A3A3A3A3A3A9D3A9D3A3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 3A9D9D3A9D9D9D9D9D3A9D9D9D9DFF53535353535353FF9D9D9D3A9D9D9D
+% 9D9D9D9D9D9D9D3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D9D9D9D3A9D9D3A9D9D9D3A9D9D9D9D3A9D9D3A3A9D3A9DFF53
+% 535353535353FF9D3A9D9D9D3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A7F131313131313137F3A9D9D9D9D3A3A3A3A9D3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D9D3A9D3A9D
+% 3A9D3A9DFF53535353535353FF3A3A3A3A3A3A0000000000002100101216
+% 12C518181818181818181218161812101010101010101012181818181818
+% 10121010101010101210121018181818181800FF16181216121012101010
+% 121010101018181818181818181812161216121012101218181818181A18
+% 181010101010101010101010121012101210101010101018AF121012FF7D
+% 00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A3A9D
+% 9D9D3A3A3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A3A3A3A
+% 3A3A9D3A3A3A3A9D3A9D3A3A9D3A9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9DFF535353535353535353FF3A9D9D9D3A9D9D
+% 9D9D9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D
+% 9D3A9D9D9D3A9D9D9D9D9D9D9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A7F
+% 1313131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A3A9D9D9DFF535353
+% 535353535353FF9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A9D3A9D3A9D3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A
+% 3A9DFF535353535353535353FF3A3A3A3A3A000000000000210010121018
+% C51316181818181818181818181618181818101010101010181816181216
+% 121012101010121010101018181818181800FF1812161218101012101210
+% 10101010101218181818181818101812101210101217181A18181A181818
+% 1810101010101010101010101010101818121810101012AF121010FF7D00
+% 000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A3A9D9D
+% 9D3A3A3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A9D3A3A3A3A9D3A9D3A3A9D3A9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9DFF535353535353535353FF3A9D9D9D3A9D9D9D
+% 9D9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D
+% 3A9D9D9D3A9D9D9D9D9D9D9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A7F13
+% 13131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A3A9D9D9DFF53535353
+% 5353535353FF9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A9D3A9D3A9D3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A3A
+% 9DFF535353535353535353FF3A3A3A3A3A000000000000210010101216C5
+% 18181818181818181A181818121818181818121010101012181812161218
+% 1010121012101010101010121818181800FF101018111818181818181818
+% 1818181818181818181818181818111810101218181818181A181A181818
+% 16101010101010101010101010181718181816121010AF101210FF7D0000
+% 0000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D
+% 3A9D3A9D3A9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A9D3A7F1313131313131313137F3A3A3A3A9D3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 9D9D9D9D9DFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D9D9DFF535353535353535353FF9D9D9D9D9D9D3A9D9D
+% 3A9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F1313
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3A9D9D3A3A3A9D9D9D9D9D3A9D3A9D9D3A9D3A3A9D3A9D3AFF5353535353
+% 53535353FF3A3A3A9D3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F1313131313131313137F3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D
+% FF535353535353535353FF3A3A3A3A3A000000000000210012101210C516
+% 181818181818181818181010121018181818181818101012101018111818
+% 18181818181818181818181818181800FF12181018101818101818181818
+% 1A1818181818181A18181818161818131810181818181A18181818181818
+% 181012101010101010101010181818AFAFAFAFAFAFAF161816FF7D000000
+% 00000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D
+% 9D3A3A3A3A9D9D9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A3AFF53535353535353FF9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D9D9D3A3A9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D9D9D3A9D
+% 9D9D9D9D3A9D9D9D9D9DFF53535353535353FF9D9D9D9D3A9D3A9D9D9D9D
+% 9D3A9D3A9D9D3A3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A9D9D
+% 9D9D9D9D9D9D3A9D9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A7F1313
+% 13131313137F3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 9D9D9D9D9D3A9D3A9D3A9D9D3A3A3A3A3A3A3A9D3A9D9D3AFF5353535353
+% 5353FF9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D
+% FF53535353535353FF3A3A3A3A3A3A000000000000210018161216C51812
+% 18161818161818121012101012101818181A181810101012181018101818
+% 1018181818181A1818181818181A00FF1810121812161218101818181818
+% 181818181A18181818181818181810181018181818181818181818181818
+% 1018101210101010101010121210121818181810AF181818FF7D00000000
+% 000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D9D
+% 3A3A3A3A9D9D9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A3AFF53535353535353FF9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D9D3A3A9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D9D9D3A9D9D
+% 9D9D9D3A9D9D9D9D9DFF53535353535353FF9D9D9D9D3A9D3A9D9D9D9D9D
+% 3A9D3A9D9D3A3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A9D9D9D
+% 9D9D9D9D9D3A9D9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A7F131313
+% 131313137F3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A9D
+% 9D9D9D9D3A9D3A9D3A9D9D3A3A3A3A3A3A3A9D3A9D9D3AFF535353535353
+% 53FF9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9DFF
+% 53535353535353FF3A3A3A3A3A3A000000000000210018181812C5101210
+% 181218181810101810181010121218181818181810121810121812161218
+% 101818181818181818181A181800FF171816121610121612101818181818
+% 18181A18181818181818181818181018181818181A181818101812181810
+% 12101810121012101010101010101012181812AF181818FF7D0000000000
+% 0000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D3A9D
+% 3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A9D9D9D
+% 9D3A9D9DFFFF535353FFFF9D9D3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9DFFFF535353FFFF9D9D9D3A9D9D9D9D9D9D3A9D9D9D
+% 9D9D9D3A9D3A9D9D9D9D3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D3A9D9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313
+% 137F7F3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D3A
+% 9D9D9D3A9D3A9D9D9D3A9D9D9D9D3A9D3A9D3A9D3A9D9DFFFF535353FFFF
+% 9D9D9D3A9D3A9D9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A
+% 3A3A3A9D3A9D3A3A9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF
+% FF535353FFFF3A3A3A3A3A3A3A000000000000210018181816C510121012
+% 101216121818181818181010101012101818161818171816121610121612
+% 10181818181818181A18181800FF18181818121010101010121618181818
+% 181818181A1818181A181818181818181818181818181818181010101012
+% 101818181010101010101010101010181810AF181018FF7D000000000000
+% 00003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D
+% 3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A7F7F7F3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D3A
+% 3A9D9D9D3AFFFFFF3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D
+% 9D9D9D9D9D3A9D3A9D9DFFFFFF9D9D3A9D9D9D9D9D3A3A9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D3A9D9D
+% 9D9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F
+% 3A3A3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D
+% 3A3A9D9D9D9D9D9D3A9D3A3A9D3A3A3A3A3A9D3A3A3A9D3AFFFFFF9D9D3A
+% 9D9D3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F7F7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D3A3A9D3A3A3A9D9D9D9D
+% FFFFFF3A3A3A3A3A3A3A3A3A000000000000210010181318C51010101012
+% 101210101210181818181818121012101018181818181818121010101010
+% 121618181818181818181A00FF1012181010101010101010101218181818
+% 1818181818181A181A181818181818181818181818181818101210101010
+% 1812101810121010101010121012181818AF181618FF7D00000000000000
+% 003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A
+% 3A9D9D9D9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D3A3A
+% 9D9D9D3AFF3A9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D
+% 9D9D9D9D3A9D3A9D9D3A9D9DFF9D3A9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D3A9D9D9D
+% 9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A
+% 3A9D9D9D9D9D9D3A9D3A3A9D3A3A3A3A3A9D3A3A3A9D3AFF3A9D9D9D3A9D
+% 9D3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D3A3A9D3A3A3A9D9D9D9D3A
+% 9DFF3A3A3A3A3A3A3A3A3A000000000000210016181618C5121012101012
+% 101012101210121818181818101012121218121012181010101010101010
+% 1012181818181818181800FF121010101010081010101010101010121816
+% 1818181A181A181818181817181818181818181818181818101010101010
+% 10121012101018101010101012181818AF181818FF7D0000000000000000
+% 3A3A3A3A3A3A3A3A9D3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D9D9D9D9D3A3A
+% 3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A7F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D
+% 9D9DFF9D3A9D3A3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D3A3A3A9D9DFF3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D
+% 9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D
+% 3A9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D3A3A
+% 9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A3A9DFF3A3A9D9D3A3A9D3A
+% 3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A9D3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D9D9D3A9D
+% FF3A3A3A3A9D3A3A3A3A000000000000210018181818C518181810181010
+% 1210101210101012101818C5C5C5C51010101210C5C51010081010C5C5C5
+% C5C51012181618181800FF101012101010100A0F0A101010101010101216
+% 18181820181A181818181818121618181818181818181812101010101010
+% 101010181818181712101816181818AF181818FF7D00000000000000003A
+% 3A3A3A3A3A3A3A9D9D9D9D3A9D9D9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A3A
+% 9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A9D9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A9D9D9D
+% 3AFF3A9D3A9D9D3A3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A9D3A7F3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D
+% 3A3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D3A9DFF9D9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D
+% 9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D
+% 9D9D3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A3A3A3A9D9D3A3A9D3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D
+% 3A9D3A9D9D3A3A3A3A3A3A9D9D9D3A9D9D3A3A3AFF9D9D3A9D9D9D9D9D3A
+% 9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A9D9D3A9D3A9D9D9D3A9D3A3A3A3A9D3A3A3A3A9D3A3A9D3A9D3A
+% FF3A3A3A3A3A3A3A3A000000000000210018181818C51818181812101210
+% 101210121012101012C5C51618C5C510101010C5C51010100A0F0A10C5C5
+% 101010101216181800FF10121010101010100A1010080A10081010101818
+% 181A18181818181818101810181217181818181818181010101010101010
+% 1012181818181818181818181818AF181818FF7D00000000000000003A3A
+% 3A3A3A3A3A3A9D9D9D9D3A9D9D9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A3A9D
+% 9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A9D9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A9D9DFFFF
+% FF3A9D3A9D9D3A3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A9D3A7F7F7F3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A
+% 3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D9D9D3A9D9DFFFFFF3A9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D
+% 3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D9D
+% 9D3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A9D9D3A3A9D3A3A3A3A3A9D3A9D9D9D3A9D9D9D9D3A
+% 9D3A9D9D3A3A3A3A3A3A9D9D9D3A9D9D3AFFFFFF9D9D3A9D9D9D9D9D3A9D
+% 9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D7F7F7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A9D9D3A9D3A9D9D9D3A9D3A3A3A3A9D3A3A3A3A9D3A3A9D3A9D3AFF
+% FFFF3A3A3A3A3A3A000000000000210018181816C5181818181612161012
+% 1010101010101010C5C51818C5C518181012101010101010C5C5C5C5C5C5
+% 0810101018181800FF181216121818181010101010081008101010121818
+% 18181A18181818181810121010181812181812161216121010100A101010
+% 12181818181818181818181A18AF181818FF7D00000000000000003A3A3A
+% 3A3A3A3A3A9D3A9D9D3A9D3A9D9D3A3A9D9D3A3A3A9D9D9D9D3A3A9D3A9D
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F13
+% 13137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D9D9D9D9D3A3A9D9D9D3A9D9D9D9D3AFFFF535353
+% FFFF9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A9D3A3A9D3A9D3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D3A
+% 9D3A9D9D9D3A9DFFFF535353FFFF9D9D9D3A9D9D9D9D9D9D9D3A9D3A3A9D
+% 3A9D3A9D9D9D9D9D3A9D3A9D9D3A9D9D9D3A9D9D9D9D3A9D9D9D9D3A9D9D
+% 9D9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D9D9D9D
+% 9D9D9D9D3A3A9D3A3A9D3A3A9D9DFFFF535353FFFF9D9D9D9D3A9D9D9D9D
+% 3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F
+% 7F1313137F7F3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A
+% 3A3A9D9D3A9D9D9D3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D9D3A3AFFFF5353
+% 53FFFF3A3A3A3A000000000000210018181812C5C5C5C5C5C5C5C5C5C5C5
+% C5C51212161210C5C51818C5C518181812161218181810C51010C5C51008
+% 10101012181800FF18161210181618101210101010080A10101010101818
+% 181818181817121012161218101210101010121018121012101010101012
+% 181818181A18181818181818AF181818FF7D00000000000000003A3A3A3A
+% 3A3A3A3A9D9D9D9D9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D9D3A3A3A3A9D9D
+% 3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A7F13131313
+% 1313137F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D3A3A9D9D9D9DFF535353535353
+% 53FF9D9D9D9D9D3A9D9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A9D3A9D3A3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D3A9D3AFF53535353535353FF9D3A9D9D3A9D9D3A9D3A9D3A9D3A3A9D
+% 3A9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D3A3A3A9D3A3A3A9D3A3A3A9D3A7F131313131313137F3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A9D9D3A3A3A9D3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D
+% 9D3A9D3A9D3A9D9D9D3A9D9DFF53535353535353FF9D3A9D9D9D3A9D9D3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313
+% 13131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D
+% 9D3A9D9D3A9D9D3A9D9D9D3A3A9D3A9D9D9D3A9D9D3A9D9DFF5353535353
+% 5353FF3A3A3A000000000000210018181010C51818171818101810181018
+% 181818181818C5C51012C5C51618181612101816181012C510C5C5080A10
+% 101010101800FF1012101012181818181012101010101010101218181818
+% 181818181812101218121012101210121010181216121010101010101018
+% 18181A181A18181A181A18AF181810FF7D00000000000000003A3A3A3A3A
+% 3A3A3A9D9D9D9D9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D9D3A3A3A3A9D9D3A
+% 9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 13137F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D3A3A9D9D9D9DFF53535353535353
+% FF9D9D9D9D9D3A9D9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A9D3A9D3A3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D
+% 9D3A9D3AFF53535353535353FF9D3A9D9D3A9D9D3A9D3A9D3A9D3A3A9D3A
+% 9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D3A3A3A9D3A3A3A9D3A3A3A9D3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A9D9D3A3A3A9D3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D9D
+% 3A9D3A9D3A9D9D9D3A9D9DFF53535353535353FF9D3A9D9D9D3A9D9D3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F131313
+% 131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D
+% 3A9D9D3A9D9D3A9D9D9D3A3A9D3A9D9D9D3A9D9D3A9D9DFF535353535353
+% 53FF3A3A3A000000000000210018101210C5101812181012181818181818
+% 18181A1818C5C51210C5C5121810121010121818181810C5C5C510101010
+% 1012181800FF181012101210101012101210100A08101010101012101218
+% 101810121010101010181818181010101010101210121810181012121818
+% 181A1818181818181818AF181010FF7D00000000000000003A3A3A3A3A3A
+% 3A3A9D3A9D9D3A9D3A9D9D9D3A9D3A3A9D3A9D9D3A9D9D3A9D3A9D9D3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 13137F3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D9D9D9D9D9D9D3A9D9D9D9D3A3A9D9D9DFF535353535353535353
+% FF9D9D9D3A3A9D9D3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D
+% 3A9D9D9D9D3A9D9D9D3A9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D3A3A
+% 9D9DFF535353535353535353FF9D9D9D9D9D9D3A9D3A9D3A3A3A3A3A9D3A
+% 9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A
+% 3A3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A9D9D9D9D9D
+% 3A9D9D3A3A3A9D3A9DFF535353535353535353FF9D9D9D3A3A9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D
+% 9D9D9D9D9D9D3A9D9D9D3A3A9D3A3A9D9D3A9D9D9DFF5353535353535353
+% 53FF3A3A000000000000210010101010C510101010121010101012101818
+% 18181818C5C51818C5C5101818101210121010101210C5C5C50A08101010
+% 10101200FF18181810101010121012161010101010101010101010121012
+% 121010101010101018181818181810181010101012101816121816181818
+% 181818181818181818AF181010FF7D00000000000000003A3A3A3A3A3A3A
+% 3A9D3A9D9D9D9D9D9D3A3A3A9D3A9D9D3A9D9D9D3A3A3A3A9D9D3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313
+% 137F3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A9D3A9D9D9D9D9D9D3A9D9D9D9DFF535353535353535353FF
+% 9D3A9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A
+% 9DFF535353535353535353FF3A3A9D3A9D3A3A3A9D3A9D9D3A3A3A9D3A3A
+% 9D9D9D9D9D9D9D3A9D3A9D9D3A9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D3A
+% 3A3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A9D
+% 3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D
+% 3A3A9D9D3A3A9D9DFF535353535353535353FF9D3A9D9D9D9D3A9D3A9D9D
+% 3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A9D3A9D
+% 9D9D9D9D3A3A9D9D9D9D3A3A3A3A3A3A9D3A9D9DFF535353535353535353
+% FF3A3A000000000000210010101010C51010101012101210121018181718
+% 18161818C5C5C5C51818181818181010101012101216C5C5101010101010
+% 101000FF1818171818181810101210121010101010101010101010101010
+% 101010081010101218181818181818101010101010181212161818181A16
+% 1818181816181818AF101210FF7D00000000000000003A3A3A3A3A3A3A3A
+% 9D3A9D9D9D9D9D9D3A3A3A9D3A9D9D3A9D9D9D3A3A3A3A9D9D3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F131313131313131313
+% 7F3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A9D3A9D9D9D9D9D9D3A9D9D9D9DFF535353535353535353FF9D
+% 3A9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A7F1313131313131313137F3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D
+% FF535353535353535353FF3A3A9D3A9D3A3A3A9D3A9D9D3A3A3A9D3A3A9D
+% 9D9D9D9D9D9D3A9D3A9D9D3A9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D3A3A
+% 3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A9D3A
+% 9D3A9D3A9D3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A
+% 3A9D9D3A3A9D9DFF535353535353535353FF9D3A9D9D9D9D3A9D3A9D9D3A
+% 3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 13137F3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A9D3A9D9D
+% 9D9D9D3A3A9D9D9D9D3A3A3A3A3A3A9D3A9D9DFF535353535353535353FF
+% 3A3A000000000000210012101010C5101210181818101810181018121818
+% 181818181818181818181818171818181810101210121010101010101010
+% 1000FF121818181818181012101810181816121818181210101012101010
+% 10100A081010181818181818181818121010101012101012121818181818
+% 16181818181816AF101010FF7D00000000000000003A3A3A3A3A3A3A3A9D
+% 3A9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D3A3A9D9D9D3A9D3A3A
+% 3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A9D7F131313131313137F3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A9D9D9D9D3A9D9D3A9D9D9D9D9D3A3A3A3AFF53535353535353FF3A3A9D
+% 9D9D9D9D9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D7F131313131313137F3A3A3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D3A9D3A9D9D9D3A9D9D9D9D9D3A9D3A9D3A9D9D3A
+% FF53535353535353FF9D9D3A9D3A3A9D3A9D3A9D3A3A3A3A3A9D3A9D3A3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D9D9D9D9D3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A9D3A3A3A3A3A9D
+% 3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A9D9D9D9D3A3A9D9D9D
+% 3A9D9D9D3A9D9DFF53535353535353FF9D9D9D9D9D3A9D9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A9D9D3A3A3A9D9D9D9D9D3A9D
+% 3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9DFF53535353535353FF3A3A
+% 3A000000000000210010101010C512161818181818181210121012101810
+% 181818181818181818121818181818181012101810181816121818181210
+% 00FF10121012101212101012121618181818181818181818101818131610
+% 101010101210181818181818181010101010101010101010121018181818
+% 181818181818AF121010FF7D00000000000000003A3A3A3A3A3A3A3A9D3A
+% 9D9D3A9D9D9D3A9D3A3A3A9D9D3A9D3A9D9D9D3A3A9D9D3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D
+% 9D9D9D9D9D9D9D3A3A9D9D9D3A3A3A3A3AFF53535353535353FF9D3A9D9D
+% 3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A7F131313131313137F3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D3A9D
+% 9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A9D9D3A9D9DFF
+% 53535353535353FF9D9D3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D3A9D9D3A
+% 9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D9D3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D9D9D9D9D3A9D3A3A9D9D3A
+% 9D9D9D9D3A9DFF53535353535353FF9D9D9D9D9D3A9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D3A9D3A9D9D9D9D9D9D
+% 9D3A9D9D9D3A9D3A9D3A9D9D3A9D3A3A9D9DFF53535353535353FF3A3A3A
+% 000000000000210010101010C518181818181A1818181010101010121012
+% 101812161818181010121012101212101012121618181818181818181800
+% FF1210101210101010181010121818181818181A181A1818181718181218
+% 181810101810181218181012101210101818101010101010121218181818
+% 1818181818AF101210FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D
+% 9D3A9D9D9D3A9D3A3A3A9D9D3A9D3A9D9D9D3A3A9D9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D
+% 9D9D9D9D9D9D3A3A9D9D9D3A3A3A3A3A3AFFFF535353FFFF9D9D3A9D9D3A
+% 9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A7F7F1313137F7F9D3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D9D3A9D9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A9D9D3A9D9D9DFF
+% FF535353FFFF3A9D9D3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D3A9D9D3A9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 9D3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D9D9D9D9D3A9D3A3A9D9D3A9D
+% 9D9D9D3A9D9DFFFF535353FFFF3A9D9D9D9D9D3A9D9D9D3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D3A9D3A9D9D9D9D9D9D9D
+% 3A9D9D9D3A9D3A9D3A9D9D3A9D3A3A9D9D3AFFFF535353FFFF3A3A3A3A00
+% 0000000000210012101010C51818181A1818181818121010101010121010
+% 101012101818101210101210101010181010121818181818181A181A00FF
+% 1818121010121012121012101018181618181A1818181818181818171818
+% 181612121012101010181810101212181018181010101210101818181A18
+% 18181818AF161210FF7D00000000000000003A3A3A3A9D3A3A3A9D9D9D9D
+% 9D9D9D9D9D9D3A9D9D9D9D3A9D9D3A3A9D9D9D3A9D9D3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D9D3A3A3A3A3A3A3A3AFFFFFFFF3A3A9D3A9D3A3A3A9D
+% 9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A9D9D9D9D9D9D3A9D
+% 9D9D9D3A9D9D9D3A9D3A9D9D3A9D3A3A9D9D9D9D3A9D9D3A9D9D3A9D9D9D
+% FFFFFF9D9D9D9D3A3A9D3A9D3A3A9D3A3A9D3A9D3A9D3A9D3A9D3A9D3A9D
+% 9D9D9D9D9D9D3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A3A3A9D3A3A
+% 3A9D3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A9D9D3A
+% 9D9D9D9D9D3A9DFFFFFF3A3A3A9D9D9D3A9D3A9D3A9D9D9D9D9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A9D3A9D9D9D3A
+% 9D3A9D9D3A9D3A9D3A3A9D9D9D9D9D9D3A9D3AFFFFFF3A3A3A3A3A3A0000
+% 00000000210012101212C518181818181A18181810100B0E10101010100E
+% 1010101218181818121010121012121012101018181618181A181800FF18
+% 181818121010181010101012181810101818181818121012101818181818
+% 181810101010101018181012101010181818181816121612181818181818
+% 181818AF181018FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D
+% 9D9D3A9D9D3A3A9D3A9D9D3A9D3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D
+% 9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3AFF3A3A3A3A3A3A9D9D9D3A9D9D9D
+% 9D3A9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D3A9D9D9D
+% 9DFF9D3A9D3A9D3A3A9D3A9D9D3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A3A
+% 3A3A3A3A9D3A3A3A3A7F3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D9D3A
+% 3A3A3A9D3A3A3A3A9D9D9D3A9D9D3A9D9D9D3A9D9D3A3A3A3A3A9D9D9D3A
+% 9D9D9D3A9D9DFF3A9D3A3A3A9D9D9D9D9D9D3A9D9D3A9D9D9D3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D9D9D9D3A9D3A9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D3A3A9D9D9D9D3A9D3A3A3AFF3A3A3A3A3A3A000000
+% 000000210010181316C5181818181A181A181812101010101010100A100A
+% 101012161818181818121010181010101012181810101818181800FF1618
+% 18181818101818181012101010181816181818161810101018131618181A
+% 181818121010101810181210181813181818181818181818181818181818
+% 1818AF181012FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D
+% 9D3A9D9D3A3A9D3A9D9D3A9D3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D9D9D
+% 9D3A9D9D9D9D3A3A3A3A3A3A3A3A3AFF3A3A3A3A3A3A9D9D9D3A9D9D9D9D
+% 3A9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D3A9D9D9D9D
+% 9DFF3A9D3A9D3A3A9D3A9D9D3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A3A3A
+% 3A3A3A9D3A3A3A7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D9D3A3A
+% 3A3A9D3A3A3A3A9D9D9D3A9D9D3A9D9D9D3A9D9D3A3A3A3A3A9D9D9D3A9D
+% 9D9D3A9DFF3A3A9D3A3A3A9D9D9D9D9D9D3A9D9D3A9D9D9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A9D3A9D9D9D9D3A9D3A9D9D9D9D9D
+% 9D9D9D3A9D9D9D9D3A3A9D9D9D9D3A9D3A3A3A3AFF3A3A3A3A3A00000000
+% 0000210010121618C518181A181A181818181818181810100A1010101010
+% 1010121816181818181810181818101210101018181618181800FF121818
+% 181818181818171210101010121012181018121818181812161812161818
+% 1818181018101210121618181018181818181818181818181818181A1818
+% 18AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D
+% 9D9D9D3A9D3A3A9D9D3A9D3A3A9D3A9D9D9D3A3A9D9D3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D9D9D9D3A9D3A9D
+% 9D9D3A9D3A3A9D9D9D9D9DFFFFFFFF3A3A3A3A3A3A9D9D9D9D9D3A3A9D9D
+% 9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A7F7F7F3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D3A9D9D
+% FFFFFF3A9D3A3A3A9D3A3A9D9D3A9D3A3A3A3A9D3A3A9D3A3A3A9D3A9D3A
+% 9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D9D3A3A9D9D3A9D9D9D9D9D
+% 3AFFFFFF3A9D9D3A3A3A9D9D3A9D9D3A9D3A9D3A9D9D3A9D3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A7F7F7F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A9D9D3A9D3A3A3A9D9D9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3AFFFFFF3A3A3A0000000000
+% 00210018181818C518181818181818181216181812101010121010101010
+% 10101212181818181818181817121010101012101218101800FF10121612
+% 181610181818121612101012101210121618181818181818121018101216
+% 18161818181018101210121018161818181818181A181A18181818181818
+% AF181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D
+% 9D9D3A9D3A9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A
+% 9D9D9D3A3A9D9D9DFFFF535353FFFF9D3A3A3A3A3A3A3A9D9D9D9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F
+% 7F1313137F7F3A3A9D3A3A3A9D3A9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D
+% 3A9D9D3A9D9D9D3A9D9D3A9D9D3A9D9D3A9D3A9D9D9D3A9D3A9D9DFFFF53
+% 5353FFFF3A3A9D3A9D9D3A9D3A3A3A9D3A9D3A3A9D3A3A9D3A9D9D9D9D9D
+% 9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A3A9D3A3A3A3A3A3A
+% 3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D9D3A9D3A9D3A9D3A3A9D9DFFFF
+% 535353FFFF3A9D3A3A9D9D9D9D9D3A9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F9D3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A9D9D3A9D9D9D3A9D9D
+% 9D9D9D9D3A9D9D9D3A9D9D9D3A9D3A3AFFFF535353FFFF3A000000000000
+% 210018181818C51018181818181812101012161818121018101010101010
+% 101010121612181610181818121612101012101210121600FF1010121012
+% 181218161810121012101010101010121216181818161810181010121012
+% 18181818161210181012101212101818181818181818181A18181A1E18AF
+% 181818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D
+% 9D3A9D3A9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F131313131313137F3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A9D
+% 9D9D3A3A9D9DFF53535353535353FF3A3A3A3A3A3A3A9D9D9D9D9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313
+% 13131313137F3A9D3A3A3A9D3A9D3A9D9D3A9D9D9D9D9D9D3A9D9D9D9D3A
+% 9D9D3A9D9D9D3A9D9D3A9D9D3A9D9D3A9D3A9D9D9D3A9D3A9DFF53535353
+% 535353FF3A9D3A9D9D3A9D3A3A3A9D3A9D3A3A9D3A3A9D3A9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A3A9D3A3A3A3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D9D3A9D3A9D3A9D3A3A9DFF535353
+% 53535353FF9D3A3A9D9D9D9D9D3A9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D3A3A9D9D3A9D9D9D3A9D9D9D
+% 9D9D9D3A9D9D9D3A9D9D9D3A9D3AFF53535353535353FF00000000000021
+% 0018181818C5181818181818181810101210181216181810121010121010
+% 1010101210121812181618101210121010101010101200FF101010101012
+% 161218101812161210101012101210101210181218181818181810181810
+% 1212101218101210101010101210121818181818181818181A181A18AF18
+% 1818FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A
+% 9D9D9D3A9D3A9D3A9D9D3A3A9D9D9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D9D9D9D9D
+% 3A3A3A3A3AFF53535353535353FF9D9D3A3A3A3A9D3A9D9D9D9D9D3A3A9D
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D7F131313
+% 131313137F3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9DFF5353535353
+% 5353FF3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A3A9D3A9D9D3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A3A3A9D3A3A3A3A9D3A7F13
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D9D3A9D3A3A3A3A3A
+% 9D3A3A3A3A9D9D3A9D9D9D3A9D9D3A9D9D3A3A9D3A9D3A9D9DFF53535353
+% 535353FF9D3A3A9D9D9D9D9D9D9D3A9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A9D3A3A3A
+% 3A3A3A3A3A3A9D9D3A3A9D9D3A9D9D3A3A9D9D3A9D9D9D3A9D9D9D3A9D9D
+% 9D9D9D9D3A9D9D3A9D9D3A9D3AFF53535353535353FF0000000000002100
+% 18181818C51818181A181818181010121012101210121810121010101210
+% 10101010101216121810181216121010101210121000FF10101012181818
+% 161818181018161810101010101012101210101218161818181818101210
+% 101018161210121010101010101210121612181818181A18201E1AAF1A18
+% 18FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D3A3A3A
+% 9D3A9D9D3A9D9D9D9D3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D3A
+% 3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A
+% 9D3A3AFF535353535353535353FF9D3A3A3A3A3A9D9D9D9D9D9D9D3A9D9D
+% 9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A9D3A9D3A9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D9D9DFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D9D
+% 3A9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D3A3A3A9D3A3A3A3A3A7F131313
+% 1313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A
+% 3A9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D3A3A9D3A9D3A9DFF535353535353
+% 535353FF3A3A9D9D3A9D9D3A9D9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F3A3A9D3A3A
+% 3A3A3A3A9D3A3A3A9D9D3A9D9D3A3A3A9D9D9D3A9D3A3A9D9D9D9D9D3A9D
+% 3A3A3A9D3A9D3A9D9D3A9DFF535353535353535353FF0000000000210018
+% 181818C51818181818181818181812101010101018101818181812101010
+% 101012181818161818181018161810101010101000FF1810121018181818
+% 121810181818181810121012101010101012101012121018181018101010
+% 10181210101010101010101010101010101818181818181A181AAF181818
+% FF7D00000000000000003A3A3A3A3A3A3A3A9D3A9D9D9D3A9D9D3A3A3A9D
+% 3A9D9D3A9D9D9D9D3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D3A3A
+% 3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A9D
+% 3A3AFF535353535353535353FF9D3A3A3A3A3A9D9D9D9D9D9D9D3A9D9D9D
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A3A9D3A9D3A9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D9D9DFF5353535353535353
+% 53FF3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9D9D9D3A
+% 9D9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D3A3A3A9D3A3A3A3A3A7F13131313
+% 13131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D3A3A9D3A9D3A9DFF53535353535353
+% 5353FF3A3A9D9D3A9D9D3A9D9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F3A3A9D3A3A3A
+% 3A3A3A9D3A3A3A9D9D3A9D9D3A3A3A9D9D9D3A9D3A3A9D9D9D9D9D3A9D3A
+% 3A3A9D3A9D3A9D9D3A9DFF535353535353535353FF000000000021001818
+% 1816C5181818181818181818121010101010101012101818181818181810
+% 1210181818181218101818181818101210121000FF1A1818181618181816
+% 181818181618181818101010101012121012101010101218101210121010
+% 18101612101010101010101010101010101818181818181820AF1A1818FF
+% 7D00000000000000003A3A3A3A3A3A3A9D9D3A9D9D9D9D9D9D3A9D3A9D9D
+% 3A9D9D3A9D3A9D3A3A3A9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A3A9D9D9D9D9D3A9D9D9D9D3A9D3A3A3A3A
+% 3AFF535353535353535353FF9D3A3A9D3A3A9D9D9D9D9D9D9D3A9D9D9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A7F13131313131313
+% 13137F3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D3AFF535353535353535353
+% FF3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A9D3A9D3A9D3A9D3A9D9D3A9D3A3A3A9D9D3A9DFF5353535353535353
+% 53FF9D3A9D9D9D9D3A9D3A9D3A3A9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A
+% 3A3A9D9D3A9D9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D9D
+% 9D9D9D9D9D9D9D3A9DFF535353535353535353FF00000000002100181818
+% 12C5121018181810121612161010101010121010181818181818181A1818
+% 18161818181618181818161818181810101000FF181818181A1818181810
+% 181018181816181216121018101010121010101010101012101216181812
+% 18181818181010101010101010101010121018181A202118AF181818FF7D
+% 00000000000000003A3A9D3A3A3A3A3A9D3A9D9D3A9D9D3A9D3A3A9D3A9D
+% 3A9D9D9D9D9D9D3A9D3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F131313131313137F3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D3A3A3A3A3A
+% 3AFF53535353535353FF9D9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D
+% 9D9D9D9D9D9D9D3A9D3A9D9D9D9D3A9D9D9D9D3AFF53535353535353FF3A
+% 3A9D3A3A9D3A3A9D3A3A3A3A3A3A9D3A9D3A3A3A9D9D9D9D3A9D9D9D9D9D
+% 9D9D9D3A3A9D9D9D9D3A9D9D9D9D3A3A9D9D3A3A3A3A3A3A7F1313131313
+% 13137F3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A9D3A9D3A9D3A9D9D9D9D3A3A9D3A3A9D9D9DFF53535353535353FF
+% 3A3A3A9D9D9D9D3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A
+% 9D3A3A9D9D3A9D3A9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D3A3A3A3A
+% 9D9D9D9D9D9D3A9D3AFF53535353535353FF000000000000210018181016
+% C5101210101210181018181310100A08081010101216181A181818181818
+% 1A1818181810181018181816181216121000FF1818181818181A18101812
+% 121212181818181012101210101216181818181210121018101812181612
+% 1816181818181010101010101010101012181818201A18AF181212FF7D00
+% 000000000000003A3A9D3A3A3A3A3A9D3A9D9D3A9D9D3A9D3A3A9D3A9D3A
+% 9D9D9D9D9D9D3A9D3A9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F131313131313137F3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A
+% FF53535353535353FF9D9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 3A3A3A3A3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D9D
+% 9D9D9D9D9D9D3A9D3A9D9D9D9D3A9D9D9D9D3AFF53535353535353FF3A3A
+% 9D3A3A9D3A3A9D3A3A3A3A3A3A9D3A9D3A3A3A9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D3A3A9D9D9D9D3A9D9D9D9D3A3A9D9D3A3A3A3A3A3A7F131313131313
+% 137F3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A9D3A9D3A9D3A9D9D9D9D3A3A9D3A3A9D9D9DFF53535353535353FF3A
+% 3A3A9D9D9D9D3A9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D
+% 3A3A9D9D3A9D3A9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D3A3A3A3A9D
+% 9D9D9D9D9D3A9D3AFF53535353535353FF000000000000210012121812C5
+% 10181012101810121816181810101008101010181818181A181818181818
+% 181A181018121212121818181810121000FF1A181A181A18181818121010
+% 101010181210101012101216121818181818181818181812161812101210
+% 1212161818101010121010100E101010121818181818AF101010FF7D0000
+% 0000000000003A3A3A3A3A3A3A3A9D9D9D9D9D9D9D3A3A9D3A3A9D9D3A9D
+% 3A9D3A9D9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F7F1313137F7F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D3A9D9D9D3A3A3A3A3A3A3A9D
+% FFFF535353FFFF9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D7F7F1313137F7F3A3A
+% 3A3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 3A9D3A9D9D9D9D9D3A9D9D9D9D9D3A9D9D9D9DFFFF535353FFFF3A9D3A3A
+% 3A9D3A3A9D3A3A3A3A3A9D9D3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D9D3A9D3A9D9D9D9D3A3A9D9D3A9D3A3A3AFFFF535353FFFF9D3A3A
+% 3A9D3A9D9D9D9D9D9D3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D3A3A3A3A3A3A3A3A9D
+% 9D9D9D9D9D9D3A3A3A3A9D9D9D3A9D9D9D3A9D3A3A9D9D9D9D3A3A3A9D3A
+% 9D3A9D9D3A9D3A3AFFFF535353FFFF3A000000000000210010101210C518
+% 1818181012101210181818181210101010121818181A18181A181A181A18
+% 18181812101010101018121010101200FF18181818181818181810101010
+% 101018181012101010121012161818181818181818181818131612101010
+% 101212181012101010101010101010101216181812AF121012FF7D000000
+% 00000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A3A9D3A3A9D9D3A
+% 9D9D9D3A3A9D3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A9D9D
+% 9DFFFFFF3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A
+% 3A3A3A3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D3A9D9D9D3A9D9D9D3A9D3A3A9D3A9D9D9D9DFFFFFF3A3A3A3A3A9D9D
+% 3A3A9D3A3A9D3A3A3A3A3A9D3A3A3A9D9D3A9D3A3A9D9D3A9D9D9D9D9D9D
+% 9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A3A3A3A3A9D3A3A3A3A7F7F7F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D9D9D3A3A9D9D9D9D3A3A3A3A9D9D9D9D9D9D9D9DFFFFFF9D3A3A9D3A3A
+% 9D9D9D3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D9D3A9D3A9D3A3A9D3A9D3A9D9D9D9D3A3A9D9D9D9D3A9D3A9D9D3A9D3A
+% 9D9D9D3A9D3A3A3A3AFFFFFF3A3A3A000000000000210010121012C51818
+% 18181812101010101218181818121012101818181A181818181818181818
+% 181810101010101018181012101000FF1818181818181818121010101010
+% 101818101010101218101012181818181818181818181816181818121010
+% 1010121612181010121010101010101210121010AF101010FF7D00000000
+% 000000003A3A3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A3A9D3A3A9D9D3A9D
+% 9D9D3A3A9D3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A9D9D9D
+% FF3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A
+% 3A3A3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D3A9D9D9D3A9D9D9D3A9D3A3A9D3A9D9D9D9D3A9DFF3A3A3A3A3A9D9D3A
+% 3A9D3A3A9D3A3A3A3A3A9D3A3A3A9D9D3A9D3A3A9D9D3A9D9D9D9D9D9D9D
+% 9D3A9D9D3A9D9D9D9D9D3A3A9D3A3A3A3A3A9D3A3A3A7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9D
+% 9D9D3A3A9D9D9D9D3A3A3A3A9D9D9D9D9D9D9D9DFF9D9D9D3A3A9D3A3A9D
+% 9D9D3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 9D3A9D3A9D3A3A9D3A9D3A9D9D9D9D3A3A9D9D9D9D3A9D3A9D9D3A9D3A9D
+% 9D9D3A9D3A3A3A3A3A3A3AFF3A3A000000000000210010101012C5181818
+% 18181810121010101010121618121618181818181A181818181818181818
+% 1210101010101018181010101000FF181818181818181810121010101010
+% 181618181216121010101210181810121818181818181818181818161210
+% 10101812161210101010101012101012101012AF101010FF7D0000000000
+% 0000003A3A3A3A3A3A3A9D3A3A9D9D3A9D9D3A3A9D3A3A9D3A9D3A9D3A9D
+% 3A9D3A3A3A3A9D9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A
+% 3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A3A3A9D9DFF3A
+% 3A3A3A9D9D9D9D3A3A3A3A9D3A9D3A9D3A9D9D3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A7F3A3A3A9D3A3A
+% 9D3A9D9D9D3A9D9D9D9D3A9D9D9D3A9D9D9D9D3A9D9D3A9D9D9D3A9D3A9D
+% 9D9D9D3A9D9D9D9D3A9D3A3A9D9D9D3A9D3A9D9DFF9D9D3A3A9D3A3A3A9D
+% 3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D9D9D9D9D9D9D9D3A3A3A9D3A3A3A3A3A3A9D3A3A7F3A3A9D3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A9D9D3A9D
+% 9D9D9D3A3A9D9D9D3A3A9D3A3A9D3A9D9D9DFF9D9D3A3A3A3A3A3A3A9D9D
+% 3A9D9D3A3A9D9D3A3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D7F3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D9D
+% 9D3A3A9D3A9D9D3A9D9D9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D
+% 9D3A9D3A3A3A3A3A3A3AFF3A3A000000000000210010101010C51818181A
+% 181812101010100808101012101812171818181818181818181818181810
+% 12101010101018161818121600FF12161818101810181210121010101212
+% 181818181812101010121613101210181618181818181818181818181010
+% 101216121012101216181810181012101216AF101010FF7D000000000000
+% 00003A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D9D3A9D9D9D9D9D9D
+% 9D9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D9D3A3A3A9D3A3A3A9D9DFF9D9D
+% 9D9D9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A7F3A3A3A3A3A3A3A
+% 3A9D3A9D3A3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D3AFF3A9D3A3A9D3A3A9D3A
+% 9D9D9D9D3A9D3A3A9D3A3A3A9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D3A9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A9D7F3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D
+% 3A3A9D9D3A9D9D3A3A9D3A9D9D9D3A9D9DFF3A3A3A3A3A9D3A3A9D9D9D9D
+% 3A9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D
+% 3A9D3A3A3A9D3A3A3AFF3A3A000000000000210010101010C51818181818
+% 181818101010100A10101010121612181818181812161818101810181210
+% 121010101212181818181800FF1012101012161210101010121012161818
+% 181A18181210181218181818101218121216181818181218181818181012
+% 1018101810181012181818181812161218AF101010FF7D00000000000000
+% FFFFFF3A3A3A3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D
+% 9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A7F7F7F
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D9D3A3A3A9D3A3A3AFFFFFF9D9D9D
+% 9D9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A7F7F7F3A3A3A3A3A
+% 9D3A9D3A3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D3AFFFFFF3A3A9D3A3A9D3A9D
+% 9D9D9D3A9D3A3A9D3A3A3A9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D3A9D9D9D9D9D3A3A9D3A9D3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D3A
+% 3A9D9D3A9D9D3A3A9D3A9D9D9D3AFFFFFF3A3A3A3A3A9D3A3A9D9D9D9D3A
+% 9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A
+% 9D3A3A3A9D3A3A3A3AFFFFFF0000000000210010101010C51818181A2018
+% 1818101210101010100A1012101818181818121012101012161210101010
+% 121012161818181A181800FF181818181810121612161216121818181818
+% 1A1818181010101010121018101010101218181818101210101010181810
+% 12101812181816181A18181818181618AF181818FF7D0000000000FFFF53
+% 5353FFFF3A9D3A3A9D3A9D9D9D9D9D3A3A9D9D3A3A9D9D9D3A9D3A9D9D9D
+% 9D3A3A3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F
+% 7F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A9D9D9D9D9D3A9D9D9D9D9D3A3A3A3A3AFFFF535353FFFF9D9D
+% 9D3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A3A9D9D3A9D3A9D3A9D9D3AFFFF535353FFFF3A3A9D3A9D9D9D
+% 9D9D9D9D3A3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D9D9D9D9D3A9D3A9D3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A9D9D3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D3A9D
+% 3A9D3A3A9D9D3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A9D9D3A3A9D9D9D
+% 9D9D3A9D9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A
+% 3A3A9D3A9D9D9D9D3A9D3A9D9D3A9D9D3A9D9D9D9D9D3A9D3A9D9D9D3A9D
+% 3A3A3A3A3A3AFFFF535353FFFF000000210018181010C510101216181818
+% 181810121010101010101010101012101818181818181810121612161216
+% 1218181818181A181800FF1A1818181012101810121012AF181818181818
+% 181818181010101010101010101010101216181818101012101218181010
+% 1216121618181818181A1818181818AF181816FF7D00000000FF53535353
+% 535353FF3A3A3A9D3A9D3A9D9D9D3A3A3A3A9D3A3A9D9D3A3A9D9D3A9D9D
+% 9D9D3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D9D9D3A9D3A9D9D9D3A3A3A3AFF53535353535353FF3A9D
+% 9D9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A9D3A
+% 9D3A3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A
+% 9D3A9D9D9D9D3A9D9D9D9D9D9D3AFF53535353535353FF3A9D9D3A9D9D9D
+% 3A9D3A9D9D3A3A3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A9D3A9D3A3A3A7F131313131313137F3A3A9D3A3A3A3A3A3A3A
+% 3A9D3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D
+% 9D9D9D9D9D9D3A9D9DFF53535353535353FF9D9D3A9D9D9D9D9D3A9D9D3A
+% 9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D
+% 3A3A3A3A9D9D9D9D9D3A9D9D3A9D3A9D3A9D9D3A9D9D3A9D9D9D9D3A9D3A
+% 3A3A3A3AFF53535353535353FF0000210018161818C51010101218101810
+% 18121010101012101010101210121618181A181818101210181012101216
+% 181818181818181800FF18181818181118131612161218AFAF1818181A18
+% 181818101010101010100A1010101010AFAFAFAFAFAF1012161218101210
+% 1813181818181818181818181810AF161318FF7D00000000FF5353535353
+% 5353FF3A3A3A9D3A9D3A9D9D9D3A3A3A3A9D3A3A9D9D3A3A9D9D3A9D9D9D
+% 9D3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D9D9D3A9D3A9D9D9D3A3A3A3AFF53535353535353FF3A9D9D
+% 9D9D3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A9D3A9D
+% 3A3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D
+% 3A9D9D9D9D3A9D9D9D9D9D9D3AFF53535353535353FF3A9D9D3A9D9D9D3A
+% 9D3A9D9D3A3A3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D3A9D3A9D3A3A3A7F131313131313137F3A3A9D3A3A3A3A3A3A3A3A
+% 9D3A3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D
+% 9D9D9D9D9D3A9D9DFF53535353535353FF9D9D3A9D9D9D9D9D3A9D9D3A9D
+% 9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A
+% 3A3A3A9D9D9D9D9D3A9D9D3A9D3A9D3A9D9D3A9D9D3A9D9D9D9D3A9D3A3A
+% 3A3A3AFF53535353535353FF0000210013181818C5121010121010121018
+% 101012101216181618121618181818181818181818111813161216121818
+% 181818181A181800FF1A1818181818161818121012161818AFAF1A181818
+% 1818101010101010101010100810AFAFAFAFAFAFAFAF1018161216121810
+% 18101810181818181818181010AF121818FF7D000000FF53535353535353
+% 5353FF3A9D9D9D9D9D9D3A9D9D3A3A9D9D3A3A9D9D3A3A9D9D3A9D9D9D9D
+% 9D3A9D9D9D3A3A3A3A9D3A3A3A3A3A9D3A3A9D7F1313131313131313137F
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A9D9D3A9D9D3A9D9D9D3A3A3AFF535353535353535353FF9D9D9D
+% 9D3A3A3A3A3A3A9D9D9D3A9D9D9D9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A9D3A9D3A
+% 3A9D9D9D9D3A9D9D9D9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D3A9D3A9D9D9D9D3A3AFF535353535353535353FF9D9D9D9D9D9D9D9D
+% 3A9D3A9D3A9D3A9D3A9D3A9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D3A9D3A9D3A3A7F1313131313131313137F3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A9D9D3A3A3A3A9D3A3A3A3A3A9D3A9D9D3A3A9D3A3A9D
+% 9D3A9D9D9D9DFF535353535353535353FF9D9D9D9D3A9D9D3A3A9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F1313131313131313137F3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A9D9D9D9D3A9D9D3A3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A
+% 3AFF535353535353535353FF00210018181818C518181010101012101210
+% 1210121618181818181818181818181A1818181818161818121012161818
+% 1A181A18181800FF1818181818181818171216181818181818AFAFAFAFAF
+% 18111812101210101010101008AF101018181818AF181210121018101810
+% AFAFAFAFAFAFAFAFAFAFAFAFAF181618FF7D000000FF5353535353535353
+% 53FF3A3A9D3A9D9D3A9D9D3A3A9D3A3A3A9D9D3A3A9D9D9D9D3A3A9D9D9D
+% 9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A7F1313131313131313137F3A
+% 3A3A3A3A3A9D9D9D9D9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A9D3A9D9D3A9D3A9D9D3A9D3A3AFF535353535353535353FF9D9D9D9D
+% 3A3A3A3A3A3A3A9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F3A3A3A9D3A3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A3A9D
+% 9D9D3A9D3A3A9D3A9D9DFF535353535353535353FF3A9D9D3A9D9D3A3A3A
+% 9D3A3A9D3A9D3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D9D
+% 3A3A9D3A9D3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A9D9D9D9D9D9D3A9D
+% 9D9D3A9D9DFF535353535353535353FF9D9D3A3A9D9D3A9D9D9D3A9D9D3A
+% 3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D9D7F1313131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A
+% 9D9D9D3A3A9D9D9D9D3A9D9D3A9D9D3A9D9D3A9D9D3A9D9D3A9D3A3A3A3A
+% FF535353535353535353FF00210016181818C5C5C5C5C5C5C51018101810
+% 121018181818181818181818181818181818181818181712161818181818
+% 18181A181800FF18181818181A181818181218181818AFAF181818181618
+% 181612161810121012101010AF101010121816AF18181018181316131810
+% 1210101216121818121618AF181818FF7D000000FF535353535353535353
+% FF3A9D3A9D9D3A9D9D9D9D3A3A3A3A9D9D9D9D3A3A9D9D9D3A9D9D9D9D9D
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A
+% 3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 9D9D9D9D3A3A9D9D3A9D9D9D3A3AFF535353535353535353FF9D9D9D9D3A
+% 3A9D3A3A9D9D9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A9D3A3A3A3A9D
+% 9D9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D3A9D3A
+% 9D9D3A9D9D9D9D9D3AFF535353535353535353FF3A9D3A9D3A3A9D3A3A3A
+% 3A9D3A3A3A9D3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D9D9D3A9D9D9D9D9D3A
+% 3A9D3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A3A9D3A9D9D
+% 3A9D3A9DFF535353535353535353FF3A3A9D9D9D3A9D3A9D9D9D9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A
+% 3A7F1313131313131313137F3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D9D9D3A
+% 3A3A3A9D3A9D3A9D3A9D3A9D3A9D3A9D9D9D3A9D3A9D9D3A9D3A3A9D3AFF
+% 535353535353535353FF00210018181018C5181818181718181812101012
+% 1012121818181818181818181818181818181A1818181812181818181818
+% 1818181800FF18181A18181818181818171818AFAF121818181818121210
+% 1210121012161210121012AFAFAFAFAFAFAFAF1613101216181812161210
+% 12101012161818101818AF181818FF7D00000000FF53535353535353FF3A
+% 3A9D3A9D9D3A9D9D9D9D3A3A3A3A9D9D9D9D3A3A9D9D9D3A9D9D9D9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A
+% 9D3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D
+% 9D9D9D3A3A9D9D3A9D9D9D3A3A3AFF53535353535353FF3A9D9D9D9D3A3A
+% 9D3A3A9D9D9D3A3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A9D3A3A3A3A9D9D
+% 9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D3A9D3A9D
+% 9D3A9D9D9D9D9D3A9DFF53535353535353FF3A3A9D3A9D3A3A9D3A3A3A3A
+% 9D3A3A3A9D3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D9D9D3A9D9D9D9D9D3A3A
+% 9D3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A3A9D3A9D9D3A
+% 9D3A9D9DFF53535353535353FF3A3A3A9D9D9D3A9D3A9D9D9D9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A
+% 3A7F131313131313137F3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D9D9D3A3A
+% 3A3A9D3A9D3A9D3A9D3A9D3A9D3A9D9D9D3A9D3A9D9D3A9D3A3A9D3A3AFF
+% 53535353535353FF0000210018181612C518181818181816121612101010
+% 10121018181818181A18181818181A181818181818181718181818121818
+% 18181800FF1818181818181A1818181818AF181818161818161810101010
+% 1010101210121816121010AFAFAFAFAFAF10181810121818181018101010
+% 101010121810181018AF161818FF7D00000000FF53535353535353FF3A3A
+% 3A9D3A9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D3A9D9D9D3A9D3A9D9D3A9D9D
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A
+% 3A3A9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D3A9D9D3A9D9D9D9D3A3A3A3AFF53535353535353FF3A9D9D9D9D3A3A3A
+% 3A3A9D9D9D9D9D9D9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A9D3A9D9D9D
+% 9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D
+% 3A3A9D3A9D9D3A9DFF53535353535353FF9D3A3A9D3A9D9D3A3A3A3A9D3A
+% 3A9D3A9D3A9D3A9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A3A9D
+% 3A9D3A3A3A7F131313131313137F3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D3A9D9D9D3A9D3A
+% 9D9D3AFF53535353535353FF3A9D9D9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A
+% 7F131313131313137F3A3A9D3A3A3A3A3A9D3A3A3A3A3A9D9D3A3A3A3A3A
+% 9D9D3A9D3A9D3A3A9D9D9D3A9D3A9D3A9D9D9D9D9D3A9D3A3A3A3A3AFF53
+% 535353535353FF0000210018181818C51018181818161210181012101010
+% 1010181612181818181A181818181818181A181818181818181818161818
+% 161800FF181818181A181818181818181818181818181818181818101012
+% 101010101018121810181010101012161818181818161817181818181810
+% 1010101012181810AF121818FF7D0000000000FFFF535353FFFF9D3A3A9D
+% 3A9D9D9D9D9D9D9D3A3A3A9D3A3A9D9D3A3A3A9D9D3A3A9D9D3A9D3A9D9D
+% 9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F9D3A3A3A3A3A3A
+% 3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D
+% 9D9D3A9D9D9D9D9D9D9D3A3A3AFFFF535353FFFF3A3A9D9D9D9D3A3A3A3A
+% 9D3A9D9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A9D3A9D3A9D3A9D9D
+% 9D9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A9D
+% 3A3A9D9D3A9D9D3AFFFF535353FFFF3A3A3A9D3A9D3A9D3A3A3A3A3A9D3A
+% 3A3A9D3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D3A
+% 3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A9D9D9D3A9D9D9D9D9D3A
+% 9D9D9DFFFF535353FFFF9D9D9D3A9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A3A3A
+% 7F7F1313137F7F3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A9D3A9D3A3A9D9D3A
+% 3A9D9D9D9D3A3A3A9D9D3A9D3A3A9D9D9D3A9D9D3A9D3A3A9D3A3A3AFFFF
+% 535353FFFF000000210018181818C5101210121012101218161210101010
+% 10101010101216181810181818181A181818181818181818181818181818
+% 1800FF1818181A1E181A1818181612101218181818181818101810121010
+% 101010101218171818101210101018181818181818181818171818161012
+% 10121012101010AF101010FF7D00000000000000FFFFFF3A3A9D3A3A9D3A
+% 9D9D9D9D9D9D9D3A3A3A9D3A3A9D9D3A3A3A9D9D3A3A9D9D3A9D3A9D9D9D
+% 9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D7F7F7F3A3A9D3A3A3A3A3A3A3A
+% 3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D9D
+% 9D3A9D9D9D9D9D9D9D3A3A3A3A3AFFFFFF9D3A3A3A9D9D9D9D3A3A3A3A9D
+% 3A9D9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A9D3A9D3A9D3A9D9D9D
+% 9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A
+% 3A9D9D3A9D9D3A9D3AFFFFFF9D9D3A3A3A9D3A9D3A9D3A3A3A3A3A9D3A3A
+% 3A9D3A9D3A9D9D9D3A9D3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D3A3A
+% 3A3A3A3A9D3A7F7F7F3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A9D9D9D3A9D9D9D9D9D3A9D
+% 9D9D9D9DFFFFFF3A9D9D9D9D3A9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A3A3A3A
+% 3A7F7F7F9D3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A9D3A9D3A3A9D9D3A3A
+% 9D9D9D9D3A3A3A9D9D3A9D3A3A9D9D9D3A9D9D3A9D3A3A9D3A3A3A3A3AFF
+% FFFF0000000000210010101012C512161216121810181012101010101010
+% 1010101010121810181818181A1E181A1818181612101218181818181818
+% 00FF1818181A181A18181812101210101012161218101818181010101010
+% 101010121612181818181812181818181818181818181818181818181816
+% 121010121012AF101010FF7D00000000000000003AFF3A3A3A3A3A3A9D3A
+% 9D9D9D9D9D3A3A3A3A9D3A9D9D9D3A3A9D9D9D3A9D9D9D9D3A9D9D9D9D9D
+% 9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D9D9D9D9D9D9D9D3A3A3A9D9DFF9D9D9D3A3A9D9D9D9D3A3A3A3A3A9D9D
+% 9D9D9D9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A9D3A9D3A9D9D9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D9D9D3A9D3A9D9D
+% 9D9D9D9D3A9D3A9D3A3AFF3A3A3A3A9D3A3A3A9D9D3A3A3A9D3A9D3A9D3A
+% 3A3A9D3A9D3A9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A
+% 3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 3A3A9D3A3A9D3A3A3A9D3A3A3A9D3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D3A
+% 3A9D3AFF3A9D9D9D3A9D9D9D9D9D3A9D3A9D9D3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D9D3A3A3A3A
+% 3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A9D3A9D3A9D
+% 3A9D9D3A9D3A9D3A3A9D3A9D9D3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 000000000000210010101010C51012101210101012101210101210100810
+% 100F1010101012181818181A181A18181812101210101012161218101800
+% FF1818181A18181A18181810101010121012101212161218101010100A10
+% 10101012181818181818181818181A181818181818161812181818181818
+% 1012101018AF101010FF7D00000000000000003AFF3A3A3A3A3A9D3A9D9D
+% 9D9D9D3A9D3A3A9D9D3A3A9D9D3A9D3A9D9D9D3A3A3A9D9D3A9D9D3A9D9D
+% 9D3A3A3A3A3A3A3A3A3A9D3A3A3A7F3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D9D3A
+% 3A9D9D9D9D9D9D9D3A3A3AFF3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D9D
+% 9D9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A9D3A3A9D3A3A3A3A3A3A7F3A3A3A9D3A3A9D3A9D3A9D9D9D9D9D3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D3A9D3A9D3A9D9D3A
+% 9D9D9D3A9D3A9D3A3AFF3A3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D9D
+% 3A9D3A9D3A9D3A3A9D3A9D3A3A9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A3A3A
+% 3A3A3A7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D9D9D3A9D9D3A9D9D9D
+% 3AFF9D9D9D9D3A3A9D9D9D9D9D3A9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A
+% 3A7F3A3A3A3A3A9D3A3A9D3A3A9D3A3A9D9D3A3A3A3A9D3A9D9D3A9D9D9D
+% 9D9D3A3A3A9D9D9D3A9D3A9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00
+% 0000000000210010101010C51216131810181218161818181010100A100B
+% 101010101010181818181A18181A181818101010101210121012121600FF
+% 1818181A181A181818181210121010121010101012101218181010101008
+% 10121618181A181A181818181A1818181818181012121816181212181018
+% 16101018AF181818FF7D00000000000000003A3AFF3A3A3A3A9D3A9D9D9D
+% 9D9D3A9D3A3A9D9D3A3A9D9D3A9D3A9D9D9D3A3A3A9D9D3A9D9D3A9D9D9D
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A7F3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A
+% 9D9D9D9D9D9D9D3A3A3AFF3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D9D9D
+% 9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A9D3A3A9D3A3A3A3A3A3A9D7F3A3A9D3A3A9D3A9D3A9D9D9D9D9D3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D3A9D3A9D3A9D9D3A9D
+% 9D9D3A9D3A9D3A3A3AFF3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D9D3A
+% 9D3A9D3A9D3A3A9D3A9D3A3A9D9D9D9D3A9D9D9D9D9D3A9D3A9D3A3A3A3A
+% 3A3A7F3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D9D9D3A9D9D3A9D9D9D3A
+% FF9D9D9D9D3A3A9D9D9D9D9D3A9D9D9D9D3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A
+% 3A7F3A3A3A3A9D3A3A9D3A3A9D3A3A9D9D3A3A3A3A9D3A9D9D3A9D9D9D9D
+% 9D3A3A3A9D9D9D3A9D3A9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000
+% 00000000210018181010C512161216101316101818181818181010100E10
+% 1010101010181818181A181A18181818121012101012101010101200FF10
+% 181818181818181818161216121810181018101210101818161010101008
+% 1012161818181818181A1818181818181810121010101218101010121012
+% 181818AF18181AFF7D00000000000000003A3AFFFFFF3A3A3A9D3A9D9D3A
+% 3A3A9D9D3A9D9D3A3A3A9D3A3A3A9D9D3A9D3A9D9D3A9D3A3A9D9D9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 9D9D9D9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D9D9D3A3A9D
+% 9D9D9D9D9D9D9DFFFFFF3A9D3A3A3A3A3A3A3A9D3A3A3A9D9D9D9D9D9D9D
+% 9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A9D3A9D3A9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D9D3A9D3A9D9D3A9D3A9D3A9D9D9D3A9D9D3A9D3A9D9D9D9D3A
+% 3A9D3A3A3A3A9D3AFFFFFF3A3A3A3A9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D9D9D9D3A9D9D9D3A9D9D9D9D3A9D9D9D3A9D3A3A3A3A3A3A7F
+% 7F7F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A9D9D9D9D3A9D9D9D9DFFFFFF
+% 9D9D3A9D3A3A3A3A9D3A3A9D9D9D9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A3A
+% 7F7F7F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A9D9D3A3A9D3A9D9D9D
+% 3A9D3A9D9D9D3A3A3A9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000
+% 0000002100181A1818C51818121818181218181818181818161210101010
+% 101010101210181818181818181818161216121810181018101200FF1012
+% 161818181818101210121018181818181312101010101212181810121012
+% 101818181818181818181A18181818101210101010121610101210121012
+% 1018AF181818FF7D0000000000000000FFFF535353FFFF9D3A3A9D9D9D9D
+% 9D9D3A9D9D9D3A3A9D9D3A9D3A9D9D3A3A3A3A9D3A3A3A9D3A3A9D9D9D9D
+% 3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D
+% 9D9D3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D3A9D3A
+% 9D3A9D9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A7F7F1313137F7F3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D3A3A9D9D3A3A9D
+% 3A9D3A9D3AFFFF535353FFFF9D3A9D3A9D9D3A3A3A9D3A3A9D3A9D3A9D3A
+% 3A9D3A3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D3A9D3A9D3A3A3A3A7F7F1313
+% 137F7F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A
+% 3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D9D9D3AFFFF535353FF
+% FF9D9D9D9D9D3A3A9D9D9D3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A9D
+% 3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D3A7F7F13
+% 13137F7F3A3A3A3A9D3A3A9D3A9D3A3A3A9D3A9D9D3A9D9D9D3A9D3A9D3A
+% 3A3A9D9D3A9D3A3A3A9D3A9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A00000000
+% 0000210018181818C5101816181018181612101010121818181010101010
+% 1010101010121618181818181012101210181818181813121000FF101218
+% 161818101010101010101818181818181010101010101618181816101018
+% 181818181818181818181818181012101010101010121010101210101210
+% 18AF121612FF7D00000000000000FF53535353535353FF3A3A9D9D9D9D9D
+% 9D3A9D9D9D3A3A9D9D3A9D3A9D9D3A3A3A3A9D3A3A3A9D3A3A9D9D9D9D3A
+% 3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D
+% 9D3AFF53535353535353FF3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D3A9D3A9D
+% 3A9D9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D7F131313131313137F3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D3A3A9D9D3A3A9D3A
+% 9D3A9DFF53535353535353FF3A9D3A9D9D3A3A3A9D3A3A9D3A9D3A9D3A3A
+% 9D3A3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D3A9D3A9D3A3A3A7F1313131313
+% 13137F3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D9D9DFF53535353535353
+% FF9D9D9D9D3A3A9D9D9D3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A
+% 3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D7F13131313
+% 1313137F3A3A3A9D3A3A9D3A9D3A3A3A9D3A9D9D3A9D9D9D3A9D3A9D3A3A
+% 3A9D9D3A9D3A3A3A9D3A9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A0000000000
+% 00210016121818C518181218181810181010101010101218101010121010
+% 10101010121816181810101010101010181818181818101000FF10101012
+% 101012101010101012181818181818181010080A10101012181818181818
+% 181818181218181818171812181010101010101012181810101012161818
+% AF181618FF7D00000000000000FF53535353535353FF9D9D9D9D3A9D3A9D
+% 9D9D3A9D3A3A9D9D3A3A9D9D9D3A3A9D9D3A3A9D3A3A3A3A9D3A3A9D3A3A
+% 3A9D3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A
+% 3AFF53535353535353FF3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D9D9D9D9D9D
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A7F131313131313137F3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D9D9D3A9D9D9D9D9D3A3A3A3A9D9D9D9D3A9D3A3A9D9D3A9D9D9D3A3A3A
+% 9D3AFF53535353535353FF3A9D3A9D3A3A3A3A9D3A3A3A9D3A3A3A9D3A9D
+% 3A9D3A3A9D3A9D9D9D9D9D9D9D3A9D9D9D3A3A9D3A3A3A7F131313131313
+% 137F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D9DFF53535353535353FF
+% 3A9D3A9D9D9D9D9D9D3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D7F1313131313
+% 13137F9D3A3A3A3A3A9D3A3A3A3A3A9D3A9D9D9D3A9D9D3A9D9D3A3A9D3A
+% 9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000
+% 210016181618C51A18181810121818181812101010101018121610101210
+% 101010101012101012101010101012181818181818181000FF1210121018
+% 101810101010101012161818181A18181010101010101010101018181818
+% 1818181618181818181018101210101010101218181818161210121018AF
+% 181818FF7D000000000000FF535353535353535353FF3A9D9D3A9D9D9D9D
+% 9D9D9D3A3A9D9D3A3A9D9D9D3A9D3A9D9D9D9D3A3A3A3A9D9D9D9D3A3A3A
+% 3A3A7F1313131313131313137F3A9D3A3A3A3A9D3A3A9D9D9D3A3A3A9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9DFF
+% 535353535353535353FF3A3A3A3A3A9D9D3A9D9D9D3A3A9D3A3A9D3A9D3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F1313131313131313137F3A3A3A9D9D9D9D9D9D9D9D9D3A9D3A9D
+% 9D3A9D3A9D3A3A3A3A3A3A9D3A3A9D3A9D9D3A9D3A9D9D3A3A9D3A3A3A9D
+% FF535353535353535353FF3A9D3A9D3A9D3A3A3A3A9D3A3A3A9D3A3A9D3A
+% 3A3A9D3A9D9D9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A7F1313131313131313
+% 137F3A3A3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3AFF535353535353535353FF
+% 9D9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A9D9D7F13131313131313
+% 13137F3A3A3A3A9D9D3A9D3A3A3A9D9D3A3A3A3A9D9D9D3A9D9D3A3A3A9D
+% 9D9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021
+% 0018181818C5181818181816181818181710101210121612101210101818
+% 101210121018101810101010101012161818181A181800FF181818181818
+% 181810121210121012181818181818181010101010101010101012161818
+% 18181818171812161210121010121012181818181818181810121018AF18
+% 1818FF7D000000000000FF535353535353535353FF3A9D9D3A9D9D9D9D9D
+% 9D9D3A3A9D9D3A3A9D9D9D3A9D3A9D9D9D9D3A3A3A3A9D9D9D9D3A3A3A3A
+% 3A7F1313131313131313137F3A9D3A3A3A3A9D3A3A9D9D9D3A3A3A9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9DFF53
+% 5353535353535353FF3A3A3A3A3A9D9D3A9D9D9D3A3A9D3A3A9D3A9D3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F1313131313131313137F3A3A3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D
+% 3A9D3A9D3A3A3A3A3A3A9D3A3A9D3A9D9D3A9D3A9D9D3A3A9D3A3A3A9DFF
+% 535353535353535353FF3A9D3A9D3A9D3A3A3A3A9D3A3A3A9D3A3A9D3A3A
+% 3A9D3A9D9D9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A7F131313131313131313
+% 7F3A3A3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3AFF535353535353535353FF9D
+% 9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A9D9D7F1313131313131313
+% 137F3A3A3A3A9D9D3A9D3A3A3A9D9D3A3A3A3A9D9D9D3A9D9D3A3A3A9D9D
+% 9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100
+% 18181818C518181818181811181218181818161210181018101818181818
+% 18181818181818181012121012101218181818181800FF1A181818181818
+% 181818101010101012181818181818101810121010101010101210181218
+% 18181818161218101210101010101018181818181A181810101012AF1216
+% 12FF7D000000000000FF535353535353535353FF3A9D9D9D9D9D9D3A9D3A
+% 9D9D3A3A9D3A3A9D9D9D3A9D9D3A9D9D3A9D9D9D3A9D9D3A9D3A3A3A3A3A
+% 7F1313131313131313137F3A3A3A9D3A3A3A3A3A3A9D9D9D9D9D9D3A3A3A
+% 3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D3A3A9DFF5353
+% 53535353535353FF9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D3A
+% 3A7F1313131313131313137F3A9D3A3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D
+% 9D3A9D3A9D9D9D9D9D3A3A9D3A3A9D9D9D3A3A3A3A9D9D3A9D3A9D3AFF53
+% 5353535353535353FF3A9D9D3A3A9D3A3A9D3A9D9D3A9D3A9D9D3A9D9D3A
+% 3A9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D3A7F1313131313131313137F
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A9DFF535353535353535353FF9D3A
+% 3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D9D9D3A7F131313131313131313
+% 7F3A9D3A9D3A3A9D3A3A3A9D3A3A9D3A9D9D9D9D3A9D3A3A3A3A9D9D9D9D
+% 9D3A9D3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210016
+% 121612C512101818181818101012101818181012101210121818181A181A
+% 181818181818181818101010101012181818181800FF1818181818181818
+% 181818121010101210121018101812101216181018121610101210181718
+% 181818181810181012101010121818181A181818181818101010AF101010
+% FF7D00000000000000FF53535353535353FF3A3A9D9D3A9D9D9D9D9D9D9D
+% 9D3A9D9D3A3A9D9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A9D9D3A3A3A3A3A3A
+% 7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D3AFF5353
+% 5353535353FF9D9D9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D3A9D9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A7F131313131313137F3A3A3A3A3A9D9D9D9D3A9D9D9D3A9D3A9D3A9D3A
+% 9D9D3A9D3A3A3A3A3A3A9D3A9D9D3A3A3A9D9D9D3A3A3A3A3A3A9D9DFF53
+% 535353535353FF3A9D9D9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D3A3A7F131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF53535353535353FF3A3A3A9D
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A7F131313131313137F3A
+% 3A3A3A9D9D9D3A9D3A9D9D9D9D3A3A9D9D3A9D3A3A9D3A9D3A9D9D9D9D9D
+% 3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021001010
+% 1010C5101012161818101210101018121210101810121018181818181818
+% 1818181818181818181210101012101210181000FF1A1818181818181818
+% 1818181812101010101010121010181012181818181A1810121018181818
+% 18181818181817181012101216181818181A18181812101010AF101010FF
+% 7D00000000000000FF53535353535353FF3A3A9D9D3A9D9D9D9D9D9D9D9D
+% 3A9D9D3A3A9D9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A9D9D3A3A3A3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D3AFF535353
+% 53535353FF9D9D9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D3A9D9D3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 7F131313131313137F3A3A3A3A3A9D9D9D9D3A9D9D9D3A9D3A9D3A9D3A9D
+% 9D3A9D3A3A3A3A3A3A9D3A9D9D3A3A3A9D9D9D3A3A3A3A3A3A9D9DFF5353
+% 5353535353FF3A9D9D9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A9D3A3A7F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF53535353535353FF3A3A3A9D3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A7F131313131313137F3A3A
+% 3A3A9D9D9D3A9D3A9D9D9D9D3A3A9D9D3A9D3A3A9D3A9D3A9D9D9D9D9D3A
+% 3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100101010
+% 10C5101010121816181810121012101010101210181018161818181A1818
+% 18181818181818181818121010101010101200FF18181818181012101817
+% 181818181810121010121010121012161818181818181818101818181818
+% 18181818181818181816181818181A181818181618181810AF101010FF7D
+% 0000000000000000FFFF535353FFFF9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D
+% 3A9D9D3A9D9D3A3A9D9D9D3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D7F
+% 7F1313137F7F3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D3AFFFF5353
+% 53FFFF3A3A9D3A9D3A3A9D9D3A9D9D9D9D3A9D3A9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 7F7F1313137F7F3A3A3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D
+% 3A9D3A9D3A9D3A3A3A3A3A9D9D3A9D3A3A9D3A9D9D9D3A9D3A9D3AFFFF53
+% 5353FFFF3A9D9D9D3A9D3A3A9D9D9D9D9D3A9D9D9D3A3A9D3A3A3A3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A3A3A3A7F7F1313137F7F3A3A3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3AFFFF535353FFFF3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F7F1313137F7F3A3A3A3A
+% 3A9D3A9D3A3A3A9D3A3A3A3A9D9D9D3A9D3A9D3A3A3A3A9D9D9D9D9D3A3A
+% 3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010101010
+% C51010101010121818181818181612161216121012101218181818181818
+% 181012101817181818181810121010121000FF1818111210101010121818
+% 181818181818181210101210101210121810121618181818181818181A18
+% 18181818181818181818181818181A1818181818181810AF101210FF7D00
+% 000000000000003A3AFFFFFF3A3A3A3A3A9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A9D3A3A9D9D3A
+% 7F7F7F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3AFFFFFF
+% 3A3A3A9D9D3A3A9D3A9D9D9D9D3A9D3A3A9D9D9D9D3A3A3A9D3A3A3A3A3A
+% 9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A7F7F7F3A3A3A3A3A3A9D3A3A9D9D9D9D9D9D9D3A9D3A3A9D9D3A3A3A3A
+% 9D3A9D3A3A3A9D3A9D3A3A3A9D3A3A9D3A9D9D3A9D9D3A3A9D9D9D9DFFFF
+% FF9D9D3A9D9D3A9D3A3A9D3A9D9D9D9D9D9D3A9D3A3A3A3A3A9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3AFFFFFFFF9D3A3A9D3A3A9D3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F7F3A3A3A3A3A9D
+% 3A9D3A3A9D9D9D9D3A3A9D9D9D9D9D3A3A3A9D3A3A3A9D9D3A9D9D3A3A3A
+% 9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210012101012C5
+% 101010101018181820191818181818181818101010121012181818111210
+% 1010101218181818181818181812101000FF181818161812181612101212
+% 181818181018101813181818181612161818181818181818181A18181818
+% 1A181818181210131618181818181818181210121012AF121618FF7D0000
+% 0000000000003A3A3A3AFF3A3A3A3A3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D
+% 9D3A9D9D9D9D9D3A3A3A3A9D3A3A3A3A3A3A9D9D3A3A3A9D3A3A9D9D3A7F
+% 3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3AFF9D9D3A
+% 3A3A9D9D3A3A9D3A9D9D9D9D3A9D3A3A9D9D9D9D3A3A3A9D3A3A3A3A3A9D
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A7F3A3A3A3A3A3A9D3A3A9D9D9D9D9D9D9D3A9D3A3A9D9D3A3A3A3A9D
+% 3A9D3A3A3A9D3A9D3A3A3A9D3A3A9D3A9D9D3A9D9D3A3A9D9D9D9D3A3AFF
+% 9D9D3A9D9D3A9D3A3A9D3A9D9D9D9D9D9D3A9D3A3A3A3A3A9D9D9D9D9D9D
+% 9D3A9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A9D3A3A3AFF3A3A3A9D3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A9D3A
+% 9D3A3A9D9D9D9D3A3A9D9D9D9D9D3A3A3A9D3A3A3A9D9D3A9D9D3A3A3A9D
+% 9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210016181818C510
+% 101010101018181818181818181810181818181012101018181818161812
+% 18161210121218181818101810181300FF10181018101818181818101618
+% 181818181818181816181212101210121818181818181818181818181818
+% 181A18181618181818181818181818181610101210AF121018FF7D000000
+% 00000000003A3A3A3A3AFF3A3A9D9D9D9D9D3A9D3A3A9D3A9D9D9D3A9D9D
+% 9D3A9D3A3A9D3A9D3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A9D3A7F3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D9D9D3A9DFF3A9D9D9D9D
+% 9D3A9D3A9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A7F3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D3A
+% 9D3A9D3A9D3A3A9D9D9D3A9D3A3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9DFF
+% 9D9D9D9D3A9D3A3A9D9D9D3A9D9D3A9D9D3A9D3A9D3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D9D9D3A3A9D3A9D3A3A3A3A3A7F3A3A9D9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A9D3A3A9D3A3A3A3AFF3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A
+% 3A9D3A9D9D3A9D3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A9D3A9D3A
+% 9D9D9D9D3A3A9D9D3A9D9D9D9D9D3A9D3A3A3A9D9D9D9D9D9D3A9D3A3A9D
+% 9D9D9D3A9D3A3A3A3A3A9D3A3A3A3A000000000000210010181018C51012
+% 101010121012101217181818101818181810121010101810181018101818
+% 181818101618181818181818181800FF1012121212101018181818181210
+% 121018121612161210101010101010101018181818181818181818181A18
+% 1818181818181818181818181818181818131010AF101210FF7D00000000
+% 000000003A3A3A3A9DFFFFFF9D3A9D9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D
+% 9D9D9D3A3A9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A7F7F7F3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D9D3A9DFFFFFF9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A9D3A9D9D3A9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A7F7F7F3A3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A9D3A9D9D
+% 3A3A3A9D3A9D9D3A3A3A3A9D3A3A9D9D3A9D3A9D3A9D3A9D3A3A3A9DFFFF
+% FF9D9D9D3A3A3A9D9D3A9D3A3A3A3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D3A3A3A9D9D3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3AFFFFFFFF3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F7F7F7F9D9D9D9D9D9D
+% 9D9D9D3A9D9D3A9D3A9D9D9D9D9D3A9D3A3A3A9D9D9D9D3A9D3A3A3A9D9D
+% 9D9D3A9D3A3A9D3A3A3A3A3A3A3A000000000000210012101218C5101010
+% 101010101012181812181618101210121010101010121012121212101018
+% 1818181812101210181216121600FF121010101010121810181818161010
+% 12101010121012161210101010101010101210161818181818181A182018
+% 1A181818181818181818181818181818161810AF101010FF7D0000000000
+% 0000003A3A3AFFFF535353FFFF9D9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D9D
+% 9D9D3A3A9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A7F7F1313137F7F3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D9D9DFFFF535353FFFF9D9D9D9D9D
+% 9D9D9D9D9D9D3A9D3A9D3A9D9D3A9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F
+% 1313137F7F3A3A9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A9D3A9D9D3A
+% 3A3A9D3A9D9D3A3A3A3A9D3A3A9D9D3A9D3A9D3A9D3A9D3A3AFFFF535353
+% FFFF9D3A3A3A9D9D3A9D3A3A3A3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D3A3A3A9D9D3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A9DFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A7F7F1313137F7F9D9D9D9D9D
+% 9D9D3A9D9D3A9D3A9D9D9D9D9D3A9D3A3A3A9D9D9D9D3A9D3A3A3A9D9D9D
+% 9D3A9D3A3A9D3A3A3A3A3A3A3A000000000000210010101210C512121012
+% 101012101012101817121210181618101210101010121010101010121810
+% 18181816101012101010121000FF10101012101210101218181810121012
+% 161218121612101010101008100E1010101012121618181818181A181818
+% 181818181818181818181818181818181810AF101010FF7D000000000000
+% 00003A3AFF53535353535353FF9D9D9D9D3A9D3A3A9D9D9D3A9D3A9D9D9D
+% 3A3A3A9D3A9D9D9D3A9D9D9D9D3A3A9D3A3A3A7F131313131313137F3A3A
+% 3A3A3A3A3A9D3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A9D3A3A3A3A3A3A3A9D3AFF53535353535353FF9D9D3A3A9D
+% 9D9D3A3A3A9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D7F131313
+% 131313137F3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A9D3A9D
+% 3A9D9D3A9D3A9D9D3A3A3A9D9D3A9D3A9D9D9D9D9D9D3AFF535353535353
+% 53FF3A9D3A9D9D9D3A3A9D9D9D9D9D9D3A3A3A9D9D9D3A9D9D9D9D9D9D9D
+% 9D9D3A3A9D3A9D3A7F131313131313137F3A3A9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 9D9D3A9D3AFF53535353535353FF3A9D3A9D3A9D9D9D9D9D3A9D9D9D9D9D
+% 9D9D3A9D9D9D9D3A9D9D3A9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F9D9D9D9D9D
+% 9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A9D9D9D3A9D9D9D3A3A3A9D9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010101012C51817181612
+% 101010121618181818101618181816181618181010101012101210101218
+% 181810121012161218121600FF1012101010101010101012181818181012
+% 1010101012101210101010101010101010101012181818181A18181A1818
+% 1818181718181216181818181816181810AF101008FF7D00000000000000
+% 003A3AFF53535353535353FF9D9D3A9D9D9D9D3A9D9D3A3A3A9D3A9D3A3A
+% 3A9D3A9D3A3A3A3A9D3A9D9D3A9D3A3A3A3A7F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3AFF53535353535353FF9D9D3A3A9D9D
+% 9D3A3A9D3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A7F13131313
+% 1313137F3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D9D3A3A3A
+% 9D3A9D3A9D3A9D3A9D3A9D3A9D9D9D9D3A9D3A9D3A9DFF53535353535353
+% FF3A3A3A3A9D9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A9D9D3A9D3A7F131313131313137F3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D3AFF53535353535353FF3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A7F131313131313137F9D9D9D9D3A9D
+% 9D3A3A9D9D3A3A3A9D9D9D3A3A3A9D9D3A9D3A9D9D3A9D3A9D9D9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A000000000000210010081010C5181818181818
+% 181613181818181818121818181818181818101012101010101010101012
+% 1818181810121010101000FF121010121012101210121010121612161216
+% 121813181718101210101010101010101010101816181818181A18181818
+% 18181210121012181217181818121810AF100810FF7D0000000000000000
+% 3AFF535353535353535353FF9D3A9D9D9D9D3A9D9D3A3A3A9D3A9D3A3A3A
+% 9D3A9D3A3A3A3A9D3A9D9D3A9D3A3A3A7F1313131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353535353535353FF9D3A3A9D9D9D
+% 3A3A9D3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F131313131313
+% 1313137F9D9D3A9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D9D3A3A3A9D
+% 3A9D3A9D3A9D3A9D3A9D3A9D9D9D9D3A9D3A9D3AFF535353535353535353
+% FF3A3A3A9D9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 3A9D9D3A9D7F1313131313131313137F3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9DFF535353535353535353FF3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F9D9D9D3A9D9D
+% 3A3A9D9D3A3A3A9D9D9D3A3A3A9D9D3A9D3A9D9D3A9D3A9D9D9D9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A000000000000210008101010C51818181A18181A
+% 181818181A18181818181818181818181810121010121012101210121010
+% 12161216121612181300FF10101210101210181210121010121018181818
+% 1816181818181610101010100A0E10101212181218181818181818181818
+% 101012101010121618121612181610AF10080AFF7D00000000000000003A
+% FF535353535353535353FF9D9D3A9D9D9D3A3A9D3A9D9D9D9D3A3A3A3A3A
+% 3A9D9D9D3A9D9D9D3A3A9D9D9D3A9D7F1313131313131313137F9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF9D9D3A9D9D9D9D
+% 9D9D9D9D3A9D9D9D9D3A3A9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 13137F3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A3A9D9D9D9D
+% 9D3A9D3A9D3A9D3A3A9D9D9D3A3A9D9D3A9D3AFF535353535353535353FF
+% 9D3A9D3A3A9D9D9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A
+% 9D3A9D3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3AFF535353535353535353FF3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A7F1313131313131313137F9D9D9D9D3A3A9D
+% 9D9D9D9D9D3A3A3A3A9D3A9D9D9D9D9D9D3A9D9D9D3A3A9D9D9D9D3A9D3A
+% 3A9D3A3A3A3A3A3A3A0000000000002100080A1010C51818181820181818
+% 181818181818181812101210181216181010101210101210181210121010
+% 121018181818181600FF12181018181818181A1818181810101818161216
+% 121012101218181818121010101010101010101818181818181818101018
+% 1218161216121012101210101218AF101010FF7D00000000000000003AFF
+% 535353535353535353FF9D3A9D3A9D3A3A9D3A3A9D9D3A3A3A9D9D3A9D3A
+% 3A3A3A9D3A3A9D9D9D3A9D9D9D3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF9D3A9D9D3A9D3A3A
+% 9D9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313
+% 137F3A3A9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D3A9D3A9D3A9D
+% 9D9D3A9D9D3A3A3A9D3A3A9D9D3A3A9D9D3AFF535353535353535353FF3A
+% 3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A
+% 3A9D3A7F1313131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F1313131313131313137F9D3A3A3A3A9D3A9D
+% 3A9D3A3A9D9D3A3A9D9D9D9D9D3A3A9D9D3A9D3A3A9D9D9D3A9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A000000000000210010101010C51816181A181A181A18
+% 181A181A1816121010C5C5C5C51210181218C5C5181818181AC5C5C5C510
+% 1018181612161200FF101012101818181818181A18181218181818181812
+% 101810101218181010101010101010101210121618181818161210121018
+% 10181818181018101210121012AF121010FF7D00000000000000003A3AFF
+% 53535353535353FF3A9D3A9D3A9D3A3A9D3A3A9D9D3A3A3A9D9D3A9D3A3A
+% 3A3A9D3A3A9D9D9D3A9D9D9D3A3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF9D9D3A9D9D3A9D3A3A9D
+% 9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 3A3A3A9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D3A9D3A9D3A9D9D
+% 9D3A9D9D3A3A3A9D3A3A9D9D3A3A9D9D3A9DFF53535353535353FF9D3A3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A3A
+% 9D3A3A7F131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F9D9D3A3A3A3A9D3A9D3A
+% 9D3A3A9D9D3A3A9D9D9D9D9D3A3A9D9D3A9D3A3A9D9D9D3A9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A000000000000210010101010C518181818181A18181A18
+% 1A181818181210C5C51210C5C518121010C5C518181818C5C51A18C5C518
+% 18181818181200FF10121010121012181818181818181818181818181718
+% 131010101010121010101010101010101210101212121012101010121012
+% 181818181818101210101010AF121818FF7D00000000000000003A3AFF53
+% 535353535353FF9D9D9D9D9D9D9D3A9D3A9D9D9D3A3A9D9D3A3A3A9D9D9D
+% 9D9D3A9D3A9D9D9D3A9D9D9D3A7F131313131313137F3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3AFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A7F131313131313137F3A
+% 9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D3A9D9D9D3A9D
+% 9D9D3A3A3A9D3A9D9D3A9D3A9D3A3A3A3AFF53535353535353FF3A3A3A3A
+% 3A3A3A9D3A3A9D3A9D3A3A3A9D9D9D9D3A9D9D9D3A9D9D9D9D3A9D3A3A9D
+% 9D3A7F131313131313137F3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF
+% 53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F131313131313137F9D9D3A3A9D9D9D9D3A9D3A
+% 9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A000000000000210018181813C5181818181A181818181818
+% 181818181010C5C51010C5C510101012101012101218C5C51818C5C51818
+% 181818181700FF1810101210101818181818181818181818181818181816
+% 101012101010101010101010101210101210121010121010101010101216
+% 1818181818181010101010AF101018FF7D00000000000000003A3A3AFFFF
+% 535353FFFF9D9D9D3A9D9D3A9D3A9D3A9D3A3A9D3A9D3A3A9D9D3A9D9D9D
+% 9D3A9D3A9D3A9D9D9D9D3A9D9D7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3AFFFF535353FFFF3A3A3A9D9D3A3A3A9D9D3A3A3A
+% 9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A7F7F1313137F7F3A3A3A
+% 9D3A9D3A3A9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A3A9D3A9D3A9D3A9D
+% 3A9D9D9D3A9D3A9D9D3A9D3A3A9D3A3A3AFFFF535353FFFF9D3A3A3A9D3A
+% 3A9D3A3A9D3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A
+% 3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A9D3A9D9D3A9D9D9D9D3A3A3A9D3A9D9D3A9D3A3AFF
+% FF535353FFFF9D3A3A9D9D3A3A3A9D9D3A9D9D3A9D9D9D9D3A9D3A9D3A3A
+% 9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F9D3A3A9D9D9D9D9D9D9D3A3A9D
+% 9D3A9D9D9D9D3A3A9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A3A9D3A3A
+% 9D3A3A3A3A000000000000210010181816C5C5C5C5C5C5C5C5C5C5C5C5C5
+% 1818101010C5C51010C5C510181810101210101818C5C51818C5C5181818
+% 1818181800FF181818101612181018181818181818181818181818181818
+% 181010121018181010101210101012101010101210101210101010101218
+% 18181818181012101010AF0A1010FF7D00000000000000003A3A3A3A3AFF
+% FFFF3A9D9D9D9D3A9D9D3A9D3A9D3A9D3A3A9D3A9D3A3A9D9D3A9D9D9D9D
+% 3A9D3A9D3A9D9D9D9D3A9D9D3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3AFFFFFF3A9D3A3A3A9D9D3A3A3A9D9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A7F7F7F3A3A3A3A3A9D
+% 3A9D3A3A9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A3A9D3A9D3A9D3A9D3A
+% 9D9D9D3A9D3A9D9D3A9D3A3A9D3A3A3A9D3AFFFFFF3A3A9D3A3A3A9D3A3A
+% 9D3A3A9D3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A3A
+% 3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A9D3A9D9D3A9D9D9D9D3A3A3A9D3A9D9D3A9D3A3A9D9D
+% FFFFFF9D3A9D3A3A9D9D3A3A3A9D9D3A9D9D3A9D9D9D9D3A9D3A9D3A3A9D
+% 3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A9D9D3A3A9D9D9D9D9D9D9D3A3A9D9D
+% 3A9D9D9D9D3A3A9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A3A9D3A3A9D
+% 3A3A3A3A000000000000210010101012C518181818181818181718181818
+% 12101210C5C51210C5C518181818181016121810C5C5C5C5C51818181818
+% 18181800FF1A181812121210181012121018181818181818181216181818
+% 101210181818101018101818181816121012101012161010101010121818
+% 181818181012101010AF100810FF7D00000000000000003A3A3A3A3A3A3A
+% FF9D3A9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D3A9D3A9D9D9D9D9D9D3A9D3A
+% 9D9D9D9D9D3A3A9D9D9D9D9D7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A9D3A3AFF3A3A3A3A3A3A3A9D9D3A3A9D9D3A3A3A3A3A3A
+% 9D3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A7F3A3A3A3A3A9D3A
+% 9D3A3A9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A9D3A9D9D9D3A
+% 9D3A9D3A9D3A9D9D3A3A9D3A9D3A3A3A3A9D3A3AFF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D3A9D3A3A3A3A
+% 3A3A7F3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A9D3A3A3A3A9D3A9D3A3A3A9D9D3A3A3A3A3A9D3A9D3AFF
+% 3A9D3A9D9D3A3A9D9D3A3A3A3A9D9D3A9D3A9D3A9D9D3A3A3A3A9D3A9D3A
+% 9D9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A9D
+% 3A3A9D3A3A9D3A3A3A3A3A3A7F3A3A9D3A3A9D9D9D9D3A9D9D3A9D9D9D9D
+% 9D3A9D9D9D3A3A9D9D9D9D3A9D3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A000000000000210008101010C51818171818181818181818181612
+% 101010C5C51012C5C518181A18181212121018C5C5121018181818181818
+% 181200FF1818121010101012101010121012181818171818161812181012
+% 1210121218181818181818181A1818181010181818101210101010101818
+% 1818181612171210AF101010FF7D00000000000000003A3A3A9D3A3A3AFF
+% 3A9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D9D3A9D9D9D9D7F9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A3A3A3A3A9D
+% 9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A3A9D3A9D9D3A9D9D9D3A9D3A9D
+% 3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A9D3A3A9DFF3A3A9D3A9D3A3A9D3A3A
+% 3A9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D3A3A3A3A
+% 7F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D3A9D3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A9D9D3A3AFF9D3A
+% 9D3A3A3A3A3A3A9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A9D9D3A9D3A9D
+% 3A3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A7F9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D
+% 9D9D9D3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A000000000000210010101010C5101218181818181818111813181010
+% 1010C5C51218C5C5181A181812101010101210C5C5121012181818171818
+% 1600FF181810101010101210101010101216181818181012101210101010
+% 101010101818181818181818181818181818181818101010101012181818
+% 18181818181810AF181210FF7D00000000000000003A3A3A9D3A3A3A9DFF
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D9D
+% 9D9D9D9D9D3A9D9D9D9D7F9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D3A3A3A9D3A3A3A3A9D3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D7F3A3A3A3A3A3A9D9D
+% 3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A3A9D3A9D9D3A9D9D9D3A9D3A9D3A
+% 3A3A9D3A9D3A3A9D3A3A3A3A3A3A9D3A3A9DFF3A3A9D3A9D3A3A9D3A3A3A
+% 9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D3A3A3A3A7F
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A9D3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A9D9D3A3AFF9D3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A9D9D3A9D3A9D3A
+% 3A3A9D3A3A3A9D3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D7F9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D
+% 9D9D3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A000000000000210012101010C510101818181818181818181810121010
+% 1010C5C5C5C5181A1818181010101010121010C5C5C5C516181818181012
+% 00FF1818181010080810101010100B0E0A10101618181818181810181316
+% 121612161210121618181818181818161218101818181818161010101012
+% 101010121010AF121818FF7D00000000000000003A3A3A3A3A3A3A3AFFFF
+% FF3A9D3A9D3A9D3A9D9D9D3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D3A9D9D
+% 9D3A9D9D9D3A7F7F7F9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A9D3A
+% 9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D3A9D3A9D9D3A3A9D9D9D9D3A3A3A
+% 9D3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A9DFFFFFF3A3A3A3A3A9D3A9D3A
+% 3A3A9D3A3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D3A3A9D3A9D3A3A7F7F7F3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D9D3A3A3A3A9D9D3A3A9D9D3A3A3A9D3A3A9D3A3A3AFFFFFF3A3A9D3A
+% 3A3A3A9D3A9D3A9D3A3A9D9D3A9D9D3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D7F7F7F9D9D9D9D9D3A9D3A9D3A9D3A9D9D9D9D9D
+% 9D3A9D3A3A9D9D9D9D3A9D9D3A9D9D9D3A9D3A9D3A3A3A9D3A3A3A3A3A3A
+% 000000000000210018181618C51010181618181A18181813101018101810
+% 12181818181A18181818181010080810101010100B0E0A10101618181800
+% FF1818181612101010101010101010101010121818181818161018101612
+% 171218101210121818181818181818101810181818181818181810121010
+% 1210101010AF161818FF7D00000000000000003A3A3A3A3A3AFFFF535353
+% FFFF9D9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A9D
+% 9D9D9D7F7F1313137F7F9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 9DFFFF535353FFFF3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A7F7F1313137F7F3A9D3A9D9D3A
+% 3A9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A3A9D3A3A9D9D3A9D3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A9D3A3A9D3A3A3AFFFF535353FFFF9D3A9D3A9D3A3A9D
+% 9D3A3A9D9D9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D3A3A3A7F7F1313137F7F
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A
+% 9D3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A9D3AFFFF535353FFFF3A9D3A
+% 9D3A9D3A9D3A3A3A9D3A3A9D3A9D9D9D3A3A3A3A3A3A9D3A3A3A9D3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A7F7F1313137F7F9D9D9D9D3A9D9D3A9D9D3A9D3A9D9D9D
+% 9D9D9D3A3A9D9D3A9D9D3A9D9D3A9D9D9D3A9D3A3A3A3A3A9D3A3A3A3A00
+% 0000000000210018181818C5181818181818181818181810121012181612
+% 16181818181A1818181816121010101010101010101010101218181800FF
+% 181618181818181012101010101010101010121618181818121812181218
+% 121012101210121018181816181810181210121018181818181210101210
+% 12101210AF121818FF7D00000000000000003A3A3A3A3AFF535353535353
+% 53FF9D9D3A9D3A3A3A3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A9D9D
+% 9D7F131313131313137F3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFF
+% 53535353535353FF3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A7F131313131313137F9D3A9D9D3A3A
+% 9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A3A9D3A3A9D9D3A9D3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A9D3A3A9D3A3AFF53535353535353FF3A9D3A9D3A3A9D9D
+% 3A3A9D9D9D9D9D9D3A9D9D9D9D9D3A9D9D3A9D3A3A7F131313131313137F
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D
+% 3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A9DFF53535353535353FF9D3A9D
+% 3A9D3A9D3A3A3A9D3A3A9D3A9D9D9D3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A3A3A7F131313131313137F9D9D9D3A9D9D3A9D9D3A9D3A9D9D9D9D
+% 9D9D3A3A9D9D3A9D9D3A9D9D3A9D9D9D3A9D3A3A3A3A3A9D3A3A3A3A0000
+% 00000000210018181818C518181818181818181818181813161810121818
+% 1818181A181818161818181818101210101010101010101012161800FF18
+% 12121618181812161210101010100A0F1010121818181618171816181618
+% 101210101010121018181812161218101010181818181818121612AFAFAF
+% AFAFAFAF101216FF7D00000000000000003A3A3A3A3AFF53535353535353
+% FF9D9D9D9D3A9D3A3A3A3A9D3A9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D3A9D
+% 7F131313131313137F9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53
+% 535353535353FF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A9D3A9D3A3A9D
+% 9D9D9D9D9D9D9D9D9D3A9D9D3A3A9D3A9D3A9D3A9D3A3A9D3A3A3A3A3A9D
+% 9D3A9D3A9D3A3A3A9D3A3A9DFF53535353535353FF3A3A3A3A3A3A3A3A9D
+% 3A3A9D9D9D9D9D9D9D9D9D9D3A3A9D9D3A9D3A9D7F131313131313137F3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D3A
+% 9D3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3AFF53535353535353FF3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A9D3A3A3A3A9D9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A7F131313131313137F9D9D3A9D9D3A9D9D3A9D9D9D9D3A9D3A9D
+% 3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000
+% 000000210012161818C518181A1818181A18181818181818181818161818
+% 18181818181812121618181812161210101010100A0F1010121800FF1810
+% 101012101216121810121010121010101010121618181818181818181810
+% 101010121010101210121612181018101818181818181816121810181018
+% 1210AF101212FF7D00000000000000003A3A3A3AFF535353535353535353
+% FF9D9D3A3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D3A9D9D3A3A9D3A9D9D7F13
+% 13131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353
+% 535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A7F1313131313131313137F3A3A9D9D3A3A9D
+% 9D9D9D9D9D9D3A9D9D3A3A3A3A9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A9D3A
+% 9D3A3A3A3A9D3A3A3A9DFF535353535353535353FF3A3A9D3A9D3A9D3A3A
+% 9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D9D7F1313131313131313137F3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353535353535353FF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F1313131313131313137F9D9D9D3A9D3A9D3A9D9D3A9D9D9D3A9D9D
+% 9D3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A3A9D3A3A3A3A3A3A00000000
+% 0000210012121818C518181818181818181A181818181818181818181818
+% 1818181818101010121012161218101210101210101010101200FF181612
+% 101010101210121612101216101010101210121818181818181818181612
+% 101010101210121012101216121818181818181612101210121012101210
+% 10AF101010FF7D00000000000000003A3A3A3AFF535353535353535353FF
+% 9D9D3A3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D3A9D9D3A3A9D3A9D9D7F1313
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF53535353
+% 5353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A7F1313131313131313137F3A3A9D9D3A3A9D9D
+% 9D9D9D9D9D3A9D9D3A3A3A3A9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A9D3A9D
+% 3A3A3A3A9D3A3A3A9DFF535353535353535353FF3A3A9D3A9D3A9D3A3A9D
+% 9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D9D7F1313131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A7F1313131313131313137F9D9D9D3A9D3A9D3A9D9D3A9D9D9D3A9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A3A3A9D3A3A3A3A3A3A0000000000
+% 00210010101218C518181818181818181818181A18181818181818181818
+% 18181818161210101010121012161210121610101010121000FF18181816
+% 101010101012101210181216121010101012161818181818181818121810
+% 101010101010121012101810181818181818181210101210121012101210
+% AF100F10FF7D00000000000000003A3A3A3AFF535353535353535353FF3A
+% 9D9D3A3A3A3A9D9D3A9D9D3A9D3A9D9D9D9D9D3A3A9D9D9D9D3A7F131313
+% 1313131313137F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9DFF5353535353
+% 53535353FF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A7F1313131313131313137F3A3A3A9D9D3A9D9D9D
+% 9D9D9D9D9D9D3A3A9D3A3A3A9D9D9D3A9D3A3A9D9D9D3A3A3A3A9D3A9D3A
+% 9D3A9D3A3A3A3A3AFF535353535353535353FF3A9D3A3A3A3A3A3A9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A9D3A3A9D3A9D7F1313131313131313137F3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 7F1313131313131313137F9D9D9D9D9D3A3A9D9D9D9D9D9D9D3A9D3A9D3A
+% 3A9D9D9D9D9D3A9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000
+% 21000F101010C5101818181818181A18181A181A18181818181818181818
+% 181818181816101010101012101210181216121010101000FF1818181818
+% 121010101010101818181810101010101012181818181818181818181012
+% 1010101210101010121018181018181818181816121010101210101010AF
+% 100A10FF7D00000000000000003A3A3A3A3AFF53535353535353FF9D9D9D
+% 9D3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D9D9D3A9D3A9D9D9D9D7F131313
+% 131313137F9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF5353535353
+% 5353FF3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A7F131313131313137F9D3A9D3A9D9D3A3A9D9D9D
+% 9D9D9D9D3A9D3A9D9D3A3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A3A9D3A3A3A
+% 3A3A3A3A3A9D3A3AFF53535353535353FF3A3A3A3A9D3A9D3A3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D3A9D3A3A9D3A7F131313131313137F3A3A3A3A3A
+% 3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 7F131313131313137F9D9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D9D3A3A9D
+% 9D9D9D3A9D9D3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021
+% 000A101010C510121018181818181A181A18201818181818181818181818
+% 1818181818181210101010101018181818101010101000FF181818181010
+% 1010101012181816181818181818181818181A1818181810101816181818
+% 18121610121012101810121010101818181012101012101010121010AF10
+% 0A08FF7D00000000000000003A3A3A3A3AFF53535353535353FF9D9D9D9D
+% 3A3A9D3A9D9D9D9D3A3A9D9D3A9D9D9D9D9D3A9D3A9D9D9D9D7F13131313
+% 1313137F9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF535353535353
+% 53FF3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A7F131313131313137F9D3A9D3A9D9D3A3A9D9D9D9D
+% 9D9D9D3A9D3A9D9D3A3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A3A9D3A3AFF53535353535353FF3A3A3A3A9D3A9D3A3A9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A9D3A3A9D3A7F131313131313137F3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A7F
+% 131313131313137F9D9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D9D3A3A9D9D
+% 9D9D3A9D9D3A3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100
+% 0A081010C510101012161818181818181A18181818101012101810181618
+% 18181818101010101010121818161818181818181800FF16181818101012
+% 081010101012181818181818181818181A181A1818161212181818181818
+% 161210121010121216181010121010101212161218101210101012AF1010
+% 10FF7D00000000000000003A3A3A3A3A3AFFFF535353FFFF9D9D9D9D9D3A
+% 3A3A9D9D9D9D3A9D9D3A3A9D3A9D3A9D9D3A9D9D9D9D3A9D3A7F7F131313
+% 7F7F9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D3A9D3A9D9D3A3A9D9D9D9D
+% 9D9D9D3A9D9D3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A3A9D3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D3A3A3A9D3A9D3A9D3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D7F
+% 7F1313137F7F3A9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D3A9D
+% 9D9D9D3A9D9D9D9D9D3A9D3A3A9D3A3A9D3A3A3A3A000000000000210010
+% 101010C5101010101218181818181A181818181810121012101210121216
+% 181818101012081010101012181818181818181800FF121018121612100E
+% 0A081010101818181A18181A181818181818181812101018181818181818
+% 1810101813181618181210101010101010181816181810181216AF101010
+% FF7D00000000000000003A3A3A3A3A3A3A9DFFFFFF9D3A3A9D3A9D3A3A3A
+% 9D9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A9D7F7F7F9D
+% 3A9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A9D3A3A9D3A9D9D3A3A9D9D9D9D9D
+% 3A9D3A9D3A9D3A3A3A3A3A9D3A9D3A3A3A9D3A3A3A9D3A9D9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A9D3A3A9D3A3A9D9D9D9D3A9D9D9D9D
+% 9D9D9D3A3A9D3A9D9D3A9D3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 7F7F7F3A9D3A9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D3A9D9D3A3A9D3A3A9D
+% 9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021001010
+% 1010C5100E1010121018181818181A181818181012161210181012101210
+% 18121612100E0A081010101818181A18181A1800FF101210181818181010
+% 100A1010181818181818181818181A181818181010121012101818181810
+% 18121816181818181618101010101012181818181618121618AF121010FF
+% 7D00000000000000003A3A3A3A3A3A3A9D3A9DFF9D3A3A9D3A9D3A3A3A9D
+% 9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A9D7F9D9D9D3A
+% 9D9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F3A3A3A9D3A3A9D3A9D9D3A3A9D9D9D9D9D3A
+% 9D3A9D3A9D3A3A3A3A3A9D3A9D3A3A3A9D3A3A3A9D3A9D9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3AFF3A3A3A3A9D3A3A9D3A3A9D9D9D9D3A9D9D9D9D9D
+% 9D9D3A3A9D3A9D9D3A9D3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D
+% 3A7F3A9D3A9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D3A9D9D3A3A9D3A3A9D9D
+% 9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100101010
+% 10C510101010101212101818181818181818181810181112101610101210
+% 181818181010100A1010181818181818181800FF10101010121018181810
+% 10101218181818181818181818181A181818101210101010121012101018
+% 101818181818181818181818181010181218101212101218AF101810FF7D
+% 00000000000000003A3A3A3A3A3A3A3A9D3A9DFF9D9D9D9D9D3A3A3A9D9D
+% 9D3A9D9D9D9D3A3A3A9D9D3A9D3A9D9D9D9D9D9D9D3A9D7F3A9D9D9D9D3A
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A7F3A3A9D9D3A3A3A3A9D9D3A3A9D9D9D9D9D9D
+% 9D9D3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A9D3A3A3A3A9D3A3AFF3A9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D9D
+% 3A3A9D3A3A9D3A9D3A3A3A3A3A7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A9D9D3A
+% 3A3A3A3A9D9D9D9D9D9DFF3A3A9D3A9D9D3A9D3A3A3A3A3A3A9D3A3A3A3A
+% 9D3A9D3A9D3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D3A3A9D
+% 9D7F9D3A9D3A9D9D3A9D9D9D9D3A9D9D3A9D3A9D3A9D9D3A3A3A3A3A3A9D
+% 9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018101210
+% C51010101010101012181818181818181018101812181018121010101010
+% 121018181810101012181818181818181800FF1012101010101218181810
+% 121618181818181818181818181818181012101010101010101210121210
+% 1810181818181818181A18181812161216121010101012AF181012FF7D00
+% 000000000000003A9D3A3A3A3A3A9D3A9D9DFFFFFF9D3A9D3A3A9D9D9D9D
+% 9D9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F7F7F9D9D3A9D9D3A9D
+% 3A9D9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A9D3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A7F7F7F3A9D3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D
+% 3A9D3A9D3A9D3A9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A
+% 3A3A9D9D3A3A3A3AFFFFFF3A9D3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 3A9D3A3A9D3A9D9D3A3A7F7F7F3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A9D9D9D9D9D
+% 3A3A9D9D9D3A9DFFFFFF3A3A3A9D9D9D9D9D3A3A3A3A9D9D9D3A3A3A3A3A
+% 9D9D9D9D3A3A3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A9D3A3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D
+% 7F7F7F3A9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D3A3A9D9D3A3A3A9D
+% 9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010121010C5
+% 101010101012101012121818161210101212161216181818121012101010
+% 1012181818101216181818181818181800FF1810121010100E1816121810
+% 181618161818161818181818181818121012101010101218181812101012
+% 12161218161818181818181818181818181018121010AF101216FF7D0000
+% 0000000000003A9D3A3A3A3A3A9D3AFFFF535353FFFF9D3A3A9D9D9D9D9D
+% 9D3A9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D7F7F1313137F7F3A9D9D3A9D3A
+% 9D9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFFFF535353FFFF3A3A3A9D3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A7F7F1313137F7F3A3A9D3A9D9D9D3A9D9D9D9D9D9D9D3A
+% 9D3A9D3A9D3A9D9D3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A
+% 3A9D9D3A3AFFFF535353FFFF3A9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A
+% 9D3A3A9D3A9D9D7F7F1313137F7F3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D9D9D3A3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A3A9D9D9D9D9D3A
+% 3A9D9D9DFFFF535353FFFF3A9D9D9D9D9D3A3A3A3A9D9D9D3A3A3A3A3A9D
+% 9D9D9D3A3A3A3A3A9D9D9D9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A7F7F13
+% 13137F7F9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D9D9D3A3A9D9D3A3A3A9D9D
+% 9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210012161210C510
+% 101010101012101210121012101010101012181818181818181012101010
+% 0E181612181018161816181816181800FF181810100A100A101010101812
+% 121218181818181818181818181810101010101216181818181810121010
+% 12161218181210101018181818181818181818180AAF101216FF7D000000
+% 00000000003A3A3A9D3A3A3A9DFF53535353535353FF3A3A9D9D9D9D9D9D
+% 9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D7F131313131313137F9D9D9D3A9D3A
+% 9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F131313131313137F3A9D3A9D9D9D3A3A9D9D9D9D9D9D9D9D
+% 9D3A9D9D3A3A3A9D9D3A9D3A9D3A3A3A3A3A9D3A9D3A3A3A9D3A3A3A3A9D
+% 3A3A3AFF53535353535353FF3A3A9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D
+% 3A3A3A3A9D7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A9D3A3A
+% 3A3AFF53535353535353FF3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A3A9D3A3A3A3A9D9D3A3A3A9D3A3A9D9D3A3A3A3A3A3A9D3A3A
+% 3A3A9D3A3A3A9D3A9D3A9D9D3A3A3A9D3A9D9D3A3A9D9D9D9D7F13131313
+% 1313137F3A9D3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D9D9D9D9D9D3A3A9D9D
+% 9D9D9D3A9D3A3A9D3A3A3A3A3A3A3A000000000000210012161210C51010
+% 10101010101010101012101010101010121018181A181A181810100A100A
+% 101010101812121218181818181800FF181818101010100B101012101818
+% 181818181818181010181216121012101010101218181818181A18181216
+% 1218181210181818181818181816121816181818AF101210FF7D00000000
+% 000000003A3A3A3A3A3A3A3AFF53535353535353FF3A3A9D9D9D3A9D9D9D
+% 3A9D9D3A3A3A3A9D3A3A3A3A3A3A7F131313131313137F9D9D9D3A9D9D9D
+% 9D3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F131313131313137F3A3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A
+% 9D3A9D3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9DFF53535353535353FF3A3A9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D3A3A
+% 9D3A3A9D7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A
+% 3AFF53535353535353FF3A3A3A3A3A9D3A3A9D9D3A3A3A9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A
+% 3A9D3A3A3A9D9D3A3A3A9D9D3A3A3A3A9D3A3A9D9D3A9D9D7F1313131313
+% 13137F3A9D3A3A3A3A9D3A9D9D3A9D9D3A9D9D9D9D9D3A9D9D9D3A9D9D9D
+% 9D9D3A9D3A3A3A3A3A9D3A3A3A3A000000000000210012101818C5101216
+% 12181818181010121010121010101010121818181818181818101010100B
+% 1010121018181818181818181800FF181818101210101010101012101210
+% 181818181818181211181012101012101010101818181818181812101210
+% 18181012101218101818181818121012121018AF181012FF7D0000000000
+% 0000003A3A3A3A3A3A3AFF535353535353535353FF3A9D9D9D3A9D9D9D3A
+% 9D9D3A3A3A3A9D3A3A3A3A3A7F1313131313131313137F9D9D3A9D9D9D9D
+% 3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A7F1313131313131313137F3A9D3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D
+% 3A9D3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF535353535353535353FF3A9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D3A3A9D
+% 3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3AFF
+% 535353535353535353FF3A3A3A3A9D3A3A9D9D3A3A3A9D3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A
+% 9D3A3A3A9D9D3A3A3A9D9D3A3A3A3A9D3A3A9D9D3A9D7F13131313131313
+% 13137F9D3A3A3A3A9D3A9D9D3A9D9D3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D
+% 9D3A9D3A3A3A3A3A9D3A3A3A3A000000000000210010121612C518181818
+% 181818181218161812101010101010101012181818181818101210101010
+% 10101210121018181818181800FF16181216121012101010121010101018
+% 181818181818181812161216121012101218181818181A18181010101010
+% 101010101010121012101210101010101018AF121012FF7D000000000000
+% 00003A3A3A3A3A3A3AFF535353535353535353FF3A9D3A9D9D9D9D9D3A9D
+% 3A3A3A9D3A3A3A9D3A9D3A7F1313131313131313137F9D9D9D3A9D9D9D3A
+% 3A3A9D3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A9D3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A7F1313131313131313137F3A9D3A9D9D9D3A3A9D9D9D9D9D9D9D9D3A9D
+% 3A9D9D3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A3AFF
+% 535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D3A9D3A
+% 3A7F1313131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D9D3A3A3A9D3A3A3AFF53
+% 5353535353535353FF9D3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A9D9D3A3A3A9D3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A9D9D9D3A9D3A9D3A9D9D9D9D7F1313131313131313
+% 137F9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A9D3A9D9D9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010121018C51316181818
+% 181818181818181618181818101010101010181816181216121012101010
+% 121010101018181818181800FF1812161218101012101210101010101012
+% 18181818181818101812101210101217181A18181A181818181010101010
+% 1010101010101010101818121810101012AF121010FF7D00000000000000
+% 003A3A3A3A3A3A3AFF535353535353535353FF3A9D9D9D3A9D9D3A9D9D9D
+% 9D9D3A3A3A3A3A3A3A3A7F1313131313131313137F9D3A9D3A3A9D3A9D3A
+% 3A3A3A3A3A9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 7F1313131313131313137F3A9D3A9D3A3A9D9D3A3A9D9D9D9D9D9D9D9D3A
+% 9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3AFF53
+% 5353535353535353FF9D9D9D9D9D9D9D9D9D3A3A3A9D9D9D3A9D3A9D3A3A
+% 7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 9D9D9D3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A9D9D3A9D9D3A3A3AFF5353
+% 53535353535353FF9D9D9D3A3A3A9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D
+% 3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D9D3A3A3A3A3A3A9D3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A3A3A9D3A9D3A9D3A3A3A9D3A9D7F131313131313131313
+% 7F9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A3A9D9D9D9D9D3A
+% 9D3A3A3A3A3A9D3A3A3A3A000000000000210010101216C5181818181818
+% 18181A181818121818181818121010101012181812161218101012101210
+% 1010101010121818181800FF101018111818181818181818181818181818
+% 1818181818181818111810101218181818181A181A181818161010101010
+% 10101010101010181718181816121010AF101210FF7D0000000000000000
+% 3A3A3A3A3A3A3A9DFF53535353535353FF3A3A9D9D9D3A9D9D3A9D9D9D9D
+% 9D3A3A3A3A3A3A3A3A9D7F131313131313137F9D9D3A9D3A3A9D3A9D3A3A
+% 3A3A3A3A9D3A9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9DFF53535353535353FF3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 7F131313131313137F3A3A9D3A9D3A3A9D9D3A3A9D9D9D9D9D9D9D9D3A9D
+% 3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF53
+% 535353535353FF9D9D9D9D9D9D9D9D9D9D3A3A3A9D9D9D3A9D3A9D3A3A3A
+% 7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 9D9D3A3A9D3A3A3A3A3A3A9D9D3A3A3A3A3A9D9D3A9D9D3A3A3A9DFF5353
+% 5353535353FF3A9D9D9D3A3A3A9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A9D3A
+% 3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D9D3A3A3A3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A9D3A9D3A9D3A3A3A9D3A9D3A7F131313131313137F9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D3A3A9D9D9D9D9D3A9D
+% 3A3A3A3A3A9D3A3A3A3A000000000000210012101210C516181818181818
+% 181818181010121018181818181818101012101018111818181818181818
+% 18181818181818181800FF121810181018181018181818181A1818181818
+% 181A18181818161818131810181818181A18181818181818181012101010
+% 101010101010181818181818181810AF161816FF7D00000000000000003A
+% 3A3A3A3A3A3A3AFF53535353535353FF3A3A9D9D9D9D3A9D3A9D9D9D3A9D
+% 9D9D3A3A3A3A3A3A3A7F131313131313137F3A3A9D3A3A9D9D9D9D3A9D3A
+% 9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3AFF53535353535353FF9D3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F
+% 131313131313137F3A3A3A3A9D3A9D9D9D3A3A9D9D9D9D9D9D9D9D9D3A9D
+% 9D9D3A3A3A9D9D3A9D3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3AFF5353
+% 5353535353FF9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D3A3A9D3A9D3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A
+% 9D3A3A3A3A9D3A3A3A3A9D9D3A3A3A3A3A3A9D9D9D9D3A3A3A3AFF535353
+% 53535353FF3A3A3A9D9D3A3A3A3A9D9D9D3A3A9D3A3A9D9D9D9D9D9D3A3A
+% 3A3A9D9D9D3A3A3A3A3A9D9D9D9D3A3A3A3A3A3A3A9D3A9D9D9D3A3A9D3A
+% 3A3A3A9D3A3A3A9D3A9D9D3A9D9D3A9D3A9D3A7F131313131313137F9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D3A9D3A3A9D9D3A9D9D3A9D3A
+% 3A3A3A3A3A3A3A3A3A000000000000210018161216C51812181618181618
+% 18121012101012101818181A181810101012181018101818101818181818
+% 1A1818181818181A00FF1810121812161218101818181818181818181A18
+% 181818181818181810181018181818181818181818181818101810121010
+% 1010101010121210121818181810AF181818FF7D00000000000000003A3A
+% 3A3A3A3A3A9D3AFFFF535353FFFF9D3A3A9D3A3A9D9D9D9D9D9D9D9D3A3A
+% 3A9D3A3A9D3A3A3A3A7F7F1313137F7F9D9D9D3A3A9D3A9D3A9D3A3A3A3A
+% 9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A7F
+% 7F1313137F7F3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A
+% 3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D3A3A3A9DFFFF53
+% 5353FFFF9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D3A3A3A3A9D3A7F
+% 7F1313137F7F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A9D3A3A3A3A3AFFFF5353
+% 53FFFF3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A
+% 9D3A3A3A9D3A3A9D3A9D3A3A9D3A3A9D3A9D9D7F7F1313137F7F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D3A3A9D9D9D9D9D9D3A9D3A3A
+% 9D3A3A3A3A3A3A3A000000000000210018181812C5101210181218181810
+% 101810181010121218181818181810121810121812161218101818181818
+% 181818181A181800FF17181612161012161210181818181818181A181818
+% 18181818181818181018181818181A181818101812181810121018101210
+% 12101010101010101012181812AF181818FF7D00000000000000003A3A3A
+% 3A3A3A3A9D3A9D9DFFFFFF9D3A9D3A3A9D3A3A9D9D9D9D9D9D9D9D3A3A3A
+% 9D3A3A9D3A3A3A3A3A9D7F7F7F9D9D9D9D9D3A3A9D3A9D3A9D3A3A3A3A9D
+% 9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3AFFFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A
+% 7F7F7F7F3A3A3A3A3A3A9D9D9D3A9D9D9D3A9D9D9D9D9D9D9D9D9D9D3A3A
+% 9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3AFFFF
+% FF9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D3A3A3A3A9D3A9D7F
+% 7F7F7F9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A9DFFFFFF
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A9D
+% 3A3A3A9D3A3A9D3A9D3A3A9D3A3A9D3A9D9D9D9D7F7F7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D9D3A3A9D9D9D9D9D9D3A9D3A3A9D
+% 3A3A3A3A3A3A3A000000000000210018181816C510121012101216121818
+% 181818181010101012101818161818171816121610121612101818181818
+% 18181A18181800FF18181818121010101010121618181818181818181A18
+% 18181A181818181818181818181818181818181010101012101818181010
+% 101010101010101010181810AF181018FF7D00000000000000003A3A3A3A
+% 3A9D3A9D3A3A9D9D3AFF9D9D3A3A3A9D3A3A9D9D9D3A9D3A9D3A9D9D3A3A
+% 3A3A3A3A3A3A3A3A7F9D9D9D3A9D9D9D3A9D3A9D9D3A9D9D9D3A9D3A9D3A
+% 9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D3A7F9D3A3A3A3A3A9D3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D3A9D
+% 9D3A3A9D3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFF
+% 9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D3A9D3A3A9D3A9D7F3A
+% 9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9DFF3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A
+% 3A3A3A3A3A3A9D9D3A9D3A3A3A9D3A9D3A9D3A9D9D7F3A3A3A3A9D3A9D9D
+% 3A9D3A9D3A3A3A9D9D9D9D3A3A9D9D9D3A3A9D3A9D9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A000000000000210010181318C51010101012101210101210
+% 181818181818121012101018181818181818121010101010121618181818
+% 181818181A00FF1012181010101010101010101218181818181818181818
+% 1A181A181818181818181818181818181818101210101010181210181012
+% 1010101010121012181818AF181618FF7D00000000000000003A3A9D3A3A
+% 3A3A9D3A9D9D3A9D9DFFFFFF3A3A9D9D9D3A9D9D3A9D9D9D9D9D9D3A3A3A
+% 3A3A3A9D3A7F7F7F9D9D9D3A9D3A3A9D3A9D3A9D9D3A9D9D3A3A9D3A9D9D
+% 3A9D9D9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3AFFFFFFFF3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3A7F7F7F7F3A9D3A9D9D3A3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D3A9D3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9DFF
+% FFFF9D9D9D9D9D9D9D3A3A9D9D3A3A9D9D9D9D9D3A9D3A3A7F7F7F7F3A9D
+% 9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A3A9D9D3A3A9D3A3A3A3A9D3A9D9D9D3A3A3A3AFFFFFF3A3A3A3A3A
+% 3A9D3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A9D3A
+% 3A9D9D3A9D3A9D9D3A9D9D3A3A9D9D9D3A9D9D3A9D7F7F7F3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D9D3A9D9D3A9D9D3A9D9D9D3A9D9D9D3A9D3A3A3A3A3A
+% 3A3A3A3A3A000000000000210016181618C5121012101012101012101210
+% 121818181818101012121218121012181010101010101010101218181818
+% 1818181800FF1210101010100810101010101010101218161818181A181A
+% 181818181817181818181818181818181818101010101010101210121010
+% 18101010101012181818AF181818FF7D00000000000000003A3A9D3A3A3A
+% 3A9D3A9D9D3AFFFF535353FFFF9D9D9D3A9D9D3A9D9D9D9D9D9D3A3A3A3A
+% 3A3A7F7F1313137F7F9D3A9D3A3A9D3A9D3A9D9D3A9D9D3A3A9D3A9D9D3A
+% 9D9D9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF
+% FF535353FFFF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D7F
+% 7F1313137F7F3A9D9D3A3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D3A9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFFFF5353
+% 53FFFF9D9D9D9D9D3A3A9D9D3A3A9D9D9D9D9D3A9D7F7F1313137F7F9D9D
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D3A3A3A3A3A
+% 3A3A3A9D9D3A3A9D3A3A3A3A9D3A9D9D9D3A3AFFFF535353FFFF3A3A3A3A
+% 9D3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A
+% 9D3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A9D3A9D3A3A
+% 9D9D3A9D3A9D9D3A9D9D3A3A9D9D9D3A9D9D7F7F1313137F7F3A3A3A3A3A
+% 3A3A3A9D3A3A9D9D3A9D9D3A9D9D3A9D9D9D3A9D9D9D3A9D3A3A3A3A3A3A
+% 3A3A3A3A000000000000210018181818C518181810181010121010121010
+% 101210181818181810101010121010101010081010101010101010121816
+% 18181800FF101012101010100A0F0A10101010101010121618181820181A
+% 181818181818121618181818181818181812101010101010101010181818
+% 181712101816181818AF181818FF7D00000000000000003A3A3A3A3A3A3A
+% 9D3A9D9DFF53535353535353FF9D9D3A9D9D9D9D9D3A9D9D3A3A9D3A3A3A
+% 7F131313131313137F9D3A3A9D9D9D9D3A9D9D3A9D9D3A3A9D3A3A9D9D3A
+% 9D9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF5353
+% 5353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F1313
+% 13131313137F9D3A9D9D9D3A9D9D9D3A3A3A9D9D9D9D9D9D9D9D3A9D9D3A
+% 3A3A3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A3A9D3A3A3AFF5353535353
+% 5353FF9D9D9D9D3A3A9D9D9D9D9D9D3A9D9D3A7F131313131313137F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D3A3A3A3A3A
+% 3A3A9D9D9D9D3A3A3A9D3A9D9D9D9D9D3AFF53535353535353FF3A9D3A3A
+% 9D9D9D3A3A3A9D3A9D9D9D9D3A3A3A9D9D9D3A9D9D9D3A3A3A9D9D9D9D9D
+% 3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A9D3A9D9D9D9D3A9D3A3A9D3A9D9D3A7F131313131313137F3A3A3A3A3A
+% 9D3A9D9D9D9D3A9D9D9D9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A3A3A3A9D3A
+% 3A3A3A000000000000210018181818C5C5C5C5C5C5C51210101210121012
+% 1010121018161816121010101012101010100A0F0A101010101010101216
+% 181800FF10121010101010100A1010080A10081010101818181A18181818
+% 181818101810181217181818181818181010101010101010101218181818
+% 1818181818181818AF181818FF7D00000000000000003A3A3A3A3A3A3A9D
+% 3A9D9DFF53535353535353FF9D3A3A3A9D9D3A3A9D3A9D9D3A3A3A3A3A7F
+% 131313131313137F9D9D9D9D9D9D9D3A9D3A9D9D9D3A3A9D9D3A3A3A9D3A
+% 9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3AFF535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313
+% 131313137F9D9D3A9D3A9D3A9D9D3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D3A
+% 9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9DFF535353535353
+% 53FF9D9D3A3A3A9D9D9D3A9D3A9D9D9D9D3A7F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A9D9D3A3A9D
+% 9D3A9D3A9D9D3A9D9D3A9D9D3A9D3A7F131313131313137F3A3A9D3A3A9D
+% 3A9D9D3A9D9D3A9D3A9D3A3A9D9D9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A000000000000210018181816C5181818181612161012101010101010
+% 1010101218181818181810121010101010100A1010080A10081010101818
+% 1800FF18121612181818101010101008100810101012181818181A181818
+% 18181810121010181812181812161216121010100A101010121818181818
+% 18181818181A18AF181818FF7D00000000000000003A3A3A3A3A3A3A9D3A
+% 9DFF535353535353535353FF3A3A3A9D9D3A3A9D3A9D9D3A3A3A3A7F1313
+% 131313131313137F9D9D9D9D9D9D3A9D3A9D9D9D3A3A9D9D3A3A3A9D3A9D
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3AFF5353535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F9D3A9D3A9D3A9D9D3A9D3A3A3A9D9D9D9D9D9D9D9D3A9D3A9D
+% 9D9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3AFF5353535353535353
+% 53FF9D3A3A3A9D9D9D3A9D3A9D9D9D9D7F1313131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A9D9D3A3A9D9D
+% 3A9D3A9D9D3A9D9D3A9D9D3A9D7F1313131313131313137F3A9D3A3A9D3A
+% 9D9D3A9D9D3A9D3A9D3A3A9D9D9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A000000000000210018181812C518181818181210121018101812121612
+% 101010181818181818181216121818181010101010081008101010121818
+% 00FF18161210181618101210101010080A10101010101818181818181817
+% 121012161218101210101010121018121012101010101012181818181A18
+% 181818181818AF181818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D
+% FF535353535353535353FF3A9D3A9D9D9D9D3A9D9D9D3A9D3A3A7F131313
+% 1313131313137F9D3A9D3A3A3A3A9D3A9D3A9D9D3A9D3A3A9D9D9D9D9D3A
+% 3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A7F131313131313
+% 1313137F9D3A9D9D3A9D3A9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D3A9D3A3A
+% 3A3A9D3A9D3A3A3A3A9D3A3A3A9D3A3A3A3A9D9DFF535353535353535353
+% FF3A3A3A3A9D9D3A9D9D9D9D3A9D9D7F1313131313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3AFF535353535353535353FF3A3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D9D
+% 3A9D9D9D9D9D9D3A9D9D9D9D7F1313131313131313137F3A3A3A3A3A3A9D
+% 9D3A3A9D9D9D9D9D3A3A9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 000000000000210018181010C51818171818101810181018181818181818
+% 181210121218161818161210181618101210101010080A10101010101800
+% FF1012101012181818181012101010101010101218181818181818181812
+% 10121812101210121012101018121612101010101010101818181A181A18
+% 181A181A18AF181810FF7D00000000000000003A3A3A3A3A3A3A9D3A9DFF
+% 535353535353535353FF3A3A3A9D9D9D9D3A3A3A9D3A3A3A3A7F13131313
+% 13131313137F9D9D3A9D9D9D3A9D3A9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A
+% 3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3AFF53535353535353
+% 5353FF9D3A9D3A3A9D9D3A3A3A3A9D9D3A3A9D3A3A9D9D3A3A9D9D3A3A9D
+% 9D3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 13137F3A3A9D9D3A9D3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9DFF535353535353535353FF
+% 3A9D9D9D9D3A9D9D3A9D9D3A3A9D7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A9D9D
+% 9D3A9D9D9D9D3A9D9D9D9D7F1313131313131313137F3A3A9D3A9D3A9D3A
+% 3A9D9D9D9D9D9D3A3A9D9D9D9D9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A00
+% 0000000000210018101210C510181218101218181818181818181A181818
+% 1812101010121810121010121818181810121010101010101012181800FF
+% 181012101210101012101210100A08101010101012101218101810121010
+% 101010181818AFAFAFAFAFAF101210121810181012121818181A18181818
+% 18181818AF181010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9DFF
+% 53535353535353FF9D3A3A3A9D9D9D9D3A3A3A9D3A3A3A3A3A7F13131313
+% 1313137F3A9D9D3A9D9D9D3A9D3A9D9D9D3A3A9D3A9D3A3A9D3A9D9D3A3A
+% 3A3A3A3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A3A3A9DFF53535353535353
+% FF3A9D3A9D3A3A9D9D3A3A3A3A9D9D3A3A9D3A3A9D9D3A3A9D9D3A3A9D9D
+% 3A3A9D3A3A3A9D3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F9D3A3A9D9D3A9D3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9DFF53535353535353FF3A3A
+% 9D9D9D9D3A9D9D3A9D9D3A3A9D3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A9D9D9D
+% 3A9D9D9D9D3A9D9D9D9D9D7F131313131313137F3A3A3A9D3A9D3A9D3A3A
+% 9D9D9D9D9D9D3A3A9D9D9D9D9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A0000
+% 00000000210010101010C510101010121010101012101818181818181818
+% 181818181018181012101210101012101210100A0810101010101200FF18
+% 181810101010121012161010101010101010101010121012121010101010
+% 10101818AFAFAFAFAFAFAFAF101012101816121816181818181818181818
+% 181818AF181010FF7D00000000000000003A3A3A3A9D3A3A3A3A9D9DFF53
+% 535353535353FF3A3A3A9D9D9D9D9D3A9D9D9D3A3A3A3A3A7F1313131313
+% 13137F9D9D9D9D9D9D9D3A3A9D3A9D9D3A9D3A3A3A3A9D9D9D9D3A3A3A9D
+% 9D9D3A9D3A9D9D9D3A3A3A3A3A9D3A3A9D3A9D3A9DFF53535353535353FF
+% 3A3A9D3A3A3A9D3A9D3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 3A9D9D3A3A3A9D9D3A9D3A3A9D9D3A3A9D3A9D3A9D3A3A9D3A3A3A9D3A9D
+% 3A9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F
+% 9D9D3A9D9D9D9D9D9D3A9D9D9D3A9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D3A9D3A3A9D9D9D9D9D9D9D9D9DFF53535353535353FF9D9D3A
+% 9D3A9D3A9D9D9D3A9D3A9D3A9D7F131313131313137F3A3A3A3A3A9D3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3AFF53535353535353FF3A3A3A3A9D3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D
+% 9D9D9D9D3A9D9D3A9D9D7F131313131313137F9D3A3A3A3A3A9D9D9D3A9D
+% 9D9D3A9D3A3A3A9D3A9D3A9D9D9D9D3A9D3A3A3A9D3A3A3A3A3A3A000000
+% 000000210010101010C51010101012101210121018181718181618181818
+% 181818181818181810101010121012161010101010101010101000FF1818
+% 171818181810101210121010101010101010101010101010101010081010
+% 101218AF181818181810AF10101010181212161818181A16181818181618
+% 1818AF101210FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3AFFFF
+% 535353FFFF9D3A3A9D9D9D9D9D9D9D3A9D9D3A9D3A3A9D3A7F7F1313137F
+% 7F9D3A9D9D9D9D3A9D3A9D3A9D9D9D9D3A9D9D3A3A9D3A9D3A3A9D9D9D9D
+% 3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A9D9D9D3A3A3AFFFF535353FFFF3A3A
+% 3A9D3A3A3A9D9D9D3A9D3A9D9D9D3A9D3A3A9D9D3A3A9D9D9D9D3A3A9D3A
+% 9D3A3A3A9D3A9D3A3A3A3A9D3A3A9D9D3A9D3A9D3A3A3A9D9D9D9D3A9D3A
+% 9D9D3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D7F7F1313137F7F9D9D
+% 3A3A9D9D9D9D3A3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFF535353FFFF9D3A9D9D9D
+% 9D3A9D9D3A9D3A9D3A9D3A3A9D7F7F1313137F7F9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A9D3A3AFFFF535353FFFF3A9D3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A9D9D
+% 3A9D9D3A9D9D9D9D9D3A7F7F1313137F7F3A3A3A9D3A3A9D3A9D9D3A9D9D
+% 9D9D9D3A3A9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000
+% 0000210012101010C5101210181818101810181018121818181818181818
+% 1818181818181718181818101012101210101010101010101000FF121818
+% 18181818101210181018181612181818121010101210101010100A081010
+% 1818AF181818181818AF1010101012101012121818181818161818181818
+% 16AF101010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3A9D9DFF
+% FFFFFF3A9D3A3A9D9D9D9D9D9D9D3A9D9D3A9D3A3A9D3A3A3A7F7F7F9D9D
+% 9D3A9D9D9D9D3A9D3A9D3A9D9D9D9D3A9D9D3A3A9D3A9D3A3A9D9D9D9D3A
+% 9D3A9D3A9D3A3A3A3A3A3A3A3A3A9D9D9D3A3A3A9D9DFFFFFF3A9D3A3A3A
+% 9D3A3A3A9D9D9D3A9D3A9D9D9D3A9D3A3A9D9D3A3A9D9D9D9D3A3A9D3A9D
+% 3A3A3A9D3A9D3A3A3A3A9D3A3A9D9D3A9D3A9D3A3A3A9D9D9D9D3A9D3A9D
+% 9D3A9D3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D9D7F7F7F3A3A9D9D3A
+% 3A9D9D9D9D3A3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFFFF3A3A9D3A9D9D9D9D
+% 3A9D9D3A9D3A9D3A9D3A3A9D9D9D7F7F7F9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A9D3A3A3A3AFFFFFF3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A9D9D3A
+% 9D9D3A9D9D9D9D9D3A9D9D7F7F7F3A3A3A3A3A9D3A3A9D3A9D9D3A9D9D9D
+% 9D9D3A3A9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000
+% 00210010101010C512161818181818181210121012101810181818181818
+% 18181812181818181818101210181018181612181818121000FF10121012
+% 101212101012121618181818181818181818101818131610101010101210
+% 18AFAFAFAFAFAFAFAF101010101010101010121018181818181818181818
+% AF121010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3A9D9D3A3A
+% 3AFF9D3A9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A7F3A3A9D9D9D3A
+% 9D9D9D3A9D9D9D3A9D3A9D3A9D3A9D3A3A3A9D9D9D9D9D9D3A3A9D3A9D9D
+% 9D9D9D9D3A3A9D3A3A3A3A3A3A9D9D3A9D9D3A9DFF3A9D3A3A3A9D3A3A9D
+% 3A3A3A9D9D9D3A9D3A9D9D9D9D9D9D3A9D9D3A3A9D9D9D9D9D3A3A3A9D9D
+% 3A3A9D9D9D9D9D3A3A9D9D3A9D3A3A9D9D9D3A3A3A9D9D3A9D3A9D9D3A9D
+% 3A9D3A9D9D9D9D3A3A3A3A9D3A3A9D3A3A3A9D9D3A9D3A7F9D9D9D3A9D3A
+% 9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3AFF9D3A9D9D9D9D3A3A9D
+% 9D9D9D9D9D9D3A3A9D9D9D9D7F9D9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D3A9D9D9D3A9D3A
+% 9D3A3A9D9D9D3A9D9D9D3A9D7F3A3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D9D
+% 9D3A3A9D9D9D3A3A9D3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A000000000000
+% 210010101010C518181818181A1818181010101010121012101812161818
+% 181010121012101212101012121618181818181818181800FF1210101210
+% 101010181010121818181818181A181A1818181718181218181810101810
+% 18AFAFAFAFAFAF12101018181010101010101212181818181818181818AF
+% 101210FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3A3A
+% FFFFFFFF9D3A9D3A9D3A9D9D9D9D9D3A3A3A3A3A7F7F7F3A3A9D9D9D9D3A
+% 9D9D9D9D9D9D3A9D3A9D9D9D9D3A3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D9D
+% 3A9D9D3A3A3A3A3A3A3A3A3A3A3A3A9D3AFFFFFF3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A9D3A3A9D3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D9D9D7F7F7F3A3A3A9D
+% 9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3A9D9DFFFFFF9D9D9D3A3A3A9D
+% 9D9D9D9D3A3A9D3A3A7F7F7F9D9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A9D9D9D3A9D9D9D9D
+% 3A3A9D9D9D9D9D3A9D9D9D9D7F7F7F3A3A3A3A3A3A9D9D3A3A9D3A9D9D9D
+% 3A3A9D3A3A9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021
+% 0012101010C51818181A1818181818121010101010121010101012101818
+% 101210101210101010181010121818181818181A181A00FF181812101012
+% 1012121012101018181618181A1818181818181818171818181612121012
+% 101010181810101212181018181010101210101818181A1818181818AF16
+% 1210FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D9D9D3AFFFF
+% 535353FFFF9D3A9D3A9D9D9D9D9D3A3A3A7F7F1313137F7F9D9D9D9D3A9D
+% 9D9D9D9D9D3A9D3A9D9D9D9D3A3A9D3A9D3A3A9D3A3A3A9D3A9D3A9D9D3A
+% 9D9D3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A3A3A3A9D3A9D3A3A3A9D3A3A9D3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D7F7F1313137F7F3A9D9D
+% 9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A3AFFFF535353FFFF9D3A3A3A9D9D
+% 9D9D9D3A3A9D7F7F1313137F7F3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A9D9D3A9D9D9D3A9D9D9D9D3A
+% 3A9D9D9D9D9D3A9D9D7F7F1313137F7F3A3A3A3A9D9D3A3A9D3A9D9D9D3A
+% 3A9D3A3A9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100
+% 12101212C518181818181A18181810100B0E10101010100E101010121818
+% 1818121010121012121012101018181618181A181800FF18181818121010
+% 181010101012181810101818181818121012101818181818181810101010
+% 101018181012101010181818181816121612181818181818181818AF1810
+% 18FF7D00000000000000003A3A3A3A3A3A3A3A3A9D9D3A9D9D3AFF535353
+% 53535353FF3A9D9D3A9D9D3A9D3A9D7F131313131313137F9D9D9D3A9D9D
+% 9D3A9D9D3A3A9D3A9D9D3A9D9D3A9D9D9D9D3A9D3A3A3A3A9D9D9D3A9D9D
+% 3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A7F131313131313137F3A3A3A
+% 3A3A9D3A9D9D3A3A9D3A9D3A9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D3A3A3A9DFF53535353535353FF9D9D3A9D3A9D
+% 3A9D9D9D7F131313131313137F9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A
+% 3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A9D3A9D3A9D3A9D3A9D9D3A9D9D9D3A9D9D3A3A
+% 9D9D9D3A3A9D9D7F131313131313137F3A3A9D3A9D9D3A9D3A9D9D9D3A3A
+% 9D9D9D9D9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210010
+% 181316C5181818181A181A181812101010101010100A100A101012161818
+% 181818121010181010101012181810101818181800FF1618181818181018
+% 18181012101010181816181818161810101018131618181A181818121010
+% 1018101812101818131818181818181818181818181818181818AF181012
+% FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A3A9DFF53535353
+% 535353FF3A3A9D9D3A9D9D9D3A3A7F131313131313137F9D9D9D3A9D9D9D
+% 3A9D3A3A9D3A3A9D9D9D3A3A9D9D3A3A3A9D3A3A9D3A9D9D9D3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D7F131313131313137F3A3A9D3A
+% 3A9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A3A3A9D9D9DFF53535353535353FF9D9D9D3A9D3A3A
+% 9D9D9D7F131313131313137F9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D3A3A3A
+% 9D9D9D9D3A9D7F131313131313137F3A3A3A3A9D9D3A9D3A9D9D9D3A9D9D
+% 3A9D3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A00000000000021001012
+% 1618C518181A181A181818181818181810100A1010101010101012181618
+% 1818181810181818101210101018181618181800FF121818181818181818
+% 171210101010121012181018121818181812161812161818181818101810
+% 1210121618181018181818181818181818181818181A181818AF181818FF
+% 7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A3AFF535353535353
+% 535353FF3A9D9D3A9D9D9D3A7F1313131313131313137F9D9D3A9D9D9D3A
+% 9D3A3A9D3A3A9D9D9D3A3A9D9D3A3A3A9D3A3A9D3A9D9D9D3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D7F1313131313131313137F3A9D3A3A
+% 9D9D9D9D9D3A9D9D9D3A9D9D9D9D9D9D3A3A3A9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A3A3A9D9DFF535353535353535353FF9D9D3A9D3A3A9D
+% 9D7F1313131313131313137F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF
+% 535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A9D9D9D9D9D9D9D9D9D9D3A3A3A9D
+% 9D9D9D3A7F1313131313131313137F3A3A3A9D9D3A9D3A9D9D9D3A9D9D3A
+% 9D3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A0000000000002100181818
+% 18C518181818181818181216181812101010121010101010101012121818
+% 18181818181817121010101012101218101800FF10121612181610181818
+% 121612101012101210121618181818181818121018101216181618181810
+% 18AFAF101210181618181818AFAFAFAFAFAFAFAFAFAFAFAFAF181818FF7D
+% 00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D9DFF53535353535353
+% 5353FF9D3A9D3A3A3A9D3A7F1313131313131313137F3A3A3A9D9D3A9D3A
+% 9D3A9D3A9D3A9D9D9D9D3A9D9D3A3A3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF535353535353535353FF9D3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A9D3A3A9D9D3A9D3A9D3A9D3A3A9D3A3A3A9D9D3A3A3A9D9D3A9D3A
+% 9D9D3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A9D9D9D3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3AFF535353535353535353FF3A9D3A9D3A9D3A3A
+% 7F1313131313131313137F9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFF53
+% 5353535353535353FF3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A9D9D3A9D3A9D9D3A3A9D9D3A3A3A3A9D9D
+% 3A9D3A7F1313131313131313137F3A3A9D9D3A3A3A9D3A9D3A9D3A9D9D3A
+% 9D9D3A9D9D9D3A9D3A9D3A9D3A3A3A3A3A3A000000000000210018181818
+% C51018181818181812101012161818121018101010101010101010121612
+% 181610181818121612101012101210121600FF1010121012181218161810
+% 121012101010101010121216181818161810181010121012181818181612
+% AFAF1012101212101818181818181818181A18181A1E18AF181818FF7D00
+% 000000000000003A3A3A3A3A3A3A9D3A9D9D3A9D9DFF5353535353535353
+% 53FF3A9D3A3A9D3A9D3A7F1313131313131313137F9D3A9D3A9D3A9D3A9D
+% 3A9D3A3A9D9D3A3A9D9D9D9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A9D3A3A
+% 3A3A3A3A3A3A3AFF535353535353535353FF3A9D9D3A9D3A9D9D3A3A3A9D
+% 9D3A3A3A3A9D3A3A9D3A3A3A9D3A9D9D3A3A3A9D3A3A9D3A3A9D9D9D9D3A
+% 9D3A3A3A3A3A3A9D3A9D9D9D3A9D3A9D3A3A9D9D9D3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A9D7F1313131313131313137F3A9D9D9D9D3A
+% 9D3A9D3A3A3A9D3A3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A7F
+% 1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9DFF5353
+% 53535353535353FF3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D3A9D9D9D3A9D3A9D9D9D3A3A9D9D9D
+% 3A9D7F1313131313131313137F3A9D9D9D9D3A3A9D9D9D9D3A9D9D3A3A9D
+% 9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181818C5
+% 181818181818181810101210181216181810121010121010101010121012
+% 1812181618101210121010101010101200FF101010101012161218101812
+% 161210101012101210101210181218181818181810181810121210121810
+% 1210101010101210121818181818181818181A181A18AF181818FF7D0000
+% 0000000000003A3A3A3A3A3A3A9D3A9D9D3A9D9D9DFF53535353535353FF
+% 9D3A9D3A3A9D3A9D3A9D7F131313131313137F9D9D3A9D3A9D3A9D3A9D3A
+% 9D3A3A9D9D3A3A9D9D9D9D3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3AFF53535353535353FF3A3A9D9D3A9D3A9D9D3A3A3A9D9D
+% 3A3A3A3A9D3A3A9D3A3A3A9D3A9D9D3A3A3A9D3A3A9D3A3A9D9D9D9D3A9D
+% 3A3A3A3A3A3A9D3A9D9D9D3A9D3A9D3A3A9D9D9D3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A9D3A7F131313131313137F9D3A9D9D9D9D3A9D
+% 3A9D3A3A3A9D3A3A3A3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A7F
+% 131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF5353
+% 5353535353FF3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D9D9D3A9D9D3A9D9D9D3A9D3A9D9D9D3A3A9D9D9D3A
+% 9D3A7F131313131313137F3A3A9D9D9D9D3A3A9D9D9D9D3A9D9D3A3A9D9D
+% 9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181818C518
+% 18181A181818181010121012101210121810121010101210101010101012
+% 16121810181216121010101210121000FF10101012181818161818181018
+% 161810101010101012101210101218161818181818101210101018161210
+% 121010101010101210121612181818181A18201E1AAF1A1818FF7D000000
+% 00000000003A3A3A3A3A3A3A9D3A9D9D9D3A3A9DFF53535353535353FF9D
+% 3A9D9D9D3A9D3A3A3A7F131313131313137F9D9D9D9D9D9D3A3A9D9D3A9D
+% 3A9D3A3A9D9D9D9D9D3A3A9D9D9D3A9D3A3A9D3A9D9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF53535353535353FF9D3A3A9D3A9D3A9D3A3A3A3A3A3A3A
+% 9D9D3A9D9D9D9D9D3A3A9D9D9D9D9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A
+% 3A3A9D9D3A9D3A3A9D9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A9D3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3AFF53535353535353FF3A3A3A3A3A3A9D3A3A3A7F13
+% 1313131313137F3A9D3A3A9D3A3A3A3A9D3A3A9D3A3A9D3A3A3A9D3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D9D9D3A9D3A9D9D3A9D9D3A9D3A9D9D9D3A3A3A3A9D9D3A9D3A
+% 9D7F131313131313137F3A3A3A3A9D3A9D3A9D3A9D3A9D3A9D9D9D3A9D9D
+% 3A9D9D3A9D3A9D3A3A3A3A3A3A3A3A000000000000210018181818C51818
+% 181818181818181812101010101018101818181812101010101012181818
+% 161818181018161810101010101000FF1810121018181818121810181818
+% 181810121012101010101012101012121018181018101010101812101010
+% 10101010101010101010101818181818181A181AAF181818FF7D00000000
+% 000000003A3A3A3A3A3A3A9D3A9D9D9D9D9D3A3AFFFF535353FFFF9D9D9D
+% 9D3A9D3A9D9D3A9D3A7F7F1313137F7F9D9D9D9D3A9D9D3A9D3A3A3A3A3A
+% 9D9D9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A9D3A3A9D3A3A3A3A3A3A3A7F7F
+% 1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFFFF5353
+% 53FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D9D3A9D9D9D3A3A3A9D9D9D3A9D9D
+% 9D7F7F1313137F7F3A3A3A3A9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D3A9D9D
+% 9D9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181816C5181818
+% 181818181818121010101010101012101818181818181810121018181818
+% 1218101818181818101210121000FF1A1818181618181816181818181618
+% 181818101010101012121012101010101218101210121010181016121010
+% 10101010101010101010101818181818181820AF1A1818FF7D0000000000
+% 0000003A3A3A3A3A3A3A9D3A9D9D9D9D9D3A3A3A9DFFFFFF3A9D9D9D9D9D
+% 3A9D3A9D9D3A9D3A3A3A7F7F7F3A3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A9D
+% 9D9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A7F
+% 7F7F3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3AFFFFFFFF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A9D9D3A9D3A9D3A3A9D3A3A9D3A9D9D3A9D9D9D3A3A3A9D9D9D3A9D9D9D
+% 9D3A7F7F7F3A3A3A3A3A3A9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D3A9D9D9D
+% 9D3A9D3A3A3A3A3A3A3A3A3A3A000000000000210018181812C512101818
+% 1810121612161010101010121010181818181818181A1818181618181816
+% 18181818161818181810101000FF181818181A1818181810181018181816
+% 18121612101810101012101010101010101210121618181218AF18181810
+% 10101010101010101010121018181A202118AF181818FF7D000000000000
+% 00003A3A3A3A3A3A3A9D3A9D9D9D3A9D9D3A3A3A3A9DFF3A9D9D3A9D9D9D
+% 9D9D9D9D3A3A3A3A7F3A3A3A3A3A9D9D9D9D3A9D9D9D9D9D3A3A9D3A9D9D
+% 3A9D9D3A3A3A9D3A3A9D3A9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3AFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A9D3A3A7F3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3AFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D9D
+% 9D3A9D9D9D9D3A3A3A9D9D9D3A9D9D3A9D9D9D3A3A9D9D9D9D3A9D9D3A9D
+% 9D3A3A7F3A3A3A3A3A9D3A9D9D3A9D9D3A9D3A3A3A9D9D9D9D9D3A9D9D9D
+% 3A9D3A3A3A9D3A3A3A3A3A3A000000000000210018181016C51012101012
+% 10181018181310100A08081010101216181A1818181818181A1818181810
+% 181018181816181216121000FF1818181818181A18101812121212181818
+% 181012101210101216181818181210121018AFAFAFAF161218AF18181818
+% 1010101010101010101012181818201A18AF181212FF7D00000000000000
+% 003A3A3A3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A9D3A3AFFFFFF9D3A9D9D3A
+% 9D9D9D3A9D7F7F7F3A3A3A9D3A9D9D9D3A9D9D9D9D9D3A3A9D9D3A9D3A9D
+% 9D3A9D9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9DFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFFFFFF3A3A3A3A3A3A3A3A3A3A7F7F7F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFFFF3A3A9D3A3A
+% 3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D
+% 9D3A3A9D3A3A3A3A9D9D3A9D9D9D3A3A3A9D3A3A3A9D9D3A9D3A9D9D3A9D
+% 3A3A3A7F7F7F3A3A3A3A9D9D9D3A9D9D9D3A9D3A3A9D9D3A3A9D3A9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A000000000000210012121812C5101810121018
+% 10121816181810101008101010181818181A181818181818181A18101812
+% 1212121818181810121000FF1A181A181A18181818121010101010181210
+% 1010121012161218181818181818181818AFAFAF12101210AF1216181810
+% 1010121010100E101010121818181818AF101010FF7D0000000000000000
+% 3A3A3A3A3A3A3A3A3A9D9D9D3A9D9D3A3A3A9DFFFF535353FFFF9D9D3A9D
+% 9D9D7F7F1313137F7F3A9D3A9D9D9D3A9D9D9D9D9D3A3A9D9D3A9D3A9D9D
+% 3A9D9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3AFF
+% FF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F7F3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A3A7F7F1313137F7F3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF3A9D3A3A3A
+% 3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A9D9D
+% 3A3A9D3A3A3A3A9D9D3A9D9D9D3A3A3A9D3A3A3A9D9D3A9D3A9D9D3A9D3A
+% 7F7F1313137F7F3A3A9D9D9D3A9D9D9D3A9D3A3A9D9D3A3A9D3A9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A000000000000210010101210C518181818101210
+% 1210181818181210101010121818181A18181A181A181A18181818121010
+% 10101018121010101200FF18181818181818181810101010101018181012
+% 10101012101216181818181818181818AF18AF16121010AF101212181012
+% 101010101010101010101216181812AF121012FF7D00000000000000003A
+% 3A9D3A3A3A3A9D3A9D9D9D9D3A9D9D3A3AFF53535353535353FF9D3A9D3A
+% 7F131313131313137F3A3A9D9D9D3A9D3A9D9D3A3A3A3A3A9D3A9D9D3A3A
+% 9D3A9D3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3AFF5353
+% 5353535353FF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3AFF53535353535353FF3A3A3A9D7F131313131313137F3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D3A9D
+% 9D3A9D3A3A9D9D9D9D9D9D9D3A3A3A9D3A3A9D9D9D9D9D3A9D9D9D9D7F13
+% 1313131313137F9D9D3A3A9D9D3A9D3A3A9D3A9D9D9D9D9D9D9D9D3A9D3A
+% 9D3A3A3A3A3A3A3A3A000000000000210010121012C51818181818121010
+% 10101218181818121012101818181A181818181818181818181810101010
+% 101018181012101000FF1818181818181818121010101010101818101010
+% 101218101012181818181818181818AF18AFAFAFAFAFAF10101012161218
+% 1010121010101010101210121010AF101010FF7D00000000000000003A3A
+% 3A3A3A9D3A9D3A9D9D9D3A9D9D3A3A3AFF53535353535353FF9D3A9D3A7F
+% 131313131313137F3A9D9D9D9D3A9D9D9D3A3A3A3A3A3A9D9D9D9D3A9D9D
+% 9D9D9D3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3AFF535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3AFF53535353535353FF3A3A3A3A7F131313131313137F9D3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3AFF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A
+% 3A3A9D3A9D9D3A3A9D9D3A9D9D9D9D3A3A9D9D9D9D9D3A9D3A9D9D7F1313
+% 13131313137F9D9D9D3A9D9D9D9D3A3A9D3A9D9D9D3A9D9D9D9D3A9D3A3A
+% 3A3A3A3A3A3A3A3A000000000000210010101012C5181818181818101210
+% 10101010121618121618181818181A181818181818181818121010101010
+% 1018181010101000FF181818181818181810121010101010181618181216
+% 1210101012101818101218181818AF1818AFAFAFAF161210101018121612
+% 10101010101012101012101012AF101010FF7D00000000000000003A3A3A
+% 3A3A9D3A9D3A9D9D9D3A9D9D3A3AFF535353535353535353FF3A9D7F1313
+% 131313131313137F9D9D9D9D3A9D9D9D3A3A3A3A3A3A9D9D9D9D3A9D9D9D
+% 9D9D3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9DFF5353535353
+% 53535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A9D3AFF535353535353535353FF3A3A7F1313131313131313137F3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3AFF535353535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 3A9D3A9D9D3A3A9D9D3A9D9D9D9D3A3A9D9D9D9D9D3A9D3A9D7F13131313
+% 13131313137F9D9D3A9D9D9D9D3A3A9D3A9D9D9D3A9D9D9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A000000000000210010101010C51818181A181812101010
+% 100808101012101812171818181818181818181818181810121010101010
+% 18161818121600FF12161818101810181210121010101212181818181812
+% 101010121613101210181618181818181818181818181010101216121012
+% 101216181810181012101216AF101010FF7D00000000000000003A3A3A3A
+% 3A3A3A9D3A9D9D9D3A3A9D9D3AFF535353535353535353FF3A3A7F131313
+% 1313131313137F3A9D9D3A3A9D9D9D9D9D3A3A3A3A3A9D3A9D9D9D3A9D3A
+% 9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3AFF535353535353535353FF3A3A7F1313131313131313137F3A3A3A3A
+% 3A3A3A3A3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3AFF535353535353535353FF3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A9D3A
+% 3A3A9D3A9D3A9D9D9D9D9D9D3A3A3A9D9D9D9D3A3A3A3A9D7F1313131313
+% 131313137F9D3A3A9D9D9D9D3A3A9D3A3A9D9D9D3A9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A000000000000210010101010C51818181818181818101010
+% 100A10101010121612181818181812161818101810181210121010101212
+% 181818181800FF1012101012161210101010121012161818181A18181210
+% 181218181818101218121216181818181218181818181012101810181018
+% 1012181818181812161218AF101010FF7D00000000000000003A3A3A3A3A
+% 3A3A9D3A9D9D9D9D9D3A9D3AFF535353535353535353FF3A9D7F13131313
+% 13131313137F3A9D9D9D3A3A9D3A3A3A3A9D3A3A9D3A9D9D3A9D3A9D9D9D
+% 3A9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3AFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF535353535353535353FF3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A0000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000000000000000007F131313131313
+% 1313137F0000000000000000000000000000000000000000000000000000
+% 0000000000000000000000210010101010C51818181A2018181810121010
+% 1010100A1012C5C5C5C51818121012C5C51216121010C5C5C5C512161818
+% 181A181800FF1818181818101216121612161218181818181A1818181010
+% 101010121018101010101218181818101210101010181810121018121818
+% 16181A18181818181618AF181818FF7D00000000000000003A3A3A3A3A3A
+% 3A9D3A9D9D9D9D9D3A9D3A3AFF53535353535353FF9D3A9D9D7F13131313
+% 1313137F3A3A9D9D9D3A3A9D3A3A3A3A9D3A3A9D3A9D9D3A9D3A9D9D9D3A
+% 9D9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353
+% FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF53535353535353FF3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF2121212121212121212121212121212121212121212121
+% 212121212121212121212121212121212121212121212121212121212121
+% 21212121212121212121212121212121212121212121217F131313131313
+% 137F21212121212121212121212121212121212121212121212121212121
+% 21212121212100000000210018181010C510101216181818181810121010
+% 10101010C5C51010C5C518181818C5C518101216C5C51216C5C518181818
+% 1A181800FF1A181818101210181012101216181818181818181818181010
+% 101010101010101010101216181818101012101218181010121612161818
+% 1818181A1818181818AF181816FF7D00000000000000003A3A3A3A3A3A3A
+% 9D3A9D9D3A9D3A9D9D3A3AFF53535353535353FF3A9D3A9D7F1313131313
+% 13137F3A9D9D9D9D9D3A3A9D3A9D3A3A9D9D3A9D9D9D9D3A9D9D3A9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3AFF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF53535353535353FF3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D3A
+% 9D3A9D9D9D3A3A9D3A3A3A9D3A9D9D3A9D3A9D9D9D9D7F13131313131313
+% 7F9D9D3A3A9D3A9D9D3A9D3A9D9D3A3A9D9D3A9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A002100000000210018161818C51010101218101810181210101010
+% 121010C5C51210C5C518181A18181810121018C5C51012C5C51818181818
+% 181800FF1818181818111813161216121818181818181A18181818101010
+% 101010100A10101010101218181818181012161218101210181318181818
+% 1818181818181810AF161318FF7D00000000000000003A3A3A3A3A3A3A9D
+% 3A9D9D9D9D9D9D9D3A3A3AFFFF535353FFFF9D9D3A9D9D9D7F7F1313137F
+% 7F3A3A3A9D9D9D9D3A9D3A9D3A9D9D3A9D9D3A9D9D9D3A3A9D3A9D9D3A9D
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D3A9D3AFFFF535353FFFF9D9D
+% 9D9D3A3A9D3A9D3A9D9D3A9D3A9D3A9D3A9D3A9D3A3A3A3A3A3A3A9D3A9D
+% 9D9D3A9D3A3A3A9D3A9D3A9D3A3A3A9D3A9D3A3A3A9D9D3A9D9D3A3A3A9D
+% 3A9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D
+% 3A3A3A7F7F1313137F7F9D9D9D9D3A9D3A3A3A3A3A9D9D9D9D9D9D3A9D3A
+% 9D3A9D3A9D3A9D3A9D9D9D3A3A9D3A9D9D3A9D9D3A3A9D3A3A9D3A9D9D3A
+% FFFF535353FFFF9D3A9D3A9D3A7F7F1313137F7F3A3A3A9D3A9D3A9D3A3A
+% 3A3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D3A9D3A9D
+% 3A9D3A9D3A3A3A9D3A9DFFFF535353FFFF9D3A9D3A9D3A3A9D3A9D3A3A9D
+% 3A9D3A9DFF7D3A3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A3A3A9D3A3A3A3A3A
+% 3A9D9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D9D9D9D9D
+% 9D3A9D9D3A3A9D3A9D3A3A9D9D3A9D9D9D9D9D9D3A3A7F7F1313137F7F3A
+% 3A9D9D9D3A9D3A9D3A3A3A3A3A9D3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A002100000000210013181818C5C5C5C5C5C5C5C5C5C5C5C5C5101216
+% 1816C5C51618C5C518181818181818111813C5C51612C5C5181818181A18
+% 1800FF1A18181818181618181210121618181A181A181818181810101010
+% 101010101010081010101818181818181018161216121810181018101818
+% 18181818181010AF121818FF7D00000000000000003A3A3A3A3A3A3A9D3A
+% 9D9D9D9D9D9D9D3A3A3A9D3AFFFFFF3A3A9D9D3A9D9D9D9D7F7F7F7F3A3A
+% 3A3A3A9D9D9D9D3A9D3A9D3A9D9D3A9D9D3A9D9D9D3A3A9D3A9D9D3A9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A9D9D3A9D9D3A9D3A3A9DFFFFFF9D9D9D9D9D
+% 9D3A3A9D3A9D3A9D9D3A9D3A9D3A9D3A9D3A9D3A3A3A3A3A3A3A9D3A9D9D
+% 9D3A9D3A3A3A9D3A9D3A9D3A3A3A9D3A9D3A3A3A9D9D3A9D9D3A3A3A9D3A
+% 9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D3A
+% 3A3A3A3A7F7F7F3A9D9D9D9D9D3A9D3A3A3A3A3A9D9D9D9D9D9D3A9D3A9D
+% 3A9D3A9D3A9D3A9D9D9D3A3A9D3A9D9D3A9D9D3A3A9D3A3A9D3A9D9D3A3A
+% 9DFFFFFFFF9D9D3A9D3A9D3A3A3A7F7F7F3A3A3A3A3A9D3A9D3A9D3A3A3A
+% 3A3A3A9D3A9D3A9D3A3A3A3A3A3A3A3A9D3A9D3A9D3A3A3A9D3A9D3A9D3A
+% 9D3A9D3A3A3A9D3A9D3A9DFFFFFF3A3A9D3A9D3A9D3A3A9D3A9D3A3A9D3A
+% 9D3A9DFF7D3A3A3A9D3A3A3A9D3A9D3A9D3A9D3A3A3A3A9D3A3A3A3A3A3A
+% 9D9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A9D9D9D9D9D9D9D9D
+% 3A9D9D3A3A9D3A9D3A3A9D9D3A9D9D9D9D9D9D3A3A3A3A7F7F7F7F3A3A3A
+% 9D9D9D3A9D3A9D3A3A3A3A3A9D3A3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A002100000000210018181818C518181010101012101210121012161818
+% 18C5C51818C5C518181A1818181818161818C5C5C5C518181A181A181818
+% 00FF1818181818181818171216181818181818181A181818181118121012
+% 101010101010080E10101818181818181210121018101810121812101818
+% 181817181218AF181618FF7D00000000000000003A3A3A3A3A3A3A9D3A9D
+% 9D9D9D9D9D3A9D3A3A3A3A9D9D3AFFFFFF9D9D9D7F7F7F7F3A3A3A3A3A3A
+% 3A3A9D9D9D9D9D9D9D3A3A3A9D3A9D3A9D9D3A9D9D9D3A9D3A3A9D3A9D3A
+% 3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A3A9D3AFFFFFF9D3A9D3A3A3A3A3A3A
+% 3A3A9D3A9D3A9D3A9D3A9D3A3A3A9D3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 9D3A9D3A9D3A3A3A9D3A3A3A9D3A9D9D3A3A9D3A3A9D3A9D3A3A9D3A9D3A
+% 9D3A9D3A3A9D3A9D9D3A9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D3A3A9D3A9D
+% 3A3A3A3A3A3A7F7F7F3A3A3A9D3A9D9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 9D3A3A9D3A3A9D3A3A9D3A9D3A9D3A9D9D3A9D3A9D3A3A9D9D9D3A9D9D9D
+% 9D3A9DFFFFFFFF3A9D9D7F7F7F3A3A3A3A3A3A9D3A3A9D3A3A9D9D3A3A3A
+% 9D9D9D3A9D3A9D3A9D3A3A3A3A3A9D9D9D9D3A3A3A3A9D3A9D3A9D3A9D3A
+% 9D3A3A3A9D9D3AFFFFFF3A9D3A9D3A9D3A9D3A9D9D9D3A9D3A3A3A9D3A9D
+% 9D3AFF7D9D9D9D3A3A9D9D3A9D9D3A9D9D3A3A9D3A9D3A9D3A3A3A9D3A9D
+% 3A9D3A9D3A9D3A9D3A9D3A9D9D3A3A3A3A3A3A3A9D9D3A3A9D3A9D3A3A3A
+% 9D9D3A3A3A9D3A3A9D3A9D9D3A9D9D9D3A9D9D3A3A3A3A3A3A7F7F7F7F9D
+% 3A9D9D9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 002100000000210016181818C51818181810121018101810121018181818
+% C5C51818C5C518181818181818181818C5C51618C5C5181818181A181800
+% FF18181818181A1818181812181818181818181818181618181612161810
+% 121012101010101010101218161818181018181316131810121010121612
+% 1818121618AF181818FF7D00000000000000003A3A3A9D3A3A3A9D3A9D9D
+% 3A9D3A9D9D9D3A3A3A3A9DFFFF535353FFFF7F1313137F7F3A3A3A3A3A9D
+% 3A9D9D9D3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D3A9D3A9D3A9D9D3A9D3A3A
+% 9D3A3A3A3A3A3A9D3A3A3A3A3A3A3AFFFF535353FFFF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 9D3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D9D3A9D3A
+% 3A3A3A7F7F1313137F7F3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9DFFFF535353FFFF7F1313137F7F3A3A3A3A9D3A3A3A3A3A3A3A3A9D9D3A
+% 3A3A9D3A9D3A3A9D3A3A3A9D9D3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D3A3A
+% 9D9D9D9DFFFF535353FFFF9D3A3A9D3A9D9D9D3A9D3A9D9D9D3A3A9D9D3A
+% 9DFF7D3A3A9D3A9D3A9D9D3A9D9D3A9D9D3A3A3A3A9D9D9D9D9D9D3A9D9D
+% 3A3A9D9D3A9D9D9D3A9D9D3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D9D9D
+% 9D9D3A3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A3A7F7F1313137F7F
+% 9D9D3A9DB2FF8B9D3A9D9D9D9D9D3A9D9D3A9D3A8BFF603A3A3A3A3A3A00
+% 2100000000210018181018C51818181817181818121010121012121818C5
+% C51818C5C5181818181818181A1818C5C51218C5C51818181818181800FF
+% 18181A181818181818181718181818121818181818121210121012101216
+% 121012101210101010101012181613101216181812161210121010121618
+% 18101818AF181818FF7D00000000000000003A3A3A9D3A3A3A9D3A9D9D3A
+% 9D3A9D9D9D3A3A3A3AFF53535353535353FF13131313137F3A3A3A3A9D3A
+% 9D9D9D3A9D9D9D9D9D9D9D3A3A9D9D9D9D9D3A9D3A9D3A9D9D3A9D3A3A9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D
+% 3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D9D3A9D3A3A
+% 3A7F131313131313137F3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF
+% 53535353535353FF13131313137F3A3A3A9D3A3A3A3A3A3A3A3A9D9D3A3A
+% 3A9D3A9D3A3A9D3A3A3A9D9D3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D3A3A9D
+% 9D9DFF53535353535353FF3A3A9D3A9D9D9D3A9D3A9D9D9D3A3A9D9D3A9D
+% FF7D3A3A9D3A9D3A9D9D3A9D9D3A9D9D3A3A3A3A9D9D9D9D9D9D3A9D9D3A
+% 3A9D9D3A9D9D9D3A9D9D3A3A3A3A3A3A3A9D3A3A9D3A9D3A9D3A9D9D9D9D
+% 9D3A3A9D9D3A9D3A9D9D9D9D3A9D9D9D9D3A3A3A3A7F131313131313137F
+% 9D3A9DE3E33A9D3A9D9D9D9D9D3A9D9D3A9D3A3AC8C83A3A3A3A3A3A0021
+% 00000000210018181612C5181818181818161216121010101012101818C5
+% C5C5C518181818181A181818181818C5C5C5C5181812181818181800FF18
+% 18181818181A181818181818181818161818161810101010101010121012
+% 181612101010100A10101210181810121818181018101010101010121810
+% 181018AF161818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3A9D
+% 9D9D3A9D9D3A3A3AFF53535353535353FF13131313137F9D3A3A3A3A3A9D
+% 9D9D9D9D3A9D9D3A9D3A9D9D3A3A9D3A3A9D3A3A3A9D3A9D3A9D3A3A3A3A
+% 9D3A3A3A3A9D3A9D9D9D9D9DFF53535353535353FF9D9D9D9D9D9D9D3A9D
+% 9D9D9D9D9D9D3A9D9D9D9D3A3A3A9D3A3A3A3A9D3A9D9D9D3A9D3A3A3A9D
+% 9D9D9D9D3A3A3A3A9D9D3A3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D9D3A9D3A
+% 9D9D9D9D9D9D3A9D9D9D9D3A3A9D9D9D9D9D9D3A9D9D3A3A3A9D3A9D3A3A
+% 7F131313131313137F9D3A3A3A9D3A9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D
+% 9D9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D3A9D9D3A3A3AFF53
+% 535353535353FF13131313137F3A9D3A3A9D3A9D9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A9D9D3A3A
+% 3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9DFF
+% 7D3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D9D3A3A9D3A9D3A3A9D9D3A9D9D9D
+% 9D9D3A3A9D3A9D9D3A9D9D3A9D9D3A9D3A3A3A3A7F131313131313137F9D
+% 9D82FF603A9D9D3A3A9D9D3A9D9D9D3A9D3A3A60FF823A3A3A3A3A002100
+% 000000210018181818C51018181818161210181012101010101018161218
+% 1818181A181818181818181A181818181818181818161818161800FF1818
+% 18181A181818181818181818181818181818181818101012101010101018
+% 121810181010101012161818181818161817181818181810101010101218
+% 1810AF121818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D3A
+% 9D9D3A9D3A9DFF535353535353535353FF13131313137F3A3A3A3A3A9D9D
+% 9D3A9D9D9D3A9D3A9D9D3A9D9D9D9D3A9D3A3A3A9D3A9D9D9D3A3A3A3A3A
+% 3A3A3A3A9D3A9D9D9D9DFF535353535353535353FF3A9D9D9D9D3A3A9D3A
+% 9D9D9D9D3A3A3A9D9D9D3A3A9D3A3A3A3A3A3A9D9D9D3A3A9D3A3A3A3A9D
+% 9D9D3A3A9D3A9D9D3A3A9D3A9D9D9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A9D
+% 3A9D9D9D3A9D3A3A9D9D9D3A3A3A9D9D9D9D3A3A9D9D3A3A3A9D3A9D7F13
+% 13131313131313137F9D9D3A3A3A9D9D9D9D9D3A3A9D3A9D3A3A3A9D3A9D
+% 9D9D3A3A9D9D9D9D9D3A3A3A9D3A3A9D9D9D3A3A9D9D9D9D9D3AFF535353
+% 535353535353FF13131313137F9D3A9D9D3A3A9D9D9D3A3A9D9D9D9D9D9D
+% 9D9D9D3A3A3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D9D3A9D3A3AFF
+% 535353535353535353FF9D9D3A9D3A9D3A9D3A3A9D3A9D9D3A9D3A9DFF7D
+% 9D9D9D3A3A9D3A9D3A9D3A3A9D9D3A9D9D9D3A3A3A9D9D7F7F7F7F7F3A3A
+% 3A9D3A9D3A3A9D9D3A3A3A3A3A3A9D9D3A9D9D9D9D3A3A9D9D3A9D9D3A9D
+% 9D9D3A3A3A9D3A9D3A9D9D9D9D9D9D3A3A3A7F1313131313131313137FA3
+% FFB19D3A3A9D3A9D9D9D9D9D9D9D3A9D3A9D3AB1FF483A3A3A3A00210000
+% 0000210018181818C5101210121012101218161210101010101010101012
+% 16181810181818181A1818181818181818181818181818181800FF181818
+% 1A1E181A1818181612101218181818181818101810121010101010101218
+% 171818101210101018181818181818181818171818161012101210121010
+% 10AF101010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D3A9D
+% 9D3A9D3A9DFF535353535353535353FF13131313137F3A3A3A3A3A9D9D9D
+% 3A9D9D9D3A9D3A9D9D3A9D9D9D9D3A9D3A3A3A9D3A9D9D9D3A3A3A3A3A3A
+% 3A3A3A9D3A9D9D9D9DFF535353535353535353FF3A9D9D9D9D3A3A9D3A9D
+% 9D9D9D3A3A3A9D9D9D3A3A9D3A3A3A3A3A3A9D9D9D3A3A9D3A3A3A3A9D9D
+% 9D3A3A9D3A9D9D3A3A9D3A9D9D9D9D3A9D9D3A9D9D9D9D9D3A3A9D3A9D3A
+% 9D9D9D3A9D3A3A9D9D9D3A3A3A9D9D9D9D3A3A9D9D3A3A3A9D3A9D7F1313
+% 131313131313137F9D9D3A3A3A9D9D9D9D9D3A3A9D3A9D3A3A3A9D3A9D9D
+% 9D3A3A9D9D9D9D9D3A3A3A9D3A3A9D9D9D3A3A9D9D9D9D9D3AFF53535353
+% 5353535353FF13131313137F9D3A9D9D3A3A9D9D9D3A3A9D9D9D9D9D9D9D
+% 9D9D3A3A3A9D9D9D9D9D9D3A9D3A9D9D9D9D9D3A9D9D9D9D3A9D3A3AFF53
+% 5353535353535353FF9D9D3A9D3A9D3A9D3A3A9D3A9D9D3A9D3A9DFF7D9D
+% 9D9D3A3A9D3A9D3A9D3A3A9D9D3A9D9D9D3A3A7F7F7F13131313137F7F7F
+% 9D3A9D3A3A9D9D3A3A3A3A3A3A9D9D3A9D9D9D9D3A3A9D9D3A9D9D3A9D9D
+% 9D3A3A3A9D3A9D3A9D9D9D9D9D9D3A3A3A7F1313131313131313137FC0FF
+% 609D3A3A9D3A9D9D9D9D9D9D9D3A9D3A9D3A60FF823A3A3A3A0021000000
+% 00210010101012C512161216121810181012101010101010101010101012
+% 1810181818181A1E181A181818161210121818181818181800FF1818181A
+% 181A18181812101210101012161218101818181010101010101010121612
+% 181818181812181818181818181818181818181818181816121010121012
+% AF101010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D
+% 9D9D3A9DFF535353535353535353FF13131313137F3A3A3A3A3A9D9D9D9D
+% 9D3A9D9D9D9D9D3A9D3A9D9D9D3A9D3A9D3A3A3A3A9D3A9D3A9D3A3A9D3A
+% 9D3A3A3A9D9D9D9DFF535353535353535353FF9D3A9D9D9D3A3A3A9D3A9D
+% 9D9D9D3A9D3A9D9D3A3A9D3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A3A9D9D3A
+% 3A9D3A9D9D9D3A3A9D3A9D9D9D3A3A9D3A3A3A9D9D9D9D3A9D9D3A3A9D9D
+% 9D9D3A3A9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D7F131313
+% 1313131313137F9D9D3A3A9D3A9D9D9D9D3A3A9D3A9D3A9D9D3A9D9D9D9D
+% 3A3A9D3A9D9D9D3A3A9D9D3A9D3A9D9D3A3A9D9D9D9D3A3AFF5353535353
+% 53535353FF13131313137F9D9D9D9D3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A
+% 3A9D3A3A3A3A9D3A9D3A9D9D9D3A9D9D9D3A3A3A9D3A3A9D3A9D3AFF5353
+% 53535353535353FF9D3A3A9D3A9D9D9D3A3A3A9D9D9D9D3A3A9DFF7D9D9D
+% 3A9D3A9D9D9D9D3A3A9D9D3A9D9D3A3A9D7F13131313131313131313137F
+% 9D3A3A9D3A3A3A3A3A3A3A9D9D3A3A9D3A9D3A9D9D54C6FFFFDAB73A3A9D
+% 3A3A9DA6C0E3FFC8C0A39D9D3A3A3A3A7F1313131313131313137FC8E39D
+% 9D9D3AC6FFBD3A9D3A9D9DBDFFC63A3A3A3AC8C83A3A3A3A002100000000
+% 210010101010C51012101210101012101210101210100810100F10101010
+% 12181818181A181A18181812101210101012161218101800FF1818181A18
+% 181A18181810101010121012101212161218101010100A10101010121818
+% 18181818181818181A1818181818181618121818181818181012101018AF
+% 101010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D3A9D9D
+% 3A3A9D3AFF53535353535353FF13131313137F3A3A3A3A3A3A9D9D9D3A9D
+% 9D3A9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D9D9D9D3AFF53535353535353FF3A9D3A9D9D9D3A3A9D3A9D9D9D
+% 9D3A3A9D3A9D9D3A3A9D3A3A3A3A3A9D9D9D9D3A9D9D3A3A3A9D3A9D3A3A
+% 3A3A9D9D9D3A9D3A9D3A9D9D9D3A9D3A3A3A9D9D9D9D3A9D3A3A3A3A9D9D
+% 9D3A3A9D3A9D9D9D3A3A9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A3A7F131313
+% 131313137F9D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D3A3A3A3A9D9D9D9D3A
+% 9D3A3A9D9D9D3A9D9D3A3A3A3A9D9D3A3A9D3A9D9D9D3A9DFF5353535353
+% 5353FF13131313137F9D9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D9D9D3A3A9D
+% 9D9D3A3A3A3A9D9D3A3A9D3A3A9D9D9D3A3A9D9D3A3A9D9D9D3A3AFF5353
+% 5353535353FF9D9D9D3A9D9D9D9D9D3A3A9D3A9D9D9D3A9D3AFF7D9D9D3A
+% 3A9D9D9D9D9D3A3A9D3A9D9D9D3A7F7F131313131313131313131313137F
+% 7F3A9D3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D3AB2FFFFFFFFFFFFD13A9D3A
+% 9DB2FFFFFFFFFFFFFFB23A3A3A9DA3FF7F131313131313137F60FFC69D3A
+% 9D3A3ADAFF549D9D9DA9FFC89D3A3A3A3A8BFF603A3A3A00210000000021
+% 0010101010C51216131810181218161818181010100A100B101010101010
+% 181818181A18181A181818101010101210121012121600FF1818181A181A
+% 18181818121012101012101010101210121818101010100810121618181A
+% 181A181818181A181818181818101212181618121218101816101018AF18
+% 1818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D3A9D9D3A
+% 3A9D3AFF53535353535353FF13131313137F3A3A3A3A3A3A9D9D9D3A9D9D
+% 3A9D3A9D9D9D3A9D9D9D3A9D9D3A9D3A9D3A3A9D9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D9D9D9D3AFF53535353535353FF3A9D3A9D9D9D3A3A9D3A9D9D9D9D
+% 3A3A9D3A9D9D3A3A9D3A3A3A3A3A9D9D9D9D3A9D9D3A3A3A9D3A9D3A3A3A
+% 3A9D9D9D3A9D3A9D3A9D9D9D3A9D3A3A3A9D9D9D9D3A9D3A3A3A3A9D9D9D
+% 3A3A9D3A9D9D9D3A3A9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A3A7F13131313
+% 1313137F9D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D3A3A3A3A9D9D9D9D3A9D
+% 3A3A9D9D9D3A9D9D3A3A3A3A9D9D3A3A9D3A9D9D9D3A9DFF535353535353
+% 53FF13131313137F9D9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D9D9D3A3A9D9D
+% 9D3A3A3A3A9D9D3A3A9D3A3A9D9D9D3A3A9D9D3A3A9D9D9D3A3AFF535353
+% 53535353FF9D9D9D3A9D9D9D9D9D3A3A9D3A9D9D9D3A9D3AFF7D9D9D3A3A
+% 9D9D9D9D9D3A3A9D3A9D9D9D3A7F1313131313131313131313131313137F
+% 3A9D3A3A3A3A3A3A9D9D3A9D9D3A9D3A9D48FFFFB73A9DA9D1FF719D3AA3
+% E3FF8BA33AA3C6FFE3483A3AB7FFA37F131313131313137F82FFB49D3A9D
+% 3A3AA9FFC89D9D9DDAFF599D3A3A3A3A66FF823A3A3A0021000000002100
+% 18181010C512161216101316101818181818181010100E10101010101018
+% 1818181A181A18181818121012101012101010101200FF10181818181818
+% 181818161216121810181018101210101818161010101008101216181818
+% 1818181A1818181818181810121010101218101010121012181818AF1818
+% 1AFF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D9D9D3A
+% 3A9D3AFFFF535353FFFF7F1313137F7F9D9D9D9D3A9D3A9D9D3A9D3A9D9D
+% 9D9D9D9D3A3A9D9D3A3A9D9D3A3A9D3A3A9D3A9D9D3A3A3A3A9D3A3A3A3A
+% 3A9D9D9D9D3A3AFFFF535353FFFF9D3A3A9D9D9D9D3A3A9D3A3A9D9D9D9D
+% 3A3A3A3A9D3A3A3A3A3A9D3A3A9D9D9D3A3A9D3A3A9D3A3A9D3A3A3A9D9D
+% 9D9D9D3A3A9D3A9D9D9D9D3A3A9D3A9D3A9D9D9D3A9D9D3A3A9D9D9D9D3A
+% 3A3A3A3A9D3A3A3A3A9D9D9D9D3A3A3A9D9D9D3A3A3A9D3A3A7F7F131313
+% 7F7F9D9D9D9D3A3A3A3A9D9D9D9D3A3A9D3A3A3A9D3A9D9D9D9D3A9D3A3A
+% 9D9D9D3A3A3A3A9D3A3A9D9D9D3A3A3A3A3A3A3A3A3A3AFFFF535353FFFF
+% 7F1313137F7F3A3A9D9D9D9D3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A9D3A3A
+% 9D3A3A3A3A9D3A3A3A3A9D9D9D3A3A9D9D3A3A9D9D9D9D9D3A9DFFFF5353
+% 53FFFF3A9D9D3A3A9D3A3A9D9D3A9D3A3A9D9D9D3A9D3AFF7D9D9D3A9D3A
+% 9D9D9D9D3A9D3A3A9D9D9D7F13131313131313131313131313131313137F
+% 9D3A3A3A3A3A9D3A9D3A3A9D9D9D9D3A77FFC03A9D9D3AA3FFB19D9D77FF
+% CB9D9D9D9D3A96FFBD3A3AB1FF483A7F7F1313137F7F3ACBFF549D9D9D3A
+% 3A3ABDFFC69DBDFFC63A9D3A3A3A3A54FF963A3A3A002100000000210018
+% 1A1818C51818121818181218181818181818161210101010101010101210
+% 181818181818181818161216121810181018101200FF1012161818181818
+% 101210121018181818181312101010101212181810121012101818181818
+% 181818181A181818181012101010101216101012101210121018AF181818
+% FF7D00000000000000003A3A3A9D3A3A3A9D3A9D9D3A9D3A9D3A9D9D3A3A
+% 9D3A3A9DFFFFFFFFFFFF7F7F7F9D3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A
+% 9D9D9D3A3A9D9D3A9D9D9D3A9D9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A
+% 9D9D9D9D9DFFFFFFFFFFFF9D9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A
+% 9D3A3A9D3A9D3A3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A3A9D9D3A9D3A9D9D
+% 9D9D3A3A9D9D3A9D9D9D3A9D9D9D3A9D9D9D9D3A9D3A3A9D3A9D9D9D3A3A
+% 9D3A3A3A9D3A3A3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D3A3A3A3A7F7F7F7F
+% 7F7F9D9D9D3A3A9D3A9D9D9D3A3A9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A
+% 9D9D3A3A9D9D9D3A9D3A9D9D3A3A9D3A9D9D9D3A3A3A3A3AFFFFFFFFFFFF
+% 7F7F7F3A3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D3A
+% 3A3A3A3A9D3A9D3A9D9D9D9D3A3A9D3A9D3A3A9D9D9D9D3AFFFFFFFFFFFF
+% 9D3A9D9D9D3A3A9D3A3A3A9D3A3A3A3A9D9D9D3A3A3AFF7D9D9D3A3A3A9D
+% 9D9D3A3A3A9D9D9D9D7F131313131313131313131313131313131313137F
+% 3A3A3A3A3A9D3A9D3A9D3A9D9D9D3AA3FF549D9D3A3A9D9D3A3A9DA3FFAF
+% 9D9D9D3A9DAFFFA33A3A3A3A3A3A3A3A7F7F7F7F7F7FFFFF9D9D9D9D9D3A
+% 9D3AD1FF77FFE39D3A9D3A3A9D3A3AFFFF3A3A3A00210000000021001818
+% 1818C5101816181018181612101010121818181010101010101010101012
+% 1618181818181012101210181818181813121000FF101218161818101010
+% 101010101818181818181010101010101618181816101018181818181818
+% 18181818181818101210101010101012101010121010121018AF121612FF
+% 7D00000000000000003A3A3A9D3A3A3A9D3A9D9D3A9D3A9D3A9D9D3A3A9D
+% 3A3A9D9DFFFF535353FFFF9D9D3A3A3A3A3A3A3A3A9D3A9D3A9D9D9D3A9D
+% 9D9D3A3A9D9D3A9D9D9D3A9D9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A9D3A9D
+% 9D9DFFFF535353FFFF9D9D9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A9D
+% 3A3A9D3A9D3A3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A3A9D9D3A9D3A9D9D9D
+% 9D3A3A9D9D3A9D9D9D3A9D9D9D3A9D9D9D9D3A9D3A3A9D3A9D9D9D3A3A9D
+% 3A3A3A9D3A3A3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D3A3A3A3A3A7F7F1313
+% 137F7F9D3A3A9D3A9D9D9D3A3A9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A9D
+% 9D3A3A9D9D9D3A9D3A9D9D3A3A9D3A9D9D9D3A3A3A3A3A3AFFFF535353FF
+% FF3A3A3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D9D9D3A3A9D3A9D3A3A
+% 3A3A3A9D3A9D3A9D9D9D9D3A3A9D3A9D3A3A9D9D9DFFFF535353FFFF9D9D
+% 3A9D9D9D3A3A9D3A3A3A9D3A3A3A3A9D9D9D3A3A3AFF7D9D9D3A3A3A9D9D
+% 9D3A3A3A9D9D9D9D7F131313131313131313131313131313131313137F3A
+% 3A3A3A3A9D3A9D3A9D3A9D9D9D3AFFFF3A9D9D3A3A9D9D3A3A9DFFFF9D9D
+% 9D9D3A9D9DFFFF3A3A3A3A3A486082B17F7F1313137F7F9D9D9D9D9D3A9D
+% 3AA6FFFFFFAF9D3A9D3A3A9D3A3AFFFF3A3A3A0021000000002100161218
+% 18C518181218181810181010101010101218101010121010101010101218
+% 16181810101010101010181818181818101000FF10101012101012101010
+% 101012181818181818181010080A10101012181818181818181818181218
+% 181818171812181010101010101012181810101012161818AF181618FF7D
+% 00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D3A9D3A3A9D
+% 3A3AFF53535353535353FF9D9D9D3A9D9D9D9D3A9D3A3A9D9D3A9D9D9D9D
+% 3A3A9D9D3A9D3A9D9D3A3A9D9D3A9D3A9D9D3A3A3A3A3A3A3A3A3A9D3A9D
+% FF53535353535353FF9D9D9D3A9D3A9D9D9D3A3A9D3A3A9D9D9D3A9D9D3A
+% 3A3A3A9D3A3A3A3A3A3A9D9D9D3A3A9D3A3A3A3A9D3A9D3A3A9D9D9D9D9D
+% 3A3A9D3A3A9D9D3A3A9D3A3A9D3A9D9D9D3A3A9D3A3A3A9D9D9D3A9D9D3A
+% 3A9D3A9D3A9D9D9D9D9D3A3A9D9D9D9D9D3A9D3A3A3A3A3A7F1313131313
+% 13137F9D3A9D3A9D9D9D9D3A9D3A9D3A3A3A3A9D9D9D3A3A9D3A9D3A9D9D
+% 3A3A3A9D3A3A9D3A9D9D3A9D9D3A3A9D3A9D9D3A9D9DFF53535353535353
+% FF3A3A3A9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D9D9D9D3A9D9D3A3A9D9D3A
+% 9D3A9D9D3A3A3A9D9D9D9D3A9D9D3A3A3A9D9DFF53535353535353FF3A3A
+% 3A9D9D3A3A9D9D3A9D9D3A3A9D3A9D9D9D3A3A9DFF7D3A3A9D9D3A9D9D9D
+% 9D3A9D3A9D9D9D7F131313131313131313131313131313131313137F3A3A
+% 3A3A3A9D9D3A9D9D9D9D3A3A9DFFFF3A9D9D3A3A9D9D9D3A9DFFFF9D3A3A
+% 9D9D3A3AFFFF9D3A3A59A3FFFFFF7F131313131313137F9D3A9D9D9D3A3A
+% 9D96FFDA9D9D3A9D3A3A3A3A3AFFFF3A3A3A002100000000210016181618
+% C51A18181810121818181812101010101018121610101210101010101012
+% 101012101010101012181818181818181000FF1210121018101810101010
+% 101012161818181A18181010101010101010101018181818181818161818
+% 1818181018101210101010101218181818161210121018AF181818FF7D00
+% 000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D3A9D3A9D3A3A9D
+% 3AFF53535353535353FF9D9D3A9D3A9D3A9D9D9D9D9D3A3A9D9D9D9D3A9D
+% 3A9D3A3A9D9D9D9D3A3A9D9D3A3A3A3A9D3A9D3A3A3A3A9D3A3A9D3A9DFF
+% 53535353535353FF9D9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A9D9D3A3A
+% 9D3A9D9D3A3A3A3A3A9D9D9D3A9D9D9D3A9D3A3A9D3A3A3A9D9D9D3A3A3A
+% 3A9D3A9D9D9D9D3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D9D3A3A9D3A9D
+% 3A3A9D3A9D9D9D9D9D3A3A9D9D9D9D3A3A3A9D3A3A3A9D7F131313131313
+% 137F3A3A3A9D9D9D9D3A3A9D3A9D3A3A3A9D9D9D9D3A3A9D3A3A9D9D9D9D
+% 3A9D9D3A3A3A9D9D9D3A9D9D3A3A3A3A3A9D9D9D9DFF53535353535353FF
+% 9D3A3A9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A3A3A3A9D3A
+% 3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D9D9DFF53535353535353FF9D3A9D
+% 9D9D9D9D9D9D3A3A3A9D9D3A9D9D9D9D3A9D9DFF7D9D3A3A9D3A9D9D9D9D
+% 3A3A3A9D9D7F1313131313131313131313131313131313131313137F3A3A
+% 3A9D3A9D3A9D9D3A9D3A9D9DFFFF3A3A9D3A3A3A9D9D3A3AFFFF9D9D9D9D
+% 3A3A9DFFFF9D3A60FFFFFFFFB17F131313131313137F9D9D9D3A9D3A3AAF
+% FFFFFFB49D3A9D3A3A3A3A3AFFFF3A3A3A002100000000210018181818C5
+% 181818181816181818181710101210121612101210101818101210121018
+% 101810101010101012161818181A181800FF181818181818181810121210
+% 121012181818181818181010101010101010101012161818181818181718
+% 12161210121010121012181818181818181810121018AF181818FF7D0000
+% 0000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D9D9D3A9D3A9D3A3A9DFF
+% 535353535353535353FF9D3A9D3A9D3A9D9D9D9D9D3A3A9D9D9D9D3A9D3A
+% 9D3A3A9D9D9D9D3A3A9D9D3A3A3A3A9D3A9D3A3A3A3A9D3A3A9D3AFF5353
+% 53535353535353FF9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A9D9D3A3A9D
+% 3A9D9D3A3A3A3A3A9D9D9D3A9D9D9D3A9D3A3A9D3A3A3A9D9D9D3A3A3A3A
+% 9D3A9D9D9D9D3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D9D3A3A9D3A9D3A
+% 3A9D3A9D9D9D9D9D3A3A9D9D9D9D3A3A3A9D3A3A3A7F1313131313131313
+% 137F3A3A9D9D9D9D3A3A9D3A9D3A3A3A9D9D9D9D3A3A9D3A3A9D9D9D9D3A
+% 9D9D3A3A3A9D9D9D3A9D9D3A3A3A3A3A9D9D9DFF535353535353535353FF
+% 3A3A9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A3A3A3A9D3A3A
+% 3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D9DFF535353535353535353FF3A9D9D
+% 9D9D9D9D9D3A3A3A9D9D3A9D9D9D9D3A9D9DFF7D9D3A3A9D7F7F7F7F7F7F
+% 7F7F7F7F7F1313131313131313131313131313131313131313137F7F7F7F
+% 7F7F7F7F7F7F3A9D3A9D9DA3FF543A9D3A3A3A9D9D3A3AA3FFAF9D9D9D3A
+% 3AAFFFA39D3AB1FF9660487F1313131313131313137F9D9D3A9D3A3AE3FF
+% B4E3E3A63A9D3A3A3A3A54FF963A3A3A002100000000210018181818C518
+% 181818181811181218181818161210181018101818181818181818181818
+% 18181012121012101218181818181800FF1A181818181818181818101010
+% 101012181818181818101810121010101010101210181218181818181612
+% 18101210101010101018181818181A181810101012AF121612FF7D000000
+% 00000000003A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D9D9D3A9D3A3A9DFF53
+% 5353535353535353FF3A9D9D3A9D9D9D3A9D3A3A9D3A9D3A9D9D9D3A3A9D
+% 3A3A9D3A9D9D3A3A9D9D3A9D3A9D3A3A3A3A3A9D3A3A9D3A3A9DFF535353
+% 535353535353FF9D9D3A9D9D9D9D9D3A3A9D3A9D9D9D9D3A3A9D3A9D3A3A
+% 9D3A3A3A3A3A3A9D9D9D3A3A9D9D3A3A3A9D3A9D3A9D9D9D9D3A3A3A3A9D
+% 9D3A9D9D9D3A9D9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D9D3A3A9D3A9D3A3A
+% 3A9D3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D3A3A9D7F131313131313131313
+% 7F3A3A3A9D9D9D9D3A9D9D9D3A3A9D9D9D9D3A3A9D9D9D3A9D9D9D3A3A9D
+% 3A9D3A3A3A9D9D3A3A9D3A9D3A3A3A3A9D9DFF535353535353535353FF3A
+% 3A9D9D9D9D3A3A9D9D9D9D9D9D3A3A3A9D9D9D3A3A9D3A9D3A3A9D9D3A3A
+% 9D3A3A9D9D9D3A9D9D3A3A9D3A9D9DFF535353535353535353FF3A3A9D9D
+% 3A3A9D9D3A9D3A3A3A3A9D9D9D9D3A9D3AFF7D9D9D3A3A7F7F7F7F7F7F7F
+% 7F7F7F7F1313131313131313131313131313131313131313137F7F7F7F7F
+% 7F7F7F7F7F9D3A9D9D9DBDFFC63A9D3A3A54FFDA9D9DBDFFCB9D3A9D3A3A
+% 96FFBD9D3AFFFF9D3A3A7F1313131313131313137F9D3A9D9D9D8BFFBD9D
+% 66FFD13A9D3A3A3A3A60FF823A3A3A002100000000210016121612C51210
+% 1818181818101012101818181012101210121818181A181A181818181818
+% 181818101010101012181818181800FF1818181818181818181818121010
+% 101210121018101812101216181018121610101210181718181818181810
+% 181012101010121818181A181818181818101010AF101010FF7D00000000
+% 000000003A3A9D3A3A3A3A9D3A9D9D3A9D3A9D3A9D3A3A3A3A3A3AFF5353
+% 53535353535353FF9D9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D3A3A3A9D9D3A
+% 9D3A9D9D3A3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3AFF53535353
+% 5353535353FF9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A9D3A9D3A3A9D
+% 3A9D3A3A3A3A9D9D9D3A3A9D3A9D9D3A3A9D3A9D9D9D9D3A3A3A9D3A9D3A
+% 3A9D9D3A3A9D9D3A3A9D9D9D9D3A9D9D3A3A3A9D9D9D3A3A3A3A9D9D3A9D
+% 3A9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A3A3A3A7F1313131313131313137F
+% 9D3A3A9D9D9D3A3A3A9D9D3A3A9D9D9D9D3A3A3A9D9D3A3A9D9D9D3A3A9D
+% 9D3A3A9D9D9D3A3A3A3A9D9D3A3A3A9D9DFF535353535353535353FF3A3A
+% 3A9D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A3A9D3A3A3A9D3A3A3A3A
+% 9D9D9D9D9D3A9D3A3A3A9D9D9D9DFF535353535353535353FF3A9D9D9D3A
+% 3A9D9D3A9D9D3A3A9D9D9D9D9D3A3A9DFF7D9D9D3A3A3A9D9D9D3A3A3A9D
+% 9D9D7F1313131313131313131313131313131313131313137F3A3A3A9D3A
+% 9D9D9D9D3A9D9D3A9D48FFFFBD489D54A3FF719D9DA3E3FFC6A39D488BFF
+% E3A39D9DA3FF82A33A7F1313131313131313137F9D9D9D9DAFFFB19D9D9D
+% CBFF719D3A3A3A3A82FF603A3A3A002100000000210010101010C5101012
+% 161818101210101018121210101810121018181818181818181818181818
+% 1818181210101012101210181000FF1A1818181818181818181818181210
+% 1010101010121010181012181818181A1810121018181818181818181818
+% 17181012101216181818181A18181812101010AF101010FF7D0000000000
+% 0000003A3A9D3A3A3A3A9D3A9D9D3A9D3A9D3A9D3A3A3A3A3A3A9DFF5353
+% 5353535353FF9D9D9D3A3A9D9D9D9D3A9D9D3A3A9D9D9D3A3A3A9D9D3A9D
+% 3A9D9D3A3A3A9D9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A9DFF53535353
+% 535353FF9D9D9D3A3A3A9D9D9D3A3A9D3A3A9D9D9D3A3A9D3A9D3A3A9D3A
+% 9D3A3A3A3A9D9D9D3A3A9D3A9D9D3A3A9D3A9D9D9D9D3A3A3A9D3A9D3A3A
+% 9D9D3A3A9D9D3A3A9D9D9D9D3A9D9D3A3A3A9D9D9D3A3A3A3A9D9D3A9D3A
+% 9D9D9D9D9D3A3A9D9D9D9D3A3A9D3A3A3A3A9D7F131313131313137F3A9D
+% 3A3A9D9D9D3A3A3A9D9D3A3A9D9D9D9D3A3A3A9D9D3A3A9D9D9D3A3A9D9D
+% 3A3A9D9D9D3A3A3A3A9D9D3A3A3A9D9D9DFF53535353535353FF9D3A3A3A
+% 9D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D9D3A3A3A9D3A3A3A9D3A3A3A3A9D
+% 9D9D9D9D3A9D3A3A3A9D9D9D9D9DFF53535353535353FF9D3A9D9D9D3A3A
+% 9D9D3A9D9D3A3A9D9D9D9D9D3A3A9DFF7D9D9D3A3A3A9D9D9D3A3A3A9D9D
+% 9D7F1313131313131313131313131313131313131313137F3A3A3A9D3A9D
+% 9D9D9D3A9D9D3A9D3AB2FFFFFFFFFFFFD13A9D9D9DB2FFFFFFFFFFFFE3B2
+% 9D9D9D4EC8FFFFFFFF7F131313131313137F9D9D9D9D9DE3FF549D9D9D9D
+% E3FFA63A3A3A3AC8FF3A3A3A3A002100000000210010101010C510101012
+% 1816181810121012101010101210181018161818181A1818181818181818
+% 18181818121010101010101200FF18181818181012101817181818181810
+% 121010121010121012161818181818181818101818181818181818181818
+% 18181816181818181A181818181618181810AF101010FF7D000000000000
+% 00003A3A3A3A3A3A3A9D3A9D9D9D3A9D3A9D3A3A3A9D9D3A9D3AFF535353
+% 53535353FF9D9D3A9D3A3A9D3A3A9D9D9D9D3A9D9D3A3A9D3A9D9D3A3A3A
+% 3A9D9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A9D9D3A9D9DFF5353535353
+% 5353FF9D9D9D3A9D3A9D9D9D3A3A9D3A3A9D9D9D3A3A9D3A9D9D3A9D9D3A
+% 3A3A3A3A9D9D9D9D3A3A9D3A9D3A3A9D3A9D9D9D3A3A9D3A3A3A3A9D3A9D
+% 9D9D3A9D9D9D3A3A9D9D9D3A9D9D3A3A3A9D9D9D3A3A9D3A9D9D3A3A3A3A
+% 9D9D9D9D3A3A9D9D9D9D3A9D3A3A3A3A9D9D7F131313131313137F3A9D3A
+% 3A9D9D9D9D3A9D9D3A3A3A9D9D9D9D3A3A9D9D9D3A3A9D9D3A3A9D9D3A3A
+% 3A3A9D9D3A3A9D9D9D9D9D3A3A9D9D9DFF53535353535353FF9D3A3A3A9D
+% 9D9D3A3A9D3A9D9D9D3A9D3A9D9D9D9D3A3A9D3A9D3A3A9D3A3A3A3A9D9D
+% 9D9D9D3A3A9D3A3A3A9D9D9D9DFF53535353535353FF3A9D9D9D9D3A3A9D
+% 3A9D9D9D3A3A9D3A9D9D9D3A9D9DFF7D9D9D9D3A3A9D9D9D9D3A3A9D3A9D
+% 9D7F131313131313131313131313131313131313137F3A3A9D3A3A3A9D9D
+% 9D3A9D9D3A9D9D3A9D548BC8FFB1B79D3A3A9D9D9DA682E3FFC8C0A63A9D
+% 9D3A9DA3C0B1FFFF7F131313131313137FB29D9D9DC6FF773A9D9D3A9DAF
+% FFDA3A3A3A60FF823A3A3A3A002100000000210010101010C51010101010
+% 121818181818181612161216121012101218181818181818181012101817
+% 181818181810121010121000FF1818111210101010121818181818181818
+% 181210101210101210121810121618181818181818181A18181818181818
+% 18181818181818181A1818181818181810AF101210FF7D00000000000000
+% 003A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A9D3A9D9D3A3A3A7FFFFF5353
+% 53FFFFFF9D9D9D3A3A9D9D9D9D9D9D3A9D9D9D9D3A3A3A9D9D9D3A9D3A9D
+% 9D3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A9D9D9D3AFFFFFFFF535353FF
+% FF9D9D9D9D3A3A3A9D9D9D3A3A3A3A9D9D9D9D9D3A3A9D9D3A3A3A3A3A3A
+% 3A3A9D9D9D9D3A3A3A3A3A9D9D3A3A9D9D9D9D3A9D3A9D9D3A3A3A3A9D9D
+% 9D3A9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D9D9D3A3A3A3A9D9D3A9D3A3A9D
+% 9D9D9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A9D7F7F1313137F7F7F7F9D9D3A
+% 9D9D9D3A9D9D9D9D3A3A9D9D9D3A9D3A3A9D9D9D3A9D9D9D3A9D9D9D3A3A
+% 3A9D9D3A3A3A9D9D9D3A3A9D9D9D9D7FFFFF535353FFFFFF3A3A3A3A9D9D
+% 9D9D3A3A3A9D9D9D3A3A9D3A9D9D9D3A3A3A9D9D3A3A3A3A9D3A3A3A3A9D
+% 9D3A3A3A3A3A9D9D9D9D9D9DFFFFFF535353FFFF3A9D9D3A9D9D3A3A9D3A
+% 9D9D9D3A3A9D3A9D9D9D3A3A3AFF7D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D
+% 7F131313131313131313131313131313131313137F3A3A3A3A3A9D9D3A9D
+% 9D3A9D9D9D9D9D3A3A9D9D3A3A9D3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D
+% 3A9D3A3A9D9D9D9D7F7F1313137F7F7FDA3A9D3A3A3A3A3A9D9D3A9D9D3A
+% 9D9D3A3ADAFF483A3A3A3A002100000000210012101012C5101010101018
+% 181820191818181818181818101010121012181818111210101010121818
+% 1818181818181812101000FF181818161812181612101212181818181018
+% 101813181818181612161818181818181818181A181818181A1818181812
+% 10131618181818181818181210121012AF121618FF7D0000000000000000
+% 3A3A3A3A3A3A3A9D3A9D9D9D9D9D9D9D3A9D3A9D9D3A7F7F131313FFFFFF
+% 535353FFFF9D3A3A9D9D9D9D9D9D3A9D9D9D9D3A3A3A9D9D9D3A9D3A9D9D
+% 3A9D9D9D9D3A9D9D3A9D3A3A3A3A3A3A3A9D9DFFFF535353FFFFFFFF3A3A
+% 9D9D9D9D3A3A3A9D9D9D3A3A3A3A9D9D9D9D9D3A3A9D9D3A3A3A3A3A3A3A
+% 3A9D9D9D9D3A3A3A3A3A9D9D3A3A9D9D9D9D3A9D3A9D9D3A3A3A3A9D9D9D
+% 3A9D9D9D9D9D9D9D9D3A9D9D3A3A3A9D9D9D3A3A3A3A9D9D3A9D3A3A9D9D
+% 9D9D3A3A9D9D3A9D3A3A3A3A3A3A3A3A9D9D3A7F7F7F7F1313137F7F3A9D
+% 9D9D3A9D9D9D9D3A3A9D9D9D3A9D3A3A9D9D9D3A9D9D9D3A9D9D9D3A3A3A
+% 9D9D3A3A3A9D9D9D3A3A9D9D7F7F131313FFFFFF535353FFFF3A3A9D9D9D
+% 9D3A3A3A9D9D9D3A3A9D3A9D9D9D3A3A3A9D9D3A3A3A3A9D3A3A3A3A9D9D
+% 3A3A3A3A3A9D9D9D9DFFFF535353FFFFFF9D3A3A9D9D3A9D9D3A3A9D3A9D
+% 9D9D3A3A9D3A9D9D9D3A3A3AFF7D9D9D3A3A3A9D9D9D9D3A3A9D3A9D9D7F
+% 131313131313131313131313131313131313137F3A3A3A3A3A9D9D3A9D9D
+% 3A9D9D9D9D9D3A3A9D9D3A3A9D3A3A3A3A3A9D9D9D3A3A9D3A3A9D3A9D3A
+% 9D3A3A9D9D9D9D9D9D7F7F7F1313137F7F9D3A3A3A3A3A9D9D3A9D9D3A9D
+% 9D3A60FF823A3A3A3A3A002100000000210016181818C510101010101018
+% 181818181818181810181818181012101018181818161812181612101212
+% 18181818101810181300FF10181018101818181818101618181818181818
+% 181816181212101210121818181818181818181818181818181A18181618
+% 181818181818181818AFAFAFAFAFAFAF121018FF7D00000000000000003A
+% 3A3A9D3A3A3A9D3A9D9D3A9D3A9D3A9D3A9D3A3A7F1313131313FF535353
+% 53535353FF9D9D9D9D9D3A9D9D9D9D9D3A3A3A3A9D9D9D9D3A9D3A9D3A3A
+% 3A9D9D9D3A3A9D9D9D3A9D3A3A3A3A3A9DFF53535353535353FF9D9D9D9D
+% 9D9D9D3A3A9D9D9D9D9D3A3A9D9D9D9D9D9D3A3A9D9D3A9D3A3A3A3A3A3A
+% 3A9D9D9D9D3A3A9D9D9D9D3A3A9D9D9D3A9D3A9D9D9D9D3A3A9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D9D3A9D3A9D9D9D3A3A9D9D9D9D9D
+% 9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D9D3A3A7F131313131313137F9D9D
+% 9D9D9D9D9D9D3A3A3A9D9D3A3A3A3A9D9D9D9D9D9D3A9D9D9D3A3A3A9D9D
+% 9D3A3A3A9D9D9D3A9D3A7F1313131313FF53535353535353FF3A9D3A9D9D
+% 3A3A3A9D9D3A3A3A3A3A9D9D9D3A3A3A9D9D3A9D3A3A3A3A3A3A9D9D9D3A
+% 3A3A9D9D3A9D9DFF53535353535353FF3A3A3A3A3A3A9D9D3A3A3A3A9D9D
+% 9D3A9D3A3A9D9D9D3A3A3AFF7D9D9D3A9D3A9D9D9D9D3A3A3A9D3A9D9D7F
+% 13131313131313131313131313131313137F3A3A3A3A3A3A9D3A3A9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A3A3A9D9D9D9D3A9D9D9D9D3A9D9D9D9D9D3A9D3A
+% 3A9D9D3A3A9D9D9D7F131313131313137F3A3A9D9D9D9D9D9D9D9D3A9D3A
+% 3AC8C83A3A3A3A3A3A002100000000210010181018C51012101010121012
+% 101217181818101818181810121010101810181018101818181818101618
+% 181818181818181800FF1012121212101018181818181210121018121612
+% 161210101010101010101018181818181818181818181A18181818181818
+% 1818181818181818181818131010AF101210FF7D00000000000000003A3A
+% 3A3A3A9D3A9D3A9D9D9D9D9D9D3A9D3A3A3A9D7F1313131313FF53535353
+% 535353FF3A9D9D9D3A9D9D9D9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A3A3A
+% 9D9D9D3A9D9D3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A3A9D3A9D
+% 3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D3A9D3A
+% 3A9D3A9D3A3A3A3A9D3A3A9D3A9D3A3A3A3A9D3A9D3A3A9D3A3A9D3A3A3A
+% 3A3A3A3A9D3A3A3A9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A9D9D3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D7F131313131313137F3A9D3A
+% 9D3A3A9D3A3A3A9D3A9D3A3A3A3A3A9D3A9D3A3A9D9D3A9D9D3A9D9D9D9D
+% 9D9D9D9D3A9D3A3A3A7F1313131313FF53535353535353FF3A3A3A9D3A3A
+% 3A9D3A9D3A3A9D3A9D3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3AFF53535353535353FF9D3A9D3A9D3A9D3A3A3A3A9D3A9D9D
+% 3A9D3A9D3A9D9D3A9D3AFF7D3A9D3A9D3A9D9D9D9D9D3A3A3A9D3A3A9D7F
+% 1313131313131313131313131313137F3A3A3A3A3A3A9D3A9D9D3A9D9D9D
+% 3A9D9D9D9D3A9D3A9D3A3A9D9D9D3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D
+% 3A3A9D9D3A9D9D7F131313131313137F3A9D3A9D9D9D3A9D9D9D3A9D3A8B
+% FF593A3A3A3A3A3A002100000000210012101218C5101010101010101012
+% 181812181618101210121010101010121012121212101018181818181210
+% 1210181216121600FF121010101010121810181818161010121010101210
+% 12161210101010101010101210161818181818181A1820181A1818181818
+% 18181818181818181818161810AF101010FF7D00000000000000003A3A3A
+% 3A3A9D3A9D3A9D9D9D9D9D9D3A9D3A3A3A7F1313131313FF535353535353
+% 535353FF9D9D9D3A9D9D9D9D3A3A9D3A3A9D9D9D9D9D3A9D3A9D3A3A3A9D
+% 9D9D3A9D9D3A3A3A3A3A3A3A3A3AFF535353535353535353FF3A9D3A9D3A
+% 3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A9D3A9D3A3A
+% 9D3A9D3A3A3A3A9D3A3A9D3A9D3A3A3A3A9D3A9D3A3A9D3A3A9D3A3A3A3A
+% 3A3A3A9D3A3A3A9D3A9D3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A9D9D3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D7F1313131313131313137F9D3A9D
+% 3A3A9D3A3A3A9D3A9D3A3A3A3A3A9D3A9D3A3A9D9D3A9D9D3A9D9D9D9D9D
+% 9D9D9D3A9D3A3A7F1313131313FF535353535353535353FF3A3A9D3A3A3A
+% 9D3A9D3A3A9D3A9D3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3AFF535353535353535353FF3A9D3A9D3A9D3A3A3A3A9D3A9D9D3A
+% 9D3A9D3A9D9D3A9D3AFF7D3A9D3A9D3A9D9D9D9D9D3A3A3A9D3A3A9D7F7F
+% 131313131313131313131313137F7F3A3A3A3A3A3A9D3A9D9D3A9D9D9D3A
+% 9D9D9D9D3A9D3A9D3A3A9D9D9D3A3A3A9D3A9D3A9D9D9D9D9D9D9D9D9D3A
+% 3A9D9D3A9D7F1313131313131313137F9D3A9D9D9D3A9D9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A002100000000210010101210C512121012101012101012
+% 101817121210181618101210101010121010101010121810181818161010
+% 12101010121000FF10101012101210101218181810121012161218121612
+% 101010101008100E1010101012121618181818181A181818181818181818
+% 181818181818181818181810AF101010FF7D00000000000000003A3A3A3A
+% 3A3A3A9D3A9D9D9D3A9D9D3A9D3A3A9D7F1313131313FF53535353535353
+% 5353FF9D9D9D9D9D9D9D9D3A3A9D3A9D9D9D9D9D9D3A9D9D9D3A3A3A9D9D
+% 9D3A3A9D3A9D3A3A3A3A3A3A3AFF535353535353535353FF3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A7F1313131313FF535353535353535353FF3A3A3A3A9D9D3A
+% 3A3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A
+% 3A9D3AFF535353535353535353FF9D3A9D3A9D9D9D9D9D9D9D9D9D3A3A9D
+% 3A9D3A9D3A9D9D3AFF7D3A3A3A9D3A3A9D3A9D9D9D3A3A3A3A9D3A3A9D7F
+% 13131313131313131313137F3A3A3A3A3A3A3A3A3A9D3A3A9D3A9D9D9D9D
+% 9D3A3A9D9D9D3A3A9D3A9D3A9D3A3A9D9D3A9D3A9D9D9D9D3A9D3A9D3A9D
+% 9D9D9D9D7F1313131313131313137F9D3A9D9D9D9D9D9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A002100000000210010101012C51817181612101010121618
+% 181818101618181816181618181010101012101210101218181810121012
+% 161218121600FF1012101010101010101012181818181012101010101210
+% 1210101010101010101010101012181818181A18181A1818181818171818
+% 1216181818181816181810AF101008FF7D00000000000000003A3A3A3A3A
+% 3A3A9D3A9D9D3A9D9D9D3A3A9D3A9D7F1313131313FF5353535353535353
+% 53FF3A3A9D3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A9D9D9D3A3A9D9D9D9D
+% 3A3A9D3A3A3A3A3A9D3A3A3AFF535353535353535353FF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A7F1313131313131313137F3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F1313131313FF535353535353535353FF3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3AFF535353535353535353FF3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A9D3A3A3A9DFF7D3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A7F
+% 7F7F13131313137F7F7F3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A9D9D3A9D9D
+% 3A3A9D9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D
+% 9D9D3A7F1313131313131313137F9D3A9D3A9D3A3A9D9D3A9D3A3A3A9D3A
+% 3A3A3A3A3A002100000000210010081010C5181818181818181613181818
+% 181818121818181818181818101012101010101010101012181818181012
+% 1010101000FF121010121012101210121010121612161216121813181718
+% 101210101010101010101010101816181818181A18181818181812101210
+% 12181217181818121810AF100810FF7D00000000000000003A3A3A3A3A3A
+% 3A9D3A9D9D3A9D9D9D3A3A9D3A9D3A7F1313131313FF53535353535353FF
+% 9D3A3A9D3A3A3A3A3A3A9D3A9D9D9D9D9D3A9D3A9D9D9D3A3A9D9D9D9D3A
+% 3A9D3A3A3A3A3A9D3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A7F1313131313FF53535353535353FF3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3AFF53535353535353FF3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A9D3A3A3A9DFF7D3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A7F7F7F7F7F3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A9D3A9D9D3A9D9D3A
+% 3A9D9D3A9D3A3A9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D3A9D7F131313131313137F9D9D3A9D3A9D3A3A9D9D3A9D3A3A3A9D3A3A
+% 3A3A3A3A002100000000210008101010C51818181A18181A181818181A18
+% 181818181818181818181810121010121012101210121010121612161216
+% 12181300FF10101210101210181210121010121018181818181618181818
+% 1610101010100A0E10101212181218181818181818181818101012101010
+% 121618121612181610AF10080AFF7D00000000000000003A3A3A3A3A3A3A
+% 9D3A9D9D9D9D9D9D3A9D9D3A3A3A7F1313131313FF535353535353FFFFFF
+% 9D9D3A9D3A9D9D3A9D3A9D9D9D3A9D9D9D9D9D9D9D3A9D9D3A9D3A9D3A9D
+% 3A9D3A9D3A3A3A3A9D3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313137F7F7F3A3A3A3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A7F7F1313131313FF5353535353FFFFFF3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A
+% 3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A9D9D3A9D9D9D9D3A3A9D
+% 3A9D9D9D3A3A9D3A9D9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D
+% 9D9D7F13131313137F7F7F9D9D3A3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A
+% 3A3A3A0021000000002100080A1010C51818181820181818181818181818
+% 181812101210181216181010101210101210181210121010121018181818
+% 181600FF12181018181818181A1818181810101818161216121012101218
+% 181818121010101010101010101818181818181818101018121816121612
+% 1012101210101218AF101010FF7D00000000000000003A3A3A9D3A3A3A9D
+% 3A9D9D3A9D9D9D3A9D3A3A7F7F137F7F1313137FFFFF5353FFFF535353FF
+% FF9D9D3A3A3A3A9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D9D9D9D9D3A3A9D
+% 9D3A9D9D3A3A3A3AFFFF53FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F137F7F1313137F7F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D3A3A
+% 7F7F13137F7F1313137FFFFF53FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF
+% 53FFFF535353FFFF3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D3A9D3A3A9D3A
+% 9D9D9D3A9D9D9D9D9D9D3A3A9D3A9D9D3A9D9D3A9D3A3A9D9D9D9D9D9D9D
+% 9D3A7F7F137F7F1313137F7F9D9D3A9D9D9D9D9D3A9D3A3A3A3A3A9D3A3A
+% 3A3A002100000000210010101010C51816181A181A181A18181A181A1816
+% 1210101210121012101812181018181818181A1818181810101818161216
+% 1200FF101012101818181818181A18181218181818181812101810101218
+% 181010101010101010101210121618181818161210121018101818181810
+% 18101210121012AF121010FF7D00000000000000003A3A3A9D3A3A3A9D3A
+% 9D9D3A9D9D9D3A9D3A7F13131313137F7F7F9D9D3AFFFF53535353535353
+% FF9D3A3A3A3A9D9D9D3A9D9D9D9D9D9D3A9D9D3A3A9D9D9D9D9D3A3A9D9D
+% 3A9D9D3A3A3AFF5353535353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A7F131313131313137F3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A9D3A7F13
+% 13131313137F7F7F3A3A9DFF53535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 5353FFFFFF3A9D3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D9D3A9D3A3A9D3A9D
+% 9D9D3A9D9D9D9D9D9D3A3A9D3A9D9D3A9D9D3A9D3A3A9D9D9D9D9D9D9D9D
+% 3A9D3A7F131313131313137F9D3A9D9D9D9D9D3A9D3A3A3A3A3A9D3A3A3A
+% 3A002100000000210010101010C5C5C5C5C5C5C518181A181A1818181812
+% 101210121012101812101012101818181818181A18181218181818181812
+% 00FF10121010121012181818181818181818181818181718131010101010
+% 121010101010101010101210101212121012101010121012181818181818
+% 101210101010AF121818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D
+% 9D9D9D9D9D3A9D9D7F131313131313137F9D3A9D3AFF53535353535353FF
+% 3A9D3A9D9D3A9D9D9D9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D3A3A9D3A3A
+% 3A3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313137F3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313
+% 13131313137F3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A9D3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A9D9D9D9D3A3A9D9D3A3A
+% 9D3A9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A7F131313131313137F3A3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A
+% 002100000000210018181813C5181818181A181818181818181818181010
+% 101010101012101010121010121012181818181818181818181818181700
+% FF1810101210101818181818181818181818181818181816101012101010
+% 101010101010101210101210121010121010101010101216181818181818
+% 1010101010AF101018FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D
+% 9D3A9D9D3A9D7F1313131313131313137F9D3AFF535353535353535353FF
+% 3A9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D3A3A3A9D3A3A9D
+% 9D9D9DFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D3A9D9D3A9D9D3A9D3A9D
+% 3A9D3A9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A9D3A9D3A9D3A9D9D3A9D3A3A
+% 9D3A3A9D3A9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D9D3A9D3A9D3A
+% 9D3A9D3A3A9D3A9D3A9D3A7F1313131313131313137F9D3A3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313
+% 13131313137F3A3AFF535353535353535353FF3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3AFF535353535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A3A3A9D9D9D3A9D
+% 9D9D9D9D9D9D3A9D3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D
+% 7F1313131313131313137F3A9D9D9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A00
+% 2100000000210010181816C5181818181818181818181818181810101010
+% 1010101210101818101012101018181818181818181818181818181800FF
+% 181818101612181018181818181818181818181818181818181010121018
+% 181010101210101012101010101210101210101010101218181818181810
+% 12101010AF0A1010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D
+% 3A9D9D3A9D7F1313131313131313137F9D3AFF535353535353535353FF3A
+% 9D9D9D9D9D9D9D9D9D3A9D9D9D3A9D3A9D9D9D9D9D9D3A3A3A9D3A3A9D9D
+% 9D9DFF535353535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D3A9D9D3A9D3A9D9D9D9D3A9D3A9D9D3A9D9D3A9D3A9D3A
+% 9D3A9D9D3A3A9D9D3A9D3A9D9D3A9D3A3A9D3A9D3A9D3A9D9D3A9D3A3A9D
+% 3A3A9D3A9D3A9D3A9D3A3A9D3A9D3A9D3A3A9D3A9D3A9D9D3A9D3A9D3A9D
+% 3A9D3A3A9D3A9D3A9D3A7F1313131313131313137F9D3A3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313131313
+% 131313137F3A3AFF535353535353535353FF3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3AFF53535353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% FF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A9D9D9D3A3A3A9D9D9D3A9D9D
+% 9D9D9D9D9D3A9D3A3A9D3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A9D9D9D7F
+% 1313131313131313137F3A9D9D9D9D9D3A9D3A9D3A3A3A3A3A3A3A3A0021
+% 00000000210010101012C518181818181818181718181818121012101010
+% 12101018181818181810161218101818181818181818181818181800FF1A
+% 181812121210181012121018181818181818181216181818101210181818
+% 101018101818181816121012101012161010101010121818181818181012
+% 101010AF100810FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D
+% 9D9D3A9D7F1313131313131313137F3A3AFF535353535353535353FF3A9D
+% 3A3A3A9D9D9D9D9D9D9D9D9D3A3A9D9D3A9D3A3A9D9D3A3A9D9D3A3A3A3A
+% 3AFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A7F1313131313131313137F3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 1313137F3A9DFF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A9D3A9D3A9D9D3A3A9DFF5353535353535353
+% 53FF3A9D3A9D3A9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D9DFF
+% 7D9D9D9D9D9D3A9D9D9D9D9D3A9D9D9D9D3A9D3A9D3A9D3A9D3A9D3A9D9D
+% 3A9D9D3A9D9D3A9D9D3A9D3A9D9D3A9D3A9D9D9D9D3A3A9D9D9D9D3A3A9D
+% 9D9D9D3A9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A3A3A9D9D9D9D3A9D7F13
+% 13131313131313137F3A9D9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A3A002100
+% 000000210008101010C51818171818181818181818181612101010101010
+% 12181818181A181812121210181012121018181818181818181200FF1818
+% 121010101012101010121012181818171818161812181012121012121818
+% 1818181818181A1818181010181818101210101010101818181818161217
+% 1210AF101010FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3A3A9D
+% 9D3A9D9D7F131313131313137F9D3A3A9DFF53535353535353FF9D3A3A9D
+% 9D9D9D3A9D9D9D9D3A9D3A9D9D9D3A9D3A9D3A9D9D3A3A9D9D9D9D9D9D9D
+% 9DFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D9D3A9D3A3A9D9D3A9D9D9D9D3A9D
+% 3A9D3A3A9D3A3A9D3A3A9D3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A3A9D
+% 3A3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D3A9D3A3A
+% 9D3A3A9D3A3A9D3A9D7F131313131313137F3A3A3A9D3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F131313131313
+% 137F3A3A3A3AFF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF7D
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3A9D9D3A9D9D3A9D3A
+% 9D9D9D9D9D3A9D9D9D9D9D9D3A3A9D9D9D3A3A3A9D3A3A9D9D60FF8B7F13
+% 1313131313137F9D3A9D9DC6FFB23A9D3A3A3A3A3A3A3A3A3A3A00210000
+% 0000210010101010C5101218181818181818111813181010101010101218
+% 1818181A18181210101010121010101210121818181718181600FF181810
+% 101010101210101010101216181818181012101210101010101010101818
+% 181818181818181818181818181818101010101012181818181818181818
+% 10AF181210FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D3A3A9D9D
+% 3A9D9D7F131313131313137F9D3A3A9DFF5353535353FFFFFF9D3A3A9D9D
+% 9D9D3A9D9D9D9D3A9D3A9D9D9D3A9D3A9D3A9D9D3A3A9D9D9D9D9D9D9D9D
+% FF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D9D3A9D3A3A9D9D3A9D9D9D9D3A9D3A
+% 9D3A3A9D3A3A9D3A3A9D3A3A3A9D3A3A9D3A9D3A3A3A3A3A3A9D3A3A9D3A
+% 3A3A9D3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A9D3A9D3A3A9D
+% 3A3A9D3A3A9D3A9D7F13131313137F7F7F3A3A3A9D3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F13131313131313
+% 7F3A3A3A3AFF5353535353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53535353535353FF3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF7D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D9D9D9D9D9D3A3A9D9D3A9D9D3A9D3A9D
+% 9D9D9D9D3A9D9D9D9D9D9D3A3A9D9D9D3A3A3A9D3A3A9D9DC8E33A7F1313
+% 1313137F7F7F9D3A9D9D9DE3E33A9D3A3A3A3A3A3A3A3A3A3A0021000000
+% 00210012101010C510101818181818181818181810121010101012161818
+% 181A1818181010101010121010101010121618181818101200FF18181810
+% 10080810101010100B0E0A10101618181818181810181316121612161210
+% 121618181818181818161218101818181818161010101012101010121010
+% AF121818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D9D3A9D9D
+% 7F7F137F7F1313137F7F9D9D3A3A9D9DFFFF53FFFF535353FFFF9D9D3A9D
+% 9D9D9D3A9D9D9D3A9D9D3A9D9D9D3A9D3A9D9D3A9D9D9D9D3A9D9DFFFF53
+% FFFF535353FFFF9D9D9D3A9D3A9D9D3A9D9D9D9D9D9D3A9D3A9D9D3A9D9D
+% 9D9D9D9D3A9D9D3A3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D7F7F137F7F1313137F7F9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D7F7F137F7F1313137F7F9D
+% 9D9D9D9D9DFFFF53FFFF535353FFFF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFF53FFFF535353FFFF9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF7D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D3A3A9D3A9D3A9D9D3A9D9D3A
+% 9D9D9D9D9D9D9D3A9D3A9D9D9D3A9D9D9D9D9D9D9D9DC0FF609D3A7F7F13
+% 7F7F1313137F7F9D9D9DB2FF829D3A3A3A9D3A3A3A3A3A3A002100000000
+% 210018181618C51010181618181A1818181310101810181012181818181A
+% 18181818181010080810101010100B0E0A10101618181800FF1818181612
+% 101010101010101010101010121818181818161018101612171218101210
+% 1218181818181818181018101818181818181818101210101210101010AF
+% 161818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D9D7F13
+% 131313137F7F7F3A9D3A9D3A3A9D9D9D3AFF53535353535353FF9D3A9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D9D9D9D9DFF53535353
+% 53FFFFFF9D9D3A3A9D9D3A9D9D9D3A9D9D9D3A9D9D3A9D9D9D3A9D9D3A9D
+% 9D3A9D9D9D9D3A3A9D9D3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A9D3A3A9D3A9D3A9D9D9D3A9D3A9D3A9D3A3A3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A9D3A3A9D9D3A9D3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A
+% 9D3A9D9D3A9D3A9D3A9D9D9D9D9D9D3A9D7F13131313137F7F7F9D9D9D9D
+% 9D9D9D9D9D9DFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF5353535353FFFFFF9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF7D9D9D9D
+% 3A9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9D9D9D9DFFFFFFFFFF3A9D3A9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D3A3A9D9D3A9D3A9D9D9D9D9D9D
+% 3A3A9D9D3A9D3A9D9D3A9D9D9D9D9D3A9D3A9D9DA3FFDA3A9D3A3A9D7F13
+% 1313131313137F9D3A9DDAFFA33A3A3A3A3A3A3A3A3A3A00210000000021
+% 0018181818C518181818181818181818181012101218161216181818181A
+% 1818181816121010101010101010101010101218181800FF181618181818
+% 181012101010101010101010121618181818121812181218121012101210
+% 12101818181618181018121012101818181818121010121012101210AF12
+% 1818FF7D00000000000000003A3A3A3A3A3A3A9D3A9D9D9D3A9D9D7F1313
+% 13131313137F3A9D3A9D3A3A9D9D9D3AFF53535353535353FF9D3A9D3A9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D9D9D9D9D9DFF5353535353
+% 5353FF9D9D3A3A9D9D3A9D9D9D3A9D9D9D3A9D9D3A9D9D9D3A9D9D3A9D9D
+% 3A9D9D9D9D3A3A9D9D3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A9D3A3A9D3A9D3A9D9D9D3A9D3A9D3A9D3A3A3A3A3A3A9D3A3A
+% 3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A
+% 9D3A3A9D9D3A9D3A7F131313131313137F3A3A3A3A3A3A3A3A3A3A9D3A9D
+% 3A9D9D3A9D3A9D3A9D9D9D9D9D9D3A9D7F131313131313137F9D9D9D9D9D
+% 9D9D9D9D9DFF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF53535353535353FF9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF7D9D9D9D3A
+% 9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9DFFFFFF5353535353FFFFFF9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A9D9D3A9D9D9D3A3A9D9D3A9D3A9D9D9D9D9D9D3A
+% 3A9D9D3A9D3A9D9D3A9D9D9D9D9D3A9D3A9D9DC0FFB23A9D3A3A9D7F1313
+% 13131313137F9D3A9DB2FFC03A3A3A3A3A3A3A3A3A3A0021000000002100
+% 18181818C5181818181818181818181818131618101218181818181A1818
+% 18161818181818101210101010101010101012161800FF18121216181818
+% 12161210101010100A0F1010121818181618171816181618101210101010
+% 121018181812161218101010181818181818121612181018101810AF1012
+% 16FF7D00000000000000003A3A3A3A3A3A3A9D3A9D3A9D9D9D7F13131313
+% 13131313137F3A9D9D3A3A3A9D3AFF535353535353535353FF9D9D3A9D3A
+% 9D9D9D9D9D3A9D3A9D3A9D9D9D3A9D3A3A9D9D9D9D9DFF53535353535353
+% 5353FF9D9D9D9D9D9D3A9D9D9D9D9D9D9D9D3A9D3A9D3A9D9D9D9D9D9D3A
+% 9D3A9D3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D9D9D9D3A3A3A3A3A9D9D3A3A
+% 3A9D9D3A3A3A3A3A9D3A3A3A3A3A9D9D3A3A3A3A3A9D9D3A3A3A9D9D9D3A
+% 3A3A3A9D9D3A3A3A3A9D9D3A3A9D9D3A3A3A3A3A3A9D9D9D3A3A3A9D9D3A
+% 3A3A9D9D9D9D7F1313131313131313137F3A9D3A3A3A3A3A9D9D3A3A3A3A
+% 9D3A3A9D9D9D9D3A3A3A3A9D9D3A7F1313131313131313137F3A3A3A3A3A
+% 3A9D3AFF535353535353535353FF3A9D9D9D3A3A3A3A3A3A9D3A3A3A3A3A
+% 9D9D9D3A3A3A3A9D9D3A3A3AFF535353535353535353FF3A3A9D9D9D9D9D
+% 3A3A3A3A3A3A9D9D3A3A3A3A9D3A3A9D3A3A3A3A9D9D9DFF7D9D3A9D9D3A
+% 9D9D3A9D9D3A9D9D3A9D9D3A3A9DFF5353535353535353535353FF9D9D9D
+% 9D3A9D3A9D9D9D9D9D9D9D3A9D9D9D3A3A9D71D1FFFFB1BD9D9D9D9D9DFF
+% FF3A9D9DFFFF9D9D9D9D3A9DFFFF9D9D3A9DE3E39D3A9D3AC67F13131313
+% 13131313137F9D9D9DC8E33A3A3A3A3A3A3A3A3A3A002100000000210012
+% 161818C518181A1818181A18181818181818181818161818181818181818
+% 12121618181812161210101010100A0F1010121800FF1810101012101216
+% 121810121010121010101010121618181818181818181810101010121010
+% 1012101216121810181018181818181818161218101810181210AF101212
+% FF7D00000000000000003A3A9D3A3A9D3A9D3A9D9D9D3A9D7F1313131313
+% 131313137F3A9D9D3A9D9D9D9DFF535353535353535353FF9D9D9D3A9D3A
+% 9D3A9D3A9D9D9D9D9D9D3A9D3A9D3A3A9D9D9D9D9DFF5353535353535353
+% 53FF9D3A9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D3A9D
+% 9D9D9D3A3A9D9D9D9D3A3A3A3A3A9D3A9D9D9D9D3A3A9D3A3A9D3A9D9D3A
+% 9D9D9D3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D9D3A9D9D9D3A3A
+% 3A3A9D3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A9D9D9D9D3A3A3A9D9D9D9D
+% 9D9D9D9D9D7F1313131313131313137F9D9D9D3A3A3A3A9D9D3A9D9D3A9D
+% 9D3A9D3A3A3A3A3A3A3A3A9D3A7F1313131313131313137F9D3A3A3A3A9D
+% 9D3AFF535353535353535353FF3A3A9D9D9D3A3A3A3A3A9D9D9D9D3A3A9D
+% 9D9D9D9D3A9D9D3A3A3A3AFF535353535353535353FF3A3A3A9D9D9D9D3A
+% 3A3A3A3A3A9D9D9D9D3A3A3A3A3A9D9D9D3A3A9D9D9DFF7D9D9D9D3A9D9D
+% 9D9D9D9D9D9D3A9D9D3A9DFFFF53535353535353535353535353FFFF9D9D
+% 9D9D9D9D9D9D9D9D9D9D3A3A9D9D3A3AD1FFFFFFFFFFFFC8A39D9D9DFFFF
+% 9D9D9DFFFF9D3A9D9D9D3AFFFF3A9D9DB2FFC69D3A3A3A9D7F1313131313
+% 131313137F9D9D9D8BFF609D3A3A3A3A3A3A3A3A00210000000021001212
+% 1818C518181818181818181A181818181818181818181818181818181810
+% 1010121012161218101210101210101010101200FF181612101010101210
+% 121612101216101010101210121818181818181818181612101010101210
+% 12101210121612181818181818161210121012101210121010AF101010FF
+% 7D00000000000000003A3A9D3A3A9D3A9D3A9D9D9D3A9D7F131313131313
+% 1313137F3A9D9D3A9D9D9D9DFF535353535353FFFFFFFF9D9D9D3A9D3A9D
+% 3A9D3A9D9D9D9D9D9D3A9D3A9D3A3A9D9D9D9D9DFF535353535353535353
+% FF9D3A9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9D9D9D3A9D9D
+% 9D9D3A3A9D9D9D9D3A3A3A3A3A9D3A9D9D9D9D3A3A9D3A3A9D3A9D9D3A9D
+% 9D9D3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D9D3A9D9D9D3A3A3A
+% 3A9D3A3A3A3A3A9D9D9D3A9D3A3A3A3A3A3A9D9D9D9D3A3A3A9D9D9D9D9D
+% 9D9D9D9D7F1313131313137F7F7F7F9D9D9D3A3A3A3A9D9D3A9D9D3A9D9D
+% 3A9D3A3A3A3A3A3A3A3A9D3A7F1313131313131313137F9D3A3A3A3A9D9D
+% 3AFF535353535353FFFFFFFF3A3A9D9D9D3A3A3A3A3A9D9D9D9D3A3A9D9D
+% 9D9D9D3A9D9D3A3A3A3AFF535353535353535353FF3A3A3A9D9D9D9D3A3A
+% 3A3A3A3A9D9D9D9D3A3A3A3A3A9D9D9D3A3A9D9D9DFF7D9D9D9D3A9D9D9D
+% 9D9D9D9D9D3A9D9D3A9DFF535353535353535353535353535353FF9D9D9D
+% 9D9D9D9D9D9D9D9D9D3A3A9D9D3A71FFD1AF9D9DA3C6FFC69D9D9DFFFF9D
+% 9D9DFFFF9D3A9D9D9D3AFFFF3A9D9DC0FFB49D3A3A3A9D7F131313131313
+% 137F7F7F9D9D9D66FF829D3A3A3A3A3A3A3A3A0021000000002100101012
+% 18C518181818181818181818181A18181818181818181818181818181612
+% 10101010121012161210121610101010121000FF18181816101010101012
+% 101210181216121010101012161818181818181818121810101010101010
+% 121012101810181818181818181210101210121012101210AF100F10FF7D
+% 00000000000000003A3A3A3A3A3A3A9D3A9D3A9D7F7F137F131313131313
+% 137F9D9D3A9D3A9D9D9D9D3AFF535353FFFF535353FFFF9D9D9D9D3A9D3A
+% 9D3A9D9D9D9D9D9D9D9D3A9D3A9D9D9D9D9DFFFFFF53535353535353FF9D
+% 9D9D9D9D9D9D3A9D9D9D3A3A9D3A9D9D9D3A9D9D3A9D9D9D9D3A9D9D9D9D
+% 3A3A3A9D9D9D9D3A3A3A3A3A3A3A9D9D9D9D3A3A3A3A9D3A3A9D9D9D9D9D
+% 9D3A3A3A3A9D9D3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D9D9D9D3A3A3A3A
+% 3A3A3A3A9D9D9D9D9D9D3A9D3A3A3A3A3A9D9D9D9D3A3A3A9D9D9D9D9D9D
+% 9D9D9D9D7F1313137F7F1313137F7F9D9D3A3A3A3A9D3A3A9D9D9D9D3A3A
+% 3A3A3A3A3A3A3A3A3A9D7F7F7F131313131313137F9D9D9D3A3A3A9D3A3A
+% 3AFF535353FFFF535353FFFF9D3A9D9D9D3A3A3A3A3A9D3A9D9D3A9D9D9D
+% 9D9D9D9D3A3A3AFFFF53FF53535353535353FF9D3A3A3A9D9D9D9D9D3A3A
+% 3A3A9D9D9D9D9D3A3A3A3A3A9D9D3A9D3A9D9D9DFF7D9D9D9D9D3A9D9D3A
+% 9D3A9D9D9D9D9D9DFF5353535353535353535353535353535353FF9D3A9D
+% 3A9D9D3A9D9D9D9D3A3A9D9D3AB1FFA39D3A9D9D3AFFFF9D3A3AFFFF9D9D
+% 3AFFFF9D3A9D3A9D9DFFFF9D9D9DCBFFA93A9D3A3A9D9D7F131313137F7F
+% 1313137F7F9D54FF963A3A3A3A3A3A3A3A3A00210000000021000F101010
+% C5101818181818181A18181A181A18181818181818181818181818181816
+% 101010101012101210181216121010101000FF1818181818121010101010
+% 101818181810101010101012181818181818181818181012101010121010
+% 1010121018181018181818181816121010101210101010AF100A10FF7D00
+% 000000000000003A3A3A3A3A3A3A9D3A9D9D7F1313137F13131313131313
+% 7F9D9D3A9D3A9D9D9D9D9DFF5353FF53535353535353FF9D9D9D9D9D9D3A
+% 9D9D3A9D9D9D9D9D9D9D3A3A9D9D9D3AFF5353FF53535353535353FF9D9D
+% 9D9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D
+% 3A3A9D9D9D9D3A3A3A3A3A9D9D3A3A9D9D3A3A3A3A3A9D3A9D3A3A9D9D9D
+% 3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D9D3A9D9D9D3A3A3A3A9D
+% 9D3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D9D9D3A3A3A3A9D9D9D9D9D9D
+% 9D9D9D7F13137F131313131313137F9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A
+% 3A3A3A3A3A3A3A9D7F13137F131313131313137F9D9D3A3A3A3A9D9D3A3A
+% FF5353FF53535353535353FF9D9D9D9D3A3A3A3A9D3A3A9D9D9D9D9D9D9D
+% 9D3A3A3A3AFF535353FF53535353535353FF9D3A3A3A9D9D3A9D9D3A9D3A
+% 3A9D9D3A9D9D3A3A9D3A3A3A3A9D9D9D9D9D9DFF7D9D9D9D3A9D9D9D9D9D
+% 9D9D3A9D9D9DFF53535353535353535353535353535353535353FF9D3A9D
+% 3A9D9D9D3A9D3A9D3A3A9D3A3A3A9D9D9D3A9DA6FFFF9D3A3AFFFF9D3A9D
+% FFFF9D9D9D9D3A9DFFFF9D9D3AFFFF9D9D9D3A9D9D3A7F1313137F131313
+% 131313137F3AFFFF3A3A3A3A3A3A3A3A3A00210000000021000A101010C5
+% 10121018181818181A181A18201818181818181818181818181818181818
+% 1210101010101018181818101010101000FF181818181010101010101218
+% 1816181818181818181818181A1818181810101816181818181216101210
+% 12101810121010101818181012101012101010121010AF100A08FF7D0000
+% 0000000000003A3A3A3A3A3A3A9D3A9D9D7F131313137F7F1313137F7F9D
+% 9D9D3A9D3A9D9D9D9D9D9DFFFFFF53535353535353FF9D9D9D9D9D9D3A9D
+% 9D3A9D9D9D9D9D9D9D3A3A9D9D9D3AFF535353FFFF535353FFFF9D9D9D9D
+% 9D9D9D3A9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A9D9D9D9D9D9D9D3A
+% 3A9D9D9D9D3A3A3A3A3A9D9D3A3A9D9D3A3A3A3A3A9D3A9D3A3A9D9D9D3A
+% 3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A9D3A9D9D3A9D9D9D3A3A3A3A9D9D
+% 3A3A3A3A3A9D9D9D3A3A3A3A3A3A9D9D9D9D9D3A3A3A3A9D9D9D9D9D9D9D
+% 9D9D3A7F7F7F131313131313137F9D3A3A3A3A9D9D3A9D9D3A9D3A3A3A3A
+% 3A3A3A3A3A3A9D7F1313137F7F1313137F7F3A9D9D3A3A3A3A9D9D3A3A3A
+% FFFFFF53535353535353FF9D9D9D9D3A3A3A3A9D3A3A9D9D9D9D9D9D9D9D
+% 3A3A3A3AFF53535353FFFF535353FFFF9D9D3A3A3A9D9D3A9D9D3A9D3A3A
+% 9D9D3A9D9D3A3A9D3A3A3A3A9D9D9D9D9D9DFF7D9D9D9D3A9D9D9D9D9D9D
+% 9D3A9D9D9DFF53535353535353535353535353535353535353FF9D3A9D3A
+% 9D9D9D3A9D3A9D3A3A9D3A3A3A9DA3B282DAFFFF8B9D3A3AFFFF9D3A9DFF
+% FF9D9D9D9D3A9DFFFF9D9D3AFFFF9D9D9D3A9D9D3A9D7F7F137F13131313
+% 1313137F3AFFFF3A3A3A3A3A3A3A3A3A00210000000021000A081010C510
+% 101012161818181818181A18181818101012101810181618181818181010
+% 10101010121818161818181818181800FF16181818101012081010101012
+% 181818181818181818181A181A1818161212181818181818161210121010
+% 121216181010121010101212161218101210101012AF101010FF7D000000
+% 00000000003A3A3A3A3A3A3A9D3A9D7F131313131313137F7F7F3A9D9D9D
+% 9D9D3A3A9D9D9D9D9D3A9DFF535353535353535353FF9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D3A9D9D9D9DFF535353535353FFFFFFFF9D3A9D9D3A9D
+% 3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9D3A9D3A9D9D3A9D3A9D9D9D3A3A3A
+% 9D9D9D3A3A3A3A3A3A9D3A3A3A9D9D3A3A3A3A3A9D9D3A3A3A9D9D3A3A3A
+% 3A3A9D3A3A3A3A3A9D9D3A3A3A3A3A9D9D3A3A3A9D9D9D3A3A3A3A9D9D3A
+% 3A3A3A3A9D9D9D9D3A3A3A3A3A9D9D9D9D9D3A3A3A3A3A9D9D9D9D9D9D9D
+% 3A3A3A7F1313131313131313137F3A3A3A3A9D9D9D3A3A3A9D3A3A3A3A3A
+% 9D9D9D9D9D7F1313131313137F7F7F7F3A3A9D9D3A3A3A3A9D9D3A3A3A3A
+% FF535353535353535353FF3A9D9D3A3A3A3A9D9D9D9D3A3A9D9D9D9D3A3A
+% 9D3AFF53535353535353FFFFFF3A9D9D9D3A3A3A9D3A3A9D3A3A3A3A3A9D
+% 3A3A9D9D3A3A3A3A3A9D9D3A3A3A9D9D9DFF7D9D9D3A9D3A3A9D3A9D9D9D
+% 9D9D9D3AFF53535353535353535353535353535353535353FF9D3A9D9D9D
+% 9D9D9D9D9D9D9D3A9D3A3AAFD1FFFFFFFFFFDAA63A9D3AFFFF9D9D9DFFFF
+% 9D9D3A9D9D3AFFFF9D3A9DFFFF9D9D9D3A9D9D9D9D3ACB7F131313131313
+% 1313137FFFFF3A3A9D3A3A3A3A3A3A002100000000210010101010C51010
+% 10101218181818181A181818181810121012101210121216181818101012
+% 081010101012181818181818181800FF121018121612100E0A0810101018
+% 18181A18181A181818181818181812101018181818181818181010181318
+% 1618181210101010101010181816181810181216AF101010FF7D00000000
+% 000000003A3A3A3A3A3A3A9D3A9D7F1313131313131313137F9D3A9D9D9D
+% 9D9D9D9D9D9D9D9D9D3AFF535353535353FFFFFFFF9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9DFF535353535353535353FF9D9D9D9D9D9D9D
+% 3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A3A9D
+% 9D9D3A9D3A9D3A3A3A3A9D3A9D9D3A9D3A3A9D3A3A3A3A9D3A9D3A3A9D3A
+% 3A3A3A9D3A9D9D3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A9D
+% 3A9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A9D3A3A9D9D9D9D9D3A
+% 3A9D7F1313131313137F7F7F7F3A3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A
+% 3A3A3A3A7F1313131313131313137F3A3A9D3A3A3A3A3A9D9D3A3A3A3AFF
+% 53535353535353FFFFFF3A3A3A3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3A
+% 3AFF535353535353535353FF3A9D9D3A3A3A9D9D9D3A3A9D3A3A3A3A3A3A
+% 3A9D3A3A3A3A3A9D9D9D3A3A3A9D9D9DFF7D9D9D9D9D9D9D9D9D3A9D3A9D
+% 9D9DFF535353535353535353535353535353535353535353FF9D9D3A9D3A
+% 9D9D3A9D9D9D3A9D3AB2FFFFFFFFDAC0AF9D3A9D9D9DFFFF3A9D3AFFFF9D
+% 9D9D9D3A9DFFFF9D9D9DFFFF3A9D9D3A9D9D9D3AAFFF7F1313131313137F
+% 7F7F7FFFFF3A3A3A3A3A3A3A3A3A002100000000210010101010C5100E10
+% 10121018181818181A18181818101216121018101210121018121612100E
+% 0A081010101818181A18181A1800FF101210181818181010100A10101818
+% 18181818181818181A181818181010121012101818181810181218161818
+% 18181618101010101012181818181618121618AF121010FF7D0000000000
+% 0000003A3A3A3A3A3A3A9D3A7F7F1313131313131313137F9D3A9D9D9D9D
+% 9D9D9D9D9D9D9D9D3AFF53535353FFFF535353FFFF9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9DFFFF535353535353535353FF9D9D9D9D9D9D9D3A
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A9D9D9D9D9D3A3A9D9D
+% 9D3A9D3A9D3A3A3A3A9D3A9D9D3A9D3A3A9D3A3A3A3A9D3A9D3A3A9D3A3A
+% 3A3A9D3A9D9D3A9D3A3A3A3A3A9D3A3A3A3A9D3A3A9D3A3A3A3A3A3A9D3A
+% 9D9D9D9D9D9D3A9D9D3A9D9D9D9D3A3A3A9D3A3A9D3A3A9D9D9D9D9D3A3A
+% 9D7F131313137F7F1313137F7F3A3A3A3A3A3A3A3A3A9D9D3A3A3A3A3A3A
+% 3A3A7F7F1313131313131313137F3A3A9D3A3A3A3A3A9D9D3A3A3A3AFF53
+% 53535353FFFF535353FFFF3A3A3A3A3A9D3A3A3A3A3A9D9D9D3A3A3A3AFF
+% FF535353535353535353FF3A9D9D3A3A3A9D9D9D3A3A9D3A3A3A3A3A3A3A
+% 9D3A3A3A3A3A9D9D9D3A3A3A9D9D9DFF7D9D9D9D9DFFFFFFFFFFFFFFFFFF
+% FFFF535353535353535353535353535353535353535353FFFFFFFFFFFFFF
+% FFFFFF9D9D3A9D3ADAFFCBB2489D9D9D9D3A9D9D9DFFFF3A9D3AFFFFA69D
+% 9D9D3A9DFFFF9D9D9DCBFF549D9D3A9D9D9D3AE3FF7F131313137F7F1313
+% 137F7F963A3A3A3A3A3A3A3A3A002100000000210010101010C510101010
+% 101212101818181818181818181810181112101610101210181818181010
+% 100A1010181818181818181800FF10101010121018181810101012181818
+% 18181818181818181A181818101210101010121012101018101818181818
+% 181818181818181010181218101212101218AF101810FF7D000000000000
+% 00003A3A3A3A9D3A3A9D7F13137F131313131313137F9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9DFF5353FF53535353535353FF9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9DFF5353FF53535353535353FF9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D7F13137F131313131313137F9D9D9D9D9D9D9D9D9D9D9D9D9D3A3A9D9D
+% 7F13137F131313131313137F9D3A3A3A3A3A3A9D3A3A9D3A3A3A3A3AFF53
+% 5353FF53535353535353FF3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3AFF5353
+% FF53535353535353FF3A3A3A3A3A3A3A9D9D3A3A3A9D3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A9D9D9D9DFF7D9D9D9D9DFFFFFFFFFFFFFFFFFFFF
+% FF535353535353535353535353535353535353535353FFFFFFFFFFFFFFFF
+% FFFF9D9D9D3A3AFFFF9D9D9D9D9DA3FFE39D9D9DFFFF9D9D9DFFFF719D9D
+% 9D3A54FFFF9D3A9DC0FFB29D9D3A9D9D9DC6FF779D7F13137F1313131313
+% 13137F3A3A3A3A3A3A3A3A3A002100000000210018101210C51010101010
+% 101012181818181818181018101812181018121010101010121018181810
+% 101012181818181818181800FF1012101010101218181810121618181818
+% 181818181818181818181012101010101010101210121210181018181818
+% 1818181A18181812161216121010101012AF181012FF7D00000000000000
+% 003A3A3A3A3A3A3A9D7F13137F131313131313137F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFF5353FF53535353535353FF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF5353FF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 7F13137F131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A3A7F
+% 13137F131313131313137F3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF5353
+% 53FF53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF5353FF
+% 53535353535353FF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9D9DFF7D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF
+% 535353535353535353535353535353535353535353FF9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9DD1FFC0A39D9DA9D1FFC09D9D9DFFFF9D9D9DFFFFDAC6A69D
+% A9D1FFC69D9D9DB2FFC09D9D9D9D9DAFFFDA9D9D7F13137F131313131313
+% 137F3A3A9D3A3A3A3A3A3A002100000000210010121010C5101010101012
+% 101012121818161210101212161216181818121012101010101218181810
+% 1216181818181818181800FF1810121010100E1816121810181618161818
+% 161818181818181818121012101010101218181812101012121612181618
+% 18181818181818181818181018121010AF101216FF7D0000000000000000
+% 3A3A3A3A3A3A3A7F131313137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3AFFFF535353535353FFFFFFFF3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF53535353FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A9D3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 7F7F1313131313137F7F7F7F3A3A3A3A3A3A9D3A3A3A3A9D3A9D3A7F1313
+% 13137F7F1313137F7F9D3A9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFFFFFF
+% 535353535353FFFFFFFF9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF53535353FF
+% FF535353FFFF9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9D9D9D9D9D9D9D9DFF7D9D9D9D9D9D9D9D9D9D9D9D9D9D9DFF53
+% 5353535353535353535353535353535353535353FF9D9D9D9D9D9D9D9D9D
+% 9D9D9D9D9DA6E3FFFFFFFFFFFFDA9D9D9D9DFFFF9D9D9DFFFFA6E3FFFFFF
+% FFFFA99D9D9D9DFFE39D9D9D9D9DE3FFA99D9D9D7F7F1313131313137F7F
+% 7F7F3A9D3A3A3A3A3A3A002100000000210012161210C510101010101012
+% 1012101210121010101010121818181818181810121010100E1816121810
+% 18161816181816181800FF181810100A100A101010101812121218181818
+% 181818181818181810101010101216181818181810121010121612181812
+% 10101018181818181818181818180AAF101216FF7D00000000000000003A
+% 3A3A3A3A3A7F7F1313131313137F7F7F7F3A9D3A9D9D9D3A3A9D3A9D9D3A
+% 9D3A9D3A3A9D9D9DFF53535353FFFF535353FFFF9D9D3A3A9D9D3A3A9D3A
+% 9D9DFFFFFF535353535353FFFFFFFF3A3A3A9D3A9D3A3A9D3A9D3A3A3A3A
+% 3A3A3A9D3A3A9D3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A
+% 3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 7F131313137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F131313
+% 1313137F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53
+% 535353FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353535353
+% FFFFFFFF3A3A3A3A3A3A3A3A3A9D3A3A9D3A3A3A9D3A3A3A9D3A3A3A9D3A
+% 3A3A9D3A3A3A3A3A3A3A3AFF7D3A3A3A9D3A3A3A9D3A3A9D3A9D3A3AFF53
+% 535353535353535353535353535353535353FF3A9D3A3A3A9D3A9D3A9D3A
+% 9D3A3A9D3A48C0B1FFFFD1719D3A9D3A3AFFFF3A3A9DFFFF9DA6CBFFFFC6
+% A99D9D3A9D9D82FFB23A9D9DC6FFBD3A9D9D3A9D7F131313137F7F131313
+% 7F7F3A3A3A3A3A3A3A002100000000210012161210C51010101010101010
+% 10101012101010101010121018181A181A181810100A100A101010101812
+% 121218181818181800FF181818101010100B101012101818181818181818
+% 181010181216121012101010101218181818181A18181216121818121018
+% 1818181818181816121816181818AF101210FF7D00000000000000003A3A
+% 3A3A3A7F137F1313131313131313137F3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 9D3A3A9D3A3A3AFF535353FF53535353535353FF3A3A3A3A3A3A9D3A3A3A
+% FF5353FF535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9D3A3A3A7F
+% 1313137F131313131313137F3A3A9D3A9D3A9D3A9D9D3A7F137F13131313
+% 13131313137F9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9DFF5353
+% 53FF53535353535353FF9D9D9D9D9D9D9D9D9D9DFF53FF53535353535353
+% 5353FF9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D9D
+% 9D3A9D3A3A3A9D9D9D9DFF7D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9DFF5353
+% 5353535353535353535353535353535353FF9D3A9D3A9D9D3A9D9D9D3A9D
+% 9D9D9D3A3A9D3A9D9D3A9D3A9D3A9D3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A
+% 3A3A9D3A9D48FFB19D3A3A9D3A9D3A9D3A9D3A7F1313137F131313131313
+% 137F3A3A3A3A3A3A002100000000210012101818C5101216121818181810
+% 10121010121010101010121818181818181818101010100B101012101818
+% 1818181818181800FF181818101210101010101012101210181818181818
+% 181211181012101012101010101818181818181812101210181810121012
+% 18101818181818121012121018AF181012FF7D00000000000000003A3A3A
+% 3A3A7F13137F131313131313137F3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D
+% 3A3A9D3A3A3A3AFF5353FF535353535353FFFFFF3A3A3A3A3A9D3A3A3AFF
+% 535353FF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A9D3A3A3A9D3A3A3A9D7F
+% 13137F1313131313137F7F7F3A9D3A9D3A9D3A9D9D7F7F13137F13131313
+% 1313137F9D9D9D9D9D9D9D3A9D9D3A9D9D9D9D9D9D9D9D9D9D9D9DFF5353
+% FF5353535353FFFFFF9D9D9D9D9D9D9D9D9D9DFF5353FF53535353535353
+% FF9D9D9D9D9D9D9D9D9D9D3A3A9D3A9D3A9D3A9D3A9D3A3A9D9D3A9D9D9D
+% 3A9D3A3A3A9D9D9D9DFF7D9D9D9D3A9D9D9D9D9D9D3A9D3A9D9DFF535353
+% 53535353535353535353535353535353FF9D3A9D3A9D9D3A9D9D9D3A9D9D
+% 9D9D3A3A9D3A9D9D3A9D3A9D3A9D3A9D9D9D9D3A9D3A3A3A3A3A3A3A3A3A
+% 3A9D3A9D3AC0FFB23A3A9D3A9D3A9D3A9D3A3A7F13137F13131313137F7F
+% 7F3A3A3A3A3A3A002100000000210010121612C518181818181818181218
+% 161812101010101010101012181818181818101210101010101012101210
+% 18181818181800FF16181216121012101010121010101018181818181818
+% 181812161216121012101218181818181A18181010101010101010101010
+% 121012101210101010101018AF121012FF7D00000000000000003A3A3A7F
+% 7F1313137F131313131313137F3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF53FF5353535353FFFF535353FFFF3A3A3A3A3AFFFF5353
+% 5353FF53535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A
+% 3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A9D3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F13
+% 7F13131313137F7F1313137F7F3A3A3A3A3A7F7F7F1313137F1313131313
+% 13137F3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3AFF53FF53
+% 535353FFFF535353FFFF3A3A3A3A3A3AFFFF535353FF53535353535353FF
+% 3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A9D3A9D3A3A3A3AFF7D3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3AFF535353
+% 5353535353535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF3A3A3A3A9D3A3A3A3A3A3A3A9D3A
+% 3A3A3A3A3AC8C83A3A3A3A3A3A3A3A3A3A9D7F137F131313137F7F131313
+% 7F7F3A3A3A3A002100000000210010121018C51316181818181818181818
+% 181618181818101010101010181816181216121012101010121010101018
+% 181818181800FF1812161218101012101210101010101012181818181818
+% 18101812101210101217181A18181A181818181010101010101010101010
+% 1010101818121810101012AF121010FF7D000000000000007F7F7F7F137F
+% 131313137F7F1313137F7F3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A
+% 3A3A3A3A3A3AFFFF53535353FF5353535353FFFFFFFFFFFFFF53FF535353
+% 5353FFFF535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F7F
+% 131313137F13131313137F7F7F7F7F7F7F13137F131313137F7F1313137F
+% 7F3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3AFFFF5353
+% 53FF5353535353FFFFFFFFFFFF3AFF53FF53535353FFFF535353FFFF3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 535353535353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A59FF8B3A3A3A3A3A3A3A3A3A3A3A7F7F1313137F13131313137F
+% 7F7F3A7F7F7F2100000000210010101216C518181818181818181A181818
+% 121818181818121010101012181812161218101012101210101010101012
+% 1818181800FF101018111818181818181818181818181818181818181818
+% 1818111810101218181818181A181A181818161010101010101010101010
+% 10181718181816121010AF101210FF7D00000000007F7F137F7F7F137F13
+% 13131313137F7F7F7F3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF53535353FF535353FFFF53FFFF53FFFFFF53FF53535353
+% 535353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 9D3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A9D3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F13
+% 1313137F1313137F7F137F7F137F7F7F13137F1313131313137F7F7F7F3A
+% 3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3AFF535353
+% FF535353FFFF53FFFF5353FFFFFF53FF535353535353FFFFFFFF3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF535353
+% 53535353535353535353FFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313137F1313137F7F1313
+% 7F7F1313137F7F000000210012101210C516181818181818181818181010
+% 121018181818181818101012101018111818181818181818181818181818
+% 18181800FF121810181018181018181818181A1818181818181A18181818
+% 161818131810181818181A18181818181818181012101010101010101010
+% 181818181818181810AF161816FF7D000000007F13137F137F1313137F13
+% 1313131313137F3A3A3A3A3AFFFFFFFFFFFF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFF5353FF535353FF5353FF5353FF53FF535353FF53535353
+% 535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A9D3A3A3A9D3A3A9D3A3A9D3A3A3A3A3A3A3A9D3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A7F13
+% 137F1313137F13137F13137F137F131313137F131313131313137F3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53FF53
+% 5353FF5353FF535353FF53FF535353FF53535353535353FF3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF535353
+% 5353535353535353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F1313137F1313137F13
+% 1313131313137F0000210018161216C51812181618181618181210121010
+% 121018C5C5C5C5C5C51010121810181018181018181818181A1818181818
+% 181A00FF1810121812161218101818181818181818181A18181818181818
+% 18181018101818181818AF1818181818AF18101810121010101010101012
+% 1210121818181810AF181818FF7D000000007F13137F137F1313137F1313
+% 13131313137F3A3A3A3A3A3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFF5353FF535353FF5353FF5353FF53FF535353FF5353535353
+% 5353FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A7F1313
+% 7F1313137F13137F13137F137F131313137F131313131313137F3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53FF5353
+% 53FF5353FF535353FF53FF535353FF53535353535353FF3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFFFF53
+% 53535353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F1313137F1313137F1313
+% 13131313137F0000210018181812C5101210181218181810101810181010
+% 12121818C5C5181810121810121812161218101818181818181818181A18
+% 1800FF17181612161012161210181818181818181A181818181818181818
+% 18181018181818181AAF1818101812AF1810121018101210121010101010
+% 10101012181812AF181818FF7D0000007F13137F13137F131313137F7F13
+% 13137F7F3A3A3A3A3A3A3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3AFFFFFF5353FF5353FF5353FF5353FF53535353FFFF535353FF
+% FF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A9D3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F7F
+% 13137F13137F13137F13137F13131313137F7F1313137F7F3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF5353FF
+% 5353FF535353FF5353FF53535353FFFF535353FFFF3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFFFF
+% FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F13137F1313137F13131313
+% 13131313137F00210018181816C510121012101216121818181818181010
+% 101012C5C51816181817181612161012161210181818181818181A181818
+% 00FF18181818121010101010121618181818181818181A1818181A181818
+% 18181818181818AFAFAFAFAFAFAFAF101012101818181010AFAFAFAFAFAF
+% AFAFAFAFAFAFAF181018FF7D0000007F13137F1313137F13131313137F7F
+% 7F3A3A3A3A3A3A3A3A3A3AFFFF3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3AFFFF53FF5353FF5353FF535353FF5353535353FFFFFF3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F7F
+% 137F13137F13137F1313137F1313131313137F7F7F3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFF53FF53
+% 53FF535353FF535353FF5353535353FFFFFF3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3AFF7D3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A
+% 3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A7F137F1313137F1313131313
+% 131313137F00210010181318C5C5C5C5C5C5C5C5C5C5C5C5C51818181818
+% 1210C5C51018181818181818121010101010121618181818181818181A00
+% FF10121810101010101010101012181818181818181818181A181A181818
+% 181818181818AFAFAFAFAFAFAFAF10101010181210181012101010101012
+% 101218181817181618FF7D0000007F13137F1313137F131313131313137F
+% 00000000000000000000FFFF000000000000000000000000000000000000
+% 000000000000FF53FF5353FF5353FF535353FF53535353535353FF000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000007F13
+% 7F13137F13137F1313137F131313131313137F0000000000000000000000
+% 00000000000000000000000000000000000000000000000000FF53FF5353
+% FF535353FF535353FF53535353535353FF00000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00FF7D000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000007F137F1313137F131313131313
+% 1313137F0021001618161816121012101012101012101210121818181818
+% 10C5C51212181210121810101010101010101012181818181818181800FF
+% 1210101010100810101010101010101218161818181A181A181818181817
+% 181818181818181818181818AF1010101010101210121010181010101010
+% 1218181818181818FF7D000000007F13137F1313137F7F1313137F7F0000
+% 000000000000000000FFFF00000000000000000000000000000000000000
+% 000000000000FFFFFF5353FF5353FF535353FFFF535353FFFF0000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000007F7F
+% 7F13137F13137F1313137F7F1313137F7F00000000000000000000000000
+% 00000000000000000000000000000000000000000000000000FFFFFF5353
+% FF535353FF535353FFFF535353FFFF000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% FF7D00000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000007F7F7F1313137F131313131313
+% 137F00002100181818181818181810181010121010121010101210181818
+% C5C51010101012101010101008101010101010101012181618181800FF10
+% 1012101010100A0F0A10101010101010121618181820181A181818181818
+% 1216181818181818181818AF101010101010101010181818181712101816
+% 1818181A181818FF7D000000007F13137F13131313137F7F7F0000000000
+% 000000000000FFFFFFFF0000000000000000000000000000000000000000
+% 00000000000000FF5353FF5353FF5353535353FFFFFF0000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000000000000000000000000000007F
+% 13137F13137F13131313137F7F7F00000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000FF5353FF
+% 535353FF5353535353FFFFFF000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000000000FF
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7F1313137F13131313131313
+% 7F0000210018181818181818181812101210101210121012101012C5C5C5
+% C516121010101012101010100A0F0A101010101010101216181800FF1012
+% 1010101010100A1010080A10081010101818181A18181818181818101810
+% 181217181818181818181010101010101010101218181818181818181818
+% 181818181818FF7D00000000007F7F137F7F1313137F7F00000000000000
+% 00000000000000FFFF000000000000000000000000000000000000000000
+% 00000000000000FFFF53FFFF53FFFF535353FFFF00000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 00000000000000000000000000000000000000000000000000000000007F
+% 7F137F7F137F7F1313137F7F000000000000000000000000000000000000
+% 0000000000000000000000000000000000000000000000000000FFFF53FF
+% FF5353FFFF535353FFFF0000000000000000000000000000000000000000
+% 00000000000000000000000000000000000000000000000000000000FFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F7F13137F7F1313137F7F00
+% 00002100181818161818181818161216101210101010101010101012C5C5
+% 1818181810121010101010100A1010080A100810101018181800FF181216
+% 12181818101010101008100810101012181818181A181818181818101210
+% 10181812181812161216121010100A10101012181818181818181818181A
+% 1818181818FF7D000000000000007F7F7F7F7F7F00000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 0000000000000000FFFFFFFFFFFFFFFFFF00000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 7F7F7F7F7F7F7F7F7F000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000FFFFFF
+% FFFFFF00FFFFFF0000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000007F7F7F007F7F7F00000000
+% 002100181818121018181818181210121018101812121612101010181818
+% 18181818121612181818101010101008100810101012181800FF18161210
+% 181618101210101010080A10101010101818181818181817121012161218
+% 101210101010121018121012101010101012181818181A18181818181818
+% 18181818FF7D000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 210018181010121818171818101810181018181818181818181210121218
+% 161818161210181618101210101010080A10101010101800FF1012101012
+% 181818181012101010101010101218181818181818181812101218121012
+% 10121012101018121612101010101010101818181A181A18181A181A1818
+% 181810FF7D00000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000021
+% 00181012101010181218101218181818181818181A181818181210101012
+% 1810121010121818181810121010101010101012181800FF181012101210
+% 101012101210100A08101010101012101218101810121010101010181818
+% 181010101010101210121810181012121818181A18181818181818181818
+% 1010FF7D0000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000000000
+% 000000000000000000000000000000000000000000000000000000002100
+% 101010101010101010121010101012101818181818181818181818181018
+% 181012101210101012101210100A0810101010101200FF18181810101010
+% 121012161010101010101010101010121012121010101010101018181818
+% 181810181010101012101816121816181818181818181818181818181810
+% 10FF7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D
+% 7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D210010
+% 101010101010101012101210121018181718181618181818181818181818
+% 181810101010121012161010101010101010101000FF1818171818181810
+% 101210121010101010101010101010101010101010081010101218181818
+% 181818101010101010181212161818181A16181818181618181818101210
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001210
+% 101010101210181818101810181018121818181818181818181818181818
+% 1718181818101012101210101010101010101000FF121818181818181012
+% 10181018181612181818121010101210101010100A081010181818181818
+% 181818121010101012101012121818181818161818181818161310101010
+% 101012161818181818181210121012101810181818181818181818121818
+% 18181818101210181018181612181818121010101210101010100A081010
+% 181818181818181818121010101012101012121818181818161818181818
+% 161310101010101012161818181818181210121012101810181818181818
+% 181818121818181818181012101810181816121818181210101012101010
+% 10100A081010181818181818181818121010101012101012121818181818
+% 161818181818161310101010101012161818181818181210121012101810
+% 181818181818181818121818181818181012101810181816121818181210
+% 10101210101010100A081010181818181818181818121010101012101012
+% 121818181818161818181818161310101010101012161818181818181210
+% 121012101810181818181818181818121818181818181012101810181816
+% 12181818121010101210101010100A081010181818181818181818121010
+% 101012101012121818181818161818181818161310101010101012161818
+% 181818181210121012101810181818181818181818121818181818181012
+% 10181018181612181818121010101210101010100A081010181818181818
+% 181818121010101012101012121818181818161818181818161310101010
+% 101012161818181818181210121012101810181818181818181818121818
+% 18181818101210181018181612181818121000FF10121012101212101012
+% 121618181818181818181818101818131610101010101210181818181818
+% 181010101010101010101010121018181818181818181818101210101010
+% 1018181818181A1818181010101010121012101812161818181010121012
+% 101212101012121618181818181818181818101818131610101010101210
+% 181818181818181010101010101010101010121018181818181818181818
+% 1012101010101018181818181A1818181010101010121012101812161818
+% 181010121012101212101012121618181818181818181818101818131610
+% 101010101210181818181818181010101010101010101010121018181818
+% 1818181818181012101010101018181818181A1818181010101010121012
+% 101812161818181010121012101212101012121618181818181818181818
+% 101818131610101010101210181818181818181010101010101010101010
+% 1210181818181818181818181012101010101018181818181A1818181010
+% 101010121012101812161818181010121012101212101012121618181818
+% 181818181818101818131610101010101210181818181818181010101010
+% 101010101010121018181818181818181818101210101010101818181818
+% 1A1818181010101010121012101812161818181010121012101212101012
+% 121618181818181818181818101818131610101010101210181818181818
+% 181010101010101010101010121018181818181818181818101210101010
+% 1018181818181A1818181010101010121012101812161818181010121012
+% 101212101012121618181818181818181800FF1210101210101010181010
+% 121818181818181A181A1818181718181218181810101810181218181012
+% 101210101818101010101010121218181818181818181818101210101010
+% 1818181A1818181818121010101010121010101012101818101210101210
+% 101010181010121818181818181A181A1818181718181218181810101810
+% 181218181012101210101818101010101010121218181818181818181818
+% 1012101010101818181A1818181818121010101010121010101012101818
+% 101210101210101010181010121818181818181A181A1818181718181218
+% 181810101810181218181012101210101818101010101010121218181818
+% 1818181818181012101010101818181A1818181818121010101010121010
+% 101012101818101210101210101010181010121818181818181A181A1818
+% 181718181218181810101810181218181012101210101818101010101010
+% 1212181818181818181818181012101010101818181A1818181818121010
+% 101010121010101012101818101210101210101010181010121818181818
+% 181A181A1818181718181218181810101810181218181012101210101818
+% 1010101010101212181818181818181818181012101010101818181A1818
+% 181818121010101010121010101012101818101210101210101010181010
+% 121818181818181A181A1818181718181218181810101810181218181012
+% 101210101818101010101010121218181818181818181818101210101010
+% 1818181A1818181818121010101010121010101012101818101210101210
+% 101010181010121818181818181A181A00FF181812101012101212101210
+% 1018181618181A1818181818181818171818181612121012101010181810
+% 101212181018181010101210101818181A18181818181816121012121818
+% 181818181A18FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A18181818181816121012121818
+% 181818181A18181810100B0E10101010100E101010121818181812101012
+% 1012121012101018181618181A181800FF18181818121010181010101012
+% 181810101818181818121012101818181818181810101010101018181012
+% 101010181818181816121612181818181818181818181810181316181818
+% 18181A181AFF1812101010101010100A100A101012161818181818121010
+% 1810101010121818101018181818181210121018FF181818181810101010
+% 101018181012101010181818181816121612181818181818181818181810
+% 181316181818FF181A181A181812101010101010100A100A101012161818
+% 18181812101018101010101218181010181818181812FF12101818181818
+% 181810101010101018181012101010181818181816121612181818181818
+% 1818181818101813FF18181818181A181A181812101010101010100A100A
+% 101012161818181818121010181010101012181810101818FF1818121012
+% 101818181818181810101010101018181012101010181818181816121612
+% 18181818181818181818FF1018131618181818181A181A18181210101010
+% 1010100A100A1010121618181818181210101810101010121818FF101818
+% 181818121012101818181818181810101010101018181012101010181818
+% 181816121612181818181818FF181818181018131618181818181A181A18
+% 1812101010101010100A100A10101216181818181812101018101010FF12
+% 181810101818181818121012101818181818181810101010101018181012
+% 1010101818181818161216121818FF181818181818181810181316181818
+% 18181A181A181812101010101010100A100A101012161818181818121010
+% 181010101012181810101818181800FF1618181818181018181810121010
+% 10181816181818161810101018131618181A181818121010101810181210
+% 18181318181818181818181818181818181818181018101216181818181A
+% 181A1818FF181818181810100A1010101010101012181618181818181018
+% 18181012101010181816181818161810101018FF1618181A181818121010
+% 101810181210181813181818181818181818181818181818181810181012
+% 1618181818FF181A181818181818181810100A1010101010101012181618
+% 181818181018181810121010101818161818181618FF101018131618181A
+% 181818121010101810181210181813181818181818181818181818181818
+% 18181018101216FF1818181A181A181818181818181810100A1010101010
+% 1010121816181818181810181818101210101018181618FF181618101010
+% 18131618181A181818121010101810181210181813181818181818181818
+% 181818181818181810FF101216181818181A181A18181818181818181010
+% 0A101010101010101218161818181818101818181012101010FF18161818
+% 18161810101018131618181A181818121010101810181210181813181818
+% 1818181818181818181818FF18181018101216181818181A181A18181818
+% 1818181810100A1010101010101012181618181818181018181810FF1010
+% 10181816181818161810101018131618181A181818121010101810181210
+% 18181318181818181818181818FF1818181818181018101216181818181A
+% 181A181818181818181810100A1010101010101012181618181818181018
+% 1818101210101018181618181800FF121818181818181818171210101010
+% 121012181018121818181812161812161818181818101810121012161818
+% 1018181818181818181818181818181A1818181818181818181818181818
+% 181818FF1216181812101010121010101010101012121818181818181818
+% 171210101010121012181018121818181812FF1812161818181818101810
+% 1210121618181018181818181818181818181818181A1818181818181818
+% 18181818FF18181818181216181812101010121010101010101012121818
+% 1818181818181712101010101210121810181218FF181812161812161818
+% 1818181018101210121618181018181818181818181818181818181A1818
+% 181818181818FF1818181818181818181216181812101010121010101010
+% 10101212181818181818181817121010101012101218FF18121818181812
+% 161812161818181818101810121012161818101818181818181818181818
+% 1818181A18181818FF181818181818181818181818181216181812101010
+% 121010101010101012121818181818181818171210101010FF1012181018
+% 121818181812161812161818181818101810121012161818101818181818
+% 1818181818181818181AFF18181818181818181818181818181818181216
+% 1818121010101210101010101010121218181818181818181712FF101010
+% 121012181018121818181812161812161818181818101810121012161818
+% 101818181818181818181818FF18181A1818181818181818181818181818
+% 181818181216181812101010121010101010101012121818181818181818
+% 17121010101012101218101800FF10121612181610181818121612101012
+% 101210121618181818181818121018101216181618181810181012101210
+% 18161818181818181A181A18181818181818181818181818181018181818
+% 1818FF101012161818121018101010101010101010121612181610181818
+% 1216121010121012101216181818181818FF121018101216181618181810
+% 18101210121018161818181818181A181A18181818181818181818181818
+% 181018FF1818181812101012161818121018101010101010101010121612
+% 18161018181812161210101210121012161818FF18181818121018101216
+% 18161818181018101210121018161818181818181A181A18181818181818
+% 1818181818FF181018181818181812101012161818121018101010101010
+% 101010121612181610181818121612101012101210FF1618181818181818
+% 12101810121618161818181018101210121018161818181818181A181A18
+% 18181818181818FF18181818181018181818181812101012161818121018
+% 1010101010101010101216121816101818181216121010FF101210121618
+% 181818181818121018101216181618181810181012101210181618181818
+% 18181A181A18181818FF1818181818181818181018181818181812101012
+% 16181812101810101010101010101012161218161018181812FF12101012
+% 101210121618181818181818121018101216181618181810181012101210
+% 18161818181818181A181AFF181818181818181818181818181018181818
+% 181812101012161818121018101010101010101010121612181610181818
+% 121612101012101210121600FF1010121012181218161810121012101010
+% 101010121216181818161810181010121012181818181612101810121012
+% 12101818181818181818181A18181A1E181A181818181818181818181818
+% 18FF10101210181216181810121010121010101010121012181218161810
+% 12101210101010101012121618181816FF10181010121012181818181612
+% 10181012101212101818181818181818181A18181A1E181A181818181818
+% 1818FF181818181810101210181216181810121010121010101010121012
+% 181218161810121012101010101010121216FF1818161810181010121012
+% 18181818161210181012101212101818181818181818181A18181A1E181A
+% 18181818FF18181818181818181810101210181216181810121010121010
+% 1010101210121812181618101210121010101010FF121216181818161810
+% 18101012101218181818161210181012101212101818181818181818181A
+% 18181A1E181AFF1818181818181818181818181810101210181216181810
+% 12101012101010101012101218121816181012101210FF10101010121216
+% 181818161810181010121012181818181612101810121012121018181818
+% 18181818181A1818FF1E181A181818181818181818181818181810101210
+% 181216181810121010121010101010121012181218161810FF1012101010
+% 101010121216181818161810181010121012181818181612101810121012
+% 12101818181818181818FF1A18181A1E181A181818181818181818181818
+% 181810101210181216181810121010121010101010121012181218161810
+% 1210121010101010101200FF101010101012161218101812161210101012
+% 101210101210181218181818181810181810121210121810121010101010
+% 1210121818181818181818181A181A18181818181818181818181A181818
+% FF1010121012101210121810121010101210101010101012161218101812
+% 161210101012101210101210181218FF1818181810181810121210121810
+% 1210101010101210121818181818181818181A181A181818181818181818
+% 18FF1A181818181010121012101210121810121010101210101010101012
+% 1612181018121612101010121012101012FF181218181818181810181810
+% 1212101218101210101010101210121818181818181818181A181A181818
+% 181818FF181818181A181818181010121012101210121810121010101210
+% 10101010101216121810181216121010101210FF10101210181218181818
+% 181810181810121210121810121010101010121012181818181818181818
+% 1A181A1818FF18181818181818181A181818181010121012101210121810
+% 121010101210101010101012161218101812161210FF1012101210101210
+% 181218181818181810181810121210121810121010101010121012181818
+% 1818181818181AFF1A18181818181818181818181A181818181010121012
+% 1012101218101210101012101010101010121612181018FF161210101012
+% 101210101210181218181818181810181810121210121810121010101010
+% 121012181818181818FF18181A181A18181818181818181818181A181818
+% 181010121012101210121810121010101210101010101012161218101812
+% 16121010101210121000FF10101012181818161818181018161810101010
+% 101012101210101218161818181818101210101018161210121010101010
+% 101210121612181818181A18201E1A181A181818181818181818181818FF
+% 181812101010101018101818181812101010101012181818161818181018
+% 161810101010101012101210101218161818181818101210101018161210
+% 121010101010101210121612181818181A18201E1A181A18181818181818
+% FF1818181818181812101010101018101818181812101010101012181818
+% 161818181018161810101010101012101210101218161818181818101210
+% 101018161210121010101010101210121612181818181A18201E1A181A18
+% 1818FF181818181818181818181812101010101018101818181812101010
+% 101012181818161818181018161810101010101012101210101218161818
+% 181818101210101018161210121010101010101210121612181818181A18
+% 201E1A18FF18181818181818181818181818181812101010101018101818
+% 181812101010101012181818161818181018161810101010101012101210
+% 101218161818181818101210101018161210121010101010101210121612
+% 181818181A18FF1E1A181A18181818181818181818181818181812101010
+% 101018101818181812101010101012181818161818181018161810101010
+% 101012101210101218161818181818101210101018161210121010101010
+% 1012101216121818FF181A18201E1A181A18181818181818181818181818
+% 181812101010101018101818181812101010101012181818161818181018
+% 161810101010101000FF1810121018181818121810181818181810121012
+% 101010101012101012121018181018101010101812101010101010101010
+% 10101010101818181818181A181A1818181818161318181818181818FF18
+% 121010101010101012101818181818181810121018181818121810181818
+% 181810121012101010101012101012121018181018101010101812101010
+% 10101010101010101010101818181818181A181A181818181816131818FF
+% 181818181818121010101010101012101818181818181810121018181818
+% 121810181818181810121012101010101012101012121018181018101010
+% 10181210101010101010101010101010101818181818181A181A18181818
+% 18FF13181818181818181818121010101010101012101818181818181810
+% 121018181818121810181818181810121012101010101012101012121018
+% 18101810101010181210101010101010101010101010101818181818181A
+% 181A18FF1818181613181818181818181818121010101010101012101818
+% 181818181810121018181818121810181818181810121012101010101012
+% 101012121018181018101010101812101010101010101010101010101018
+% 1818181818FF181A18181818181613181818181818181818121010101010
+% 101012101818181818181810121018181818121810181818181810121012
+% 101010101012101012121018181018101010101812101010101010101010
+% 10101010101818FF1818181A181A18181818181613181818181818181818
+% 121010101010101012101818181818181810121018181818121810181818
+% 1818101210121000FF1A1818181618181816181818181618181818101010
+% 101012121012101010101218101210121010181016121010101010101010
+% 10101010101818181818181820181A181818121612101818181012FF1216
+% 1010101010121010181818181818181A1818181618181816181818181618
+% 181818101010101012121012101010101218101210121010181016121010
+% 10101010101010101010101818181818181820181A18181812161210FF18
+% 1810121612161010101010121010181818181818181A1818181618181816
+% 181818181618181818101010101012121012101010101218101210121010
+% 18101612101010101010101010101010101818181818181820181A181818
+% FF16121018181810121612161010101010121010181818181818181A1818
+% 181618181816181818181618181818101010101012121012101010101218
+% 101210121010181016121010101010101010101010101018181818181818
+% 2018FF181818121612101818181012161216101010101012101018181818
+% 1818181A1818181618181816181818181618181818101010101012121012
+% 101010101218101210121010181016121010101010101010101010101018
+% 18181818FF1820181A181818121612101818181012161216101010101012
+% 1010181818181818181A1818181618181816181818181618181818101010
+% 101012121012101010101218101210121010181016121010101010101010
+% 101010101018FF181818181820181A181818121612101818181012161216
+% 1010101010121010181818181818181A1818181618181816181818181618
+% 18181810101000FF181818181A1818181810181018181816181216121018
+% 101010121010101010101012101216181812181818181810101010101010
+% 10101010121018181A2021181818181810161210121010121018FF181813
+% 10100A08081010101216181A1818181818181A1818181810181018181816
+% 181216121018101010121010101010101012101216181812181818181810
+% 10101010101010101010121018181A202118181818181016121012FF1012
+% 10181018181310100A08081010101216181A1818181818181A1818181810
+% 181018181816181216121018101010121010101010101012101216181812
+% 18181818181010101010101010101010121018181A2021181818181810FF
+% 12101210101210181018181310100A08081010101216181A181818181818
+% 1A1818181810181018181816181216121018101010121010101010101012
+% 10121618181218181818181010101010101010101010121018181A202118
+% 18FF1818101612101210101210181018181310100A08081010101216181A
+% 1818181818181A1818181810181018181816181216121018101010121010
+% 101010101012101216181812181818181810101010101010101010101210
+% 18181AFF211818181818101612101210101210181018181310100A080810
+% 10101216181A1818181818181A1818181810181018181816181216121018
+% 101010121010101010101012101216181812181818181810101010101010
+% 1010101012FF18181A202118181818181016121012101012101810181813
+% 10100A08081010101216181A1818181818181A1818181810181018181816
+% 181216121000FF1818181818181A18101812121212181818181012101210
+% 101216181818181210121018101812181612181618181818101010101010
+% 1010101012181818201A181818121218121810181012101810FF18161818
+% 10101008101010181818181A181818181818181A18101812121212181818
+% 181012101210101216181818181210121018101812181612181618181818
+% 1010101010101010101012181818201A18181812121812181018FF121018
+% 10121816181810101008101010181818181A181818181818181A18101812
+% 121212181818181012101210101216181818181210121018101812181612
+% 1816181818181010101010101010101012181818201A181818121218FF18
+% 10181012101810121816181810101008101010181818181A181818181818
+% 181A18101812121212181818181012101210101216181818181210121018
+% 1018121816121816181818181010101010101010101012181818201A1818
+% FF121218121810181012101810121816181810101008101010181818181A
+% 181818181818181A18101812121212181818181012101210101216181818
+% 181210121018101812181612181618181818101010101010101010101218
+% 1818FF1A1818181212181218101810121018101218161818101010081010
+% 10181818181A181818181818181A18101812121212181818181012101210
+% 101216181818181210121018101812181612181618181818101010101010
+% 10101010FF181818201A1818181212181218101810121018101218161818
+% 10101008101010181818181A181818181818181A18101812121212181818
+% 1810121000FF1A181A181A18181818121010101010181210101012101216
+% 121818181818181818181812161812101210121216181810101012101010
+% 0E1010101218181818181810101012101818181818101210FF1018181818
+% 1210101010121818181A18181A181A181A18181818121010101010181210
+% 101012101216121818181818181818181812161812101210121216181810
+% 1010121010100E101010121818181818181010101210181818FF18101210
+% 1210181818181210101010121818181A18181A181A181A18181818121010
+% 101010181210101012101216121818181818181818181812161812101210
+% 1212161818101010121010100E1010101218181818181810101012FF1818
+% 1818181012101210181818181210101010121818181A18181A181A181A18
+% 181818121010101010181210101012101216121818181818181818181812
+% 1618121012101212161818101010121010100E10101012181818181818FF
+% 1010121018181818181012101210181818181210101010121818181A1818
+% 1A181A181A18181818121010101010181210101012101216121818181818
+% 1818181818121618121012101212161818101010121010100E1010101218
+% 18FF18181810101012101818181818101210121018181818121010101012
+% 1818181A18181A181A181A18181818121010101010181210101012101216
+% 121818181818181818181812161812101210121216181810101012101010
+% 0E1010FF1218181818181810101012101818181818101210121018181818
+% 1210101010121818181A18181A181A181A18181818121010101010181210
+% 10101200FF18181818181818181810101010101018181012101010121012
+% 161818181818181818181818131612101010101212181012101010101010
+% 101010101216181812101210121012161818181818121010101012181818
+% 18121012101818181A181818181818181818181810101010101018181012
+% 101010121012161818181818181818181818131612101010101212181012
+% 101010101010101010101216181812101210121012161818181818121010
+% 10101218181818121012101818181A181818181818181818181810101010
+% 101018181012101010121012161818181818181818181818131612101010
+% 101212181012101010101010101010101216181812101210121012161818
+% 18181812101010101218181818121012101818181A181818181818181818
+% 181810101010101018181012101010121012161818181818181818181818
+% 131612101010101212181012101010101010101010101216181812101210
+% 12101216181818181812101010101218181818121012101818181A181818
+% 181818181818181810101010101018181012101010121012161818181818
+% 181818181818131612101010101212181012101010101010101010101216
+% 181812101210121012161818181818121010101012181818181210121018
+% 18181A181818181818181818181810101010101018181012101010121012
+% 161818181818181818181818131612101010101212181012101010101010
+% 101010101216181812101210121012161818181818121010101012181818
+% 18121012101818181A181818181818181818181810101010101018181012
+% 101000FF1818181818181818121010101010101818101010101218101012
+% 181818181818181818181816181818121010101012161218101012101010
+% 101010121012101010101010101212181818181818101210101010101216
+% 18121618181818181A181818181818181818121010101010101818101010
+% 101218101012181818181818181818181816181818121010101012161218
+% 101012101010101010121012101010101010101212181818181818101210
+% 10101010121618121618181818181A181818181818181818121010101010
+% 101818101010101218101012181818181818181818181816181818121010
+% 101012161218101012101010101010121012101010101010101212181818
+% 18181810121010101010121618121618181818181A181818181818181818
+% 121010101010101818101010101218101012181818181818181818181816
+% 181818121010101012161218101012101010101010121012101010101010
+% 10121218181818181810121010101010121618121618181818181A181818
+% 181818181818121010101010101818101010101218101012181818181818
+% 181818181816181818121010101012161218101012101010101010121012
+% 101010101010101212181818181818101210101010101216181216181818
+% 18181A181818181818181818121010101010101818101010101218101012
+% 181818181818181818181816181818121010101012161218101012101010
+% 101010121012101010101010101212181818181818101210101010101216
+% 18121618181818181A181818181818181818121010101010101818101010
+% 1000FF181818181818181810121010101010181618181216121010101210
+% 181810121818181818181818181818161210101018121612101010101010
+% 12101012101012101010101010101818181A181812101010100808101012
+% 101812171818181818181818181818181810121010101010181618181216
+% 121010101210181810121818181818181818181818161210101018121612
+% 10101010101012101012101012101010101010101818181A181812101010
+% 100808101012101812171818181818181818181818181810121010101010
+% 181618181216121010101210181810121818181818181818181818161210
+% 10101812161210101010101012101012101012101010101010101818181A
+% 181812101010100808101012101812171818181818181818181818181810
+% 121010101010181618181216121010101210181810121818181818181818
+% 181818161210101018121612101010101010121010121010121010101010
+% 10101818181A181812101010100808101012101812171818181818181818
+% 181818181810121010101010181618181216121010101210181810121818
+% 181818181818181818161210101018121612101010101010121010121010
+% 12101010101010101818181A181812101010100808101012101812171818
+% 181818181818181818181810121010101010181618181216121010101210
+% 181810121818181818181818181818161210101018121612101010101010
+% 12101012101012101010101010101818181A181812101010100808101012
+% 101812171818181818181818181818181810121010101010181618181216
+% 00FF12161818101810181210121010101212181818181812101010121613
+% 101210181618181818181818181818181010101216121012101216181810
+% 181012101216121010101010101818181818181818101010100A10101010
+% 121612181818181812161818101810181210121010101212181818181812
+% 101010121613101210181618181818181818181818181010101216121012
+% 101216181810181012101216121010101010101818181818181818101010
+% 100A10101010121612181818181812161818101810181210121010101212
+% 181818181812101010121613101210181618181818181818181818181010
+% 101216121012101216181810181012101216121010101010101818181818
+% 181818101010100A10101010121612181818181812161818101810181210
+% 121010101212181818181812101010121613101210181618181818181818
+% 181818181010101216121012101216181810181012101216121010101010
+% 101818181818181818101010100A10101010121612181818181812161818
+% 101810181210121010101212181818181812101010121613101210181618
+% 181818181818181818181010101216121012101216181810181012101216
+% 121010101010101818181818181818101010100A10101010121612181818
+% 181812161818101810181210121010101212181818181812101010121613
+% 101210181618181818181818181818181010101216121012101216181810
+% 181012101216121010101010101818181818181818101010100A10101010
+% 121612181818181812161818101810181210121010101212181818181800
+% FF1012101012161210101010121012161818181A18181210181218181818
+% 101218121216181818181218181818181012101810181018101218181818
+% 1812161218121010101010101818181A20181818101210101010100A1012
+% 101818181818121012101012161210101010121012161818181A18181210
+% 181218181818101218121216181818181218181818181012101810181018
+% 1012181818181812161218121010101010101818181A2018181810121010
+% 1010100A1012101818181818121012101012161210101010121012161818
+% 181A18181210181218181818101218121216181818181218181818181012
+% 1018101810181012181818181812161218121010101010101818181A2018
+% 1818101210101010100A1012101818181818121012101012161210101010
+% 121012161818181A18181210181218181818101218121216181818181218
+% 181818181012101810181018101218181818181216121812101010101010
+% 1818181A20181818101210101010100A1012101818181818121012101012
+% 161210101010121012161818181A18181210181218181818101218121216
+% 181818181218181818181012101810181018101218181818181216121812
+% 1010101010101818181A20181818101210101010100A1012101818181818
+% 121012101012161210101010121012161818181A18181210181218181818
+% 101218121216181818181218181818181012101810181018101218181818
+% 1812161218121010101010101818181A20181818101210101010100A1012
+% 101818181818121012101012161210101010121012161818181A181800FF
+% 1818181818101216121612161218181818181A1818181010101010121018
+% 10101010121818181810121010101018181012101812181816181A181818
+% 181816181818181810101010101216181818181810121010101010101010
+% 1010121018181818181818101216121612161218181818181A1818181010
+% 101010121018101010101218181818101210101010181810121018121818
+% 16181A181818181816181818181810101010101216181818181810121010
+% 101010101010101012101818181818181810121612161216121818181818
+% 1A1818181010101010121018101010101218181818101210101010181810
+% 12101812181816181A181818181816181818181810101010101216181818
+% 181810121010101010101010101012101818181818181810121612161216
+% 1218181818181A1818181010101010121018101010101218181818101210
+% 10101018181012101812181816181A181818181816181818181810101010
+% 101216181818181810121010101010101010101012101818181818181810
+% 1216121612161218181818181A1818181010101010121018101010101218
+% 18181810121010101018181012101812181816181A181818181816181818
+% 181810101010101216181818181810121010101010101010101012101818
+% 1818181818101216121612161218181818181A1818181010101010121018
+% 10101010121818181810121010101018181012101812181816181A181818
+% 181816181818181810101010101216181818181810121010101010101010
+% 1010121018181818181818101216121612161218181818181A181800FF1A
+% 181818101210181012101216181818181818181818181010101010101010
+% 1010101012161818181010121012181810101216121618181818181A1818
+% 181818181818161818181010101218101810181210101010121010101012
+% 10121618181A181818101210181012101216181818181818181818181010
+% 101010101010101010101216181818101012101218181010121612161818
+% 1818181A1818181818181818161818181010101218101810181210101010
+% 12101010101210121618181A181818101210181012101216181818181818
+% 181818181010101010101010101010101216181818101012101218181010
+% 1216121618181818181A1818181818181818161818181010101218101810
+% 18121010101012101010101210121618181A181818101210181012101216
+% 181818181818181818181010101010101010101010101216181818101012
+% 1012181810101216121618181818181A1818181818181818161818181010
+% 10121810181018121010101012101010101210121618181A181818101210
+% 181012101216181818181818181818181010101010101010101010101216
+% 1818181010121012181810101216121618181818181A1818181818181818
+% 16181818101010121810181018121010101012101010101210121618181A
+% 181818101210181012101216181818181818181818181010101010101010
+% 1010101012161818181010121012181810101216121618181818181A1818
+% 181818181818161818181010101218101810181210101010121010101012
+% 10121618181A181818101210181012101216181818181818181800FF1818
+% 181818111813161216121818181818181A18181818101010101010100A10
+% 101010101218181818181012161218101210181318181818181818181818
+% 181012161318181818121010121010121018101012101216181618121618
+% 181818181818181818111813161216121818181818181A18181818101010
+% 101010100A10101010101218181818181012161218101210181318181818
+% 181818181818181012161318181818121010121010121018101012101216
+% 181618121618181818181818181818111813161216121818181818181A18
+% 181818101010101010100A10101010101218181818181012161218101210
+% 181318181818181818181818181012161318181818121010121010121018
+% 101012101216181618121618181818181818181818111813161216121818
+% 181818181A18181818101010101010100A10101010101218181818181012
+% 161218101210181318181818181818181818181012161318181818121010
+% 121010121018101012101216181618121618181818181818181818111813
+% 161216121818181818181A18181818101010101010100A10101010101218
+% 181818181012161218101210181318181818181818181818181012161318
+% 181818121010121010121018101012101216181618121618181818181818
+% 181818111813161216121818181818181A18181818101010101010100A10
+% 101010101218181818181012161218101210181318181818181818181818
+% 181012161318181818121010121010121018101012101216181618121618
+% 181818181818181818111813161216121818181818181A181800FF1A1818
+% 1818181618181210121618181A181A181818181810101010101010101010
+% 081010101818181818181018161216121810181018101818181818181810
+% 101012181818181818181010101012101210121012161818181818181818
+% 1818181A18181818181618181210121618181A181A181818181810101010
+% 101010101010081010101818181818181018161216121810181018101818
+% 181818181810101012181818181818181010101012101210121012161818
+% 1818181818181818181A18181818181618181210121618181A181A181818
+% 181810101010101010101010081010101818181818181018161216121810
+% 181018101818181818181810101012181818181818181010101012101210
+% 1210121618181818181818181818181A1818181818161818121012161818
+% 1A181A181818181810101010101010101010081010101818181818181018
+% 161216121810181018101818181818181810101012181818181818181010
+% 1010121012101210121618181818181818181818181A1818181818161818
+% 1210121618181A181A181818181810101010101010101010081010101818
+% 181818181018161216121810181018101818181818181810101012181818
+% 1818181810101010121012101210121618181818181818181818181A1818
+% 1818181618181210121618181A181A181818181810101010101010101010
+% 081010101818181818181018161216121810181018101818181818181810
+% 101012181818181818181010101012101210121012161818181818181818
+% 1818181A18181818181618181210121618181A181A18181800FF18181818
+% 18181818171216181818181818181A181818181118121012101010101010
+% 080E10101818181818181210121018101810121812101818181817181218
+% 181816181818181818181810FFFFFFFF1810121018181818181818181818
+% 18181818181818181818171216181818181818181A181818181118121012
+% 101010101010080E10101818181818181210121018101810121812101818
+% 1818171812FFFFFFFF1818181818FFFF18101210FFFFFFFFFFFF18181818
+% 18181818181818181818181818181818171216181818181818181A181818
+% 181118121012101010101010080E10101818181818181210121018101810
+% 12181210181818FFFFFFFF1818181618FFFF1818181818FFFFFFFFFF1810
+% 121018181818181818181818181818181818181818181712161818181818
+% 18181A181818181118121012101010101010080E10101818181818181210
+% 121018101810121812FFFFFFFF1817181218FFFF1618181818FFFFFFFF10
+% 121018101810121018181818181818181818181818181818181818181712
+% 16181818181818181A181818181118121012101010101010080E10101818
+% 1818181812101210181018FFFFFFFF1018181818FFFF1218181816FFFFFF
+% FF1818181810121018101810121018181818181818181818181818181818
+% 18181818171216181818181818181A181818181118121012101010101010
+% 080E101018181818181812101210181018101218FFFFFFFFFFFF17181218
+% 181816181818181818181810121018101810121018181818181818181818
+% 18181818181818181818171216181818181818181A181800FF1818181818
+% 1A1818181812181818181818181818181618181612161810121012101010
+% 101010101218161818181018181316131810121010121612181812161818
+% 18181810181218181818FFFF1818FFFF1012101212181818181818181818
+% 1818181818181A1818181812181818181818181818181618181612161810
+% 121012101010101010101218161818181018181316131810121010121612
+% 181812FFFF1818FFFF10181218FFFF1817181818FFFF1012101212181818
+% 1818181818181818181818181A1818181812181818181818181818181618
+% 181612161810121012101010101010101218161818181018181316131810
+% 1210101216FFFF1812FFFF18181818FFFF12181818181718FFFF12101012
+% 1012121818181818181818181818181818181A1818181812181818181818
+% 181818181618181612161810121012101010101010101218161818181018
+% 18131613181012FFFF1216FFFF18121618FFFF18181018FFFF1818FFFF18
+% 1818121010121012121818181818181818181818181818181A1818181812
+% 181818181818181818181618181612161810121012101010101010101218
+% 161818181018181316FFFF1012FFFF12161218FFFF16181818FFFF1018FF
+% FF1818181718181812101012101212181818181818181818181818181818
+% 1A1818181812181818181818181818181618181612161810121012101010
+% 101010101218161818181018181316131810121010FFFF12181812161818
+% 181818101812181818181718181812101012101212181818181818181818
+% 1818181818181A18181818121818181818181818181800FF18181A181818
+% 181818181718181818121818181818121210121012101216121012101210
+% 101010101012181613101216181812161210121010121618181018181818
+% 181816121618181818FFFF1612FFFF10101010121018181818181A181818
+% 18181A181818181818181718181818121818181818121210121012101216
+% 121012101210101010101012181613101216181812161210121010121618
+% 1810FFFF1818FFFF161216181818181818161216FFFF1010101210181818
+% 18181A18181818181A181818181818181718181818121818181818121210
+% 121012101216121012101210101010101012181613101216181812161210
+% 12101012FFFF1810FFFF18181818161216181818FFFFFFFFFFFF12101010
+% 10121018181818181A18181818181A181818181818181718181818121818
+% 181818121210121012101216121012101210101010101012181613101216
+% 181812161210FFFF1012FFFF18101818181818181612FFFF1818FFFF1816
+% 12161210101010121018181818181A18181818181A181818181818181718
+% 181818121818181818121210121012101216121012101210101010101012
+% 1816131012161818FFFF1210FFFF10121618181018181818FFFF1612FFFF
+% 18181818181612161210101010121018181818181A18181818181A181818
+% 181818181718181818121818181818121210121012101216121012101210
+% 1010101010121816131012161818121612101210FFFF1618181018181818
+% 18181612161818181818181612161210101010121018181818181A181818
+% 18181A18181818181818171818181812181818181800FF1818181818181A
+% 181818181818181818161818161810101010101010121012181612101010
+% 100A10101210181810121818181018101010101010121810181018181618
+% 1818181810181818FFFF1210FFFF121010101010181612181818181A1818
+% 18181818181A181818181818181818161818161810101010101010121012
+% 181612101010100A10101210181810121818181018101010101010121810
+% 18FFFF1816FFFF18181810181818181612101810FFFF1010101018161218
+% 1818181A181818181818181A181818181818181818161818161810101010
+% 101010121012181612101010100A10101210181810121818181018101010
+% 101010FFFF1018FFFF18161818181818101818FF1816FFFF181012101010
+% 1010181612181818181A181818181818181A181818181818181818161818
+% 161810101010101010121012181612101010100A10101210181810121818
+% 1810181010FFFF1010FFFF10181018181618181818FFFF1818FFFF161210
+% 1810121010101010181612181818181A181818181818181A181818181818
+% 181818161818161810101010101010121012181612101010100A10101210
+% 18181012181818FFFF1010FFFF10101218101810181816FFFF1818FFFF18
+% 1818181612101810121010101010181612181818181A181818181818181A
+% 181818181818181818161818161810101010101010121012181612101010
+% 100A1010121018181012181818101810101010FFFF121810181018181618
+% 1818181810181818181612101810121010101010181612181818181A1818
+% 18181818181A181818181818181818161818161800FF181818181A181818
+% 181818181818181818181818181818101012101010101018121810181010
+% 101012161818181818161817181818181810101010101218181012121818
+% 18181210121012FFFF1012FFFF1210101010101010101012161818101818
+% 18181A181818181818181818181818181818181818101012101010101018
+% 121810181010101012161818181818161817181818181810101010101218
+% FFFF1212FFFF1818121012101210121012181612FFFF1010101010101012
+% 16181810181818181A181818181818181818181818181818181818101012
+% 101010101018121810181010101012161818181818161817181818181810
+% 1010FFFF1218FFFF1212181818181210121012FF12FFFF18161210101010
+% 10101010101216181810181818181A181818181818181818181818181818
+% 181818101012101010101018121810181010101012161818181818161817
+% 18181818FFFF1010FFFF12181810121218181818FFFFFFFFFF1012101218
+% 16121010101010101010101216181810181818181A181818181818181818
+% 181818181818181818101012101010101018121810181010101012161818
+% 181818161817FFFF1818FFFF1010101012181810121218FFFFFFFF101210
+% 12101210121816121010101010101010101216181810181818181A181818
+% 181818181818181818181818181818101012101010101018121810181010
+% 101012161818181818161817181818181810FFFF10101218181012121818
+% 181812101210121012101218161210101010101010101012161818101818
+% 18181A1818181818181818181818181818181800FF1818181A1E181A1818
+% 181612101218181818181818101810121010101010101218171818101210
+% 101018181818181818181818171818161012101210121010101010101010
+% 121612161216FFFF1018FFFF101010101010101010101012181018181818
+% 1A1E181A1818181612101218181818181818101810121010101010101218
+% 1718181012101010181818181818181818181718181610121012101210FF
+% FF1010FFFF10121612161216121810FFFF1210FFFF101010101010101012
+% 1810181818181A1E181A1818181612101218181818181818101810121010
+% 101010101218171818101210101018181818181818181818171818161012
+% 10FFFF1210FFFF101010101012161216121612FFFFFF1012101010101010
+% 1010101010121810181818181A1E181A1818181612101218181818181818
+% 101810121010101010101218171818101210101018181818181818181818
+% 171818FFFF1210FFFF12101010101010101012FFFF161216121810181012
+% 1010101010101010101010121810181818181A1E181A1818181612101218
+% 181818181818101810121010101010101218171818101210101018181818
+% 1818181818FFFF1818FFFF12101210121010101010FFFF1012FFFF161216
+% 1218101810121010101010101010101010121810181818181A1E181A1818
+% 181612101218181818181818101810121010101010101218171818101210
+% 1010181818181818181818181718181610FFFF1210121010101010101010
+% 121612161216121810181012101010101010101010101012181018181818
+% 1A1E181A181818161210121818181818181800FF1818181A181A18181812
+% 101210101012161218101818181010101010101010121612181818181812
+% 181818181818181818181818181818181816121010121012101010101010
+% 1210121012FFFF1012FFFF10101210100810100F1010101012181818181A
+% 181A18181812101210101012161218101818181010101010101010121612
+% 18181818181218181818181818181818181818181818181612101012FFFF
+% 1010FFFF10101210121012101010FFFF1210FFFF10100810100F10101010
+% 12181818181A181A18181812101210101012161218101818181010101010
+% 101010121612181818181812181818181818181818181818181818181816
+% FFFF1012FFFF101010101010121012101210FFFFFF101210101210100810
+% 100F1010101012181818181A181A18181812101210101012161218101818
+% 181010101010101010121612181818181812181818181818181818181818
+% 1818FFFF1816FFFF1012101210101010101012FFFF101210101012101210
+% 101210100810100F1010101012181818181A181A18181812101210101012
+% 161218101818181010101010101010121612181818181812181818181818
+% 18181818FFFF1818FFFF18161210101210121010FFFF1010FFFF12101210
+% 101012101210101210100810100F1010101012181818181A181A18181812
+% 101210101012161218101818181010101010101010121612181818181812
+% 1818181818181818181818181818FFFFFFFF121010121012101010101010
+% 121012101210101012101210101210100810100F1010101012181818181A
+% 181A18181812101210101012161218101800FF1818181A18181A18181810
+% 101010121012101212161218101010100A10101010121818181818181818
+% 18181A181818181818161812181818181818101210101810101010101010
+% 1216131810FFFFFFFF1818181010100A100B101010101010181818181A18
+% 181A18181810101010121012101212161218101010100A10101010121818
+% 18181818181818181A18181818181816181218181818181810121010FFFF
+% FFFF101010101216131810181218FFFFFFFF1010100A100B101010101010
+% 181818181A18181A18181810101010121012101212161218101010100A10
+% 10101012181818181818181818181A181818181818161812181818181818
+% FFFFFFFF1810101010101010121613181018FFFF161818181010100A100B
+% 101010101010181818181A18181A18181810101010121012101212161218
+% 101010100A1010101012181818181818181818181A181818181818161812
+% 1818FFFFFFFF10121010181010101010101012FFFFFFFF18121816181818
+% 1010100A100B101010101010181818181A18181A18181810101010121012
+% 101212161218101010100A1010101012181818181818181818181A181818
+% 18181816FFFFFFFF181818181012101018101010FFFFFFFF121613181018
+% 1218161818181010100A100B101010101010181818181A18181A18181810
+% 101010121012101212161218101010100A10101010121818181818181818
+% 18181A181818181818161812181818FFFF18101210101810101010101010
+% 1216131810181218161818181010100A100B101010101010181818181A18
+% 181A181818101010101210121012121600FF1818181A181A181818181210
+% 12101012101010101210121818101010100810121618181A181A18181818
+% 1A1818181818181012121816181212181018161010181818181810101012
+% 161216101316101818181818181010100E101010101010181818181A181A
+% 18181818121012101012101010101210121818101010100810121618181A
+% 181A181818181A1818181818181012121816181212181018161010181818
+% 181810101012161216101316101818181818181010100E10101010101018
+% 1818181A181A181818181210121010121010101012101218181010101008
+% 10121618181A181A181818181A1818181818181012121816181212181018
+% 161010181818181810101012161216101316101818181818181010100E10
+% 1010101010181818181A181A181818181210121010121010101012101218
+% 18101010100810121618181A181A181818181A1818181818181012121816
+% 181212181018161010181818181810101012161216101316101818181818
+% 181010100E101010101010181818181A181A181818181210121010121010
+% 10101210121818101010100810121618181A181A181818181A1818181818
+% 181012121816181212181018161010181818181810101012161216101316
+% 101818181818181010100E101010101010181818181A181A181818181210
+% 12101012101010101210121818101010100810121618181A181A18181818
+% 1A1818181818181012121816181212181018161010181818181810101012
+% 161216101316101818181818181010100E101010101010181818181A181A
+% 18181818121012101012101010101200FF10181818181818181818161216
+% 1218101810181012101018181610101010081012161818181818181A1818
+% 1818181818101210101012181010101210121818181818181A1818121818
+% 121818181218181818181818161210101010101010101210181818181818
+% 181818161216121810181018101210101818161010101008101216181818
+% 1818181A1818181818181810121010101218101010121012181818181818
+% 1A1818121818121818181218181818181818161210101010101010101210
+% 181818181818181818161216121810181018101210101818161010101008
+% 1012161818181818181A1818181818181810121010101218101010121012
+% 1818181818181A1818121818121818181218181818181818161210101010
+% 101010101210181818181818181818161216121810181018101210101818
+% 1610101010081012161818181818181A1818181818181810121010101218
+% 1010101210121818181818181A1818121818121818181218181818181818
+% 161210101010101010101210181818181818181818161216121810181018
+% 1012101018181610101010081012161818181818181A1818181818181810
+% 1210101012181010101210121818181818181A1818121818121818181218
+% 181818181818161210101010101010101210181818181818181818161216
+% 1218101810181012101018181610101010081012161818181818181A1818
+% 1818181818101210101012181010101210121818181818181A1818121818
+% 121818181218181818181818161210101010101010101210181818181818
+% 181818161216121810181018101200FF1012161818181818101210121018
+% 181818181312101010101212181810121012101818181818181818181A18
+% 181818101210101010121610101210121012101818181818181818101816
+% 181018181612101010121818181010101010101010101012161818181818
+% 101210121018181818181312101010101212181810121012101818181818
+% 181818181A18181818101210101010121610101210121012101818181818
+% 181818101816181018181612101010121818181010101010101010101012
+% 161818181818101210121018181818181312101010101212181810121012
+% 101818181818181818181A18181818101210101010121610101210121012
+% 101818181818181818101816181018181612101010121818181010101010
+% 101010101012161818181818101210121018181818181312101010101212
+% 181810121012101818181818181818181A18181818101210101010121610
+% 101210121012101818181818181818101816181018181612101010121818
+% 181010101010101010101012161818181818101210121018181818181312
+% 101010101212181810121012101818181818181818181A18181818101210
+% 101010121610101210121012101818181818181818101816181018181612
+% 101010121818181010101010101010101012161818181818101210121018
+% 181818181312101010101212181810121012101818181818181818181A18
+% 181818101210101010121610101210121012101818181818181818101816
+% 181018181612101010121818181010101010101010101012161818181818
+% 1012101210181818181813121000FF101218161818101010101010101818
+% 181818181010101010101618181816101018181818181818181818181818
+% 181012101010101010121010101210101210181812161218181A18181218
+% 181810181010101010101218101010121010101010101218161818101010
+% 101010101818181818181010101010101618181816101018181818181818
+% 181818181818181012101010101010121010101210101210181812161218
+% 181A18181218181810181010101010101218101010121010101010101218
+% 161818101010101010101818181818181010101010101618181816101018
+% 181818181818181818181818181012101010101010121010101210101210
+% 181812161218181A18181218181810181010101010101218101010121010
+% 101010101218161818101010101010101818181818181010101010101618
+% 181816101018181818181818181818181818181012101010101010121010
+% 101210101210181812161218181A18181218181810181010101010101218
+% 101010121010101010101218161818101010101010101818181818181010
+% 101010101618181816101018181818181818181818181818181012101010
+% 101010121010101210101210181812161218181A18181218181810181010
+% 101010101218101010121010101010101218161818101010101010101818
+% 181818181010101010101618181816101018181818181818181818181818
+% 181012101010101010121010101210101210181812161218181A18181218
+% 181810181010101010101218101010121010101010101218161818101010
+% 10101010181818181818101000FF10101012101012101010101012181818
+% 181818181010080A10101012181818181818181818181218181818171812
+% 181010101010101012181810101012161818161816181618171A18181810
+% 121818181812101010101018121610101210101010101012101012101010
+% 101012181818181818181010080A10101012181818181818181818181218
+% 181818171812181010101010101012181810101012161818161816181618
+% 171A18181810121818181812101010101018121610101210101010101012
+% 101012101010101012181818181818181010080A10101012181818181818
+% 181818181218181818171812181010101010101012181810101012161818
+% 161816181618171A18181810121818181812101010101018121610101210
+% 101010101012101012101010101012181818181818181010080A10101012
+% 181818181818181818181218181818171812181010101010101012181810
+% 101012161818161816181618171A18181810121818181812101010101018
+% 121610101210101010101012101012101010101012181818181818181010
+% 080A10101012181818181818181818181218181818171812181010101010
+% 101012181810101012161818161816181618171A18181810121818181812
+% 101010101018121610101210101010101012101012101010101012181818
+% 181818181010080A10101012181818181818181818181218181818171812
+% 181010101010101012181810101012161818161816181618171A18181810
+% 121818181812101010101018121610101210101010101012101012101010
+% 101012181818181818181000FF1210121018101810101010101012161818
+% 181A18181010101010101010101018181818181818161818181818101810
+% 121010101010121818181816121012101818181818181818181818181816
+% 181818181710101210121612101210101818101210121018101810101010
+% 101012161818181A18181010101010101010101018181818181818161818
+% 181818101810121010101010121818181816121012101818181818181818
+% 181818181816181818181710101210121612101210101818101210121018
+% 101810101010101012161818181A18181010101010101010101018181818
+% 181818161818181818101810121010101010121818181816121012101818
+% 181818181818181818181816181818181710101210121612101210101818
+% 101210121018101810101010101012161818181A18181010101010101010
+% 101018181818181818161818181818101810121010101010121818181816
+% 121012101818181818181818181818181816181818181710101210121612
+% 101210101818101210121018101810101010101012161818181A18181010
+% 101010101010101018181818181818161818181818101810121010101010
+% 121818181816121012101818181818181818181818181816181818181710
+% 101210121612101210101818101210121018101810101010101012161818
+% 181A18181010101010101010101018181818181818161818181818101810
+% 121010101010121818181816121012101818181818181818181818181816
+% 181818181710101210121612101210101818101210121018101810101010
+% 101012161818181A181800FF181818181818181810121210121012181818
+% 181818181010101010101010101012161818181818181718121612101210
+% 101210121818181818181818101210181818181818181818181818181811
+% 181218181818161210181018101818181818181818181818181810121210
+% 121012181818181818181010101010101010101012161818181818181718
+% 121612101210101210121818181818181818101210181818181818181818
+% 181818181811181218181818161210181018101818181818181818181818
+% 181810121210121012181818181818181010101010101010101012161818
+% 181818181718121612101210101210121818181818181818101210181818
+% 181818181818181818181811181218181818161210181018101818181818
+% 181818181818181810121210121012181818181818181010101010101010
+% 101012161818181818181718121612101210101210121818181818181818
+% 101210181818181818181818181818181811181218181818161210181018
+% 101818181818181818181818181810121210121012181818181818181010
+% 101010101010101012161818181818181718121612101210101210121818
+% 181818181818101210181818181818181818181818181811181218181818
+% 161210181018101818181818181818181818181810121210121012181818
+% 181818181010101010101010101012161818181818181718121612101210
+% 101210121818181818181818101210181818181818181818181818181811
+% 181218181818161210181018101818181818181818181818181810121210
+% 12101218181818181800FF1A181818181818181818101010101012181818
+% 181818101810121010101010101210181218181818181612181012101010
+% 10101018181818181A181810101012101216121612101210181818181810
+% 1012101818181012101210121818181A181A181818181818181818101010
+% 101012181818181818101810121010101010101210181218181818181612
+% 18101210101010101018181818181A181810101012101216121612101210
+% 1818181818101012101818181012101210121818181A181A181818181818
+% 181818101010101012181818181818101810121010101010101210181218
+% 18181818161218101210101010101018181818181A181810101012101216
+% 1216121012101818181818101012101818181012101210121818181A181A
+% 181818181818181818101010101012181818181818101810121010101010
+% 10121018121818181818161218101210101010101018181818181A181810
+% 101012101216121612101210181818181810101210181818101210121012
+% 1818181A181A181818181818181818101010101012181818181818101810
+% 121010101010101210181218181818181612181012101010101010181818
+% 18181A181810101012101216121612101210181818181810101210181818
+% 1012101210121818181A181A181818181818181818101010101012181818
+% 181818101810121010101010101210181218181818181612181012101010
+% 10101018181818181A181810101012101216121612101210181818181810
+% 1012101818181012101210121818181A181A181818181818181818101010
+% 101012181818181800FF1818181818181818181818121010101210121018
+% 101812101216181018121610101210181718181818181810181012101010
+% 121818181A18181818181810101010101010101010101012161818101210
+% 101018121210101810121018181818181818181818181818181818121010
+% 101210121018101812101216181018121610101210181718181818181810
+% 181012101010121818181A18181818181810101010101010101010101012
+% 161818101210101018121210101810121018181818181818181818181818
+% 181818121010101210121018101812101216181018121610101210181718
+% 181818181810181012101010121818181A18181818181810101010101010
+% 101010101012161818101210101018121210101810121018181818181818
+% 181818181818181818121010101210121018101812101216181018121610
+% 101210181718181818181810181012101010121818181A18181818181810
+% 101010101010101010101012161818101210101018121210101810121018
+% 181818181818181818181818181818121010101210121018101812101216
+% 181018121610101210181718181818181810181012101010121818181A18
+% 181818181810101010101010101010101012161818101210101018121210
+% 101810121018181818181818181818181818181818121010101210121018
+% 101812101216181018121610101210181718181818181810181012101010
+% 121818181A18181818181810101010101010101010101012161818101210
+% 101018121210101810121018181818181818181818181818181818121010
+% 1012101210181000FF1A1818181818181818181818181210101010101012
+% 1010181012181818181A1810121018181818181818181818171810121012
+% 16181818181A181818121010101010101010101010101012181618181012
+% 1012101010101210181018161818181A1818181818181818181818181210
+% 1010101010121010181012181818181A1810121018181818181818181818
+% 17181012101216181818181A181818121010101010101010101010101012
+% 1816181810121012101010101210181018161818181A1818181818181818
+% 1818181812101010101010121010181012181818181A1810121018181818
+% 18181818181817181012101216181818181A181818121010101010101010
+% 1010101010121816181810121012101010101210181018161818181A1818
+% 1818181818181818181812101010101010121010181012181818181A1810
+% 12101818181818181818181817181012101216181818181A181818121010
+% 101010101010101010101012181618181012101210101010121018101816
+% 1818181A1818181818181818181818181210101010101012101018101218
+% 1818181A181012101818181818181818181817181012101216181818181A
+% 181818121010101010101010101010101012181618181012101210101010
+% 1210181018161818181A1818181818181818181818181210101010101012
+% 1010181012181818181A1810121018181818181818181818171810121012
+% 16181818181A181818121010101010101010101010101012181618181012
+% 1012101010101210181018161818181A1818181818181818181818181210
+% 10101010101200FF18181818181012101817181818181810121010121010
+% 121012161818181818181818101818181818181818181818181818161818
+% 18181A181818181618181810101010101010101010101010121818181818
+% 181612161216121012101218181818181818181012101817181818181810
+% 121010121010121012161818181818181818101818181818181818181818
+% 18181816181818181A181818181618181810101010101010101010101010
+% 121818181818181612161216121012101218181818181818181012101817
+% 181818181810121010121010121012161818181818181818101818181818
+% 18181818181818181816181818181A181818181618181810101010101010
+% 101010101010121818181818181612161216121012101218181818181818
+% 181012101817181818181810121010121010121012161818181818181818
+% 10181818181818181818181818181816181818181A181818181618181810
+% 101010101010101010101010121818181818181612161216121012101218
+% 181818181818181012101817181818181810121010121010121012161818
+% 18181818181810181818181818181818181818181816181818181A181818
+% 181618181810101010101010101010101010121818181818181612161216
+% 121012101218181818181818181012101817181818181810121010121010
+% 121012161818181818181818101818181818181818181818181818161818
+% 18181A181818181618181810101010101010101010101010121818181818
+% 181612161216121012101218181818181818181012101817181818181810
+% 121010121000FF1818111210101010121818181818181818181210101210
+% 101210121810121618181818181818181A18181818181818181818181818
+% 18181A181818181818181012101210101210101010101018181820191818
+% 181818181818101010121012181818111210101010121818181818181818
+% 181210101210101210121810121618181818181818181A18181818181818
+% 18181818181818181A181818181818181012101210101210101010101018
+% 181820191818181818181818101010121012181818111210101010121818
+% 181818181818181210101210101210121810121618181818181818181A18
+% 18181818181818181818181818181A181818181818181012101210101210
+% 101010101018181820191818181818181818101010121012181818111210
+% 101010121818181818181818181210101210101210121810121618181818
+% 181818181A1818181818181818181818181818181A181818181818181012
+% 101210101210101010101018181820191818181818181818101010121012
+% 181818111210101010121818181818181818181210101210101210121810
+% 121618181818181818181A1818181818181818181818181818181A181818
+% 181818181012101210101210101010101018181820191818181818181818
+% 101010121012181818111210101010121818181818181818181210101210
+% 101210121810121618181818181818181A18181818181818181818181818
+% 18181A181818181818181012101210101210101010101018181820191818
+% 181818181818101010121012181818111210101010121818181818181818
+% 1812101000FF181818161812181612101212181818181018101813181818
+% 181612161818181818181818181A181818181A1818181812101316181818
+% 181818181812101210121012161818181010101010101018181818181818
+% 181810181818181012101018181818161812181612101212181818181018
+% 101813181818181612161818181818181818181A181818181A1818181812
+% 101316181818181818181812101210121012161818181010101010101018
+% 181818181818181810181818181012101018181818161812181612101212
+% 181818181018101813181818181612161818181818181818181A18181818
+% 1A1818181812101316181818181818181812101210121012161818181010
+% 101010101018181818181818181810181818181012101018181818161812
+% 181612101212181818181018101813181818181612161818181818181818
+% 181A181818181A1818181812101316181818181818181812101210121012
+% 161818181010101010101018181818181818181810181818181012101018
+% 181818161812181612101212181818181018101813181818181612161818
+% 181818181818181A181818181A1818181812101316181818181818181812
+% 101210121012161818181010101010101018181818181818181810181818
+% 181012101018181818161812181612101212181818181018101813181818
+% 181612161818181818181818181A181818181A1818181812101316181818
+% 181818181812101210121012161818181010101010101018181818181818
+% 181810181818181012101018181818161812181612101212181818181018
+% 10181300FF10181018101818181818101618181818181818181816181212
+% 101210121818181818181818181818181818181A18181618181818181818
+% 181818181610101210101210181018121012101010121012101217181818
+% 101818181810121010101810181018101818181818101618181818181818
+% 181816181212101210121818181818181818181818181818181A18181618
+% 181818181818181818181610101210101210181018121012101010121012
+% 101217181818101818181810121010101810181018101818181818101618
+% 181818181818181816181212101210121818181818181818181818181818
+% 181A18181618181818181818181818181610101210101210181018121012
+% 101010121012101217181818101818181810121010101810181018101818
+% 181818101618181818181818181816181212101210121818181818181818
+% 181818181818181A18181618181818181818181818181610101210101210
+% 181018121012101010121012101217181818101818181810121010101810
+% 181018101818181818101618181818181818181816181212101210121818
+% 181818181818181818181818181A18181618181818181818181818181610
+% 101210101210181018121012101010121012101217181818101818181810
+% 121010101810181018101818181818101618181818181818181816181212
+% 101210121818181818181818181818181818181A18181618181818181818
+% 181818181610101210101210181018121012101010121012101217181818
+% 101818181810121010101810181018101818181818101618181818181818
+% 181800FF1012121212101018181818181210121018121612161210101010
+% 101010101018181818181818181818181A18181818181818181818181818
+% 181818181813101012101210121810101010101010101012181812181618
+% 101210121010101010121012121212101018181818181210121018121612
+% 161210101010101010101018181818181818181818181A18181818181818
+% 181818181818181818181813101012101210121810101010101010101012
+% 181812181618101210121010101010121012121212101018181818181210
+% 121018121612161210101010101010101018181818181818181818181A18
+% 181818181818181818181818181818181813101012101210121810101010
+% 101010101012181812181618101210121010101010121012121212101018
+% 181818181210121018121612161210101010101010101018181818181818
+% 181818181A18181818181818181818181818181818181813101012101210
+% 121810101010101010101012181812181618101210121010101010121012
+% 121212101018181818181210121018121612161210101010101010101018
+% 181818181818181818181A18181818181818181818181818181818181813
+% 101012101210121810101010101010101012181812181618101210121010
+% 101010121012121212101018181818181210121018121612161210101010
+% 101010101018181818181818181818181A18181818181818181818181818
+% 181818181813101012101210121810101010101010101012181812181618
+% 101210121010101010121012121212101018181818181210121018121612
+% 1600FF121010101010121810181818161010121010101210121612101010
+% 10101010101210161818181818181A1820181A1818181818181818181818
+% 181818181618101010101012101812121012101012101012101817121210
+% 181618101210101010121010101010121810181818161010121010101210
+% 12161210101010101010101210161818181818181A1820181A1818181818
+% 181818181818181818181618101010101012101812121012101012101012
+% 101817121210181618101210101010121010101010121810181818161010
+% 12101010121012161210101010101010101210161818181818181A182018
+% 1A1818181818181818181818181818181618101010101012101812121012
+% 101012101012101817121210181618101210101010121010101010121810
+% 181818161010121010101210121612101010101010101012101618181818
+% 18181A1820181A1818181818181818181818181818181618101010101012
+% 101812121012101012101012101817121210181618101210101010121010
+% 101010121810181818161010121010101210121612101010101010101012
+% 10161818181818181A1820181A1818181818181818181818181818181618
+% 101010101012101812121012101012101012101817121210181618101210
+% 101010121010101010121810181818161010121010101210121612101010
+% 10101010101210161818181818181A1820181A1818181818181818181818
+% 181818181618101010101012101812121012101012101012101817121210
+% 181618101210101010121010101010121810181818161010121010101210
+% 00FF10101012101210101218181810121012161218121612101010101008
+% 100E1010101012121618181818181A181818181818181818181818181818
+% 181818181810101010101012101817181612101010121618181818101618
+% 181816181618181010101012101210101218181810121012161218121612
+% 101010101008100E1010101012121618181818181A181818181818181818
+% 181818181818181818181810101010101012101817181612101010121618
+% 181818101618181816181618181010101012101210101218181810121012
+% 161218121612101010101008100E1010101012121618181818181A181818
+% 181818181818181818181818181818181810101010101012101817181612
+% 101010121618181818101618181816181618181010101012101210101218
+% 181810121012161218121612101010101008100E10101010121216181818
+% 18181A181818181818181818181818181818181818181810101010101012
+% 101817181612101010121618181818101618181816181618181010101012
+% 101210101218181810121012161218121612101010101008100E10101010
+% 12121618181818181A181818181818181818181818181818181818181810
+% 101010101012101817181612101010121618181818101618181816181618
+% 181010101012101210101218181810121012161218121612101010101008
+% 100E1010101012121618181818181A181818181818181818181818181818
+% 181818181810101010101012101817181612101010121618181818101618
+% 181816181618181010101012101210101218181810121012161218121600
+% FF1012101010101010101012181818181012101010101210121010101010
+% 1010101010101012181818181A18181A1818181818171818121618181818
+% 181618181010101008101018181818181818181613181818181818121818
+% 181818181818101012101010101010101012181818181012101010101210
+% 1210101010101010101010101012181818181A18181A1818181818171818
+% 121618181818181618181010101008101018181818181818181613181818
+% 181818121818181818181818101012101010101010101012181818181012
+% 1010101012101210101010101010101010101012181818181A18181A1818
+% 181818171818121618181818181618181010101008101018181818181818
+% 181613181818181818121818181818181818101012101010101010101012
+% 181818181012101010101210121010101010101010101010101218181818
+% 1A18181A1818181818171818121618181818181618181010101008101018
+% 181818181818181613181818181818121818181818181818101012101010
+% 101010101012181818181012101010101210121010101010101010101010
+% 1012181818181A18181A1818181818171818121618181818181618181010
+% 101008101018181818181818181613181818181818121818181818181818
+% 101012101010101010101012181818181012101010101210121010101010
+% 1010101010101012181818181A18181A1818181818171818121618181818
+% 181618181010101008101018181818181818181613181818181818121818
+% 1818181818181010121010101010101010121818181810121010101000FF
+% 121010121012101210121010121612161216121813181718101210101010
+% 101010101010101816181818181A18181818181812101210121812171818
+% 18121810101008101010101818181A18181A181818181A18181818181818
+% 181818181810121010121012101210121010121612161216121813181718
+% 101210101010101010101010101816181818181A18181818181812101210
+% 1218121718184774BBFFFFFF7A59201010181818FFFFFF1A181818FFFFFF
+% 1818181818FFFFFF181818101210477FFFFF7F4010121010121612161216
+% 12183077FFFFBB742E28FFFF7F1010FFFFFF1018161818FFFFFF18181818
+% 182D74FFFF8E5318FFFFFF18181218101010081010101018184777BBFFFF
+% A37746181A1818181818184677BBFFFF773E121010121012104072FFFFFF
+% 90551216121612FFFFFF17181012FFFFFF1010101010FFFFFF1816181818
+% 184F83FFFF8346181210121012181217181818FFFFFF1010081010101018
+% 18181A18183877FFFF934F18FFFFFF18181818FFFFFF1810121010FFFFFF
+% 10121012102067BBFFFF7F451218131817187FFFFF71101010FFFFFF1010
+% 101816184677BBFFFF7747181818121012FFFFFF1217181818FFFFFF1010
+% 0810103E72FFFFFF9357181A181818181A18181818181818181818181810
+% 121010121012101210121010121612161216121813181718101210101010
+% 101010101010101816181818181A18181818181812101210121812171818
+% 18121810101008101010101818181A18181A181818181A18181818181818
+% 18181818181012101012101210121012101012161216121612181300FF10
+% 101210101210181210121010121018181818181618181818161010101010
+% 0A0E10101212181218181818181818181818101012101010121618121612
+% 1816101010080A1010121818181820181818181818181818181812101210
+% 181216181010101210101210181210121010121018181818181618181818
+% 1610101010100A0E10101212181218181818181818181818101012101010
+% 1216181291FFFFFFFFFFFFFFFFBB3E12181818FFFFFF18181818FFFFFF18
+% 18181210FFFFFF1216181029BBFFFFFFFFFFFF6710121010121018181818
+% 3EFFFFFFFFFFFFFFBBFFFF350A0EFFFFFF1218121818FFFFFF181818183E
+% FFFFFFFFFFFF7FFFFFFF16121816101010080A1010122893FFFFFFFFFFFF
+% FFFFA32D18181818128EFFFFFFFFFFFFFF72101210101265FFFFFFFFFFFF
+% FF8E18181818FFFFFF18181816FFFFFF10100A0E10FFFFFF181218182EBB
+% FFFFFFFFFFFF651012101010121618121612FFFFFF1010080A1010121818
+% 1818202EFFFFFFFFFFFF6AFFFFFF12101210FFFFFF1810101012FFFFFF10
+% 18121038BBFFFFFFFFFFFF6A1816181877FFFFFF8E101010FFFFFF101212
+% 181293FFFFFFFFFFFFFF771810101210FFFFFF1618121612FFFFFF101008
+% 0A65FFFFFFFFFFFFFF931818181818181818181812101210181216181010
+% 101210101210181210121010121018181818181618181818161010101010
+% 0A0E10101212181218181818181818181818101012101010121618121612
+% 1816101010080A1010121818181820181818181818181818181812101210
+% 181216181010101210101210181210121010121018181818181600FF1218
+% 1018181818181A1818181810101818161216121012101218181818121010
+% 101010101010101818181818181818101018121816121612101210121010
+% 1218181010101010101816181A181A181A18181A181A1816121010121012
+% 1012101812181018181818181A1818181810101818161216121012101218
+% 181818121010101010101010101818181818181818101018121816121612
+% 10127FFFFFFFFFFFFFFFFFFFFFFF29181618FFFFFF181A1818FFFFFF1816
+% 121010FFFFFF10121018BBFFFFFFFFFFFFFFFF3E18181810101818161291
+% FFFF67101246BBFFFFFF101010FFFFFF1010101818FFFFFF18181810BBFF
+% FFFFFFFFFFFFFFFFFF121010121818101010101021BBFFFFFFFFFFFFFFFF
+% FFFFBB301816128EFFFFFFFFFFFFFFFFFF7710181847FFFF932E181861FF
+% FF69181612FFFFFF12101218FFFFFF1210101010FFFFFF10101818BBFFFF
+% FFFFFFFFFFFF3818161216121012101210FFFFFF18101010101010181618
+% 1A1877FFFFFFFFFFFFFFFFFFFF10101210FFFFFF1018121810FFFFFF1818
+% 1A18BBFFFFFFFFFFFFFFFF55121012BBFFFFFF6A181210FFFFFF10101010
+% 8EFFFFFFFFFFFFFFFFFF7218121816FFFFFF1012101210FFFFFF1810103E
+% FFFF8E2D161864FFFF6A1A18181A181A1816121010121012101210181218
+% 1018181818181A1818181810101818161216121012101218181818121010
+% 101010101010101818181818181818101018121816121612101210121010
+% 1218181010101010101816181A181A181A18181A181A1816121010121012
+% 1012101812181018181818181A18181818101018181612161200FF101012
+% 101818181818181A18181218181818181812101810101218181010101010
+% 101010101210121618181818161210121018101818181810181012101210
+% 121612101010101018181818181A18181A181A1818181812101210121012
+% 101812101012101818181818181A18181218181818181812101810101218
+% 181010101010101010101210121618181818161210121018101818181810
+% 46FFFFFF74291216123EBBFFFF71181818FFFFFF18181A18FFFFFF181812
+% 1012FFFFFF12101849FFFFBB3618184FFFFF931A1818121818181818FFFF
+% FF1810101235FFFFFF101010FFFFFF1012101216FFFFFF18161252FFFFBB
+% 3E18184EBBFFFFFF12101210121612101010107FFFFFBB57291A182E64FF
+% FFFF83181840FFFFFF5321122161FFFFFF401018A3FFFF29181A1818FFFF
+% BB181818FFFFFF18101012FFFFFF1010101010FFFFFF1210124DFFFFBB3E
+% 161247FFFF9210181818181018101210FFFFFF1612101010101018181818
+% 18BBFFFF6C181A3EBBFFFFFF10121012FFFFFF1812101012FFFFFF181818
+% 57FFFFBB30182893FFFFA3121018FFFFFF1828101010FFFFFF10101040FF
+% FFFF5628182860FFFFFF3E181018FFFFFF1018101210FFFFFF161210A0FF
+% FF2018181818FFFFBB181A181A1818181812101210121012101812101012
+% 101818181818181A18181218181818181812101810101218181010101010
+% 101010101210121618181818161210121018101818181810181012101210
+% 121612101010101018181818181A18181A181A1818181812101210121012
+% 101812101012101818181818181A1818121818181818181200FF10121010
+% 121012181818181818181818181818181718131010101010121010101010
+% 101010101210101212121012101010121012181818181818101210101010
+% 10121818181316181818181A181818181818181818181010101010101012
+% 101010121010121012181818181818181818181818181718131010101010
+% 121010101010101010101210101212121012101010121012181818181877
+% FFFF721010101012182EFFFFFF181818FFFFFF18181818FFFFFF18181010
+% 10FFFFFF1012107FFFFF3E10121012181818181818181818181818A3FFFF
+% 4010101010FFFFFF101010FFFFFF1010121010FFFFFF1012107FFFFF3E12
+% 1818184EFFFFFF1210101010101218181838FFFFBB2D181A181818183EA3
+% 622E18187FFFFF51101010121065FFFF7210121012181818184777FFFFFF
+% 181818FFFFFF13101010FFFFFF1010101010FFFFFF1012107FFFFF401012
+% 101010121012181818181818101210FFFFFF10121818181316181818181A
+% FFFFFF281818183EFFFFFF10101010FFFFFF1010101210FFFFFF12181893
+% FFFF4618181828A3612D171813FFFFFF1010121010FFFFFF1010107FFFFF
+% 52121212101265FFFF74101218FFFFFF1818101210FFFFFF101218181813
+% 1618184677FFFFFF18181818181818181010101010101012101010121010
+% 121012181818181818181818181818181718131010101010121010101010
+% 101010101210101212121012101010121012181818181818101210101010
+% 10121818181316181818181A181818181818181818181010101010101012
+% 101010121010121012181818181818181818181818181700FF1810101210
+% 101818181818181818181818181818181816101012101010101010101010
+% 101210101210121010121010101010101216181818181818101010101010
+% 101018181612181818181818181818181818181810101010101010121010
+% 181810101210101818181818181818181818181818181816101012101010
+% 10101010101010121010121012101012101010101010121618181818BBFF
+% FF2110101010101018FFFFFF181818FFFFFF18181818FFFFFF1810101010
+% FFFFFF121010BBFFFF10121010181818181818181818181818184FFFFFFF
+% A072532DFFFFFF101010FFFFFF1210101210FFFFFF121010FFFFFF101216
+% 181818FFFFFF1010101010101010181868FFFF5618181818181818181818
+% 181810FFFFFF2010101210102DFFFFBB12101018284F92FFFFFFFFFF9318
+% 1818FFFFFF16101012FFFFFF1010101010FFFFFF101012BBFFFF10121010
+% 1010101012161818181818181010FFFFFF101010181816121818181818FF
+% FFFF1818181818FFFFFF10101010FFFFFF1018181010FFFFFF181818FFFF
+% FF1818181818181818181816FFFFFF1010101010FFFFFF101012FFFFFF21
+% 121010121029FFFFBB101216FFFFFF1818181010FFFFFF10101018182749
+% 93FFFFFFFFFF931818181818181810101010101010121010181810101210
+% 101818181818181818181818181818181816101012101010101010101010
+% 101210101210121010121010101010101216181818181818101010101010
+% 101018181612181818181818181818181818181810101010101010121010
+% 1818101012101018181818181818181818181818181800FF181818101612
+% 181018181818181818181818181818181818181010121018181010101210
+% 10101210101010121010121010101010121818181818181012101010100A
+% 101010121818181818181818181718181818121012101010121010181818
+% 181818101612181018181818181818181818181818181818181010121018
+% 181010101210101012101010101210101210101010101218181818181810
+% 12101010100A287FFFFFA3181818FFFFFF18181718FFFFFF1210121010FF
+% FFFF101818FFFFFFFFFFFFFFFFFFFFFF181818181818181818184EA3FFFF
+% FFFFFFFFFFFF181010FFFFFF1010121010FFFFFF101012FFFFFF10101218
+% 1818FFFFFF1012101010100A101010A0FFFF351818181818181718181818
+% 1210FFFFFF10121010181818FFFFFF10161269FFFFFFFFFFFFFFBB2D1818
+% 18FFFFFF18181810FFFFFF1818101010FFFFFF101210FFFFFFFFFFFFFFFF
+% FFFFFF10121818181818181012FFFFFF100A10101012181818181818FFFF
+% FF1718181818FFFFFF10101012FFFFFF1818181818FFFFFF181018FFFFFF
+% 1818181818181818181818FFFFFF1012101818FFFFFF121010FFFFFF1010
+% 1012101012FFFFFF101012FFFFFF1818181012FFFFFF100A101065FFFFFF
+% FFFFFFFFBB2D181718181818121012101010121010181818181818101612
+% 181018181818181818181818181818181818181010121018181010101210
+% 10101210101010121010121010101010121818181818181012101010100A
+% 101010121818181818181818181718181818121012101010121010181818
+% 18181810161218101818181818181818181818181800FF1A181812121210
+% 181012121018181818181818181216181818101210181818101018101818
+% 181816121012101012161010101010121818181818181012101010101008
+% 10101010181817181818181818181818161210101010101012181818181A
+% 181812121210181012121018181818181818181216181818101210181818
+% 101018101818181816121012101012161010101010121818181818181012
+% 10294671BBFFFFFFFF52181817FFFFFF18181818FFFFFF1210101010FFFF
+% FF181818BBFFFFFFFFFFFFFFFFFFBB1210181818181818181812162E4E69
+% 8EFFFFFFFF181010FFFFFF1818181612FFFFFF101216FFFFFF1010121818
+% 18FFFFFF10121010101010081010FFFFFF18171818181818181818181612
+% 10FFFFFF2010121818182EFFFFFF12125CFFFFFFFFFFFF77471818181818
+% FFFFFF18181810FFFFFF1818101018FFFFFF181816BBFFFFFFFFFFFFFFFF
+% FFBB10121818181818181012FFFFFFFFFFFFFFFFFFFFFF18171818FFFFFF
+% 1818181816FFFFFF10101010FFFFFF18181A1818FFFFFF101810FFFFFF18
+% 18181818181818121618FFFFFF1210181818FFFFFF101818FFFFFF221012
+% 1010122CFFFFFF101012FFFFFF1818181012FFFFFF10100859FFFFFFFFFF
+% FF774718181818181818161210101010101012181818181A181812121210
+% 181012121018181818181818181216181818101210181818101018101818
+% 181816121012101012161010101010121818181818181012101010101008
+% 10101010181817181818181818181818161210101010101012181818181A
+% 181812121210181012121018181818181818181200FF1818121010101012
+% 101010121012181818171818161812181012121012121818181818181818
+% 1A1818181010181818101210101010101818181818161217121010101010
+% 1010101012181818181818181118131810101010101012181818181A1818
+% 121010101012101010121012181818171818161812181012121012121818
+% 1818181818181A18181810101818181012101010101018181818181640A3
+% FFFFFFFFFFFFFF6510101218FFFFFF18181811FFFFFF3E10101020FFFFFF
+% 18181877FFFF2210101010FFFF7F10121012181818171818161812181012
+% 12FFFFFF181818FFFFFF46181A1829FFFFFF1818187FFFFF3E1010101847
+% FFFFFF16121712101010101010FFFFFF1218181818181818111813181010
+% 71FFFF5212181818186CFFFF741010BBFFFFFF723E1210121818181718FF
+% FFFF12181012FFFFFF4018181829FFFFFF181A1877FFFF2118181810FFFF
+% 7F10101018181818181612FFFFFFFFFFFFFFFFFFFFFF12181818FFFFFF18
+% 11181318FFFFFF10101012FFFFFF471A181822FFFFFF1012107FFFFF3E12
+% 181828A3612E161812FFFFFF1210121218FFFFFF18181877FFFF57181010
+% 18186AFFFF71101010FFFFFF4618181622FFFFFF101010BBFFFFFF724018
+% 1818181818181118131810101010101012181818181A1818121010101012
+% 101010121012181818171818161812181012121012121818181818181818
+% 1A1818181010181818101210101010101818181818161217121010101010
+% 1010101012181818181818181118131810101010101012181818181A1818
+% 1210101010121010101210121818181718181600FF181810101010101210
+% 101010101216181818181012101210101010101010101818181818181818
+% 181818181818181818101010101012181818181818181818101818121010
+% 100A10101818181818181818181810121010101012161818181A18181810
+% 101010101210101010101216181818181012101210101010101010101818
+% 181818181818181818181818181818101010101012181818181883FFFFFF
+% FFFFFFFFA03E100A101018FFFFFF18181818FFFFFFBB3E101072FFFFFF18
+% 181A47FFFF8E21102171FFFF47101010121618181818A0FFFF2A10101010
+% FFFFFF101818FFFFFFBB47181877FFFFFF1818184EFFFFBB3E101246BBFF
+% FFFF18181818101818121010FFFFFF10181818181818181818181012103E
+% FFFFFF5528182964FFFFFF3E1010FFFFFF2910101021FFFFA3181818FFFF
+% FF12101010FFFFFFBB3E181877FFFFFF18181846FFFF9229182977FFFF46
+% 10101218181818181818FFFFFFFFFFFFFFFFFFFFFF10181818FFFFFF1818
+% 181810FFFFFF10101216FFFFFFBB47181872FFFFFF10121052FFFFBB3016
+% 2892FFFFA0121012FFFFFF1010101010FFFFFF18181847FFFFFF57281829
+% 61FFFFFF3E101010FFFFFFBB46181877FFFFFF181812FFFFFF2210101828
+% FFFFA3181818181810121010101012161818181A18181810101010101210
+% 101010101216181818181012101210101010101010101818181818181818
+% 181818181818181818101010101012181818181818181818101818121010
+% 100A10101818181818181818181810121010101012161818181A18181810
+% 10101010121010101010121618181818101200FF18181810100808101010
+% 10100B0E0A10101618181818181810181316121612161210121618181818
+% 181818161218101818181818161010101012101010121010101218181618
+% 101010181618181A1818181310101810181012181818181A181818181810
+% 10080810101010100B0E0A10101618181818181810181316121612161210
+% 12161818181818181816121810181818181816101010101252FFFFFFFFBB
+% 72492D18161810101018FFFFFF1A181818FFFFFFFFFFFFFFFFFFFF77181A
+% 181892FFFFFFFFFFFFFFBB1010100B0E0A1010161857FFFF9328101867FF
+% FF83121612FFFFFFFFFFFFFFFFFFFF7512181018BBFFFFFFFFFFFFFFFFFF
+% FF1010121010101218181692FFFF29181618181A1818181310101810187F
+% FFFFFFFFFFFFFFFFFF831810106AFFFF461010208BFFFF52101618FFFFFF
+% 18181018FFFFFFFFFFFFFFFFFFFF771818181892FFFFFFFFFFFFFFBB1818
+% 161010101012101010FFFFFF101218181618101010181618FFFFFF181813
+% 1010FFFFFF10121818FFFFFFFFFFFFFFFFFFFF6A08101010BBFFFFFFFFFF
+% FFFFFF4718FFFFFFFFFFFFFF121612FFFFFF1216181883FFFFFFFFFFFFFF
+% FFFF8318181816FFFFFFFFFFFFFFFFFFFF7210121877FFFF4710102891FF
+% FF571818181310101810181012181818181A181818181810100808101010
+% 10100B0E0A10101618181818181810181316121612161210121618181818
+% 181818161218101818181818161010101012101010121010101218181618
+% 101010181618181A1818181310101810181012181818181A181818181810
+% 10080810101010100B0E0A10101618181800FF1818181612101010101010
+% 101010101010121818181818161018101612171218101210121818181818
+% 181818101810181818181818181810121010121010101012161818181818
+% 18181818181818181818181012101218161216181818181A181818181612
+% 101010101010101010101010121818181818161018101612171218101210
+% 1218181818181818181018101818181818181818101210A0FFFF7F361012
+% 161818181818181818FFFFFF18181818FFFFFF65FFFFFFFFFFFF3518181A
+% 181893FFFFFFFFFFFF36101010101010101012181892FFFFFFFFFFFFFFFF
+% 3C121810FFFFFF69FFFFFFFFFFFF35101810183EFFFFFFFFFFFF8EFFFFFF
+% 1210101010121618181869FFFF5718181818181818181810121012181690
+% FFFFFFFFFFFFFF831818161229BBFFFFFFFFFFFFFF8E10101218FFFFFF18
+% 161018FFFFFF68FFFFFFFFFFFF35181818181893FFFFFFFFFFFF3E181818
+% 1818101210101210FFFFFF121618181818181818181818FFFFFF18181810
+% 12FFFFFF16121618FFFFFF6CFFFFFFFFFFFF2E1010101029BBFFFFFFFFFF
+% FF691818FFFFFFFFFFFFFF121712FFFFFF101218181892FFFFFFFFFFFFFF
+% 831818181818FFFFFF67FFFFFFFFFFFF2E1216182EBBFFFFFFFFFFFFFF92
+% 18181818181012101218161216181818181A181818181612101010101010
+% 101010101010121818181818161018101612171218101210121818181818
+% 181818101810181818181818181810121010121010101012161818181818
+% 18181818181818181818181012101218161216181818181A181818181612
+% 1010101010101010101010101218181800FF181618181818181012101010
+% 101010101010121618181818121812181218121012101210121018181816
+% 181810181210121018181818181210101210121012101012181818181818
+% 1818181818181818181818131618101218181818181A1818181618181818
+% 181012101010101010101010121618181818121812181218121012101210
+% 12101818181618181018121012101818181818121010FFFFFF1012101012
+% 28FFFFFF18181818FFFFFF18181818FFFFFF16478EFFFF773518181A1818
+% 18164693FFBB6A29121010101010101010101216184F83FFFFFFA06A2A18
+% 121012FFFFFF123E92FFFF75351810181210122977BBFFA35612FFFFFF10
+% 12101210101218181835FFFFBB2D18181818181835A3612B161810121847
+% 77FFFFFF7746181618181818295A90FFFFFF7F471010101012FFFFFF1818
+% 1218FFFFFF18408EFFFF732E12101818181618478EFFBB652A1018181818
+% 18121010121012FFFFFF101218181818181818181818FFFFFF1818181813
+% FFFFFF12181818FFFFFF184692FFFF77351818101210102165A0FFFF8E3E
+% 121618FFFFFFFFFFFFFF121812FFFFFF1210121018184775FFFFFF774010
+% 1210181818FFFFFF103E90FFFF72301010121818296193FFFFFF834E1818
+% 1818181818131618101218181818181A1818181618181818181012101010
+% 101010101010121618181818121812181218121012101210121018181816
+% 181810181210121018181818181210101210121012101012181818181818
+% 1818181818181818181818131618101218181818181A1818181618181818
+% 18101210101010101010101012161800FF18121216181818121612101010
+% 10100A0F1010121818181618171816181618101210101010121018181812
+% 161218101010181818181818121612181018101810101012161818181818
+% 1A1818181A18181818181818181818161818181818181818121216181818
+% 12161210101010100A0F1010121818181618171816181618101210101010
+% 121018181812161218101010181818181818121612A3FFFF652810102990
+% FFFF831818181A1818181A18181818181818181818161818181818181818
+% 12121618181812161210101010100A0F1010121818181618171816181618
+% 10121010101012101818181216121810101018181818181812FFFFFF1018
+% 10181010101216181883FFFFBB5728181A2E56FFFFFF8318181818161818
+% 18181818181812121618181812161210101010100A0F1010121818181618
+% 171816181618101210101010121018181812161218101010181818181818
+% 121612181018FFFFFF1010121618181818181A1818181A18181818181818
+% 18181816181818181818181812121618181812161210101010100A0F1010
+% 12181818FFFFFF1816181618101210101010121018181812161218101010
+% 1818181818181216121810181018101010121618181818181A1818181A18
+% 181818181818181818161818181818181818121216181818121612101010
+% 10100A0F1010121818181618171816181618101210101010121018181812
+% 161218101010181818181818121612181018101810101012161818181818
+% 1A1818181A18181818181818181818161818181818181818121216181818
+% 12161210101010100A0F1010121800FF1810101012101216121810121010
+% 121010101010121618181818181818181810101010121010101210121612
+% 181018101818181818181816121810181018121010101212181818181818
+% 18181818181A181818181818181818181818181818181810101012101216
+% 121810121010121010101010121618181818181818181810101010121010
+% 101210121612181018101818181818181816121852FFFFFFFFFFFFFFFFFF
+% FF3E18181818FFFFFF18181A181818181818181818181818181818181810
+% 101012101216121810121010121010101010121618181818181818181810
+% 101010121010101210121612181018101818181818181816FFFFFF181018
+% 12101010121218181893FFFFFFFFFFFFFFFFFFFFFF361818181818181818
+% 1818181818101010121012161218101210101210101010FFFFFF18181818
+% 181818181810101010121010101210121612181018101818181818181816
+% 1218101810FFFFFFFFFFFFFFFFFFFFFFFF1818181818181A181818181818
+% 181818181818181818181810101012101216121810121010121010101010
+% 121618FFFFFF1818181818FFFFFF10121010101210121612181018101818
+% 18181818181612181018101812101010121218181818181818181818181A
+% 181818181818181818181818181818181810101012101216121810121010
+% 121010101010121618181818181818181810101010121010101210121612
+% 181018101818181818181816121810181018121010101212181818181818
+% 18181818181A181818181818181818181818181818181810101012101216
+% 1218101210101210101010101200FF181612101010101210121612101216
+% 101010101210121818181818181818181612101010101210121012101216
+% 121818181818181612101210121012101210101010101012181818181818
+% 181818181818181A18181818181818181818181818181612101010101210
+% 121612101216101010101210121818181818181818181612101010101210
+% 121012101216121818181818181612101210121067FFFFFFFFFFFFFFFF5C
+% 1818181818FFFFFF18181818181A18181818181818181818181818181612
+% 101010101210121612101216101010101210121818181818181818181612
+% 1010101012101210121012161218181818181816121012FFFFFF12101210
+% 10101010101218181883FFFFFFFFFFFFFFFFBB4018181818181818181818
+% 18181818161210101010121012161210121610101010FFFFFF1818181818
+% 181818181612101010101210121012101216121818181818181612101210
+% 12101210FFFFFFFFFFFFFFFFFFFFFFFF1818181818181818181A18181818
+% 181818181818181818181612101010101210121612101216101010101210
+% 12183EBBFF1818181818FFFFFF1010101210121012101216121818181818
+% 181612101210121012101210101010101012181818181818181818181818
+% 181A18181818181818181818181818181612101010101210121612101216
+% 101010101210121818181818181818181612101010101210121012101216
+% 121818181818181612101210121012101210101010101012181818181818
+% 181818181818181A18181818181818181818181818181612101010101210
+% 12161210121610101010121000FF18181816101010101012101210181216
+% 121010101012161818181818181818121810101010101010121012101810
+% 18181818181818121010121012101210121010100F101010121018181818
+% 18181A18181A181A18181818181818181818181818181816101010101012
+% 101210181216121010101012161818181818181818121810101010101010
+% 12101210181018181818181818121010121012104072BBFFFFA0652D1010
+% 12101818FFFFFF181A18181A181A18181818181818181818181818181816
+% 101010101012101210181216121010101012161818181818181818121810
+% 10101010101012101210181018181818181818121010FFFFFF1012101210
+% 10100F1010101210183E77A3FFFFBB834F1A181A18181818181818181818
+% 181818181816101010101012101210181216121010FFFFFF161818181818
+% 181818121810101010101010121012101810181818181818181210101210
+% 121012FFFFFFFFFFFFFFFFFFFFFFFF18181818181A18181A181A18181818
+% 181818181818181818181816101010101012101210181216121010101012
+% 161818771818181818FFFFFF101010101010121012101810181818181818
+% 18121010121012101210121010100F10101012101818181818181A18181A
+% 181A18181818181818181818181818181816101010101012101210181216
+% 121010101012161818181818181818121810101010101010121012101810
+% 18181818181818121010121012101210121010100F101010121018181818
+% 18181A18181A181A18181818181818181818181818181816101010101012
+% 101210181216121010101000FF1818181818121010101010101818181810
+% 101010101012181818181818181818181012101010121010101012101818
+% 101818181818181612101010121010101010100A10101010101210181818
+% 18181A181A18201818181818181818181818181818181818121010101010
+% 101818181810101010101012181818181818181818181012101010121010
+% 101012101818101818181818181612101010121010101010100A10101010
+% 10121018181818181A181A18201818181818181818181818181818181818
+% 121010101010101818181810101010101012181818181818181818181012
+% 101010121010101012101818101818181818181612101010121010101010
+% 100A1010101010121018181818181A181A18201818181818181818181818
+% 181818181818121010101010101818181810101010101012181818181818
+% 181818181012101010121010101012101818101818181818181612101010
+% 121010101010100A1010101010121018181818181A181A18201818181818
+% 181818181818181818181818121010101010101818181810101010101012
+% 181818181818181818181012101010121010101012101818101818181818
+% 181612101010121010101010100A1010101010121018181818181A181A18
+% 201818181818181818181818181818181818121010101010101818181810
+% 101010101012181818181818181818181012101010121010101012101818
+% 101818181818181612101010121010101010100A10101010101210181818
+% 18181A181A18201818181818181818181818181818181818121010101010
+% 1018181818101010101000FF181818181010101010101218181618181818
+% 1818181818181A1818181810101816181818181216101210121018101210
+% 1010181818101210101210101012101010100A0810101010101012161818
+% 181818181A18181818101012101810181618181818181010101010101218
+% 1816181818181818181818181A1818181810101816181818181216101210
+% 1210181012101010181818101210101210101012101010100A0810101010
+% 101012161818181818181A18181818101012101810181618181818181010
+% 1010101012181816181818181818181818181A1818181810101816181818
+% 181216101210121018101210101018181810121010121010101210101010
+% 0A0810101010101012161818181818181A18181818101012101810181618
+% 1818181810101010101012181816181818181818181818181A1818181810
+% 101816181818181216101210121018101210101018181810121010121010
+% 1012101010100A0810101010101012161818181818181A18181818101012
+% 101810181618181818181010101010101218181618181818181818181818
+% 1A1818181810101816181818181216101210121018101210101018181810
+% 1210101210101012101010100A0810101010101012161818181818181A18
+% 181818101012101810181618181818181010101010101218181618181818
+% 1818181818181A1818181810101816181818181216101210121018101210
+% 1010181818101210101210101012101010100A0810101010101012161818
+% 181818181A18181818101012101810181618181818181010101010101218
+% 18161818181818181800FF16181818101012081010101012181818181818
+% 181818181A181A1818161212181818181818161210121010121216181010
+% 121010101212161218101210101012101010101010101010101012181818
+% 18181A181818181810121012101210121216181818101012081010101012
+% 181818181818181818181A181A1818161212181818181818161210121010
+% 121216181010121010101212161218101210101012101010101010101010
+% 10101218181818181A181818181810121012101210121216181818101012
+% 081010101012181818181818181818181A181A1818161212181818181818
+% 161210121010121216181010121010101212161218101210101012101010
+% 10101010101010101218181818181A181818181810121012101210121216
+% 181818101012081010101012181818181818181818181A181A1818161212
+% 181818181818161210121010121216181010121010101212161218101210
+% 10101210101010101010101010101218181818181A181818181810121012
+% 101210121216181818101012081010101012181818181818181818181A18
+% 1A1818161212181818181818161210121010121216181010121010101212
+% 16121810121010101210101010101010101010101218181818181A181818
+% 181810121012101210121216181818101012081010101012181818181818
+% 181818181A181A1818161212181818181818161210121010121216181010
+% 121010101212161218101210101012101010101010101010101012181818
+% 18181A181818181810121012101210121216181818101012081010101012
+% 181818181818181800FF121018121612100E0A081010101818181A18181A
+% 181818181818181812101018181818181818181010181318161818121010
+% 101010101018181618181018121612101010101010100E10101210181818
+% 18181A18181818101216121018101210121018121612100E0A0810101018
+% 18181A18181A181818181818181812101018181818181818181010181318
+% 161818121010101010101018181618181018121612101010101010100E10
+% 10121018181818181A18181818101216121018101210121018121612100E
+% 0A081010101818181A18181A181818181818181812101018181818181818
+% 181010181318161818121010101010101018181618181018121612101010
+% 101010100E1010121018181818181A181818181012161210181012101210
+% 18121612100E0A081010101818181A18181A181818181818181812101018
+% 181818181818181010181318161818121010101010101018181618181018
+% 121612101010101010100E1010121018181818181A181818181012161210
+% 18101210121018121612100E0A081010101818181A18181A181818181818
+% 181812101018181818181818181010181318161818121010101010101018
+% 181618181018121612101010101010100E1010121018181818181A181818
+% 18101216121018101210121018121612100E0A081010101818181A18181A
+% 181818181818181812101018181818181818181010181318161818121010
+% 101010101018181618181018121612101010101010100E10101210181818
+% 18181A18181818101216121018101210121018121612100E0A0810101018
+% 18181A18181A1800FF101210181818181010100A10101818181818181818
+% 18181A181818181010121012101818181810181218161818181816181010
+% 101010121818181816181216181812101010101010101010101212101818
+% 181818181818181810181112101610101210181818181010100A10101818
+% 18181818181818181A181818181010121012101818181810181218161818
+% 181816181010101010121818181816181216181812101010101010101010
+% 101212101818181818181818181810181112101610101210181818181010
+% 100A1010181818181818181818181A181818181010121012101818181810
+% 181218161818181816181010101010121818181816181216181812101010
+% 101010101010101212101818181818181818181810181112101610101210
+% 181818181010100A1010181818181818181818181A181818181010121012
+% 101818181810181218161818181816181010101010121818181816181216
+% 181812101010101010101010101212101818181818181818181810181112
+% 101610101210181818181010100A1010181818181818181818181A181818
+% 181010121012101818181810181218161818181816181010101010121818
+% 181816181216181812101010101010101010101212101818181818181818
+% 181810181112101610101210181818181010100A10101818181818181818
+% 18181A181818181010121012101818181810181218161818181816181010
+% 101010121818181816181216181812101010101010101010101212101818
+% 181818181818181810181112101610101210181818181010100A10101818
+% 18181818181800FF10101010121018181810101012181818181818181818
+% 18181A181818101210101010121012101018101818181818181818181818
+% 181010181218101212101218181018101210101010101010101012181818
+% 181818181018101812181018121010101010121018181810101012181818
+% 18181818181818181A181818101210101010121012101018101818181818
+% 181818181818181010181218101212101218181018101210101010101010
+% 101012181818181818181018101812181018121010101010121018181810
+% 10101218181818181818181818181A181818101210101010121012101018
+% 101818181818181818181818181010181218101212101218181018101210
+% 101010101010101012181818181818181018101812181018121010101010
+% 12101818181010101218181818181818181818181A181818101210101010
+% 121012101018101818181818181818181818181010181218101212101218
+% 181018101210101010101010101012181818181818181018101812181018
+% 12101010101012101818181010101218181818181818181818181A181818
+% 101210101010121012101018101818181818181818181818181010181218
+% 101212101218181018101210101010101010101012181818181818181018
+% 101812181018121010101010121018181810101012181818181818181818
+% 18181A181818101210101010121012101018101818181818181818181818
+% 181010181218101212101218181018101210101010101010101012181818
+% 181818181018101812181018121010101010121018181810101012181818
+% 181818181800FF1012101010101218181810121618181818181818181818
+% 1818181810121010101010101012101212101810181818181818181A1818
+% 181216121612101010101210181012101010101010101012101012121818
+% 161210101212161216181818121012101010101218181810121618181818
+% 181818181818181818181012101010101010101210121210181018181818
+% 1818181A1818181216121612101010101210181012101010101010101012
+% 101012121818161210101212161216181818121012101010101218181810
+% 121618181818181818181818181818181012101010101010101210121210
+% 1810181818181818181A1818181216121612101010101210181012101010
+% 101010101012101012121818161210101212161216181818121012101010
+% 101218181810121618181818181818181818181818181012101010101010
+% 1012101212101810181818181818181A1818181216121612101010101210
+% 181012101010101010101012101012121818161210101212161216181818
+% 121012101010101218181810121618181818181818181818181818181012
+% 1010101010101012101212101810181818181818181A1818181216121612
+% 101010101210181012101010101010101012101012121818161210101212
+% 161216181818121012101010101218181810121618181818181818181818
+% 1818181810121010101010101012101212101810181818181818181A1818
+% 181216121612101010101210181012101010101010101012101012121818
+% 161210101212161216181818121012101010101218181810121618181818
+% 1818181800FF1810121010100E1816121810181618161818161818181818
+% 181818121012101010101218181812101012121612181618181818181818
+% 181818181810181210101010121612101010101010101012101210121012
+% 1010101010121818181818181810121010100E1816121810181618161818
+% 161818181818181818121012101010101218181812101012121612181618
+% 181818181818181818181810181210101010121612101010101010101012
+% 1012101210121010101010121818181818181810121010100E1816121810
+% 181618161818161818181818181818121012101010101218181812101012
+% 121612181618181818181818181818181810181210101010121612101010
+% 101010101012101210121012101010101012181818181818181012101010
+% 0E1816121810181618161818161818181818181818121012101010101218
+% 181812101012121612181618181818181818181818181810181210101010
+% 121612101010101010101012101210121012101010101012181818181818
+% 1810121010100E1816121810181618161818161818181818181818121012
+% 101010101218181812101012121612181618181818181818181818181810
+% 181210101010121612101010101010101012101210121012101010101012
+% 1818181818181810121010100E1816121810181618161818161818181818
+% 181818121012101010101218181812101012121612181618181818181818
+% 181818181810181210101010121612101010101010101012101210121012
+% 1010101010121818181818181810121010100E1816121810181618161818
+% 16181800FF181810100A100A101010101812121218181818181818181818
+% 181810101010101216181818181810121010121612181812101010181818
+% 18181818181818180A101012161210121010101010101010101010121010
+% 10101010121018181A181A181810100A100A101010101812121218181818
+% 181818181818181810101010101216181818181810121010121612181812
+% 10101018181818181818181818180A101012161210121010101010101010
+% 10101012101010101010121018181A181A181810100A100A101010101812
+% 121218181818181818181818181810101010101216181818181810121010
+% 12161218181210101018181818181818181818180A101012161210121010
+% 10101010101010101012101010101010121018181A181A181810100A100A
+% 101010101812121218181818181818181818181810101010101216181818
+% 18181012101012161218181210101018181818181818181818180A101012
+% 16121012101010101010101010101012101010101010121018181A181A18
+% 1810100A100A101010101812121218181818181818181818181810101010
+% 101216181818181810121010121612181812101010181818181818181818
+% 18180A101012161210121010101010101010101010121010101010101210
+% 18181A181A181810100A100A101010101812121218181818181818181818
+% 181810101010101216181818181810121010121612181812101010181818
+% 18181818181818180A101012161210121010101010101010101010121010
+% 10101010121018181A181A181810100A100A101010101812121218181818
+% 181800FF181818101010100B101012101818181818181818181010181216
+% 121012101010101218181818181A18181216121818121018181818181818
+% 181612181618181818101210181818101216121818181810101210101210
+% 10101010121818181818181818101010100B101012101818181818181818
+% 181010181216121012101010101218181818181A18181216121818121018
+% 181818181818181612181618181818101210181818101216121818181810
+% 10121010121010101010121818181818181818101010100B101012101818
+% 181818181818181010181216121012101010101218181818181A18181216
+% 121818121018181818181818181612181618181818101210181818101216
+% 12181818181010121010121010101010121818181818181818101010100B
+% 101012101818181818181818181010181216121012101010101218181818
+% 181A18181216121818121018181818181818181612181618181818101210
+% 181818101216121818181810101210101210101010101218181818181818
+% 18101010100B101012101818181818181818181010181216121012101010
+% 101218181818181A18181216121818121018181818181818181612181618
+% 181818101210181818101216121818181810101210101210101010101218
+% 18181818181818101010100B101012101818181818181818181010181216
+% 121012101010101218181818181A18181216121818121018181818181818
+% 181612181618181818101210181818101216121818181810101210101210
+% 10101010121818181818181818101010100B101012101818181818181818
+% 1800FF181818101210101010101012101210181818181818181211181012
+% 101012101010101818181818181812101210181810121012181018181818
+% 181210121210181818101216121818181818181818181218161812101010
+% 101010101012181818181818101210101010101012101210181818181818
+% 181211181012101012101010101818181818181812101210181810121012
+% 181018181818181210121210181818101216121818181818181818181218
+% 161812101010101010101012181818181818101210101010101012101210
+% 181818181818181211181012101012101010101818181818181812101210
+% 181810121012181018181818181210121210181818101216121818181818
+% 181818181218161812101010101010101012181818181818101210101010
+% 101012101210181818181818181211181012101012101010101818181818
+% 181812101210181810121012181018181818181210121210181818101216
+% 121818181818181818181218161812101010101010101012181818181818
+% 101210101010101012101210181818181818181211181012101012101010
+% 101818181818181812101210181810121012181018181818181210121210
+% 181818101216121818181818181818181218161812101010101010101012
+% 181818181818101210101010101012101210181818181818181211181012
+% 101012101010101818181818181812101210181810121012181018181818
+% 181210121210181818101216121818181818181818181218161812101010
+% 101010101012181818181818101210101010101012101210181818181818
+% 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+% FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
+%%EndPreview
+
+
+% including file "../library/bltGraph.pro"
+
+%
+% PostScript prolog file of the BLT graph widget.
+%
+% Copyright 1989-1992 Regents of the University of California.
+% Permission to use, copy, modify, and distribute this
+% software and its documentation for any purpose and without
+% fee is hereby granted, provided that the above copyright
+% notice appear in all copies. The University of California
+% makes no representations about the suitability of this
+% software for any purpose. It is provided "as is" without
+% express or implied warranty.
+%
+% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.
+%
+% Permission to use, copy, modify, and distribute this software and its
+% documentation for any purpose and without fee is hereby granted, provided
+% that the above copyright notice appear in all copies and that both that the
+% copyright notice and warranty disclaimer appear in supporting documentation,
+% and that the names of Lucent Technologies any of their entities not be used
+% in advertising or publicity pertaining to distribution of the software
+% without specific, written prior permission.
+%
+% Lucent Technologies disclaims all warranties with regard to this software,
+% including all implied warranties of merchantability and fitness. In no event
+% shall Lucent Technologies be liable for any special, indirect or
+% consequential damages or any damages whatsoever resulting from loss of use,
+% data or profits, whether in an action of contract, negligence or other
+% tortuous action, arising out of or in connection with the use or performance
+% of this software.
+%
+
+200 dict begin
+
+/BaseRatio 1.3467736870885982 def % Ratio triangle base / symbol size
+/BgColorProc 0 def % Background color routine (symbols)
+/DrawSymbolProc 0 def % Routine to draw symbol outline/fill
+/StippleProc 0 def % Stipple routine (bar segments)
+/DashesProc 0 def % Dashes routine (line segments)
+
+% Define the array ISOLatin1Encoding (which specifies how characters are
+% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript
+% level 2 is supposed to define it, but level 1 doesn't).
+
+systemdict /ISOLatin1Encoding known not {
+ /ISOLatin1Encoding [
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /exclam /quotedbl /numbersign /dollar /percent /ampersand
+ /quoteright
+ /parenleft /parenright /asterisk /plus /comma /minus /period /slash
+ /zero /one /two /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less /equal /greater /question
+ /at /A /B /C /D /E /F /G
+ /H /I /J /K /L /M /N /O
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+ /quoteleft /a /b /c /d /e /f /g
+ /h /i /j /k /l /m /n /o
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
+ /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron
+ /space /exclamdown /cent /sterling /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen
+ /registered /macron
+ /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph
+ /periodcentered
+ /cedillar /onesuperior /ordmasculine /guillemotright /onequarter
+ /onehalf /threequarters /questiondown
+ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex
+ /Idieresis
+ /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn
+ /germandbls
+ /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex
+ /idieresis
+ /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn
+ /ydieresis
+ ] def
+} if
+
+% font ISOEncode font
+% This procedure changes the encoding of a font from the default
+% Postscript encoding to ISOLatin1. It's typically invoked just
+% before invoking "setfont". The body of this procedure comes from
+% Section 5.6.1 of the Postscript book.
+
+/ISOEncode {
+ dup length dict
+ begin
+ {1 index /FID ne {def} {pop pop} ifelse} forall
+ /Encoding ISOLatin1Encoding def
+ currentdict
+ end
+
+ % I'm not sure why it's necessary to use "definefont" on this new
+ % font, but it seems to be important; just use the name "Temporary"
+ % for the font.
+
+ /Temporary exch definefont
+} bind def
+
+/Stroke {
+ gsave
+ stroke
+ grestore
+} def
+
+/Fill {
+ gsave
+ fill
+ grestore
+} def
+
+/SetFont {
+ % Stack: pointSize fontName
+ findfont exch scalefont ISOEncode setfont
+} def
+
+/Box {
+ % Stack: x y width height
+ newpath
+ exch 4 2 roll moveto
+ dup 0 rlineto
+ exch 0 exch rlineto
+ neg 0 rlineto
+ closepath
+} def
+
+/SetFgColor {
+ % Stack: red green blue
+ CL 0 eq {
+ pop pop pop 0 0 0
+ } if
+ setrgbcolor
+ CL 1 eq {
+ currentgray setgray
+ } if
+} def
+
+/SetBgColor {
+ % Stack: red green blue
+ CL 0 eq {
+ pop pop pop 1 1 1
+ } if
+ setrgbcolor
+ CL 1 eq {
+ currentgray setgray
+ } if
+} def
+
+% The next two definitions are taken from "$tk_library/prolog.ps"
+
+% desiredSize EvenPixels closestSize
+%
+% The procedure below is used for stippling. Given the optimal size
+% of a dot in a stipple pattern in the current user coordinate system,
+% compute the closest size that is an exact multiple of the device's
+% pixel size. This allows stipple patterns to be displayed without
+% aliasing effects.
+
+/EvenPixels {
+ % Compute exact number of device pixels per stipple dot.
+ dup 0 matrix currentmatrix dtransform
+ dup mul exch dup mul add sqrt
+
+ % Round to an integer, make sure the number is at least 1, and compute
+ % user coord distance corresponding to this.
+ dup round dup 1 lt {pop 1} if
+ exch div mul
+} bind def
+
+% width height string filled StippleFill --
+%
+% Given a path and other graphics information already set up, this
+% procedure will fill the current path in a stippled fashion. "String"
+% contains a proper image description of the stipple pattern and
+% "width" and "height" give its dimensions. If "filled" is true then
+% it means that the area to be stippled is gotten by filling the
+% current path (e.g. the interior of a polygon); if it's false, the
+% area is gotten by stroking the current path (e.g. a wide line).
+% Each stipple dot is assumed to be about one unit across in the
+% current user coordinate system.
+
+% width height string StippleFill --
+%
+% Given a path already set up and a clipping region generated from
+% it, this procedure will fill the clipping region with a stipple
+% pattern. "String" contains a proper image description of the
+% stipple pattern and "width" and "height" give its dimensions. Each
+% stipple dot is assumed to be about one unit across in the current
+% user coordinate system. This procedure trashes the graphics state.
+
+/StippleFill {
+ % The following code is needed to work around a NeWSprint bug.
+
+ /tmpstip 1 index def
+
+ % Change the scaling so that one user unit in user coordinates
+ % corresponds to the size of one stipple dot.
+ 1 EvenPixels dup scale
+
+ % Compute the bounding box occupied by the path (which is now
+ % the clipping region), and round the lower coordinates down
+ % to the nearest starting point for the stipple pattern. Be
+ % careful about negative numbers, since the rounding works
+ % differently on them.
+
+ pathbbox
+ 4 2 roll
+ 5 index div dup 0 lt {1 sub} if cvi 5 index mul 4 1 roll
+ 6 index div dup 0 lt {1 sub} if cvi 6 index mul 3 2 roll
+
+ % Stack now: width height string y1 y2 x1 x2
+ % Below is a doubly-nested for loop to iterate across this area
+ % in units of the stipple pattern size, going up columns then
+ % across rows, blasting out a stipple-pattern-sized rectangle at
+ % each position
+
+ 6 index exch {
+ 2 index 5 index 3 index {
+ % Stack now: width height string y1 y2 x y
+
+ gsave
+ 1 index exch translate
+ 5 index 5 index true matrix tmpstip imagemask
+ grestore
+ } for
+ pop
+ } for
+ pop pop pop pop pop
+} bind def
+
+
+/LS { % Stack: x1 y1 x2 y2
+ newpath 4 2 roll moveto lineto stroke
+} def
+
+/EndText {
+ %Stack :
+ grestore
+} def
+
+/BeginText {
+ %Stack : w h theta centerX centerY
+ gsave
+ % Translate the origin to the center of bounding box and rotate
+ translate neg rotate
+ % Translate back to the origin of the text region
+ -0.5 mul exch -0.5 mul exch translate
+} def
+
+/DrawAdjText {
+ %Stack : str strWidth x y
+ moveto % Go to the text position
+ exch dup dup 4 2 roll
+
+ % Adjust character widths to get desired overall string width
+ % adjust X = (desired width - real width)/#chars
+
+ stringwidth pop sub exch
+ length div
+ 0 3 -1 roll
+
+ % Flip back the scale so that the string is not drawn in reverse
+
+ gsave
+ 1 -1 scale
+ ashow
+ grestore
+} def
+
+/DrawBitmap {
+ % Stack: ?bgColorProc? boolean centerX centerY width height theta imageStr
+ gsave
+ 6 -2 roll translate % Translate to center of bounding box
+ 4 1 roll neg rotate % Rotate by theta
+
+ % Find upperleft corner of bounding box
+
+ 2 copy -.5 mul exch -.5 mul exch translate
+ 2 copy scale % Make pixel unit scale
+ newpath
+ 0 0 moveto 0 1 lineto 1 1 lineto 1 0 lineto
+ closepath
+
+ % Fill rectangle with background color
+
+ 4 -1 roll {
+ gsave
+ 4 -1 roll exec fill
+ grestore
+ } if
+
+ % Paint the image string into the unit rectangle
+
+ 2 copy true 3 -1 roll 0 0 5 -1 roll 0 0 6 array astore 5 -1 roll
+ imagemask
+ grestore
+}def
+
+% Symbols:
+
+% Skinny-cross
+/Sc {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate 45 rotate
+ 0 0 3 -1 roll Sp
+ grestore
+} def
+
+% Skinny-plus
+/Sp {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ 2 idiv
+ dup 2 copy
+ newpath neg 0 moveto 0 lineto
+ DrawSymbolProc
+ newpath neg 0 exch moveto 0 exch lineto
+ DrawSymbolProc
+ grestore
+} def
+
+% Cross
+/Cr {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate 45 rotate
+ 0 0 3 -1 roll Pl
+ grestore
+} def
+
+% Plus
+/Pl {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ dup 2 idiv
+ exch 6 idiv
+
+ %
+ % 2 3 The plus/cross symbol is a
+ % closed polygon of 12 points.
+ % 0 1 4 5 The diagram to the left
+ % x,y represents the positions of
+ % 11 10 7 6 the points which are computed
+ % below.
+ % 9 8
+ %
+
+ newpath
+ 2 copy exch neg exch neg moveto dup neg dup lineto
+ 2 copy neg exch neg lineto 2 copy exch neg lineto
+ dup dup neg lineto 2 copy neg lineto 2 copy lineto
+ dup dup lineto 2 copy exch lineto 2 copy neg exch lineto
+ dup dup neg exch lineto exch neg exch lineto
+ closepath
+ DrawSymbolProc
+ grestore
+} def
+
+% Circle
+/Ci {
+ % Stack: x y symbolSize
+ 3 copy pop
+ moveto newpath
+ 2 div 0 360 arc
+ closepath DrawSymbolProc
+} def
+
+% Square
+/Sq {
+ % Stack: x y symbolSize
+ dup dup 2 div dup
+ 6 -1 roll exch sub exch
+ 5 -1 roll exch sub 4 -2 roll Box
+ DrawSymbolProc
+} def
+
+% Line
+/Li {
+ % Stack: x y symbolSize
+ 3 1 roll exch 3 -1 roll 2 div 3 copy
+ newpath
+ sub exch moveto add exch lineto
+ stroke
+} def
+
+% Diamond
+/Di {
+ % Stack: x y symbolSize
+ gsave
+ 3 1 roll translate 45 rotate 0 0 3 -1 roll Sq
+ grestore
+} def
+
+% Triangle
+/Tr {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ BaseRatio mul 0.5 mul % Calculate 1/2 base
+ dup 0 exch 30 cos mul % h1 = height above center point
+ neg % b2 0 -h1
+ newpath moveto % point 1; b2
+ dup 30 sin 30 cos div mul % h2 = height below center point
+ 2 copy lineto % point 2; b2 h2
+ exch neg exch lineto %
+ closepath
+ DrawSymbolProc
+ grestore
+} def
+% Bitmap
+/Bm {
+ % Stack: x y symbolSize
+ gsave
+ 3 1 roll translate pop DrawSymbolProc
+ grestore
+} def
+
+%%BeginSetup
+gsave % Save the graphics state
+
+% Default line/text style parameters
+
+1 setlinewidth % width
+1 setlinejoin % join
+0 setlinecap % cap
+[] 0 setdash % dashes
+
+/CL 0 def % Set color level mode
+0 0 0 setrgbcolor % color
+
+% Transform coordinate system to use X11 coordinates
+
+% Flip the y-axis by changing the origin and reversing the scale,
+% making the origin the upper left corner
+0.600000 -0.600000 scale
+0 -1319 translate
+
+% User defined page layout
+
+%% Set color level
+/CL 2 def
+
+% Set origin
+120 173 translate
+
+% Landscape orientation
+0 972.127 translate
+-90 rotate
+
+%% Set max aspect ratio
+ 1.62292 1.62292 scale
+
+%%EndSetup
+
+14 /Helvetica-Bold SetFont
+0.996109 0.996109 0.996109 SetBgColor
+69 80 479 333 Box Fill
+
+gsave clip
+
+
+% Marker "bg" is a bitmap
+0.304692 0.929702 0.578134 SetBgColor
+newpath 79 90 moveto
+539 90 lineto
+539 404 lineto
+79 404 lineto
+79 90 lineto closepath Fill
+0 0.542977 0 SetFgColor
+ gsave
+ 79 404 translate
+ 460 -314 scale
+ 460 314 true [460 0 0 -314 0 314] { <000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000040000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000400000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 002410000000000000000449010000000000002000000000000000000000
+ 000000000000000000000000000000002000000000000000000800004A00
+ 000000000000000220002002010200000080000000000008020000000000
+ 00002000000000200000000000008000000420000000000800004A000000
+ 000000000002200020020102000000800000000000080200000000000000
+ 2000000000200000000000008000000420000100000000A1000000000000
+ 001011010020881020280000020004000000088040000000000000110201
+ 411020000000000000020200004000000000804090040020800000200001
+ 00000002010008800000005110912ADEEFFDBFFFFFFFFEFDFFCAA9BA3EEF
+ D72DF36ADDE5AAF400AA5504C00000008040900400208000002000010000
+ 0002010008800000005110912ADEEFFDBFFFFFFFFEFDFFCAA9BA3EEFD72D
+ F36ADDE5AAF400AA5504C000016D572D4E95599695514A02420404200000
+ 000000000001000000000000000000000000000000244444001129550524
+ 5525544BEF6DD6DF40000100000000000000000000000000000008100844
+ 400840000100020808529B5FFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFF
+ FFFFFFFFFFFF440001000000000000000000000000000000081008444008
+ 40000100020808529B5FFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFFFFFFFF
+ FFFFFFFF4400097FFFFFFFFFFFFFFFFFFFFFFFFF9FFFFFFFFFFFFFFFFFFF
+ FFFFFFFFFFCDDDE0480A20004050C03101003DFBEFFFFDDFFF9FFFFEE57E
+ FBBF40000177F5FFEBFFFFFFFFFFFDFFFFEF9D42D21485A084817EDE24F9
+ 018006000004182060107019C63901071CFF5F7BEEAEB756F7D7EFF6EEBF
+ 40000177F5FFEBFFFFFFFFFFFDFFFFEF9D42D21485A084817EDE24F90180
+ 06000004182060107019C63901071CFF5F7BEEAEB756F7D7EFF6EEBF400001
+ 5F6BE7DFBFFFFEFF736B7FFAD71C08C18C1060C70C1E0F83F86041C41FC1
+ E61820761E790B071209831CD2FD6575BBFF4F9ABBB75EF7D54400017BEF
+ D7F6DBF6FE77FDFEFFBFBF9E0CC14E1070B70C1C0F87FC7061B400E0B618
+ 307E13FC07071B4D90FCEFEEDDDFFAEA4774EFEBBEDFF74000017BEFD7F6
+ DBF6FE77FDFEFFBFBF9E0CC14E1070B70C1C0F87FC7061B400E0B618307E
+ 13FC07071B4D90FCEFEEDDDFFAEA4774EFEBBEDFF74000015EDBD7B3FFAB
+ FD7F777F72EDEF1E03C27E18703F00FD078FFC01E13C00411F10782E0B7F
+ 03071F0F835CF6BF7EF5B5BCCED4B75DFA6FAF40002575F5B7D5BEABF53F
+ FBEDBDFAF79E0BC96E1070B7083A078FFC60E1B68048B618381C1E7D8307
+ 1E07839DEFF6EFB7FFFCCFEF7B76EC6FAF50002575F5B7D5BEABF53FFBED
+ BDFAF79E0BC96E1070B7083A078FFC60E1B68048B618381C1E7D83071E07
+ 839DEFF6EFB7FFFCCFEF7B76EC6FAF5000015FBB316BAD7D5D3EEAFF7FAB
+ F51C0FC18C1060C70C33038C78204184F0C184093C381070C3C63E06121C
+ B6DB3FADF5FDCBBFDFEDBD7FDF400001767D53A7F5FFFCBFB766EEDDB798
+ 2300092BAA042200002268980056AFDF7FFFFFFFFFFFFFFFFFFFFFFCEFD5
+ FFE5FFEDCD7E6B7D753F1B400001767D53A7F5FFFCBFB766EEDDB7982300
+ 092BAA042200002268980056AFDF7FFFFFFFFFFFFFFFFFFFFFFCEFD5FFE5
+ FFEDCD7E6B7D753F1B4000017B3B736BB7BB75BDDBDD6FD6FD9FFFFFFFFF
+ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFFB
+ CADBFAEFFD7D1F44000166DDB3AD9EF5D59FFFFFFFFFFF95A6F524894091
+ 20082524A4010100000000000000000000000000000200000000000FCDAF
+ BF389A9D5F40000166DDB3AD9EF5D59FFFFFFFFFFF95A6F5248940912008
+ 2524A4010100000000000000000000000000000200000000000FCDAFBF38
+ 9A9D5F4000017EDCF37AA3FE699800000000000000000000000000000000
+ 080000000000402000004201592D2AFF7FFDFBEF555B6D6BCF3DFBFC7B3A
+ DF40000176EFBB8D3FEEBF13FFFFFFFFFB5EB6AB35A55A4AA54AD54AA954
+ 0201000100001080000400000000000000000000008B8EFE94802EDFDF50
+ 000176EFBB8D3FEEBF13FFFFFFFFFB5EB6AB35A55A4AA54AD54AA9540201
+ 000100001080000400000000000000000000008B8EFE94802EDFDF500001
+ 7ECEF40B5BFEBB9000000000000000000000000000000000000000000000
+ 040100080000000000000200000000000093CCBA000200009F4000116E96
+ F895877ECF9B010000000000000000000000000000004000000420442010
+ 0000101000111200100000000000000B4BBF2DA7FAD3DF4100116E96F895
+ 877ECF9B0100000000000000000000000000000040000004204420100000
+ 101000111200100000000000000B4BBF2DA7FAD3DF4100017EC6F20EB5DF
+ DAAA10420000000000200000041100000000280110000400000004000000
+ 40400000000000002080841BCB973FFEFFFB1F4000016E56C1F902FAE790
+ 400000000000200010048000004000088000000000000080002000200100
+ 0840104412010100102B669F6FFDEFEB534400016E56C1F902FAE7904000
+ 000000002000100480000040000880000000000000800020002001000840
+ 104412010100102B669F6FFDEFEB53440001769203F7F97EE39400000400
+ 000000804100220001000200004200200001000001824200A05436BFE55B
+ 12E12810004BE72A6BD5FE5B7F4000057E8B27B7793EA3B0011251400502
+ 94250A4808A4A05A000311291414DBFD17780452A90010021550B56A57C4
+ B40400B77A9C57FCDFC9774000057E8B27B7793EA3B00112514005029425
+ 0A4808A4A05A000311291414DBFD17780452A90010021550B56A57C4B404
+ 00B77A9C57FCDFC9774000116AA04EDFBE1EA39D07FFFCF9F9A03CF3AF3D
+ FFCF5CFD200CFFFF8C3F71C74474163071A071BD381874E3D78B1D1A004F
+ FD1EF7D33EBCFF4000017F589FF3F78EB7B407733C70F9807833D61DFEC7
+ 0D3CD00C73779D3BB8C73C301E397184307EBCD972E3C7972918006DF320
+ E4A9FAB41B6400017F589FF3F78EB7B407733C70F9807833D61DFEC70D3C
+ D00C73779D3BB8C73C301E397184307EBCD972E3C7972918006DF320E4A9
+ FAB41B640001751ABA693D961BA006FB3D7272D83CA5C85DCEC72C3CF439
+ 1A7B1E7330CF9DDB1CBAF290F91E397972EFE79754BD823B6833DB6EDE7F
+ 1BC000216A8133F3D9C6B18400737C7272943965E2999EC70D7CF21D7A71
+ 9E3399C31CB91C797110FA3E39B9B3E7C71F69DC40BDAA97F9F1065FDF40
+ 00216A8133F3D9C6B18400737C7272943965E2999EC70D7CF21D7A719E33
+ 99C31CB91C797110FA3E39B9B3E7C71F69DC40BDAA97F9F1065FDF400001
+ 7BD272DBA57259A824F33DF2F290398AF0DD9ECF28BCF33F407B9E7728CA
+ 1FC23CFC72993B2F3D19A1EBCFAF410E40CEF51F686A56FBBB4000017752
+ F36AF9EA7985097B7C7276583BA470BC9ECF297CF11FF0F28F27B1D83FF3
+ 3E797E10392F39BF8DEF978F658E60AD6918F9A006BD3F4000017752F36A
+ F9EA7985097B7C7276583BA470BC9ECF297CF11FF0F28F27B1D83FF33E79
+ 7E10392F39BF8DEF978F658E60AD6918F9A006BD3F4000017BA4EAF7A6F3
+ 59AC01713D727610390A7C992E47657CFA0FFD7A872B12D96FFA3CBCF66B
+ 1EC7B919B2E737AF569E206F2B3BCCE00E772F4000116AC9F5C02BB9BB18
+ 02FA7C7272503926BCDDDE97223CF303F9724FAB3ACB81783CB97282BCA7
+ B9B910E3C71F4C072055CB32FB000CFEB74800116AC9F5C02BB9BB1802FA
+ 7C7272503926BCDDDE97223CF303F9724FAB3ACB81783CB97282BCA7B9B9
+ 10E3C71F4C072055CB32FB000CFEB74800017FC9CD5EB5F9992C20F13CF2
+ 784139247CBCAECF043C7120787917A709C008791E7972421CCFBD9934EB
+ D7A744C79053C5B6FC4086DC5F4000017597EC803B5DDA9800F37D72F2C8
+ 3D8A1D5D1E872E7CF216B8F2C3D3B0CBB399BCB973864E67391DF2EBCF97
+ 319F9036AAF5D7880F74DF4000017597EC803B5DDA9800F37D72F2C83D8A
+ 1D5D1E872E7CF216B8F2C3D3B0CBB399BCB973864E67391DF2EBCF97319F
+ 9036AAF5D7880F74DF4000017FD3AB003EFAEA1528F23D717AC839265CB9
+ 1ECF3CBCF31299795BCB9ACF18B89E79710AEE253D5971E7D79B29879019
+ 5B64FBC016BD574000017B5B7C803BADE8B802FB3CF2F1C81C872CBDBE57
+ 4E3CC519B8F945CF89CFBCE31673FE3F5F7A7AFD52D5E54DC6B14C0DE6DC
+ 57E00F697F5000017B5B7C803BADE8B802FB3CF2F1C81C872CBDBE574E3C
+ C519B8F945CF89CFBCE31673FE3F5F7A7AFD52D5E54DC6B14C0DE6DC57E0
+ 0F697F5000016EC75A083ED648A842FC7FFBFBC42E8F867EBF5FBCFD8A3E
+ 62FDD7F7EBEB1F4512C0210120060000000400120000080CA6F96DA08ED3
+ 374000116B85A920BBF9F569020100000008003020800010008034000900
+ 08000400100A0403148C12A9EFA5D7352B6C3F66EC04ABE6BDE00FBAFB40
+ 00116B85A920BBF9F5690201000000080030208000100080340009000800
+ 0400100A0403148C12A9EFA5D7352B6C3F66EC04ABE6BDE00FBAFB400001
+ 7E86FC003F8ADD2809525402AA28082A22C94AA5A55494082C0089255A9D
+ E8B40498EA83C2AA3555745AE6D95155560328C4B7600DFBDF4000017F17
+ 37803D5B72D4006D2BF2B55405D15146C552A554D007D07EAAB96C965350
+ 01502A01515515A54A55115420612021FECA6FC007504F4000017F173780
+ 3D5B72D4006D2BF2B55405D15146C552A554D007D07EAAB96C9653500150
+ 2A01515515A54A55115420612021FECA6FC007504F4000016B2FCB027CA6
+ F6A00200000000000000000000000000000000000000000000000000002A
+ 00000000000000000000000035C8B5E11CB59B4000017D16F64039096BB0
+ 100000000000000000000000000000000000000000000000200000000000
+ 000000000000040008403AB2B76007169B4400017D16F64039096BB01000
+ 000000000000000000000000000000000000000000002000000000000000
+ 00000000040008403AB2B76007169B4400017317F30033E0BA8200900000
+ 000000000000000480000000040000000042000000014200000020000020
+ 0000000400308AFC786014F2774000057615B5427B81EF90400200000000
+ 000004000400010000000000000080080800040800011000841000040000
+ 000000122CDE7D604EF2EF4000057615B5427B81EF904002000000000000
+ 040004000100000000000000800808000408000110008410000400000000
+ 00122CDE7D604EF2EF4000217B17D5083AC1650500800881000000000020
+ 00002800000000800000008041110080000402120000010080040820007B
+ 4FE27DE10CD2FF5000007627B740BBE6B690020020000900200000048024
+ 8000004840040000080000000000021000000022102000400004204C86E2
+ 35A00774CB4000007627B740BBE6B6900200200009002000000480248000
+ 004840040000080000000000021000000022102000400004204C86E235A0
+ 0774CB40000176137F003DF2D89680020000000000240000000000040400
+ 09000000200200000000000000000000000000000000023BC76E7B6016D1
+ F74400017C4BEB403DA0E001201000000000000044000000008000000000
+ 0080800004800200000000000800000042000000002D496E3BC00F75EF40
+ 00017C4BEB403DA0E0012010000000000000440000000080000000000080
+ 800004800200000000000800000042000000002D496E3BC00F75EF400001
+ 722BBA083F9A9F3A58000019501BE7C9F51A6F7842000000000800000081
+ 2011284849104010000080040001208000075B5C35E00555DB5000016E0C
+ A5413AAA67820880000358C245893D02EA71000254ABD441480000080000
+ 00000000004001220240000402004000EDD73AE11E7B3F4000016E0CA541
+ 3AAA67820880000358C245893D02EA71000254ABD4414800000800000000
+ 0000004001220240000402004000EDD73AE11E7B3F4000017C2CD10031AA
+ BD81480000840093548C6B002A7200020000000000000202555129520002
+ 0000800008000000000000081ACC35A00C55B7540001721637003DD27312
+ E010080050002000002000002000E3F27DDF8FFFFC7D374FD3A000000000
+ 0000000210000002008407FE3DC006BB4F400001721637003DD27312E010
+ 080050002000002000002000E3F27DDF8FFFFC7D374FD3A0000000000000
+ 000210000002008407FE3DC006BB4F4000006C636D443DD96DE876000000
+ 02000000000000028000CCF0595E3FFFFF1DD6AF85D00000000410000010
+ 004000200202AB76736096B9FF4000017E055F003DFAF2915B0008810042
+ 900204210A240800BF0FDBF9FFFFFFC7F8F93E9001104010008000000100
+ 00000000CDDE7DE00CB93F4000017E055F003DFAF2915B00088100429002
+ 04210A240800BF0FDBF9FFFFFFC7F8F93E90011040100080000001000000
+ 0000CDDE7DE00CB93F4000016C286D013BBAA8FCBF206DA2475FB3E33E69
+ C6B57848D7AFD763FFFFFFF179FCFED00000008000008000000408000000
+ 36EA77400779CB5000016C93FB483DEAF653D5007184475D33CA284D47AD
+ 2800FCCF3F9FFFFFFFFC5ED53BA000040000102004008000000400008BB6
+ FBE096F9FF4000016C93FB483DEAF653D5007184475D33CA284D47AD2800
+ FCCF3F9FFFFFFFFC5ED53BA000040000102004008000000400008BB6FBE0
+ 96F9FF4000087C11F7023FF96878EB82558C451501231A6548A9A000C36F
+ DD1FFFF4FFFF357560C09000080001008048000001000010077EDAA20EE8
+ AF4400017EA8F10075BAE52C03004410A61933322405008A00003CCD78FF
+ FA001FFF8F6C9E20010420010000000024400800100055DEFB6054FCFB50
+ 00017EA8F10075BAE52C03004410A61933322405008A00003CCD78FFFA00
+ 1FFF8F6C9E20010420010000000024400800100055DEFB6054FCFB500001
+ 6612F7443342B4F90080000000000000000000000100C2ED79FF428443FF
+ E1BD808000100004080104420004004000010B7EFC60067CDF4000017E18
+ CB003BFAE62C0008000000000000008000000000016AD1FEB80008FFF1D7
+ A000000020110000400010280004021066B6D4E096D4F74000017E18CB00
+ 3BFAE62C0008000000000000008000000000016AD1FEB80008FFF1D7A000
+ 000020110000400010280004021066B6D4E096D4F74000016A2DF6403E7B
+ 64D81000009000000000100000400008805DC7FB0848241FFCFDA1E00000
+ BC078BE7C27178EE3E0E82100AF4B36417BE6B4100216E1DBF023D2B65B8
+ 4080000100000400000010000400A1678FF48000203FFCCFA23004104806
+ 42E040984040222302114D6CEDC004DB774000216E1DBF023D2B65B84080
+ 000100000400000010000400A1678FF48000203FFCCFA2300410480642E0
+ 40984040222302114D6CEDC004DB774000053313AB003DD6EAB400080000
+ 00400022200100080002905E3FF64A10081FFF75A5A84001220406008008
+ 80400311021080D1570B51E72F4000017E93FC483FE54374000001000000
+ 000000080000201040777FE50101225FFF3F855900002000462050004840
+ 0202023089497E00007B3F4800017E93FC483FE543740000010000000000
+ 00080000201040777FE50101225FFF3F8559000020004620500048400202
+ 023089497E00007B3F480000771EBB803F93D5A800410004110040000020
+ 00000004105CFFB8D4400803FF9CA2580020542181E0C018727E1C1E02E4
+ 22DAABFFFFEA9B4000017F1DBE01366940B8000000400008000001000000
+ 0082114CFF5A80002005FF8EA24800003C818362427128420E4B021082A2
+ BFFFFFB59F4100017F1DBE01366940B80000004000080000010000000082
+ 114CFF5A80002005FF8EA24800003C818362427128420E4B021082A2BFFF
+ FFB59F4100017F57D1140F7B882810040200400000000008104002011973
+ FD6A80488081FFE7A43000804004462040C0C002031142110757D7FFFFEF
+ 5F4000006B1DD84015F7803000100004000001020000400200001157FAA8
+ 100004447FF326000000220004104004C404001102118C26F5085B7DDF41
+ 00006B1DD84015F7803000100004000001020000400200001157FAA81000
+ 04447FF326000000220004104004C404001102118C26F5085B7DDF410011
+ 7D1FEFFFFFF58020008000000000800000000000000012E7FD8D41442110
+ 7FF98400000220004620C40080064313021158B3DBEAAB7F9F480041769F
+ AFFFFF6B2100020004000420002002400000000412EFEAB2804118053FFC
+ 96010800780783E750F870783C0C0211A0085D7DFEE63F400041769FAFFF
+ FF6B2100020004000420002002400000000412EFEAB2804118053FFC9601
+ 0800780783E750F870783C0C0211A0085D7DFEE63F400000BF8EF1AFE9EA
+ 0000200000400000000020000000001030CFF142001042101FFC94000020
+ 28050183F16810A81402000140069AB7B5607B400001728FD3F6B5DE0400
+ 0000000000000000000000000000259F5414458000049EFE5A0000008000
+ 0000000000000040080480082AECFEC9FF400001728FD3F6B5DE04000000
+ 000000000000000000000000259F5414458000049EFE5A00000080000000
+ 000000000040080480082AECFEC9FF4000007FC7B2B7ABD4000000020000
+ 04000210000820080000159FD8A80048414007FE2A000000004008000002
+ 02010000001004014ABBFE5BDF4900016BCBCDF5F3282010802002002001
+ 20010000800040406B3FAC5489008100A7FFA90000820000A01000204008
+ 1000000040408DDDDACF674000016BCBCDF5F32820108020020020012001
+ 0000800040406B3FAC5489008100A7FFA90000820000A010002040081000
+ 000040408DDDDACF674000013EC36BE7B9BA000002002010800400000000
+ 02000200437FC8E9C2A80A9207FFA5002008000004001200002041200000
+ 0004497CFEA7D74000007D8BCDB2D9580002000000800000000811000000
+ 0800467F61340284500153FF978880000002000400000000000110100000
+ 4D756EEFFB4400007D8BCDB2D95800020000008000000008110000000800
+ 467F61340284500153FF9788800000020004000000000001101000004D75
+ 6EEFFB4400016BD0BBF7AAF800000012000000080080000000000000D4FF
+ D95483512A8009FF9A800000008358400004008521A0000080088EDB7AAF
+ FF4000017743FED6FDB011001000040400400000000000082004ACFF689A
+ 0884801533F7DA000000020950115A5544B81110000002408FEDAFCDB741
+ 00017743FED6FDB011001000040400400000000000082004ACFF689A0884
+ 801533F7DA000000020950115A5544B81110000002408FEDAFCDB7410000
+ EAB937FBBB60400040000000000000080000000100000BFF55678A500502
+ 89BFE9400102000186624321536011800202000067D57E9EDD4400117FED
+ 7BF7F7E20004002020001200400004A2104000101BFEB2DD452048A451FF
+ E54100001001A17BD8A0436835110820000477FDFE9BFF4000117FED7BF7
+ F7E20004002020001200400004A2104000101BFEB2DD452048A451FFE541
+ 00001001A17BD8A0436835110820000477FDFE9BFF400000B7ECAFD7CF80
+ 0000010000408019812020000000040053FF5F575594200008FFE6801008
+ 0000A1460A2CC35615882008292009EDF73D1F4000017B54A6F57B100000
+ 080800020118C40200000000200053FD7CAAEB48891250FFF68000004000
+ ADE2D314C6DEA4A00000800059FCDE7F5F4900017B54A6F57B1000000808
+ 00020118C40200000000200053FD7CAAEB48891250FFF68000004000ADE2
+ D314C6DEA4A00000800059FCDE7F5F4900017FD731F5DC00040240000100
+ 15FFF800000800020120A7FFAD7716A1500128F77A600201001000000000
+ 00000000000000042E7AFCFFDF400008AA931A13B90000080000200001ED
+ 3C000200000000042FFDD55D899BC48408FFF84000040000800000000000
+ 100000840000461533E69D400008AA931A13B90000080000200001ED3C00
+ 0200000000042FFDD55D899BC48408FFF840000400008000000000001000
+ 00840000461533E69D4000016DCBC0EE74004000008000003EF7FBCA2000
+ 248001004FFF79F54E68FE53E87FF9501000000200040448200200800200
+ 02085BCFC2FDCF4800007BD8A285A000000000040010776726C000000000
+ 4002DFFD6C5557AA7A0FD57FFDA040100008050010008080040080000020
+ 2FB31C6B5F4000007BD8A285A000000000040010776726C0000000004002
+ DFFD6C5557AA7A0FD57FFDA0401000080500100080800400800000202FB3
+ 1C6B5F4000017E9D717791100124800042026F671BA0004000040408DFF6
+ F56B1AFD3D5CFC77FCA1010080201080D5015F7F7B7800000000BFE03C3B
+ 8F400008BF55F9284010040011000807BFFFEADE040000000020BFED2C58
+ 4D544F1A157FFE30000000000040040001000090000201007FED7D1E8F48
+ 0008BF55F9284010040011000807BFFFEADE040000000020BFED2C584D54
+ 4F1A157FFE30000000000040040001000090000201007FED7D1E8F480000
+ FA5DD2823020200000110043EDF577FC000202000100BFEDF54E97FFDE9D
+ B51FFE500C02090000201210120B40A00020000015E6FA3F4F4100003EFF
+ BE103131800040000003F38CBD3C4108000020013FA614512D9E3697E83F
+ BE50000080002000810124020010100000020A79F11B1B4000003EFFBE10
+ 3131800040000003F38CBD3C4108000020013FA614512D9E3697E83FBE50
+ 000080002000810124020010100000020A79F11B1B400001F9FDB3805030
+ 40110000200366E537AA0000101000023FDD55069755374FB90FFF500010
+ 200481000A0002014050400002280510EA1F1F40000037FB35FAB0198000
+ F000001EDE7FF5DD9000000000207BAAD02C703C8B804897FF0800000111
+ 0000014CC402011000003880875E695F1F40000037FB35FAB0198000F000
+ 001EDE7FF5DD9000000000207BAAD02C703C8B804897FF08000001110000
+ 014CC402011000003880875E695F1F400000BF593B6ED0080103F084008A
+ 7FFF75F70000028102027FD75F270D4A4D92D6CFF7A8080200000004454D
+ 5041042000023C0014897A1F5B400000B7F33AF0F00004271800440FED40
+ 3725020008000000FFAD4096B228055089175FA8000848008002027FFF02
+ 00000000E70003461D8E1B400000B7F33AF0F00004271800440FED403725
+ 020008000000FFAD4096B228055089175FA8000848008002027FFF020000
+ 0000E70003461D8E1B400010F5D33990910240000A0000199F00067F0800
+ 00040004FFEEF87A6E20AA84454BFB9080800002001003E2D7E540110000
+ 83004105DB2F5F4000013F735844F00000020800007FCC0007AAC0200000
+ 2006FFFF5DDA99502AC4A935EF50080020050210139FFCE1401404200300
+ 0945BA8D174900013F735844F00000020800007FCC0007AAC02000002006
+ FFFF5DDA99502AC4A935EF50080020050210139FFCE14014042003000945
+ BA8D17490000B1B11AD38000001078220173FC2023FC60009000000AFEFE
+ B576BC844288047BBDD000010020800C1EF7A13E020000800E040082CBAE
+ 3F4000017D335C6DC0104100FA001073F80201FDE0080212044AFBFFEEBA
+ B7502A456A94F7D40020000500151DBBE7DE400000001C0092616F8EB740
+ 00017D335C6DC0104100FA001073F80201FDE0080212044AFBFFEEBAB750
+ 2A456A94F7D40020000500151DBBE7DE400000001C0092616F8EB7400000
+ BE173BDF80800003C000003FF1F9E0FF01000000000AFFDFFEEEBF50118A
+ 8ADBFFC0000012009112F36577D6008000047000005055A96F4000097F13
+ 19AF9001040300410277F078F0BED0200000000ABFF77F7D4DA98A822BA5
+ DFC8110040070014EFBE9EDBC40010106010106A678D3B4800097F1319AF
+ 9001040300410277F078F0BED0200000000ABFF77F7D4DA98A822BA5DFC8
+ 110040070014EFBE9EDBC40010106010106A678D3B480000BB5EBAD80200
+ 2006020001DBC070F07F700000000002F7EFFFD696A4A584ABDDFFCA0004
+ 00208002DD6FF3A600000000C040820C1A9D5F4000016F0F3BF400000026
+ 000011E6E074F273780090004090FF7EFD95BED24A422BA5F7C80400020A
+ 801796BBD8EBC0000040C00010270AAAEB4800016F0F3BF4000000260000
+ 11E6E074F273780090004090FF7EFD95BED24A422BA5F7C80400020A8017
+ 96BBD8EBC0000040C00010270AAAEB480000B64679E108000007F81040B5
+ A078F01E500400020404FFFFF7E74BD08D895AF7BECA0000000A0113F77E
+ FF3764800000FF020108E7B6DB400001FB3337DC209042002081006DC470
+ F03BD10000080014F7ABFFF6BCD41FEAABB7FFEA2000200984164DEF6FD9
+ 20008800008080807B1BFF400000AE1337B08000000080000075C070F03B
+ B02000002014DEFFDFD7BF685BAC56FFFFEA0004024A0012FFBF3EFB6100
+ 0001000004087DAB6F400000AE1337B08000000080000075C070F03BB020
+ 00002014DEFFDFD7BF685BAC56FFFFEA0004024A0012FFBF3EFB61000001
+ 000004087DAB6F400004BE675E3800000810020801F7C07F802A50000000
+ 001DFFEAFFFCD5B497F2AB7FDFEA08000005801753044067D88000040220
+ 1042F5B7D7400000FCBBFD3000100000000007E5C47F803BBC0000110014
+ FBBFBFFFDEAA4BD236FFBDF28100400A8045365D813DD800008000820600
+ 8EEFFB440000FCBBFD3000100000000007E5C47F803BBC0000110014FBBF
+ BFFFDEAA4BD236FFBDF28100400A8045365D813DD8000080008206008EEF
+ FB440001364AA86804010000082020BDE070F0AB10800440025DEEF6EBDE
+ 9D6C88094FFEFFE204110015081BCE4BF01B32810200020087CB7ABFDF40
+ 0000BE93724900000800800200BFC070F03BD0001000001DBFFFDEEE97B0
+ 648234DFEDF208000044801F3C3FFC9DBC000000080005A95E7AD7400000
+ BE93724900000800800200BFC070F03BD0001000001DBFFFDEEE97B06482
+ 34DFEDF208000044801F3C3FFC9DBC000000080005A95E7AD7400000FE4D
+ 58C0000000020080007DC078703BD1000008002BFFFFF5F7DFD1120D57FB
+ FFF000000012800F5E68C317EA801000000807F0EE9E2B400020B695FAC8
+ 000010080000003FE070701FD0010000882DFFEB7FAF795849028F7F9EF3
+ 0000400500155E4EC21EDC000000204009A81DDC3F400020B695FAC80000
+ 10080000003FE070701FD0010000882DFFEB7FAF795849028F7F9EF30000
+ 400500155E4EC21EDC000000204009A81DDC3F400001BF5B51F000400100
+ 090821F7A078727F78000040002BFDFFEEFD97557FFF4BFFF7B004010012
+ 80155A35875EA6810008000006EAFFBDFF440000BB16F188110000000001
+ 005FE470F07F7108000200ABF7FFBDD7FFAD00081FEEBFF10004000480B7
+ BE56C49B770002008002046C74FA2B400000BB16F188110000000001005F
+ E470F07F7108000200ABF7FFBDD7FFAD00081FEEBFF10004000480B7BE56
+ C49B770002008002046C74FA2B400000BFBAB3D10000400000000077F070
+ F0BE60002010002BFFBFFFEF7ED4810296FFF5F108100026821BB657CD9F
+ D6000000090803366EEFDB400000B7D6D7C8000004208008007F39FBC07E
+ 80000040022B7EF6EDB5D7E8B4525FFBDFB1000000950016FD53769F7500
+ 20000020400E9BBF77400000B7D6D7C8000004208008007F39FBC07E8000
+ 0040022B7EF6EDB5D7E8B4525FFBDFB1000000950016FD53769F75002000
+ 0020400E9BBF77400000BFD34B98200800080121087AE9FF03E781000400
+ 002BEFFFBBEDBDB44D0915EF7FF1000000048017BE6FB31B569000022000
+ 054D1FDFFF400000BECB47D0010000000000006FBC0003BD60000000002B
+ FFF7FFD5EFD4B2A45FFFF6F1010010130876EC1DD91FB70102080000018B
+ D7FBCF400000BECB47D0010000000000006FBC0003BD60000000002BFFF7
+ FFD5EFD4B2A45FFFF6F1010010130876EC1DD91FB70102080000018BD7FB
+ CF400008FFDECED0000000001000007FBE00051FE0008008002AFDEEB4F6
+ DEF949555FDDBFF110000004801DBE5BEC1D5F0000200000008BAEB53F40
+ 0000B746B990000001080000021FE7000EDF00001020402BB7BFEFDB7BAC
+ 5085BEFFFFF10000000B0097FA6F775F740000000810006AFEEAD3400000
+ B746B990000001080000021FE7000EDF00001020402BB7BFEFDB7BAC5085
+ BEFFFFF10000000B0097FA6F775F740000000810006AFEEAD3400000BFF6
+ FCE8084020000042100F6F8014FF01000080012BFFFABBEADFFD2A0A7FFD
+ DDF10000420A801FBCEEF51F7400080040009063EBB7FF400000BF16E190
+ 00010000220000057EFFFB5300210000002BFFB7EFFB56F94589FD6FB7F1
+ 0082002A8077B7999DDBD62020000084010ABE7817400000BF16E1900001
+ 0000220000057EFFFB5300210000002BFFB7EFFB56F94589FD6FB7F10082
+ 002A8077B7999DDBD62020000084010ABE7817400000B5D2DAD100000100
+ 8000011FDE7FE9CD880000002022DEEB7BECDBFFEA0BF7FEFF710008000A
+ 020E1F62A57E44010080000000077F726F400000FF6DF0D4010004240000
+ 20556E7BB3EE208010010029FFFBFFFB5FEB6A97BDFFEFF1110000058815
+ DEAED7B76880000900008041B6E5DB440000FF6DF0D40100042400002055
+ 6E7BB3EE208010010029FFFBFFFB5FEB6A97BDFFEFF1110000058815DEAE
+ D7B76880000900008041B6E5DB440000B598F2D00000000000480003E77B
+ 3FF400004000012BFFDFFAF5D6FDFD2BEB77BDF10000000A001EFF926CFD
+ EC000000020808086ED496400000F79AD1D00020000001000203E5FBAB3C
+ 000100200429FDFBEFFEEFBFB7DEFFFD7FB100001081905DEB03B1DF7E00
+ 1000402001006BA109400000F79AD1D00020000001000203E5FBAB3C0001
+ 00200429FDFBEFFEEFBFB7DEFFFD7FB100001081905DEB03B1DF7E001000
+ 402001006BA109400000B525F1900200408020000003FF3FD4DC00000001
+ 002DFFDEFFFD19EFFDEFD9DFEDF202200002810B2F7FF232501040090000
+ 0008344047410000B7DAF3D010000401000000027B39D5A000080000000D
+ DFFBF5FADEF9FFDFFF7BBF600000020280153F48223B5011000001008080
+ FE7C8F400000B7DAF3D010000401000000027B39D5A000080000000DDFFB
+ F5FADEF9FFDFFF7BBF600000020280153F48223B5011000001008080FE7C
+ 8F400000BFEDA1D000020008000000003FBD96C140200004022DFFEFFFEF
+ 4DDFBF5EDDFFDBF200008009005DD77AACF75804000800000A0A73FFFF40
+ 0000BDF6C39800000000020442103FFCFFC004000200001CFEEEEFBFB7F5
+ F59BF7EDFEE2080000250207D580B0C3F8000020000200007DBDFF400000
+ BDF6C39800000000020442103FFCFFC004000200001CFEEEEFBFB7F5F59B
+ F7EDFEE2080000250207D580B0C3F8000020000200007DBDFF400000BBDB
+ 55D00040100108100000017FCC00000000000015EFFBFDFF4B7EDE9FFEFF
+ BFEA0000000080163EB4AFDE2030000004200004675F7D400000BFC707D0
+ 0000012000000000013FC800000020000214BF57DFFFDDBBFA1FDBDEEFE2
+ 2008000200137F7E7B3FC04000008000102A77B797400000BFC707D00000
+ 012000000000013FC800000020000214BF57DFFFDDBBFA1FDBDEEFE22008
+ 000200137F7E7B3FC04000008000102A77B797400000B48613D000000000
+ 008000000119C000000000202814FDFFB7DF9FEFFC1FFF7BFFCA00000001
+ 0007B77B2E76E050020423C24000EEFAAD400000BD1D4B91000080084000
+ 088000100005000000808004EFE6FFFFCABAD05FB5FFFDCA000080800440
+ DF4FF3FF00000800020000805D2F57400000BD1D4B910000800840000880
+ 00100005000000808004EFE6FFFFCABAD05FB5FFFDCA000080800440DF4F
+ F3FF00000800020000805D2F57400000B7991FD021080200101000000200
+ 00000020420800047DFFFFFFEFFFA20FFFF7B7CA000008011000DDB157B5
+ 41312001260000057ED5BF400000BF3FFFD0000000044001000088002040
+ 088000000002F7AD597FFAB6804EFBBFFFC800800004801137EBFFF74090
+ 0048040A94007EBEDD440000BF3FFFD00000000440010000880020400880
+ 00000002F7AD597FFAB6804EFBBFFFC800800004801137EBFFF740900048
+ 040A94007EBEDD440000BE9AB79408201000020020200000020020010000
+ 004AFFDFEFEFEDED401BDEFEFFC01000010020403BEC45C640C00000222A
+ A880FF7B8F400000BAD1DED000810110080400000000000200000000000A
+ EEF7DFFF6350085E7BF7BFD00000400000205E6F773D41300002038A4804
+ 2FDFFF440000BAD1DED000810110080400000000000200000000000AEEF7
+ DFFF6350085E7BF7BFD00000400000205E6F773D41300002038A48042FDF
+ FF440000BECF7DD000000001200000000008000000000002010AFFFCFFDB
+ FFEC801F6EDFEF90400100002142573BD736400420000000000077DDD740
+ 0004B9FB7B9000002004002002044000800000000008004AFFEFB5FFE528
+ 097F7DFDFDB080040008060157EAA2EA2A40040020000021DABFBB400004
+ B9FB7B9000002004002002044000800000000008004AFFEFB5FFE528097F
+ 7DFDFDB080040008060157EAA2EA2A40040020000021DABFBB400000BBDF
+ F5D00008000010800000000000000080008000A4FEEDFFFDEDA020B76BBF
+ B7A1001000200000007FFF000000004200000106647EEF410000BDF7EB90
+ 0900022002020810040000080004040000CCF7B7BFDFEAA142BFFEFFFFB9
+ 008000800800004DF30000000008040804073EEDF7400000BDF7EB900900
+ 022002020810040000080004040000CCF7B7BFDFEAA142BFFEFFFFB90080
+ 00800800004DF30000000008040804073EEDF7400000BBDF55F100110007
+ C018081C110927614160100000B07FFBFCFFFAA800FFDDF7BF8A82000001
+ 001000088002000080404000000A79BFFB400000BDFDFA0E20002002E03C
+ 3E14070387B040B0100002687FEEBFEFEAA80177A7DEEB29800000000840
+ 8000800000200000002008081FFECF400000BDFDFA0E20002002E03C3E14
+ 070387B040B0100002687FEEBFEFEAA80177A7DEEB298000000008408000
+ 800000200000002008081FFECF400000BBF7E10080400004304232000C84
+ C4013190900000AE3FEFEFFFE1100ADFF8FFFF4B20000000000000000008
+ 00800000008200AE4BAFAB400000BEBFD548410002043062220488844401
+ 31801000011A3FDBFFF6F0A82497A9FFFF19A00040082000002800002400
+ 12220000010C0AF76F400000BEBFD5484100020430622204888444013180
+ 1000011A3FDBFFF6F0A82497A9FFFF19A000400820000028000024001222
+ 0000010C0AF76F400000BD7A23D130020006220632040004440031101202
+ 01CE3EEFDFDBF40202D7ACBFFE5BE0220020020000000000000000000008
+ 088774BBDB400000BF5F5B1C00008082700C2A040086C5C020E01F08014A
+ 3FFFB97EBA24212FDACBFE71A200000000000000804000008000220001AD
+ 68ED5F000000BF5F5B1C00008082700C2A040086C5C020E01F08014A3FFF
+ B97EBA24212FDACBFE71A200000000000000804000008000220001AD68ED
+ 5F000008BEB6B3C714121007A01C3A04070746206190100008C8BFBFFFFE
+ FC13480DED7CBAB3A4000881002008000202000000040010086FFA153F40
+ 0000BB8AFD3FE1000004300223040C0860303182102021ECCEEDCF77DF54
+ 4243F4EF7CA190002208208100020000024408400090373A1E4797440000
+ BB8AFD3FE1000004300223040C0860303182102021ECCEEDCF77DF544243
+ F4EF7CA190002208208100020000024408400090373A1E4797440000B6D6
+ 6803F108012430026100080420303108100005E95FF7BDFF7E25210AF6BD
+ ADABA800002004000000000000000002882846300662BB400000BBCBB3D8
+ 3C902006B16622042804406331B0500001724DFFF7BFAF138015F7F7795A
+ AC0420008001000000401000020801004E67F03B6F400000BBCBB3D83C90
+ 2006B16622042804406331B0500001724DFFF7BFAF138015F7F7795AAC04
+ 20008001000000401000020801004E67F03B6F400000AF5FC5543E000007
+ 60FA3E1F9786C7C1B1E0100005722FCF7FDFFF19088EFD5FA84360008200
+ 000000100000000010000050DE8FFCF99D000000BF8F89BFBE6120810008
+ 08100A038500000000010AD7A7DBBF7F7FC982417F75F2F3880000221000
+ 040020000080000022103E1C8C7CEB400000BF8F89BFBE61208100080810
+ 0A038500000000010AD7A7DBBF7F7FC982417F75F2F38800002210000400
+ 20000080000022103E1C8C7CEB400000BF5A3AAD17A00410000000000000
+ 0000000000200252B37FFDF7DBD1494B7BAF66D320800080002010010204
+ 0010421200A03E677A7EBF400000BD8223F48F9080000000000000000000
+ 000000000074D3EFD7DFEF89CA08BB7EE5F3320020000000004000000401
+ 0080890074CFED973B000000BD8223F48F90800000000000000000000000
+ 00000074D3EFD7DFEF89CA08BB7EE5F33200200000000040000004010080
+ 890074CFED973B000000AE3CF90545D08000000000010010000000004000
+ 013A43FA7EBF7FA16B52B9DBE5B6A80800040404000000201000040000C0
+ F9D8159D3B400000B73C9607B7680008000000000000000010010401088D
+ C9EFADDF9FCD54942DEF49F54020BFFD800000040000000000122420E8E8
+ 1BC397400000B73C9607B7680008000000000000000010010401088DC9EF
+ ADDF9FCD54942DEF49F54020BFFD800000040000000000122420E8E81BC3
+ 97400000BA79A80EB3601500208300805000401000800044017E74BFFFF7
+ FFD4C094ADBF9373D2003DCD8000800000000100080002C2F3301D679308
+ 0000AE69D807A9E00000800C0807F800008902000000409554FFF7BDFFEA
+ 554A7FFB17FD55922968240000004004002020A08902B5E01FA757400000
+ AE69D807A9E00000800C0807F800008902000000409554FFF7BDFFEA554A
+ 7FFB17FD55922968240000004004002020A08902B5E01FA757400000BAF2
+ F80DF179001000103009000C003E083C20006F2FB36EDDEFDBEBB6115CDF
+ A6AB5E000000008001C000004C0000060285E6E01AB3DF000000BCF3780E
+ B9F0010D00320002001C003B10360009AAA3FB7FFFFBFFF5534277F7AEF7
+ 72800336804012500020480600102B05A7501EF2DF400000BCF3780EB9F0
+ 010D00320002001C003B10360009AAA3FB7FFFFBFFF5534277F7AEF77280
+ 0336804012500020480600102B05A7501EF2DF400000BCC2B44D55B8103F
+ 8204001910181212042004204FF87CB76DDFD7FB54485DFF0FB3E88002C6
+ 120024100020480400000F45CDE89DCB9B000000BCF7BC076CE80031C045
+ 04113113C8656E68800125D43C9FF77F7EF4905076BE1EE1B10443A40000
+ 04100028FE0000002FC546D01EBB5F480000BCF7BC076CE80031C0450411
+ 3113C8656E68800125D43C9FF77F7EF4905076BE1EE1B10443A400000410
+ 0028FE0000002FC546D01EBB5F480000B4C5680FF4D90020C0DD140257EE
+ 50C9C1D9B08457F4EE8FFFFEFFDBAD41AFFA3D0FE7920324200004165678
+ 69E1B10112418DD81DE9D7000004BCC5688D2CB82202C073D8027B30F103
+ 4272E011BA7E774F7FF7FDF54C40DDFCB15D5FA800400004065AD4B0594A
+ D2004553CF601C599F480004BCC5688D2CB82202C073D8027B30F1034272
+ E011BA7E774F7FF7FDF54C40DDFCB15D5FA800400004065AD4B0594AD200
+ 4553CF601C599F480000BDADF427F6E80029C00104100500000800000005
+ E9D23F2FDDBFB7F9A288AD79BF2ED0DD10C004004393772C49F49DC02948
+ 49A81AE98F000000BD8EEC0DBE78081F00030000120200000000022226BD
+ 1D87FFFEFFFCB081AB726DD9FD0481E1100440000000000000054BCBCFE8
+ 9FDD6F400000BD8EEC0DBE78081F00030000120200000000022226BD1D87
+ FFFEFFFCB081AB726DD9FD0481E1100440000000000000054BCBCFE89FDD
+ 6F400000BD6BD8273668203C10010100960000000000801D5AE5CFD3D7DF
+ EFFF835696E67F72522B00A0004000008000040000002F8A5DA81EF5CB08
+ 0000B9CCEC8FBA7080B0400400000800000831000021A42B7369FDF7FBFD
+ 6D165DC9A9D571A8404406810000020100002000980A8FB81A596F400000
+ B9CCEC8FBA7080B0400400000800000831000021A42B7369FDF7FBFD6D16
+ 5DC9A9D571A8404406810000020100002000980A8FB81A596F4000083D4F
+ EC0EEE5810240080082038040000480820008186FDE8FF7FCFFDAC8A5589
+ EFAABCA90000296001020000201000142402DDA05EFDCF400000BDADEC0D
+ B6F082380000000000000101040080057F7A6DACEFF77800030557CDF6DF
+ BFF4109083E00010002000810040900A4F981CD54D000000BDADEC0DB6F0
+ 82380000000000000101040080057F7A6DACEFF77800030557CDF6DFBFF4
+ 109083E00010002000810040900A4F981CD54D000020BD4F6847FA7A003F
+ C01040020000001070200225FFBEBEE8FFBFCE837C7255E6FFF6EB5BEFEF
+ 7F5020402003100000022AC2CBE81FD9EF400000B5C5EC0DAED008011248
+ 0100082062011C040000000000030DF0A2A970536C1100495495595AAB51
+ 2200038204802200DFC94EB11EEB5B000000B5C5EC0DAED0080112480100
+ 082062011C040000000000030DF0A2A970536C1100495495595AAB512200
+ 038204802200DFC94EB11EEB5B000000AC86700E6CB800A4001004028401
+ 42005F7BFDBFB7F6F7F367AB4E000494AF99FFBFBBBF77FFBC700A6F91D2
+ B4F0000900024BD819B9DD48000034D7B806D5D2400020AE61532A3E19C1
+ 200000000000000794843FFFFF8C117C000000009120A2A03150A2455300
+ 000850850CE05FDB5B40000034D7B806D5D2400020AE61532A3E19C12000
+ 00000000000794843FFFFF8C117C000000009120A2A03150A24553000008
+ 50850CE05FDB5B400000AAD2F04DD9A80000005256A0C4124511BFFFFFFF
+ FFFFFFFF2D07FFFFFFFC2CFFFFFFFFFFFFFFFF2400010000002022220215
+ 67E81A731F400010BED37806E9D84404888000000000000E5FFFFFFFFFFF
+ FFFF9C7FFDFFFFFFAC7FFFFFFFFFFFFFFED40200000000208004A0056660
+ 1BB3DF400010BED37806E9D84404888000000000000E5FFFFFFFFFFFFFFF
+ 9C7FFDFFFFFFAC7FFFFFFFFFFFFFFED40200000000208004A00566601BB3
+ DF400000BBABD805B920108800100000000000053FFFFFFFFFFFFFFEBFFF
+ D54800FFFFF9FFFFFFFFFFFFFD25844250840002846B10A237B10EE60300
+ 0000AF494100A351400000000080000000450FFFFFFFFFFFFFFF55DFADAB
+ FD0FFDE52DFFFFFFFFFFFD46302B56337B7428B4EE2A55C023A75F400000
+ AF494100A351400000000080000000450FFFFFFFFFFFFFFF55DFADABFD0F
+ FDE52DFFFFFFFFFFFD46302B56337B7428B4EE2A55C023A75F4000003F4C
+ FFFFD6AB39B5BB9ABD735335A695792A001001000000F80006A040C001A0
+ 038903400006890112AD50A92526955BA4B4B37FFEE5BF480000B334AFFF
+ 164A952DA92C4654122AD2A516A41C1801C200008C0307A0E07007F007FE
+ 03B00403AEA4A55D6AA41864B4F7CAB488DFFE4EAB400000B334AFFF164A
+ 952DA92C4654122AD2A516A41C1801C200008C0307A0E07007F007FE03B0
+ 0403AEA4A55D6AA41864B4F7CAB488DFFE4EAB400000B7A2736B8D365A0C
+ 22D65DEB5AA537545B601E0600F700E0EA438180E01807F80FE801FC0603
+ D276DF6BF77BEFA76AACAF4ADDE7FB9937400008BE971AE9CD5BFDFBFBBB
+ 6EB3DDBEFB5B2FA01E07007700E0F4078980E0180F681FFA10DC0411ED65
+ 000000000000000040115457461C4F000008BE971AE9CD5BFDFBFBBB6EB3
+ DDBEFB5B2FA01E07007700E0F4078980E0180F681FFA10DC0411ED650000
+ 00000000000040115457461C4F0000002FD1956F18200000000000000000
+ 0008EEC01E074037008FF4038080E0100D901E8580F40C07CA98FFFFFFFF
+ FFFFFFFFFFFF0E2DAA72DD410000BE52C7AE317FFFFFFFFFFFFFFFFFFFAF
+ 55A21E076007208FDA038080E0380F181B598074004FE9FE3A6FB0D72BFF
+ BBFFDFFFB18EACC4F7400000BE52C7AE317FFFFFFFFFFFFFFFFFFFAF55A2
+ 1E076007208FDA038080E0380F181B598074004FE9FE3A6FB0D72BFFBBFF
+ DFFFB18EACC4F7400000BA68E358627FF7FBDBDFFFFFED7DDF5D6BA01C07
+ 620600CE75238080F0188E981F9B84781407ABA75FFFE55FBDFBEF7F4BF6
+ CB8BB8E9AF400000AEE638018AF3FAAFFAFEF3AF6AEFF6B7E7C05E077007
+ 00E8D4839384E0180E180E1B803C0C07AAFB3F7773BB5577BDFF4FF7A460
+ 0313E7400000AEE638018AF3FAAFFAFEF3AF6AEFF6B7E7C05E07700700E8
+ D4839384E0180E180E1B803C0C07AAFB3F7773BB5577BDFF4FF7A4600313
+ E7400000BDF29C0785BB753EBBDBDF7B6D5D563EF5E01E46380700E0F603
+ 81C0E0301C181E1BC0384483ADA55E3BF4F6FFACEB7EB6FFF5784EA7DB40
+ 000025BD27FE1BF5FCDFD9F77BEFCAFB39776F401C1E2C0704C0E80303C0
+ C0600810081180300403B3EB231BE75F5577DEBB676EA90F7A8FF7400000
+ 25BD27FE1BF5FCDFD9F77BEFCAFB39776F401C1E2C0704C0E80303C0C060
+ 0810081180300403B3EB231BE75F5577DEBB676EA90F7A8FF7400000BFFC
+ 81F0177BDDB7FEEFD7B5B57E3AB5ABEC1C1C0A061000F00217F041C00000
+ 0000010103045DF75A37B6BDFBF77EFCABDEE8A6B41DEF000000B3573202
+ 8DB5F555ADBDFBE75E76D03157B7EBFFFDFFFFFFDFFFFFDFFFFFFFFFFFFF
+ FFFFFFFFD2D54D9DEF7D6D77D79D7DEB9E004877B7440000B35732028DB5
+ F555ADBDFBE75E76D03157B7EBFFFDFFFFFFDFFFFFDFFFFFFFFFFFFFFFFF
+ FFFFD2D54D9DEF7D6D77D79D7DEB9E004877B7440000BDDD18009AEEF9AF
+ F5FEF7F7B57B4D3D6FCFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFFFFFFFF
+ FD76A335C74F7AF7DEEB66AFEF00A0DECB400000BB7BE3FC6EDF675B6F79
+ DDDE9FAE24571EEBFFFFFF7FFFFFF7FFFFB7FF5FFFFFFFFFFFFFFFFFE5D7
+ 8E7EB7B97FFBFDB6BDFEFFF057FEFF000000BB7BE3FC6EDF675B6F79DDDE
+ 9FAE24571EEBFFFFFF7FFFFFF7FFFFB7FF5FFFFFFFFFFFFFFFFFE5D78E7E
+ B7B97FFBFDB6BDFEFFF057FEFF0000009DEFF803BFBDBBBDFB7F7F77FFFF
+ FFFEFBAAFFE9FBBFFFFFD9FFFEDB7DDFFFFFDDF7FFBDA9552502AA454292
+ 45102050804000048800004000083FFFFF5FFFFFFFFFFFFFFDFEF6AAD2AB
+ 2CA504142440000000000000000000000000000000000000000000000000
+ 0000020000000000804800083FFFFF5FFFFFFFFFFFFFFDFEF6AAD2AB2CA5
+ 041424400000000000000000000000000000000000000000000000000000
+ 0200000000008048000080000000000000000000000000211224134AEAB7
+ ABBEFFFFFFFFFFFFFFFBFFFFFFFFED77FFBFFFFFFFFFFFFFFFFFFFFFFFFF
+ FFFFF08400000000A48D53EDB6EFFFFFFFFFFFF7FFDEFDB56D4AAA54A080
+ 000000000000000000000000000000000000000000000000000000000000
+ 020000900000A48D53EDB6EFFFFFFFFFFFF7FFDEFDB56D4AAA54A0800000
+ 000000000000000000000000000000000000000000000000000000000200
+ 009000000000000000000000000000000000000000000000001000000000
+ 000000000000000000000000000000000000000000000800040000800000
+ 000000000000000000000000000000000000000000000000100000000000
+ 000000000000000000000000000000100000004400948000100200400000
+ 000000000000000000000000000000000000000000001000000000000000
+ 000000000000000000000000001000000044009480001002004000000100
+ 800000000000000000000000000000000000040040880000000000000000
+ 000000000010100000000000000000004000000040000200000004000000
+ 000000008000000000000000000000000008000084200000000000000002
+ 088401008000800004000002200004000020000040000000040000000000
+ 000080000000000000000000000000080000842000000000000000020884
+ 010080008000040000022000040000200000400000000000000004400000
+ 000000000000000002080008000000000000000000000000000000000002
+ 000004010000000000000000000100010000000010000000000000000001
+ 444000020000000000002000002200000000000000000000000020200000
+ 000400040010001080000000000400100000100000000000000000014440
+ 000200000000000020000022000000000000000000000000202000000004
+ 000400100010800000000004001000000008910800000000000000000000
+ 000010002000002000800820880000000000020010080200000000000010
+ 0080000000000400001000000024810000000000800A0084000042000001
+ 008000800100420000020000000011000810802000480002010000400400
+ 040020001000000004000024810000000000800A00840000420000010080
+ 008001004200000200000000110008108020004800020100004004000400
+ 200010000000040000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000
+ 00>
+ } imagemask
+grestore
+0.500008 0.500008 0.500008 SetBgColor
+421 154 121 2 Box Fill
+
+540 86 2 70 Box Fill
+
+0.750011 0.750011 0.750011 SetBgColor
+newpath 421 156 moveto
+421 86 lineto
+542 86 lineto
+540 88 lineto
+423 88 lineto
+423 154 lineto
+421 156 lineto
+421 156 lineto closepath Fill
+0 setlinejoin
+0 setlinecap
+0 0 0 SetFgColor
+3 setlinewidth
+[ ] 0 setdash
+448 102 42 Li
+1 setlinewidth
+[ ] 0 setdash
+
+/DrawSymbolProc {
+ gsave
+ 0.996109 0.644541 0 SetBgColor
+ Fill
+ 0 0 0 SetFgColor
+ stroke
+ grestore
+} def
+
+448 102 21 Ci
+58 27 0 502 104 BeginText
+14 /Helvetica SetFont
+0 0 0 SetFgColor
+(sin\(x\)) 58 0 21 DrawAdjText
+EndText
+0 setlinejoin
+0 setlinecap
+0.542977 0.542977 0 SetFgColor
+3 setlinewidth
+[ ] 0 setdash
+448 135 42 Li
+1 setlinewidth
+[ ] 0 setdash
+
+/DrawSymbolProc {
+ gsave
+ 0.996109 0.996109 0 SetBgColor
+ Fill
+ 0.542977 0.542977 0 SetFgColor
+ stroke
+ grestore
+} def
+
+448 135 21 Ci
+64 27 0 505 137 BeginText
+14 /Helvetica SetFont
+0 0 0 SetFgColor
+(cos\(x\)) 64 0 21 DrawAdjText
+EndText
+24 16 90 537 388 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(360) 24 0 12 DrawAdjText
+EndText
+32 16 90 79 384 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(-360) 32 0 12 DrawAdjText
+EndText
+8 16 0 85 90 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+16 16 0 89 402 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(-1) 16 0 12 DrawAdjText
+EndText
+8 16 90 537 364 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+8 16 90 79 356 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0) 8 0 12 DrawAdjText
+EndText
+8 16 0 101 90 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+8 16 0 109 402 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0) 8 0 12 DrawAdjText
+EndText
+
+% Element "line2"
+
+0 setlinejoin
+0 setlinecap
+0.542977 0.542977 0 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+/DashesProc {} def
+ newpath 79 90 moveto
+ 82 90 lineto
+ 85 92 lineto
+ 88 95 lineto
+ 91 99 lineto
+ 94 104 lineto
+ 98 110 lineto
+ 101 118 lineto
+ 104 126 lineto
+ 107 135 lineto
+ 110 145 lineto
+ 114 156 lineto
+ 117 168 lineto
+ 120 180 lineto
+ 123 192 lineto
+ 126 205 lineto
+ 130 219 lineto
+ 133 232 lineto
+ 136 246 lineto
+ 139 260 lineto
+ 142 273 lineto
+ 145 287 lineto
+ 149 300 lineto
+ 152 312 lineto
+ 155 324 lineto
+ 158 336 lineto
+ 161 347 lineto
+ 165 357 lineto
+ 168 366 lineto
+ 171 374 lineto
+ 174 382 lineto
+ 177 388 lineto
+ 181 393 lineto
+ 184 397 lineto
+ 187 400 lineto
+ 190 402 lineto
+ 193 403 lineto
+ 196 402 lineto
+ 200 400 lineto
+ 203 397 lineto
+ 206 393 lineto
+ 209 388 lineto
+ 212 382 lineto
+ 216 374 lineto
+ 219 366 lineto
+ 222 357 lineto
+ 225 347 lineto
+ 228 336 lineto
+ 232 324 lineto
+ 235 312 lineto
+ 238 300 lineto
+ 241 287 lineto
+ 244 273 lineto
+ 247 260 lineto
+ 251 246 lineto
+ 254 232 lineto
+ 257 219 lineto
+ 260 205 lineto
+ 263 192 lineto
+ 267 180 lineto
+ 270 168 lineto
+ 273 156 lineto
+ 276 145 lineto
+ 279 135 lineto
+ 283 126 lineto
+ 286 118 lineto
+ 289 110 lineto
+ 292 104 lineto
+ 295 99 lineto
+ 298 95 lineto
+ 302 92 lineto
+ 305 90 lineto
+ 308 90 lineto
+ 311 90 lineto
+ 314 92 lineto
+ 318 95 lineto
+ 321 99 lineto
+ 324 104 lineto
+ 327 110 lineto
+ 330 118 lineto
+ 334 126 lineto
+ 337 135 lineto
+ 340 145 lineto
+ 343 156 lineto
+ 346 168 lineto
+ 349 180 lineto
+ 353 192 lineto
+ 356 205 lineto
+ 359 219 lineto
+ 362 232 lineto
+ 365 246 lineto
+ 369 260 lineto
+ 372 273 lineto
+ 375 287 lineto
+ 378 300 lineto
+ 381 312 lineto
+ 385 324 lineto
+ 388 336 lineto
+ 391 347 lineto
+ 394 357 lineto
+ 397 366 lineto
+ 400 374 lineto
+ 404 382 lineto
+ 407 388 lineto
+ 410 393 lineto
+ 413 397 lineto
+ 416 400 lineto
+ 420 402 lineto
+ 423 403 lineto
+ 426 402 lineto
+ 429 400 lineto
+ 432 397 lineto
+ 436 393 lineto
+ 439 388 lineto
+ 442 382 lineto
+ 445 374 lineto
+ 448 366 lineto
+ 451 357 lineto
+ 455 347 lineto
+ 458 336 lineto
+ 461 324 lineto
+ 464 312 lineto
+ 467 300 lineto
+ 471 287 lineto
+ 474 273 lineto
+ 477 260 lineto
+ 480 246 lineto
+ 483 232 lineto
+ 487 219 lineto
+ 490 205 lineto
+ 493 192 lineto
+ 496 180 lineto
+ 499 168 lineto
+ 502 156 lineto
+ 506 145 lineto
+ 509 135 lineto
+ 512 126 lineto
+ 515 118 lineto
+ 518 110 lineto
+ 522 104 lineto
+ 525 99 lineto
+ 528 95 lineto
+ 531 92 lineto
+ 534 90 lineto
+ 538 90 lineto
+DashesProc stroke
+1 setlinewidth
+[ ] 0 setdash
+
+/DrawSymbolProc {
+ gsave
+ 0.996109 0.996109 0 SetBgColor
+ Fill
+ 0.542977 0.542977 0 SetFgColor
+ stroke
+ grestore
+} def
+
+79 90 9 Ci
+82 90 9 Ci
+85 92 9 Ci
+88 95 9 Ci
+91 99 9 Ci
+94 104 9 Ci
+98 110 9 Ci
+101 118 9 Ci
+104 126 9 Ci
+107 135 9 Ci
+110 145 9 Ci
+114 156 9 Ci
+117 168 9 Ci
+120 180 9 Ci
+123 192 9 Ci
+126 205 9 Ci
+130 219 9 Ci
+133 232 9 Ci
+136 246 9 Ci
+139 260 9 Ci
+142 273 9 Ci
+145 287 9 Ci
+149 300 9 Ci
+152 312 9 Ci
+155 324 9 Ci
+158 336 9 Ci
+161 347 9 Ci
+165 357 9 Ci
+168 366 9 Ci
+171 374 9 Ci
+174 382 9 Ci
+177 388 9 Ci
+181 393 9 Ci
+184 397 9 Ci
+187 400 9 Ci
+190 402 9 Ci
+193 403 9 Ci
+196 402 9 Ci
+200 400 9 Ci
+203 397 9 Ci
+206 393 9 Ci
+209 388 9 Ci
+212 382 9 Ci
+216 374 9 Ci
+219 366 9 Ci
+222 357 9 Ci
+225 347 9 Ci
+228 336 9 Ci
+232 324 9 Ci
+235 312 9 Ci
+238 300 9 Ci
+241 287 9 Ci
+244 273 9 Ci
+247 260 9 Ci
+251 246 9 Ci
+254 232 9 Ci
+257 219 9 Ci
+260 205 9 Ci
+263 192 9 Ci
+267 180 9 Ci
+270 168 9 Ci
+273 156 9 Ci
+276 145 9 Ci
+279 135 9 Ci
+283 126 9 Ci
+286 118 9 Ci
+289 110 9 Ci
+292 104 9 Ci
+295 99 9 Ci
+298 95 9 Ci
+302 92 9 Ci
+305 90 9 Ci
+308 90 9 Ci
+311 90 9 Ci
+314 92 9 Ci
+318 95 9 Ci
+321 99 9 Ci
+324 104 9 Ci
+327 110 9 Ci
+330 118 9 Ci
+334 126 9 Ci
+337 135 9 Ci
+340 145 9 Ci
+343 156 9 Ci
+346 168 9 Ci
+349 180 9 Ci
+353 192 9 Ci
+356 205 9 Ci
+359 219 9 Ci
+362 232 9 Ci
+365 246 9 Ci
+369 260 9 Ci
+372 273 9 Ci
+375 287 9 Ci
+378 300 9 Ci
+381 312 9 Ci
+385 324 9 Ci
+388 336 9 Ci
+391 347 9 Ci
+394 357 9 Ci
+397 366 9 Ci
+400 374 9 Ci
+404 382 9 Ci
+407 388 9 Ci
+410 393 9 Ci
+413 397 9 Ci
+416 400 9 Ci
+420 402 9 Ci
+423 403 9 Ci
+426 402 9 Ci
+429 400 9 Ci
+432 397 9 Ci
+436 393 9 Ci
+439 388 9 Ci
+442 382 9 Ci
+445 374 9 Ci
+448 366 9 Ci
+451 357 9 Ci
+455 347 9 Ci
+458 336 9 Ci
+461 324 9 Ci
+464 312 9 Ci
+467 300 9 Ci
+471 287 9 Ci
+474 273 9 Ci
+477 260 9 Ci
+480 246 9 Ci
+483 232 9 Ci
+487 219 9 Ci
+490 205 9 Ci
+493 192 9 Ci
+496 180 9 Ci
+499 168 9 Ci
+502 156 9 Ci
+506 145 9 Ci
+509 135 9 Ci
+512 126 9 Ci
+515 118 9 Ci
+518 110 9 Ci
+522 104 9 Ci
+525 99 9 Ci
+528 95 9 Ci
+531 92 9 Ci
+534 90 9 Ci
+538 90 9 Ci
+
+% Element "line1"
+
+0 setlinejoin
+0 setlinecap
+0 0 0 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+/DashesProc {} def
+ newpath 79 246 moveto
+ 82 232 lineto
+ 85 219 lineto
+ 88 205 lineto
+ 91 192 lineto
+ 94 180 lineto
+ 98 168 lineto
+ 101 156 lineto
+ 104 145 lineto
+ 107 135 lineto
+ 110 126 lineto
+ 114 118 lineto
+ 117 110 lineto
+ 120 104 lineto
+ 123 99 lineto
+ 126 95 lineto
+ 130 92 lineto
+ 133 90 lineto
+ 136 90 lineto
+ 139 90 lineto
+ 142 92 lineto
+ 145 95 lineto
+ 149 99 lineto
+ 152 104 lineto
+ 155 110 lineto
+ 158 118 lineto
+ 161 126 lineto
+ 165 135 lineto
+ 168 145 lineto
+ 171 156 lineto
+ 174 168 lineto
+ 177 180 lineto
+ 181 192 lineto
+ 184 205 lineto
+ 187 219 lineto
+ 190 232 lineto
+ 193 246 lineto
+ 196 260 lineto
+ 200 273 lineto
+ 203 287 lineto
+ 206 300 lineto
+ 209 312 lineto
+ 212 324 lineto
+ 216 336 lineto
+ 219 347 lineto
+ 222 357 lineto
+ 225 366 lineto
+ 228 374 lineto
+ 232 382 lineto
+ 235 388 lineto
+ 238 393 lineto
+ 241 397 lineto
+ 244 400 lineto
+ 247 402 lineto
+ 251 403 lineto
+ 254 402 lineto
+ 257 400 lineto
+ 260 397 lineto
+ 263 393 lineto
+ 267 388 lineto
+ 270 382 lineto
+ 273 374 lineto
+ 276 366 lineto
+ 279 357 lineto
+ 283 347 lineto
+ 286 336 lineto
+ 289 324 lineto
+ 292 312 lineto
+ 295 300 lineto
+ 298 287 lineto
+ 302 273 lineto
+ 305 260 lineto
+ 308 246 lineto
+ 311 232 lineto
+ 314 219 lineto
+ 318 205 lineto
+ 321 192 lineto
+ 324 180 lineto
+ 327 168 lineto
+ 330 156 lineto
+ 334 145 lineto
+ 337 135 lineto
+ 340 126 lineto
+ 343 118 lineto
+ 346 110 lineto
+ 349 104 lineto
+ 353 99 lineto
+ 356 95 lineto
+ 359 92 lineto
+ 362 90 lineto
+ 365 90 lineto
+ 369 90 lineto
+ 372 92 lineto
+ 375 95 lineto
+ 378 99 lineto
+ 381 104 lineto
+ 385 110 lineto
+ 388 118 lineto
+ 391 126 lineto
+ 394 135 lineto
+ 397 145 lineto
+ 400 156 lineto
+ 404 168 lineto
+ 407 180 lineto
+ 410 192 lineto
+ 413 205 lineto
+ 416 219 lineto
+ 420 232 lineto
+ 423 246 lineto
+ 426 260 lineto
+ 429 273 lineto
+ 432 287 lineto
+ 436 300 lineto
+ 439 312 lineto
+ 442 324 lineto
+ 445 336 lineto
+ 448 347 lineto
+ 451 357 lineto
+ 455 366 lineto
+ 458 374 lineto
+ 461 382 lineto
+ 464 388 lineto
+ 467 393 lineto
+ 471 397 lineto
+ 474 400 lineto
+ 477 402 lineto
+ 480 403 lineto
+ 483 402 lineto
+ 487 400 lineto
+ 490 397 lineto
+ 493 393 lineto
+ 496 388 lineto
+ 499 382 lineto
+ 502 374 lineto
+ 506 366 lineto
+ 509 357 lineto
+ 512 347 lineto
+ 515 336 lineto
+ 518 324 lineto
+ 522 312 lineto
+ 525 300 lineto
+ 528 287 lineto
+ 531 273 lineto
+ 534 260 lineto
+ 538 246 lineto
+DashesProc stroke
+1 setlinewidth
+[ ] 0 setdash
+
+/DrawSymbolProc {
+ gsave
+ 0.996109 0.644541 0 SetBgColor
+ Fill
+ 0 0 0 SetFgColor
+ stroke
+ grestore
+} def
+
+79 246 9 Ci
+82 232 9 Ci
+85 219 9 Ci
+88 205 9 Ci
+91 192 9 Ci
+94 180 9 Ci
+98 168 9 Ci
+101 156 9 Ci
+104 145 9 Ci
+107 135 9 Ci
+110 126 9 Ci
+114 118 9 Ci
+117 110 9 Ci
+120 104 9 Ci
+123 99 9 Ci
+126 95 9 Ci
+130 92 9 Ci
+133 90 9 Ci
+136 90 9 Ci
+139 90 9 Ci
+142 92 9 Ci
+145 95 9 Ci
+149 99 9 Ci
+152 104 9 Ci
+155 110 9 Ci
+158 118 9 Ci
+161 126 9 Ci
+165 135 9 Ci
+168 145 9 Ci
+171 156 9 Ci
+174 168 9 Ci
+177 180 9 Ci
+181 192 9 Ci
+184 205 9 Ci
+187 219 9 Ci
+190 232 9 Ci
+193 246 9 Ci
+196 260 9 Ci
+200 273 9 Ci
+203 287 9 Ci
+206 300 9 Ci
+209 312 9 Ci
+212 324 9 Ci
+216 336 9 Ci
+219 347 9 Ci
+222 357 9 Ci
+225 366 9 Ci
+228 374 9 Ci
+232 382 9 Ci
+235 388 9 Ci
+238 393 9 Ci
+241 397 9 Ci
+244 400 9 Ci
+247 402 9 Ci
+251 403 9 Ci
+254 402 9 Ci
+257 400 9 Ci
+260 397 9 Ci
+263 393 9 Ci
+267 388 9 Ci
+270 382 9 Ci
+273 374 9 Ci
+276 366 9 Ci
+279 357 9 Ci
+283 347 9 Ci
+286 336 9 Ci
+289 324 9 Ci
+292 312 9 Ci
+295 300 9 Ci
+298 287 9 Ci
+302 273 9 Ci
+305 260 9 Ci
+308 246 9 Ci
+311 232 9 Ci
+314 219 9 Ci
+318 205 9 Ci
+321 192 9 Ci
+324 180 9 Ci
+327 168 9 Ci
+330 156 9 Ci
+334 145 9 Ci
+337 135 9 Ci
+340 126 9 Ci
+343 118 9 Ci
+346 110 9 Ci
+349 104 9 Ci
+353 99 9 Ci
+356 95 9 Ci
+359 92 9 Ci
+362 90 9 Ci
+365 90 9 Ci
+369 90 9 Ci
+372 92 9 Ci
+375 95 9 Ci
+378 99 9 Ci
+381 104 9 Ci
+385 110 9 Ci
+388 118 9 Ci
+391 126 9 Ci
+394 135 9 Ci
+397 145 9 Ci
+400 156 9 Ci
+404 168 9 Ci
+407 180 9 Ci
+410 192 9 Ci
+413 205 9 Ci
+416 219 9 Ci
+420 232 9 Ci
+423 246 9 Ci
+426 260 9 Ci
+429 273 9 Ci
+432 287 9 Ci
+436 300 9 Ci
+439 312 9 Ci
+442 324 9 Ci
+445 336 9 Ci
+448 347 9 Ci
+451 357 9 Ci
+455 366 9 Ci
+458 374 9 Ci
+461 382 9 Ci
+464 388 9 Ci
+467 393 9 Ci
+471 397 9 Ci
+474 400 9 Ci
+477 402 9 Ci
+480 403 9 Ci
+483 402 9 Ci
+487 400 9 Ci
+490 397 9 Ci
+493 393 9 Ci
+496 388 9 Ci
+499 382 9 Ci
+502 374 9 Ci
+506 366 9 Ci
+509 357 9 Ci
+512 347 9 Ci
+515 336 9 Ci
+518 324 9 Ci
+522 312 9 Ci
+525 300 9 Ci
+528 287 9 Ci
+531 273 9 Ci
+534 260 9 Ci
+538 246 9 Ci
+
+% Unset clipping
+grestore
+
+0.750011 0.750011 0.750011 SetBgColor
+0 0 598 82 Box Fill
+
+0 82 70 329 Box Fill
+
+546 82 52 329 Box Fill
+
+0 411 598 68 Box Fill
+
+0.500008 0.500008 0.500008 SetBgColor
+69 410 478 2 Box Fill
+
+545 80 2 332 Box Fill
+
+0.750011 0.750011 0.750011 SetBgColor
+newpath 69 412 moveto
+69 80 lineto
+547 80 lineto
+545 82 lineto
+71 82 lineto
+71 410 lineto
+69 412 lineto
+69 412 lineto closepath Fill
+311 29 0 308 20 BeginText
+14 /Helvetica-Bold SetFont
+0 0 0 SetFgColor
+(Sine and Cosine Functions) 311 0 23 DrawAdjText
+EndText
+7 16 0 308 463 BeginText
+8 /Helvetica SetFont
+0 0 0 SetFgColor
+(X) 7 0 13 DrawAdjText
+EndText
+40 16 0 79 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(-360°) 40 0 12 DrawAdjText
+EndText
+40 16 0 136 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(-270°) 40 0 12 DrawAdjText
+EndText
+40 16 0 193 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(-180°) 40 0 12 DrawAdjText
+EndText
+32 16 0 251 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(-90°) 32 0 12 DrawAdjText
+EndText
+16 16 0 308 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0°) 16 0 12 DrawAdjText
+EndText
+24 16 0 365 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(90°) 24 0 12 DrawAdjText
+EndText
+32 16 0 423 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(180°) 32 0 12 DrawAdjText
+EndText
+32 16 0 480 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(270°) 32 0 12 DrawAdjText
+EndText
+32 16 0 538 441 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(360°) 32 0 12 DrawAdjText
+EndText
+0 setlinejoin
+0 setlinecap
+0 0 0 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+79 416 538 416 LS
+79 416 79 428 LS
+136 416 136 428 LS
+193 416 193 428 LS
+251 416 251 428 LS
+308 416 308 428 LS
+365 416 365 428 LS
+423 416 423 428 LS
+480 416 480 428 LS
+538 416 538 428 LS
+9 16 90 18 246 BeginText
+8 /Helvetica SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(Y) 9 0 13 DrawAdjText
+EndText
+16 16 90 40 403 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(-1) 16 0 12 DrawAdjText
+EndText
+32 16 90 40 324 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(-0.5) 32 0 12 DrawAdjText
+EndText
+8 16 90 40 246 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(0) 8 0 12 DrawAdjText
+EndText
+24 16 90 40 168 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(0.5) 24 0 12 DrawAdjText
+EndText
+8 16 90 40 90 BeginText
+8 /CourierNewBold-Bold SetFont
+0.566415 0.171878 0.929702 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+0 setlinejoin
+0 setlinecap
+0.566415 0.171878 0.929702 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+65 403 65 90 LS
+65 363 59 363 LS
+65 403 53 403 LS
+65 285 59 285 LS
+65 324 53 324 LS
+65 207 59 207 LS
+65 246 53 246 LS
+65 129 59 129 LS
+65 168 53 168 LS
+65 90 53 90 LS
+8 16 0 79 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0) 8 0 12 DrawAdjText
+EndText
+24 16 0 170 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0.2) 24 0 12 DrawAdjText
+EndText
+24 16 0 262 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0.4) 24 0 12 DrawAdjText
+EndText
+24 16 0 354 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0.6) 24 0 12 DrawAdjText
+EndText
+24 16 0 446 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(0.8) 24 0 12 DrawAdjText
+EndText
+8 16 0 538 51 BeginText
+8 /CourierNewBold-Bold SetFont
+0 0 0 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+0 setlinejoin
+0 setlinecap
+0 0 0 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+79 76 538 76 LS
+124 76 124 70 LS
+79 76 79 64 LS
+216 76 216 70 LS
+170 76 170 64 LS
+308 76 308 70 LS
+262 76 262 64 LS
+400 76 400 70 LS
+354 76 354 64 LS
+492 76 492 70 LS
+446 76 446 64 LS
+538 76 538 64 LS
+8 16 0 572 403 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(0) 8 0 12 DrawAdjText
+EndText
+24 16 0 580 340 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(0.2) 24 0 12 DrawAdjText
+EndText
+24 16 0 580 277 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(0.4) 24 0 12 DrawAdjText
+EndText
+24 16 0 580 215 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(0.6) 24 0 12 DrawAdjText
+EndText
+24 16 0 580 152 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(0.8) 24 0 12 DrawAdjText
+EndText
+8 16 0 572 90 BeginText
+8 /CourierNewBold-Bold SetFont
+0.800793 0 0.800793 SetFgColor
+(1) 8 0 12 DrawAdjText
+EndText
+0 setlinejoin
+0 setlinecap
+0.800793 0 0.800793 SetFgColor
+1 setlinewidth
+[ ] 0 setdash
+551 403 551 90 LS
+551 371 557 371 LS
+551 403 563 403 LS
+551 309 557 309 LS
+551 340 563 340 LS
+551 246 557 246 LS
+551 277 563 277 LS
+551 183 557 183 LS
+551 215 563 215 LS
+551 121 557 121 LS
+551 152 563 152 LS
+551 90 563 90 LS
+showpage
+%Trailer
+grestore
+end
+%EOF
diff --git a/blt/demos/images/qv100.t.gif b/blt/demos/images/qv100.t.gif
new file mode 100644
index 00000000000..1e738ee86bb
--- /dev/null
+++ b/blt/demos/images/qv100.t.gif
Binary files differ
diff --git a/blt/demos/images/rain.gif b/blt/demos/images/rain.gif
new file mode 100644
index 00000000000..d7bb417939d
--- /dev/null
+++ b/blt/demos/images/rain.gif
Binary files differ
diff --git a/blt/demos/images/sample.gif b/blt/demos/images/sample.gif
new file mode 100644
index 00000000000..1d8a4010c38
--- /dev/null
+++ b/blt/demos/images/sample.gif
Binary files differ
diff --git a/blt/demos/images/smblue_rock.gif b/blt/demos/images/smblue_rock.gif
new file mode 100644
index 00000000000..df1c8f584f9
--- /dev/null
+++ b/blt/demos/images/smblue_rock.gif
Binary files differ
diff --git a/blt/demos/images/stopsign.gif b/blt/demos/images/stopsign.gif
new file mode 100644
index 00000000000..fe4d27ea5e5
--- /dev/null
+++ b/blt/demos/images/stopsign.gif
Binary files differ
diff --git a/blt/demos/images/tan_paper.gif b/blt/demos/images/tan_paper.gif
new file mode 100644
index 00000000000..aca947da509
--- /dev/null
+++ b/blt/demos/images/tan_paper.gif
Binary files differ
diff --git a/blt/demos/images/tan_paper2.gif b/blt/demos/images/tan_paper2.gif
new file mode 100644
index 00000000000..76d94ac3460
--- /dev/null
+++ b/blt/demos/images/tan_paper2.gif
Binary files differ
diff --git a/blt/demos/images/txtrflag.gif b/blt/demos/images/txtrflag.gif
new file mode 100644
index 00000000000..4017154c344
--- /dev/null
+++ b/blt/demos/images/txtrflag.gif
Binary files differ
diff --git a/blt/demos/scripts/barchart2.tcl b/blt/demos/scripts/barchart2.tcl
new file mode 100644
index 00000000000..c2256288d47
--- /dev/null
+++ b/blt/demos/scripts/barchart2.tcl
@@ -0,0 +1,125 @@
+
+proc FormatXTicks { w value } {
+
+ # Determine the element name from the value
+
+ set index [expr round($value)]
+ if { $index != $value } {
+ return $value
+ }
+ incr index -1
+
+ set name [lindex { A1 B1 A2 B2 C1 D1 C2 A3 E1 } $index]
+ return $name
+}
+
+source scripts/patterns.tcl
+
+image create photo bgTexture -file ./images/chalk.gif
+
+set configOptions {
+ Axis.TickFont -*-helvetica-medium-r-*-*-12-*-*
+ Axis.TitleFont -*-helvetica-bold-r-*-*-12-*-*
+ Element.Background white
+ Element.Relief raised
+ Grid.Dashes { 2 4 }
+ Grid.Hide no
+ Grid.MapX ""
+ Legend.Font "-*-helvetica*-bold-r-*-*-12-*-*"
+ Legend.ActiveBorderWidth 2
+ Legend.ActiveRelief raised
+ Legend.Anchor ne
+ Legend.BorderWidth 0
+ Legend.Position right
+ TextMarker.Font *Helvetica-Bold-R*14*
+ activeBar.Foreground black
+ activeBar.Stipple pattern1
+ BarMode stacked
+ Font -*-helvetica-bold-r-*-*-14-*-*
+ Tile bgTexture
+ Title "Comparison of Simulators"
+ x.Command FormatXTicks
+ x.Title "Simulator"
+ y.Title "Time (hrs)"
+}
+
+set resource [string trimleft $graph .]
+foreach { option value } $configOptions {
+ option add *$resource.$option $value
+}
+
+set visual [winfo screenvisual .]
+if { $visual != "staticgray" && $visual != "grayscale" } {
+ option add *print.background yellow
+ option add *quit.background red
+ option add *quit.activeBackground red2
+}
+
+vector X Y0 Y1 Y2 Y3 Y4
+
+X set { 1 2 3 4 5 6 7 8 9 }
+Y0 set {
+ 0.729111111 0.002250000 0.09108333 0.006416667 0.026509167
+ 0.007027778 0.1628611 0.06405278 0.08786667
+}
+Y1 set {
+ 0.003120278 0.004638889 0.01113889 0.048888889 0.001814722
+ 0.291388889 0.0503500 0.13876389 0.04513333
+}
+Y2 set {
+ 11.534444444 3.879722222 4.54444444 4.460277778 2.334055556
+ 1.262194444 1.8009444 4.12194444 3.24527778
+}
+Y3 set {
+ 1.015750000 0.462888889 0.49394444 0.429166667 1.053694444
+ 0.466111111 1.4152500 2.17538889 2.55294444
+}
+Y4 set {
+ 0.022018611 0.516333333 0.54772222 0.177638889 0.021703889
+ 0.134305556 0.5189278 0.07957222 0.41155556
+}
+
+
+#
+# Element attributes:
+#
+# Label yData Color Stipple Pattern
+set attributes {
+ "Load" Y2 lightblue pattern1
+ "Solve" Y3 cyan pattern2
+ "Other" Y4 lightpink pattern1
+ "Read In" Y0 lightgoldenrod pattern1
+ "Setup" Y1 lightyellow pattern2
+}
+
+foreach {label yData color stipple} $attributes {
+ $graph element create $yData \
+ -label $label \
+ -borderwidth 1 \
+ -y $yData \
+ -x X \
+ -fg ${color}1 \
+ -bg ${color}3 \
+ -stipple $stipple
+}
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+
+$graph marker bind all <B2-Motion> {
+ set coords [%W invtransform %x %y]
+ catch { %W marker configure [%W marker get current] -coords $coords }
+}
+
+$graph marker bind all <Enter> {
+ set marker [%W marker get current]
+ catch { %W marker configure $marker -bg green}
+}
+
+$graph marker bind all <Leave> {
+ set marker [%W marker get current]
+ catch { %W marker configure $marker -bg ""}
+}
+
diff --git a/blt/demos/scripts/bgtest.tcl b/blt/demos/scripts/bgtest.tcl
new file mode 100644
index 00000000000..332d801316a
--- /dev/null
+++ b/blt/demos/scripts/bgtest.tcl
@@ -0,0 +1,35 @@
+
+set fid [open "../README" "r"]
+set data [read $fid]
+close $fid
+
+regsub -all "\r|\n|\t" $data " " data
+set data [split $data " "]
+
+set count 0
+set maxWords 500
+foreach word $data {
+ if { $word == "" } {
+ continue
+ }
+ if { $count & 0x1 } {
+ puts -nonewline stderr "($word)"
+ flush stderr
+ } else {
+ puts -nonewline stdout "($word)"
+ flush stdout
+ }
+ incr count
+ if { ($count % 10) == 0 } {
+ puts stdout ""
+ puts stderr ""
+ flush stdout
+ flush stderr
+ }
+ if { $count > $maxWords } {
+ break
+ }
+ after 500
+}
+exit 0
+
diff --git a/blt/demos/scripts/clone.tcl b/blt/demos/scripts/clone.tcl
new file mode 100644
index 00000000000..874f55d54b7
--- /dev/null
+++ b/blt/demos/scripts/clone.tcl
@@ -0,0 +1,88 @@
+
+proc CopyOptions { cmd orig new } {
+ set all [eval $orig $cmd]
+ set configLine $new
+ foreach arg $cmd {
+ lappend configLine $arg
+ }
+ foreach option $all {
+ if { [llength $option] != 5 } {
+ continue
+ }
+ set switch [lindex $option 0]
+ set initial [lindex $option 3]
+ set current [lindex $option 4]
+ if { [string compare $initial $current] == 0 } {
+ continue
+ }
+ lappend configLine $switch $current
+ }
+ eval $configLine
+}
+
+proc CopyBindings { oper orig new args } {
+ set tags [$orig $oper bind]
+ if { [llength $args] > 0 } {
+ lappend tags [lindex $args 0]
+ }
+ foreach tag $tags {
+ foreach binding [$orig $oper bind $tag] {
+ set cmd [$orig $oper bind $tag $binding]
+ $new $oper bind $tag $binding $cmd
+ }
+ }
+}
+
+proc CloneGraph { orig new } {
+ graph $new
+ CopyOptions "configure" $orig $new
+ # Axis component
+ foreach axis [$orig axis names] {
+ if { [$new axis name $axis] == "" } {
+ $new axis create $axis
+ }
+ CopyOptions [list axis configure $axis] $orig $new
+ }
+ foreach axis { x y x2 y2 } {
+ $new ${axis}axis use [$orig ${axis}axis use]
+ }
+ # Pen component
+ foreach pen [$orig pen names] {
+ if { [$new pen name $pen] == "" } {
+ $new pen create $pen
+ }
+ CopyOptions [list pen configure $pen] $orig $new
+ }
+ # Marker component
+ foreach marker [$orig marker names] {
+ $new marker create [$orig marker type $marker] -name $marker
+ CopyBindings marker $orig $new $marker
+ CopyOptions [list marker configure $marker] $orig $new
+ }
+ # Element component
+ foreach elem [$orig element names] {
+ $new element create $elem
+ CopyBindings element $orig $new $elem
+ CopyOptions [list element configure $elem] $orig $new
+ }
+ # Fix element display list
+ $new element show [$orig element show]
+ # Legend component
+ CopyOptions {legend configure} $orig $new
+ CopyBindings legend $orig $new
+ # Postscript component
+ CopyOptions {postscript configure} $orig $new
+ # Grid component
+ CopyOptions {grid configure} $orig $new
+ # Grid component
+ CopyOptions {crosshairs configure} $orig $new
+ # Graph bindings
+ foreach binding [bind $orig] {
+ set cmd [bind $orig $binding]
+ bind $new $binding $cmd
+ }
+ return $new
+}
+
+toplevel .top
+pack [CloneGraph $graph .top.graph]
diff --git a/blt/demos/scripts/demo.tcl b/blt/demos/scripts/demo.tcl
new file mode 100644
index 00000000000..ef2bcf90a52
--- /dev/null
+++ b/blt/demos/scripts/demo.tcl
@@ -0,0 +1,28 @@
+# ----------------------------------------------------------------------------
+#
+# The following code is solely a convenience so that you can test the
+# BLT distribution without first installing it.
+#
+# ----------------------------------------------------------------------------
+
+# If we're in the ./demos directory, we can simply specify
+# "../library" as the library directory without having to install the
+# files.
+
+if { [file exists ../library/bltGraph.pro] } {
+ global blt_library
+ set blt_library ../library
+ set auto_path [linsert $auto_path 0 $blt_library]
+ auto_reset
+}
+
+# Add a binding for convenience to let you exit with pressing the
+# "quit" button.
+
+wm protocol . WM_DELETE_WINDOW { DoExit 0 }
+bind all <Control-KeyPress-c> { DoExit 0 }
+bind all <KeyPress-q> { DoExit 0 }
+
+proc DoExit { code } {
+ exit $code
+}
diff --git a/blt/demos/scripts/globe.tcl b/blt/demos/scripts/globe.tcl
new file mode 100644
index 00000000000..9e7e17e3074
--- /dev/null
+++ b/blt/demos/scripts/globe.tcl
@@ -0,0 +1,509 @@
+blt::bitmap define globe.0 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x1c, 0x3c, 0x00, 0x00, 0x01, 0xfe, 0x00,
+ 0x80, 0x80, 0xfe, 0x03, 0x60, 0x00, 0xff, 0x07, 0x10, 0xc0, 0xf1, 0x0f,
+ 0x00, 0x80, 0xc0, 0x1f, 0x00, 0xc0, 0x07, 0x3f, 0x00, 0xc0, 0xff, 0x3f,
+ 0x00, 0xf0, 0xff, 0x4f, 0x02, 0xf0, 0xff, 0x5d, 0x00, 0xf0, 0xff, 0x1b,
+ 0x00, 0xf0, 0xff, 0x8f, 0x02, 0xf0, 0xff, 0x0f, 0x06, 0xe0, 0xfc, 0x0f,
+ 0x0e, 0x00, 0xf8, 0x0f, 0x0f, 0x00, 0xf8, 0x07, 0x3f, 0x00, 0xf8, 0x03,
+ 0x7e, 0x00, 0xf0, 0x03, 0x7e, 0x00, 0xf0, 0x03, 0x3e, 0x00, 0xf0, 0x0b,
+ 0x3c, 0x00, 0xf0, 0x09, 0x3c, 0x00, 0xf0, 0x01, 0x18, 0x00, 0xf0, 0x00,
+ 0x18, 0x00, 0x70, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.1 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x34, 0x38, 0x00, 0x00, 0x02, 0xe8, 0x00,
+ 0x80, 0x01, 0xfa, 0x03, 0xe0, 0x00, 0xfc, 0x07, 0x30, 0x00, 0xe6, 0x0f,
+ 0x10, 0x00, 0x86, 0x1f, 0x08, 0x00, 0x3e, 0x3c, 0x04, 0x00, 0xff, 0x3f,
+ 0x04, 0x80, 0xff, 0x5f, 0x02, 0x80, 0xff, 0x3f, 0x00, 0x80, 0xff, 0x2f,
+ 0x00, 0x80, 0xff, 0x3f, 0x0c, 0x00, 0xff, 0x3f, 0x1c, 0x00, 0xee, 0x3f,
+ 0x3c, 0x00, 0xc0, 0x3f, 0x7e, 0x00, 0xc0, 0x1f, 0xfe, 0x01, 0x80, 0x1f,
+ 0xfc, 0x03, 0x80, 0x1f, 0xfc, 0x01, 0x80, 0x1f, 0xfc, 0x01, 0x80, 0x2f,
+ 0xf8, 0x01, 0x80, 0x0f, 0xf0, 0x00, 0x80, 0x17, 0xf0, 0x00, 0x80, 0x03,
+ 0xf0, 0x00, 0x80, 0x03, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00};
+}
+
+blt::bitmap define globe.2 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x04, 0xf0, 0x00,
+ 0x80, 0x07, 0xe0, 0x03, 0xe0, 0x01, 0xf0, 0x07, 0xf0, 0x00, 0x38, 0x0f,
+ 0x30, 0x00, 0x10, 0x1e, 0x18, 0x00, 0xf0, 0x30, 0x04, 0x00, 0xf8, 0x3f,
+ 0x10, 0x00, 0xf8, 0x7f, 0x12, 0x00, 0xfc, 0x7f, 0x02, 0x00, 0xfc, 0x7f,
+ 0x04, 0x00, 0xfc, 0x7f, 0x74, 0x00, 0xf8, 0x7f, 0xf0, 0x00, 0x70, 0x7f,
+ 0xf8, 0x01, 0x00, 0x7e, 0xf8, 0x03, 0x00, 0x7e, 0xf8, 0x0f, 0x00, 0x7c,
+ 0xf8, 0x1f, 0x00, 0x3c, 0xf0, 0x1f, 0x00, 0x3c, 0xf0, 0x0f, 0x00, 0x3e,
+ 0xe0, 0x0f, 0x00, 0x5e, 0xc0, 0x07, 0x00, 0x1c, 0xc0, 0x03, 0x00, 0x0e,
+ 0xc0, 0x03, 0x00, 0x04, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1c, 0x00};
+}
+
+blt::bitmap define globe.3 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xdc, 0x20, 0x00, 0x00, 0x09, 0xc0, 0x00,
+ 0x80, 0x1f, 0xa0, 0x03, 0xe0, 0x07, 0xc0, 0x07, 0xf0, 0x01, 0xc0, 0x0c,
+ 0xf8, 0x00, 0x40, 0x18, 0x78, 0x00, 0xc0, 0x23, 0x08, 0x00, 0xc0, 0x3f,
+ 0x04, 0x00, 0xe0, 0x7f, 0x54, 0x00, 0xe0, 0x7f, 0x0c, 0x00, 0xc0, 0x7f,
+ 0x10, 0x00, 0xc0, 0xff, 0xd0, 0x01, 0xc0, 0xff, 0xc0, 0x03, 0x80, 0xfb,
+ 0xe0, 0x0f, 0x00, 0xf0, 0xe0, 0x1f, 0x00, 0xf0, 0xe0, 0xff, 0x00, 0xf0,
+ 0xe0, 0xff, 0x00, 0x70, 0xc0, 0xff, 0x00, 0x70, 0xc0, 0x7f, 0x00, 0x70,
+ 0x00, 0x7f, 0x00, 0x70, 0x00, 0x3f, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x38,
+ 0x00, 0x1f, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0x00};
+}
+
+blt::bitmap define globe.4 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x00, 0x00, 0x13, 0x00, 0x00,
+ 0x80, 0x7f, 0xc0, 0x03, 0xc0, 0x1f, 0x00, 0x07, 0xe0, 0x0f, 0x00, 0x0d,
+ 0xf0, 0x03, 0x00, 0x10, 0xf0, 0x01, 0x00, 0x0e, 0x38, 0x01, 0x00, 0x3e,
+ 0x10, 0x00, 0x00, 0x7f, 0x50, 0x00, 0x00, 0x7f, 0x30, 0x00, 0x00, 0x7f,
+ 0x40, 0x00, 0x00, 0xff, 0x00, 0x1e, 0x00, 0xfe, 0x00, 0x3f, 0x00, 0xec,
+ 0x00, 0x7f, 0x00, 0xc0, 0x00, 0xff, 0x00, 0xc0, 0x00, 0xff, 0x07, 0xc0,
+ 0x00, 0xff, 0x0f, 0xc0, 0x00, 0xfe, 0x07, 0xc0, 0x00, 0xfe, 0x07, 0xc0,
+ 0x00, 0xf8, 0x03, 0x40, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xf8, 0x00, 0x20,
+ 0x00, 0xf8, 0x00, 0x20, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00};
+}
+
+blt::bitmap define globe.5 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xbc, 0x06, 0x00, 0x00, 0xcf, 0x00, 0x00,
+ 0x80, 0xff, 0x01, 0x02, 0xc0, 0x7f, 0x00, 0x06, 0xc0, 0x3f, 0x00, 0x0e,
+ 0xe0, 0x1f, 0x00, 0x14, 0xe0, 0x0f, 0x00, 0x18, 0xe0, 0x00, 0x00, 0x38,
+ 0x60, 0x00, 0x00, 0x78, 0x40, 0x08, 0x00, 0x78, 0xc0, 0x01, 0x00, 0x78,
+ 0x00, 0x02, 0x00, 0xf8, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x01, 0xb0,
+ 0x00, 0xf8, 0x07, 0x80, 0x00, 0xf8, 0x0f, 0x80, 0x00, 0xf8, 0x3f, 0x00,
+ 0x00, 0xf8, 0x7f, 0x00, 0x00, 0xf0, 0x3f, 0x80, 0x00, 0xf0, 0x3f, 0x80,
+ 0x00, 0xc0, 0x1f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x40,
+ 0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00};
+}
+
+blt::bitmap define globe.6 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x80, 0x07, 0x00, 0x00, 0x7c, 0x0d, 0x00, 0x00, 0x9f, 0x03, 0x00,
+ 0x00, 0xff, 0x07, 0x02, 0x00, 0xff, 0x03, 0x04, 0x80, 0xff, 0x00, 0x08,
+ 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x30, 0x80, 0x07, 0x00, 0x20,
+ 0x00, 0x03, 0x00, 0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0x0e, 0x00, 0x60,
+ 0x00, 0x10, 0x00, 0xe0, 0x00, 0x80, 0x07, 0xc0, 0x00, 0x80, 0x0f, 0xc0,
+ 0x00, 0x80, 0x3f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xc0, 0xff, 0x01,
+ 0x00, 0xc0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x01, 0x00, 0x80, 0xff, 0x01,
+ 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00,
+ 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
+}
+
+blt::bitmap define globe.7 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x80, 0x07, 0x00, 0x00, 0xfc, 0x1a, 0x00, 0x00, 0x7d, 0x02, 0x00,
+ 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00,
+ 0x00, 0xff, 0x03, 0x00, 0x00, 0xfe, 0x01, 0x20, 0x00, 0x1c, 0x01, 0x00,
+ 0x00, 0x1c, 0x00, 0x40, 0x00, 0x18, 0x00, 0x40, 0x00, 0x70, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x7c, 0x00,
+ 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, 0xfe, 0x0f,
+ 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xf8, 0x07,
+ 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf0, 0x01,
+ 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00};
+}
+
+blt::bitmap define globe.8 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0xfc, 0x25, 0x00, 0x00, 0xf8, 0x19, 0x00,
+ 0x00, 0xf8, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0x00,
+ 0x00, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x08, 0x00,
+ 0x00, 0xf0, 0x00, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x80, 0x03, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xe0, 0x03,
+ 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x3f,
+ 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0xc0, 0x1f,
+ 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0xc0, 0x07,
+ 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
+}
+
+blt::bitmap define globe.9 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x03, 0x00, 0x00, 0xfc, 0x27, 0x00, 0x00, 0xf0, 0x13, 0x00,
+ 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00, 0xe0, 0x7f, 0x00,
+ 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x47, 0x00,
+ 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0x00, 0x1f,
+ 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f,
+ 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7e,
+ 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x01,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00};
+}
+
+blt::bitmap define globe.10 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x06, 0x00, 0x00, 0xf4, 0x2f, 0x00, 0x00, 0xc8, 0x4f, 0x00,
+ 0x00, 0x80, 0xff, 0x01, 0x00, 0x80, 0xff, 0x01, 0x00, 0x80, 0xff, 0x01,
+ 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x3c, 0x00,
+ 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0xe0, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8,
+ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00};
+}
+
+blt::bitmap define globe.11 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x06, 0x00, 0x00, 0xec, 0x1f, 0x00, 0x00, 0x91, 0x9f, 0x00,
+ 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07,
+ 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf0, 0x01,
+ 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xe0,
+ 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0,
+ 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60,
+ 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x08,
+ 0x40, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00};
+}
+
+blt::bitmap define globe.12 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x04, 0x00, 0x00, 0xdc, 0x3f, 0x00, 0x00, 0x42, 0x7e, 0x00,
+ 0x00, 0x00, 0xf8, 0x03, 0x20, 0x00, 0xf0, 0x07, 0x10, 0x00, 0xf0, 0x0f,
+ 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x40,
+ 0x08, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+}
+
+blt::bitmap define globe.13 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x04, 0x00, 0x00, 0xbc, 0x3f, 0x00, 0x00, 0x01, 0x79, 0x00,
+ 0x80, 0x00, 0xe0, 0x03, 0x60, 0x00, 0xc0, 0x07, 0x10, 0x00, 0x80, 0x0f,
+ 0x00, 0x00, 0x80, 0x1f, 0x08, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1c,
+ 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00};
+}
+
+blt::bitmap define globe.14 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x03, 0xe6, 0x00,
+ 0x80, 0x01, 0xc0, 0x03, 0x60, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x0f,
+ 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x30,
+ 0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00};
+}
+
+blt::bitmap define globe.15 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3d, 0x00, 0x00, 0x27, 0xc8, 0x00,
+ 0x80, 0x13, 0x00, 0x03, 0xe0, 0x01, 0x00, 0x06, 0x70, 0x00, 0x00, 0x0c,
+ 0x10, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x20,
+ 0x0c, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00,
+ 0xf0, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00};
+}
+
+blt::bitmap define globe.16 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3b, 0x00, 0x00, 0x9f, 0xa0, 0x00,
+ 0x80, 0x4f, 0x00, 0x02, 0xe0, 0x0f, 0x00, 0x04, 0xf0, 0x01, 0x00, 0x08,
+ 0x70, 0x00, 0x00, 0x10, 0x38, 0x00, 0x00, 0x20, 0x3c, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x28, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00,
+ 0xe0, 0x07, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00,
+ 0xe0, 0x1f, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00};
+}
+
+blt::bitmap define globe.17 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x37, 0x00, 0x00, 0x3f, 0x42, 0x00,
+ 0x80, 0x3f, 0x01, 0x02, 0xe0, 0x1f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0xf0, 0x11, 0x00, 0x00, 0xf8, 0x04, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
+ 0x00, 0x3f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
+ 0x80, 0x7f, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00};
+}
+
+blt::bitmap define globe.18 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0x84, 0x00,
+ 0x80, 0xff, 0x04, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xf0, 0x9f, 0x00, 0x00,
+ 0xf0, 0x97, 0x00, 0x00, 0xf8, 0x27, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
+ 0xfc, 0x03, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x60, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x05, 0x00,
+ 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00,
+ 0x00, 0xfc, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, 0xfe, 0x03, 0x00,
+ 0x00, 0xfc, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00};
+}
+
+blt::bitmap define globe.19 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0x13, 0x00,
+ 0x80, 0xff, 0x13, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xf0, 0xff, 0x00, 0x00,
+ 0xf0, 0x9f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00,
+ 0xf8, 0x1f, 0x00, 0x00, 0xba, 0x07, 0x00, 0x00, 0x98, 0x23, 0x00, 0x00,
+ 0x08, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x09, 0x00, 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, 0x21, 0x0e, 0x00,
+ 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x09, 0x00,
+ 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf0, 0x1f, 0x00,
+ 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00};
+}
+
+blt::bitmap define globe.20 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0x07, 0x00,
+ 0x80, 0xff, 0x0f, 0x00, 0xe0, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0x13, 0x00,
+ 0xf0, 0xff, 0x10, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00,
+ 0xf4, 0xff, 0x00, 0x00, 0xe6, 0x1e, 0x00, 0x00, 0x62, 0x1c, 0x01, 0x00,
+ 0x20, 0x18, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00,
+ 0x01, 0xcc, 0x00, 0x00, 0x01, 0x68, 0x08, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00,
+ 0x00, 0x00, 0x7e, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x00,
+ 0x00, 0x80, 0xff, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00};
+}
+
+blt::bitmap define globe.21 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0x1f, 0x00,
+ 0x80, 0xff, 0xbf, 0x00, 0xe0, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x1f, 0x00,
+ 0xf8, 0xff, 0x17, 0x00, 0xf8, 0xff, 0x27, 0x00, 0xec, 0xff, 0x0f, 0x00,
+ 0x8c, 0xff, 0x07, 0x00, 0x9e, 0xf7, 0x00, 0x00, 0x0e, 0xe3, 0x00, 0x00,
+ 0x06, 0xc1, 0x00, 0x00, 0x06, 0x81, 0x10, 0x00, 0x03, 0x40, 0x04, 0x00,
+ 0x03, 0x20, 0x06, 0x00, 0x03, 0x40, 0x06, 0x00, 0x01, 0x80, 0x00, 0x03,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0xe0, 0x02,
+ 0x02, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03,
+ 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.22 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0x3f, 0x00,
+ 0x80, 0xff, 0x7f, 0x00, 0xe0, 0xff, 0xff, 0x00, 0xf0, 0xff, 0x7f, 0x00,
+ 0xf0, 0xff, 0x1f, 0x00, 0xe0, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x3f, 0x00,
+ 0x34, 0xfe, 0x3f, 0x00, 0x76, 0xbc, 0x07, 0x00, 0x36, 0x1c, 0x07, 0x00,
+ 0x0e, 0x08, 0x0e, 0x00, 0x1e, 0x08, 0x80, 0x00, 0x0f, 0x00, 0x02, 0x00,
+ 0x0f, 0x00, 0x20, 0x00, 0x07, 0x00, 0x36, 0x00, 0x07, 0x00, 0x04, 0x08,
+ 0x07, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x0b,
+ 0x16, 0x00, 0x80, 0x0f, 0x04, 0x00, 0xe0, 0x0f, 0x04, 0x00, 0xe0, 0x0f,
+ 0x08, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.23 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x80, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x01,
+ 0xe8, 0xff, 0xff, 0x00, 0xc0, 0xff, 0xff, 0x00, 0xfc, 0xfe, 0xff, 0x01,
+ 0xdc, 0xf2, 0xff, 0x01, 0xde, 0xe3, 0x3d, 0x00, 0xde, 0xe1, 0x38, 0x02,
+ 0x7e, 0x40, 0x70, 0x00, 0xfe, 0x40, 0x00, 0x04, 0x7f, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x30, 0x01, 0x3e, 0x00, 0xa0, 0x01, 0x1e, 0x00, 0x20, 0x20,
+ 0x1e, 0x00, 0x00, 0x20, 0x1c, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x3c,
+ 0x1c, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x00, 0x3f, 0x18, 0x00, 0x80, 0x3f,
+ 0x10, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.24 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x01, 0xc0, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0x03,
+ 0xe0, 0xff, 0xff, 0x03, 0x18, 0xff, 0xff, 0x03, 0xfc, 0xff, 0xff, 0x07,
+ 0x7c, 0x87, 0xff, 0x07, 0xfe, 0x1f, 0xef, 0x01, 0xfe, 0x0e, 0xc6, 0x01,
+ 0xfe, 0x01, 0x82, 0x03, 0xfe, 0x03, 0x02, 0x00, 0xff, 0x03, 0x00, 0x08,
+ 0xfc, 0x01, 0x80, 0x09, 0xfc, 0x00, 0x00, 0x0d, 0xfc, 0x00, 0x00, 0x00,
+ 0xf8, 0x00, 0x00, 0x80, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x20,
+ 0x78, 0x02, 0x00, 0x70, 0x70, 0x02, 0x00, 0x7c, 0x70, 0x00, 0x00, 0x3c,
+ 0x60, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.25 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0xfc, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0x07, 0xa0, 0xff, 0xff, 0x07,
+ 0x10, 0xff, 0xff, 0x07, 0x30, 0xf8, 0xff, 0x0f, 0xf8, 0xdf, 0xff, 0x1f,
+ 0xfc, 0x3b, 0xfc, 0x1f, 0xfc, 0xfb, 0x78, 0x07, 0xfe, 0x77, 0x30, 0x0e,
+ 0xfe, 0x1f, 0x30, 0x0c, 0xfe, 0x3f, 0x00, 0x48, 0xfe, 0x1f, 0x00, 0x00,
+ 0xf0, 0x0f, 0x00, 0x24, 0xf0, 0x07, 0x00, 0xa0, 0xf0, 0x07, 0x00, 0x08,
+ 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x27, 0x00, 0xc0,
+ 0xe0, 0x13, 0x00, 0x40, 0xc0, 0x13, 0x00, 0x70, 0xc0, 0x03, 0x00, 0x70,
+ 0x80, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.26 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0xe8, 0xff, 0x00,
+ 0x00, 0xfc, 0xff, 0x03, 0x00, 0xff, 0xff, 0x07, 0xc0, 0xfe, 0xff, 0x0f,
+ 0x40, 0xf0, 0xff, 0x1f, 0xe0, 0xe0, 0xff, 0x1f, 0xf0, 0xff, 0xfe, 0x3f,
+ 0xf8, 0xdf, 0xe1, 0x3f, 0xf8, 0xdf, 0xc7, 0x1b, 0xfc, 0xbf, 0x83, 0x19,
+ 0xfc, 0xff, 0x80, 0x30, 0xfc, 0xff, 0x01, 0x20, 0xf8, 0xff, 0x00, 0x00,
+ 0xc0, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0xe0, 0x80, 0x3f, 0x00, 0x20,
+ 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x80,
+ 0x80, 0x9f, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x40, 0x00, 0x0f, 0x00, 0x60,
+ 0x00, 0x0e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.27 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc4, 0x3f, 0x00, 0x00, 0xf0, 0xff, 0x00,
+ 0x00, 0xfe, 0xff, 0x03, 0x00, 0xfe, 0xff, 0x07, 0x00, 0xeb, 0xff, 0x0f,
+ 0x80, 0xc9, 0xff, 0x1f, 0x80, 0x07, 0xff, 0x3f, 0xc0, 0xff, 0xf7, 0x3f,
+ 0xe0, 0xff, 0x0e, 0x7f, 0xf0, 0xff, 0x3e, 0x6e, 0xf0, 0xff, 0x1d, 0x64,
+ 0xf0, 0xff, 0x07, 0x44, 0xf0, 0xff, 0x0f, 0x00, 0x60, 0xff, 0x0f, 0x00,
+ 0x00, 0xfe, 0x07, 0x40, 0x00, 0xfe, 0x03, 0x00, 0x01, 0xfc, 0x01, 0x00,
+ 0x01, 0xfc, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xfc, 0x09, 0x00,
+ 0x02, 0xfc, 0x08, 0x00, 0x00, 0xf8, 0x04, 0x00, 0x00, 0x78, 0x00, 0x40,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.28 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x3f, 0x00, 0x00, 0x40, 0xff, 0x00,
+ 0x00, 0xe8, 0xff, 0x03, 0x00, 0xf8, 0xff, 0x07, 0x00, 0x8c, 0xff, 0x0f,
+ 0x00, 0x06, 0xfe, 0x1f, 0x00, 0x1e, 0xf8, 0x3f, 0x00, 0xff, 0xbf, 0x3f,
+ 0x80, 0xff, 0x77, 0x7c, 0x80, 0xff, 0xff, 0x79, 0xc0, 0xff, 0xef, 0x10,
+ 0xc0, 0xff, 0x3f, 0x90, 0xc0, 0xff, 0x7f, 0x00, 0x81, 0xfb, 0x7f, 0x00,
+ 0x01, 0xf0, 0x3f, 0x00, 0x01, 0xf0, 0x1f, 0x00, 0x03, 0xe0, 0x1f, 0x00,
+ 0x07, 0xe0, 0x0f, 0x00, 0x02, 0xc0, 0x1f, 0x00, 0x02, 0xe0, 0x5f, 0x00,
+ 0x06, 0xe0, 0x47, 0x00, 0x04, 0xc0, 0x27, 0x00, 0x04, 0xc0, 0x03, 0x00,
+ 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1f, 0x00};
+}
+
+blt::bitmap define globe.29 {
+#define globe_width 32
+#define globe_height 32
+static char globe_bits[] = {
+ 0x00, 0x40, 0x01, 0x00, 0x00, 0x0c, 0x3f, 0x00, 0x00, 0x80, 0xfd, 0x00,
+ 0x00, 0xa0, 0xff, 0x03, 0x20, 0xe0, 0xff, 0x07, 0x00, 0x30, 0xfd, 0x0f,
+ 0x00, 0x10, 0xf4, 0x1f, 0x00, 0xf8, 0xc0, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
+ 0x00, 0xfc, 0xbf, 0x73, 0x00, 0xfe, 0xff, 0x67, 0x00, 0xfe, 0x7f, 0x47,
+ 0x00, 0xfe, 0xff, 0x41, 0x00, 0xfe, 0xff, 0x03, 0x01, 0xdc, 0xff, 0x03,
+ 0x03, 0x00, 0xff, 0x01, 0x07, 0x80, 0xff, 0x00, 0x0f, 0x00, 0xff, 0x00,
+ 0x1f, 0x00, 0x7e, 0x00, 0x0e, 0x00, 0xfe, 0x00, 0x0e, 0x00, 0xff, 0x02,
+ 0x0e, 0x00, 0x3f, 0x01, 0x0c, 0x00, 0x3e, 0x01, 0x0c, 0x00, 0x1e, 0x00,
+ 0x08, 0x00, 0x1c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00};
+}
diff --git a/blt/demos/scripts/graph1.tcl b/blt/demos/scripts/graph1.tcl
new file mode 100644
index 00000000000..3048a6ef67e
--- /dev/null
+++ b/blt/demos/scripts/graph1.tcl
@@ -0,0 +1,72 @@
+
+set X {
+ 2.00000e-01 4.00000e-01 6.00000e-01 8.00000e-01 1.00000e+00
+ 1.20000e+00 1.40000e+00 1.60000e+00 1.80000e+00 2.00000e+00
+ 2.20000e+00 2.40000e+00 2.60000e+00 2.80000e+00 3.00000e+00
+ 3.20000e+00 3.40000e+00 3.60000e+00 3.80000e+00 4.00000e+00
+ 4.20000e+00 4.40000e+00 4.60000e+00 4.80000e+00 5.00000e+00
+}
+
+set Y1 {
+ 4.07008e+01 7.95658e+01 1.16585e+02 1.51750e+02 1.85051e+02
+ 2.16479e+02 2.46024e+02 2.73676e+02 2.99427e+02 3.23267e+02
+ 3.45187e+02 3.65177e+02 3.83228e+02 3.99331e+02 4.13476e+02
+ 4.25655e+02 4.35856e+02 4.44073e+02 4.50294e+02 4.54512e+02
+ 4.56716e+02 4.57596e+02 4.58448e+02 4.59299e+02 4.60151e+02
+}
+
+set Y2 {
+ 5.14471e-00 2.09373e+01 2.84608e+01 3.40080e+01 3.75691e+01
+ 3.91345e+01 3.92706e+01 3.93474e+01 3.94242e+01 3.95010e+01
+ 3.95778e+01 3.96545e+01 3.97313e+01 3.98081e+01 3.98849e+01
+ 3.99617e+01 4.00384e+01 4.01152e+01 4.01920e+01 4.02688e+01
+ 4.03455e+01 4.04223e+01 4.04990e+01 4.05758e+01 4.06526e+01
+}
+
+set Y3 {
+ 2.61825e+01 5.04696e+01 7.28517e+01 9.33192e+01 1.11863e+02
+ 1.28473e+02 1.43140e+02 1.55854e+02 1.66606e+02 1.75386e+02
+ 1.82185e+02 1.86994e+02 1.89802e+02 1.90683e+02 1.91047e+02
+ 1.91411e+02 1.91775e+02 1.92139e+02 1.92503e+02 1.92867e+02
+ 1.93231e+02 1.93595e+02 1.93958e+02 1.94322e+02 1.94686e+02
+}
+
+set configOptions {
+ Axis.TitleFont {Times 18 bold}
+ Element.Pixels 6
+ Element.Smooth catrom
+ Legend.ActiveBackground khaki2
+ Legend.ActiveRelief sunken
+ Legend.Background ""
+ Title "A Simple X-Y Graph"
+ activeLine.Color yellow4
+ activeLine.Fill yellow
+ background khaki3
+ line1.Color red4
+ line1.Fill red1
+ line1.Symbol circle
+ line2.Color purple4
+ line2.Fill purple1
+ line2.Symbol arrow
+ line3.Color green4
+ line3.Fill green1
+ line3.Symbol triangle
+ x.Descending no
+ x.Loose no
+ x.Title "X Axis Label"
+ y.Rotate 90
+ y.Title "Y Axis Label"
+}
+
+set resource [string trimleft $graph .]
+foreach { option value } $configOptions {
+ option add *$resource.$option $value
+}
+$graph element create line1 -x $X -y $Y2
+$graph element create line2 -x $X -y $Y3
+$graph element create line3 -x $X -y $Y1
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
diff --git a/blt/demos/scripts/graph2.tcl b/blt/demos/scripts/graph2.tcl
new file mode 100644
index 00000000000..3ee9e1af7bc
--- /dev/null
+++ b/blt/demos/scripts/graph2.tcl
@@ -0,0 +1,138 @@
+option add *HighlightThickness 0
+option add *Tile bgTexture
+option add *Button.Tile ""
+
+image create photo bgTexture -file ./images/chalk.gif
+
+set configOptions [subst {
+ InvertXY no
+ Axis.TickFont { Helvetica 14 bold }
+ Axis.TitleFont { Helvetica 12 bold }
+ BorderWidth 2
+ Element.Pixels 8
+ Element.ScaleSymbols true
+ Element.Smooth cubic
+ Font { Helvetica 18 bold }
+ Foreground white
+ Legend.ActiveBorderWidth 2
+ Legend.ActiveRelief raised
+ Legend.Anchor ne
+ Legend.BorderWidth 0
+ Legend.Font { Helvetica 34 }
+ Legend.Foreground orange
+ #Legend.Position plotarea
+ Legend.Hide yes
+ Legend.Relief flat
+ Postscript.Preview yes
+ Relief raised
+ Shadow { navyblue 2 }
+ Title "Bitmap Symbols"
+ degrees.Command [namespace current]::FormatAxisLabel
+ degrees.LimitsFormat "Deg=%g"
+ degrees.Subdivisions 0
+ degrees.Title "Degrees"
+ degrees.stepSize 90
+ temp.LimitsFormat "Temp=%g"
+ temp.Title "Temperature"
+ y.Color purple2
+ y.LimitsFormat "Y=%g"
+ y.Rotate 90
+ y.Title "Y"
+ y.loose no
+ y2.Color magenta3
+ y2.Hide no
+ xy2.Rotate 270
+ y2.Rotate 0
+ y2.Title "Y2"
+ y2.LimitsFormat "Y2=%g"
+ x2.LimitsFormat "x2=%g"
+}]
+
+set resource [string trimleft $graph .]
+foreach { option value } $configOptions {
+ option add *$resource.$option $value
+}
+
+proc FormatAxisLabel {graph x} {
+ format "%d%c" [expr int($x)] 0xB0
+}
+
+set max -1.0
+set step 0.2
+
+set letters { A B C D E F G H I J K L }
+set count 0
+for { set level 30 } { $level <= 100 } { incr level 10 } {
+ set color [format "#dd0d%0.2x" [expr round($level*2.55)]]
+ set pen "pen$count"
+ set symbol "symbol$count"
+ bitmap compose $symbol [lindex $letters $count] \
+ -font -*-helvetica-medium-r-*-*-34-*-*-*-*-*-*-*
+ $graph pen create $pen \
+ -color $color \
+ -symbol $symbol \
+ -fill "" \
+ -pixels 13
+ set min $max
+ set max [expr $max + $step]
+ lappend styles "$pen $min $max"
+ incr count
+}
+
+$graph axis create temp \
+ -color lightgreen \
+ -title Temp
+$graph axis create degrees \
+ -rotate 90
+$graph xaxis use degrees
+
+set tcl_precision 15
+set pi1_2 [expr 3.14159265358979323846/180.0]
+
+vector create w x sinX cosX radians
+x seq -360.0 360.0 10.0
+#x seq -360.0 -180.0 30.0
+radians expr { x * $pi1_2 }
+sinX expr sin(radians)
+cosX expr cos(radians)
+cosX dup w
+vector destroy radians
+
+vector create xh xl yh yl
+set pct [expr ($cosX(max) - $cosX(min)) * 0.025]
+yh expr {cosX + $pct}
+yl expr {cosX - $pct}
+set pct [expr ($x(max) - $x(min)) * 0.025]
+xh expr {x + $pct}
+xl expr {x - $pct}
+
+$graph element create line3 \
+ -color green4 \
+ -fill green \
+ -label "cos(x)" \
+ -mapx degrees \
+ -styles $styles \
+ -weights w \
+ -x x \
+ -y cosX \
+ -yhigh yh -ylow yl
+
+$graph element create line1 \
+ -color orange \
+ -outline black \
+ -fill orange \
+ -fill yellow \
+ -label "sin(x)" \
+ -linewidth 3 \
+ -mapx degrees \
+ -pixels 6m \
+ -symbol "@bitmaps/hobbes.xbm @bitmaps/hobbes_mask.xbm" \
+ -x x \
+ -y sinX
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+#Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+Blt_PrintKey $graph
+
diff --git a/blt/demos/scripts/graph3.tcl b/blt/demos/scripts/graph3.tcl
new file mode 100644
index 00000000000..d1fa2488d83
--- /dev/null
+++ b/blt/demos/scripts/graph3.tcl
@@ -0,0 +1,78 @@
+proc FormatAxisLabel {graph x} {
+ return "[expr int($x)]\260"
+}
+
+set configOptions [subst {
+ Axis.Hide no
+ Axis.Limits "%g"
+ Axis.TickFont { helvetica 12 bold }
+ Axis.TitleFont { helvetica 12 bold }
+ BorderWidth 1
+ Element.Pixels 1.75m
+ Element.ScaleSymbols yes
+ Font { helvetica 23 bold }
+ Legend.ActiveBorderWidth 2
+ Legend.ActiveRelief raised
+ Legend.Anchor ne
+ Legend.BorderWidth 0
+ Legend.Font { Helvetica 24 }
+ Legend.Position plotarea
+ Relief sunken
+ Title "Sine and Cosine Functions"
+ x.Command [namespace current]::FormatAxisLabel
+ x.StepSize 90
+ x.Subdivisions 0
+ x.Title "X"
+ y.Color purple2
+ y.Loose no
+ y.Title "Y"
+ y.rotate 90
+ y2.color magenta3
+}]
+
+set resName [string trimleft $graph .]
+foreach { option value } $configOptions {
+ option add *$resName.$option $value
+}
+
+$graph configure -leftvar changed
+
+set tcl_precision 15
+set pi1_2 [expr 3.14159265358979323846/180.0]
+
+vector create x sinX cosX -variable ""
+x seq -360 360 5
+sinX expr { sin(x*$pi1_2) }
+cosX expr { cos(x*$pi1_2) }
+
+$graph element create line1 \
+ -label "sin(x)" \
+ -fill orange \
+ -color black \
+ -x x \
+ -y sinX
+$graph element create line2 \
+ -label "cos(x)" \
+ -color yellow4 \
+ -fill yellow \
+ -x x \
+ -y cosX
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+#Blt_PrintKey $graph
+
+$graph marker create bitmap \
+ -name bg \
+ -coords "-360 -1 360 1" \
+ -bitmap @bitmaps/greenback.xbm \
+ -bg darkseagreen1 \
+ -fg darkseagreen3 \
+ -under yes \
+ -rotate 45
+$graph postscript configure \
+ -maxpect yes \
+ -landscape yes
+
diff --git a/blt/demos/scripts/graph5.tcl b/blt/demos/scripts/graph5.tcl
new file mode 100644
index 00000000000..11b4d05e34e
--- /dev/null
+++ b/blt/demos/scripts/graph5.tcl
@@ -0,0 +1,65 @@
+
+set configOptions {
+ Element.LineWidth 0
+ Element.Pixels 0.7c
+ Element.ScaleSymbols true
+ Font { Courier 18 bold}
+ Height 4i
+ Legend.ActiveRelief raised
+ Legend.Font { Courier 14 }
+ Legend.padY 0
+ Title "Element Symbol Types"
+ Width 5i
+}
+set resName [string trimleft $graph .]
+foreach { option value } $configOptions {
+ option add *$resName.$option $value
+}
+
+vector xValues
+xValues set {
+ 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
+}
+
+for { set i 0 } { $i < 10 } { incr i } {
+ set vecName "y${i}"
+ vector ${vecName}(10)
+ $vecName variable x
+ set x(:) [expr $i*50.0+10.0]
+}
+
+set attributes {
+ none "None" red red4 y0
+ circle "Circle" yellow yellow4 y2
+ cross "Cross" cyan cyan4 y6
+ diamond "Diamond" green green4 y3
+ plus "Plus" magenta magenta4 y9
+ splus "Splus" Purple purple4 y7
+ scross "Scross" red red4 y8
+ square "Square" orange orange4 y1
+ triangle "Triangle" blue blue4 y4
+ "@bitmaps/hobbes.xbm @bitmaps/hobbes_mask.xbm"
+ "Bitmap" yellow black y5
+}
+
+set count 0
+foreach { symbol label fill color yVec } $attributes {
+ $graph element create line${count} \
+ -label $label \
+ -symbol $symbol \
+ -color $color \
+ -fill $fill \
+ -x xValues \
+ -y $yVec
+ incr count
+}
+$graph element configure line0 \
+ -dashes { 2 4 2 } \
+ -linewidth 2
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
+Blt_PrintKey $graph
+
diff --git a/blt/demos/scripts/graph8.tcl b/blt/demos/scripts/graph8.tcl
new file mode 100644
index 00000000000..86f94d7c6ee
--- /dev/null
+++ b/blt/demos/scripts/graph8.tcl
@@ -0,0 +1,85 @@
+
+set X {
+ 2.00000e-01 4.00000e-01 6.00000e-01 8.00000e-01 1.00000e+00
+ 1.20000e+00 1.40000e+00 1.60000e+00 1.80000e+00 2.00000e+00
+ 2.20000e+00 2.40000e+00 2.60000e+00 2.80000e+00 3.00000e+00
+ 3.20000e+00 3.40000e+00 3.60000e+00 3.80000e+00 4.00000e+00
+ 4.20000e+00 4.40000e+00 4.60000e+00 4.80000e+00 5.00000e+00
+}
+
+set Y1 {
+ 1.14471e+01 2.09373e+01 2.84608e+01 3.40080e+01 3.75691e+01
+ 3.91345e+01 3.92706e+01 3.93474e+01 3.94242e+01 3.95010e+01
+ 3.95778e+01 3.96545e+01 3.97313e+01 3.98081e+01 3.98849e+01
+ 3.99617e+01 4.00384e+01 4.01152e+01 4.01920e+01 4.02688e+01
+ 4.03455e+01 4.04223e+01 4.04990e+01 4.05758e+01 4.06526e+01
+}
+
+set Y2 {
+ 2.61825e+01 5.04696e+01 7.28517e+01 9.33192e+01 1.11863e+02
+ 1.28473e+02 1.43140e+02 1.55854e+02 1.66606e+02 1.75386e+02
+ 1.82185e+02 1.86994e+02 1.89802e+02 1.90683e+02 1.91047e+02
+ 1.91411e+02 1.91775e+02 1.92139e+02 1.92503e+02 1.92867e+02
+ 1.93231e+02 1.93595e+02 1.93958e+02 1.94322e+02 1.94686e+02
+}
+
+set Y3 {
+ 4.07008e+01 7.95658e+01 1.16585e+02 1.51750e+02 1.85051e+02
+ 2.16479e+02 2.46024e+02 2.73676e+02 2.99427e+02 3.23267e+02
+ 3.45187e+02 3.65177e+02 3.83228e+02 3.99331e+02 4.13476e+02
+ 4.25655e+02 4.35856e+02 4.44073e+02 4.50294e+02 4.54512e+02
+ 4.56716e+02 4.57596e+02 4.58448e+02 4.59299e+02 4.60151e+02
+}
+
+
+proc FormatLabel { w value } {
+ return $value
+}
+
+#option add *Graph.aspect 1.25
+option add *Graph.title "A Simple X-Y Graph"
+option add *Graph.x.loose yes
+option add *Graph.x.title "X Axis Label"
+option add *Graph.y.title "Y Axis Label"
+option add *Graph.y.rotate 90
+option add *Graph.y.logScale yes
+option add *Graph.y.loose no
+option add *Graph.Axis.titleFont {Times 18 bold}
+
+option add *Legend.activeRelief sunken
+option add *Legend.background ""
+option add *Legend.activeBackground khaki2
+option add *Graph.background brown
+option add *Element.xData $X
+option add *activeLine.Color yellow4
+option add *activeLine.Fill yellow
+option add *Element.smooth natural
+option add *Element.pixels 6
+option add *Element.scaleSymbols yes
+
+option add *Graph.line1.symbol circle
+option add *Graph.line1.color red4
+option add *Graph.line1.fill red1
+
+option add *Graph.line2.symbol square
+option add *Graph.line2.color purple4
+option add *Graph.line2.fill purple1
+
+option add *Graph.line3.symbol triangle
+option add *Graph.line3.color green4
+option add *Graph.line3.fill green1
+
+$graph configure \
+ -width 4i \
+ -height 5i
+$graph element create line1 \
+ -ydata $Y2
+$graph element create line2 \
+ -ydata $Y3
+$graph element create line3 \
+ -ydata $Y1
+
+Blt_ZoomStack $graph
+Blt_Crosshairs $graph
+Blt_ActiveLegend $graph
+Blt_ClosestPoint $graph
diff --git a/blt/demos/scripts/page.tcl b/blt/demos/scripts/page.tcl
new file mode 100755
index 00000000000..c00cbc0339a
--- /dev/null
+++ b/blt/demos/scripts/page.tcl
@@ -0,0 +1,131 @@
+#!/usr/local/bin/tclsh
+
+array set page "
+ rows 2
+ columns 2
+ padx 0.5
+ pady 0.5
+ width 8.5
+ height 11
+ gutter 0.25
+"
+
+proc Pica { dist } {
+ expr $dist * 72.0
+}
+
+# ------------------------------------------------------------------
+#
+# TileFiles
+#
+# Tiles graph postscript files together in a pre-defined
+# grid.
+#
+# Arguments:
+# outFile -- Resulting tiled PostScript output file.
+# args -- Names of input graph PostScript files.
+#
+# ------------------------------------------------------------------
+
+proc TileFiles { outFile args } {
+ global page
+
+ set row 0
+ set column 0
+
+
+ set padx [Pica $page(padx)]
+ set pady [Pica $page(padx)]
+ set width [Pica $page(width)]
+ set height [Pica $page(height)]
+ set gutter [Pica $page(gutter)]
+
+ set totalGutters [expr $gutter * ($page(columns) - 1)]
+ set w [expr $width - (2 * $padx) - $totalGutters]
+ set totalGutters [expr $gutter * ($page(rows) - 1)]
+ set h [expr $height - (2 * $pady) - $totalGutters]
+
+ set cellWidth [expr double($w) / $page(columns)]
+ set cellHeight [expr double($h) / $page(rows)]
+
+ set out [open $outFile "w"]
+
+ puts $out "%!PS-Adobe-3.0 EPSF-3.0"
+ puts $out "%%Pages: 1"
+ puts $out "%%Title: (Graph tiler)"
+ puts $out "%%DocumentNeededResources: font Helvetica Courier"
+ puts $out "%%CreationDate: [clock format [clock seconds]]"
+ puts $out "%%EndComments"
+
+ puts $out "/showsheet { showpage } bind def"
+ puts $out "/showpage { } def"
+ puts $out "$padx $pady translate"
+
+ set first {}
+ foreach inFile $args {
+ set in [open $inFile "r"]
+
+ # Warning, this is assuming that the BoundingBox is in the first
+ # twenty lines of the graph's PostScript.
+
+ for { set count 0 } { $count < 20 } { incr count } {
+ gets $in line
+ if { [string match "%%BoundingBox:*" $line] } {
+ set bbox $line
+ break;
+ }
+ append first "$line\n"
+ if { [eof $in] } {
+ break
+ }
+ }
+ if { ![info exists bbox] } {
+ error "can't find \"%%BoundingBox:\" line"
+ }
+ set n [scan $bbox "%%%%BoundingBox: %d %d %d %d" x1 y1 x2 y2]
+ if { $n != 4} {
+ error "Bad bounding box line \"$bbox\""
+ }
+
+ set rest [read $in]
+ close $in
+
+ set x [expr ($cellWidth + $gutter) * $column]
+ set y [expr ($cellHeight + $gutter) * $row]
+
+ set w [expr abs($x2 - $x1)]
+ set h [expr abs($y2 - $y1)]
+
+ set scaleX [expr $cellWidth / $w]
+ set scaleY [expr $cellHeight / $h]
+ if { $scaleX > $scaleY } {
+ set scale $scaleY
+ } else {
+ set scale $scaleX
+ }
+ puts $out "% "
+ puts $out "% Tiling \"$inFile\" at ($row,$column)"
+ puts $out "% "
+ puts $out "gsave"
+ puts $out "$x $y translate"
+ puts $out "$scale $scale scale"
+ puts $out "-$x1 -$y1 translate"
+ puts $out $first
+ puts $out $rest
+ puts $out "grestore"
+ incr column
+ if { $column >= $page(columns) } {
+ set column 0
+ incr row
+ }
+ }
+ puts $out "showsheet"
+ close $out
+}
+
+eval TileFiles $argv
+
+
+
+
+
diff --git a/blt/demos/scripts/patterns.tcl b/blt/demos/scripts/patterns.tcl
new file mode 100644
index 00000000000..e7f281ddfb5
--- /dev/null
+++ b/blt/demos/scripts/patterns.tcl
@@ -0,0 +1,16 @@
+blt::bitmap define pattern1 { {4 4} {01 02 04 08} }
+blt::bitmap define pattern2 { {4 4} {08 04 02 01} }
+blt::bitmap define pattern3 { {2 2} {01 02 } }
+blt::bitmap define pattern4 { {4 4} {0f 00 00 00} }
+blt::bitmap define pattern5 { {4 4} {01 01 01 01} }
+blt::bitmap define pattern6 { {2 2} {01 00 } }
+blt::bitmap define pattern7 { {4 4} {0f 01 01 01} }
+blt::bitmap define pattern8 { {8 8} {ff 00 ff 00 ff 00 ff 00 } }
+blt::bitmap define pattern9 { {4 4} {03 03 0c 0c} }
+blt::bitmap define hobbes { {25 25} {
+ 00 00 00 00 00 00 00 00 00 c0 03 00 78 e0 07 00 fc f8 07 00 cc 07 04 00
+ 0c f0 0b 00 7c 1c 06 00 38 00 00 00 e0 03 10 00 e0 41 11 00 20 40 11 00
+ e0 07 10 00 e0 c1 17 00 10 e0 2f 00 20 e0 6f 00 18 e0 2f 00 20 c6 67 00
+ 18 84 2b 00 20 08 64 00 70 f0 13 00 80 01 08 00 00 fe 07 00 00 00 00 00
+ 00 00 00 00 }
+}
diff --git a/blt/demos/scripts/ps.tcl b/blt/demos/scripts/ps.tcl
new file mode 100644
index 00000000000..2c1263a2d27
--- /dev/null
+++ b/blt/demos/scripts/ps.tcl
@@ -0,0 +1,767 @@
+#bltdebug 100
+
+array set cursors {
+ w left_side
+ e right_side
+ n top_side
+ s bottom_side
+ sw bottom_left_corner
+ ne top_right_corner
+ se bottom_right_corner
+ nw top_left_corner
+}
+
+
+array set pageInfo {
+ gripSize 8
+ scale 0.25
+ radioFont -*-helvetica-medium-r-*-*-11-120-*-*-*-*-*-*
+ labelFont -*-helvetica-bold-r-*-*-12-120-*-*-*-*-*-*
+ printCmd "nlp -d2a211"
+ printFile "out.ps"
+}
+
+
+proc SetUnits { units } {
+ global pageInfo
+ switch -glob $units {
+ "i*" { set pageInfo(uscale) [winfo fpixels . 1i] }
+ "c*" { set pageInfo(uscale) [winfo fpixels . 1c] }
+ default { error "unknown unit \"$units\"" }
+ }
+ set pageInfo(units) [string index $units 0]
+}
+
+
+proc ConvertUnits { value } {
+ global pageInfo
+ set value [expr double($value) / $pageInfo(uscale)]
+ return [format "%.1f%s" $value $pageInfo(units)]
+}
+
+
+proc SetPaperSize { unit } {
+ global pageInfo
+ SetUnits $unit
+ set pageInfo(-paperwidth) [lindex $pageInfo(paperSize) 0]
+ set pageInfo(-paperheight) [lindex $pageInfo(paperSize) 1]
+ ApplyPs
+}
+
+proc SetCanvasSize { canvas width height } {
+ global pageInfo
+
+ set width [winfo pixels . $width]
+ set height [winfo pixels . $height]
+ $canvas configure -width $width -height $height
+}
+
+proc SetCanvasOrientation { canvas } {
+ global pageInfo
+ set width $pageInfo(paperWidth)
+ set height $pageInfo(paperHeight)
+ SetCanvasSize $canvas $width $height
+}
+
+
+proc GetPsOptions { graph } {
+ global pageInfo
+
+ foreach opt [$graph postscript configure] {
+ set pageInfo([lindex $opt 0]) [lindex $opt 4]
+ }
+}
+
+proc SetOutline { canvas } {
+ global pageInfo
+ foreach var { gripSize xMin yMin xMax yMax } {
+ set $var $pageInfo($var)
+ }
+ set xMid [expr ($xMax + $xMin - $gripSize) * 0.5]
+ set yMid [expr ($yMax + $yMin - $gripSize) * 0.5]
+ $canvas coords image $xMin $yMin
+ $canvas itemconfigure image \
+ -width [expr $xMax - $xMin] -height [expr $yMax - $yMin]
+ $canvas coords nw \
+ $xMin $yMin [expr $xMin + $gripSize] [expr $yMin + $gripSize]
+ $canvas coords se \
+ [expr $xMax - $gripSize] [expr $yMax - $gripSize] $xMax $yMax
+ $canvas coords ne \
+ [expr $xMax - $gripSize] [expr $yMin + $gripSize] $xMax $yMin
+ $canvas coords sw \
+ $xMin $yMax [expr $xMin + $gripSize] [expr $yMax - $gripSize]
+ SetCanvasOrientation $canvas
+ $canvas coords n \
+ $xMid $yMin [expr $xMid + $gripSize] [expr $yMin + $gripSize]
+ $canvas coords s \
+ $xMid [expr $yMax - $gripSize] [expr $xMid + $gripSize] $yMax
+ $canvas coords e \
+ [expr $xMax - $gripSize] $yMid $xMax [expr $yMid + $gripSize]
+ $canvas coords w \
+ $xMin $yMid [expr $xMin + $gripSize] [expr $yMid + $gripSize]
+}
+
+proc CreateOutline { canvas } {
+ global pageInfo
+ foreach var { gripSize xMin yMin xMax yMax } {
+ set $var $pageInfo($var)
+ }
+ if { ![bitmap exists pattern8] } {
+ bitmap define pattern8 { {8 8} {ff 00 ff 00 ff 00 ff 00 } }
+ }
+ $canvas create eps $xMin $yMin \
+ -tags "outline image" \
+ -width [expr $xMax - $xMin] \
+ -height [expr $yMax - $yMin]
+
+ $canvas bind image <ButtonPress-1> "StartMove $canvas %x %y"
+ $canvas bind image <B1-Motion> "MoveOutline $canvas %x %y"
+ $canvas bind image <ButtonRelease-1> "EndMove $canvas"
+
+ $canvas bind image <Shift-B1-Motion> "ConstrainMoveOutline $canvas %x %y"
+ $canvas bind image <Enter> "EnterImage $canvas"
+ $canvas bind image <Leave> "LeaveImage $canvas"
+ focus $canvas
+ $canvas create rectangle \
+ $xMin $yMin [expr $xMin + $gripSize] [expr $yMin + $gripSize] \
+ -tags "outline grip nw"
+ $canvas create rectangle \
+ [expr $xMax - $gripSize] [expr $yMax - $gripSize] $xMax $yMax \
+ -tags "outline grip se"
+ $canvas create rectangle \
+ [expr $xMax - $gripSize] [expr $yMin + $gripSize] $xMax $yMin \
+ -tags "outline grip ne"
+ $canvas create rectangle \
+ $xMin $yMax [expr $xMin + $gripSize] [expr $yMax - $gripSize] \
+ -tags "outline grip sw"
+
+ set xMid [expr ($xMax + $xMin - $gripSize) * 0.5]
+ set yMid [expr ($yMax + $yMin - $gripSize) * 0.5]
+ $canvas create rectangle \
+ $xMid $yMin [expr $xMid + $gripSize] [expr $yMin + $gripSize] \
+ -tags "outline grip n"
+ $canvas create rectangle \
+ $xMid [expr $yMax - $gripSize] [expr $xMid + $gripSize] $yMax \
+ -tags "outline grip s"
+ $canvas create rectangle \
+ [expr $xMax - $gripSize] $yMid $xMax [expr $yMid + $gripSize] \
+ -tags "outline grip e"
+ $canvas create rectangle \
+ $xMin $yMid [expr $xMin + $gripSize] [expr $yMid + $gripSize] \
+ -tags "outline grip w"
+ foreach grip { e w s n sw ne se nw } {
+ $canvas bind $grip <ButtonPress-1> "StartResize %W $grip %x %y"
+ $canvas bind $grip <B1-Motion> "ResizeOutline %W %x %y"
+ $canvas bind $grip <ButtonRelease-1> "EndResize %W $grip %x %y"
+ $canvas bind $grip <Enter> "EnterGrip %W $grip %x %y"
+ $canvas bind $grip <Leave> "LeaveGrip %W $grip"
+ }
+ $canvas raise grip
+ $canvas itemconfigure grip -fill red -outline black
+
+ set pageInfo(image) [image create photo]
+ $pageInfo(graph) snap $pageInfo(image)
+ $canvas itemconfigure image -image $pageInfo(image)
+}
+
+
+proc EnterImage { canvas } {
+ global cursors
+ global pageInfo
+ bind $canvas <KeyPress-Left> {
+ MoveOutline %W [expr $pageInfo(lastX) - 1] $pageInfo(lastY)
+ }
+ bind $canvas <KeyPress-Right> {
+ MoveOutline %W [expr $pageInfo(lastX) + 1] $pageInfo(lastY)
+ }
+ bind $canvas <KeyPress-Up> {
+ MoveOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) - 1]
+ }
+ bind $canvas <KeyPress-Down> {
+ MoveOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) + 1]
+ }
+ focus $canvas
+ $canvas configure -cursor fleur
+ set pageInfo(lastX) 0
+ set pageInfo(lastY) 0
+}
+
+
+proc LeaveImage { canvas } {
+ bind $canvas <KeyPress-Left> ""
+ bind $canvas <KeyPress-Right> ""
+ bind $canvas <KeyPress-Up> ""
+ bind $canvas <KeyPress-Down> ""
+ $canvas configure -cursor ""
+}
+
+proc EnterGrip { canvas grip x y } {
+ global pageInfo
+ $canvas itemconfigure $grip -fill blue -outline black
+ set pageInfo(grip) $grip
+ global cursors
+ bind $canvas <KeyPress-Left> {
+ ResizeOutline %W [expr $pageInfo(lastX) - 1] $pageInfo(lastY)
+ }
+ bind $canvas <KeyPress-Right> {
+ ResizeOutline %W [expr $pageInfo(lastX) + 1] $pageInfo(lastY)
+ }
+ bind $canvas <KeyPress-Up> {
+ ResizeOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) - 1]
+ }
+ bind $canvas <KeyPress-Down> {
+ ResizeOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) + 1]
+ }
+ focus $canvas
+ $canvas configure -cursor $cursors($grip)
+ set pageInfo(lastX) $x
+ set pageInfo(lastY) $y
+}
+
+proc LeaveGrip { canvas grip } {
+ $canvas itemconfigure $grip -fill red -outline black
+ bind $canvas <KeyPress-Left> ""
+ bind $canvas <KeyPress-Right> ""
+ bind $canvas <KeyPress-Up> ""
+ bind $canvas <KeyPress-Down> ""
+ $canvas configure -cursor ""
+}
+
+proc StartMove { canvas x y } {
+ global pageInfo
+ set pageInfo(lastX) $x
+ set pageInfo(lastY) $y
+ set pageInfo(direction) "undecided"
+ $canvas configure -cursor fleur
+}
+
+proc MoveOutline { canvas x y } {
+ global pageInfo
+ $canvas move outline [expr $x - $pageInfo(lastX)] [expr $y - $pageInfo(lastY)]
+ set pageInfo(lastX) $x
+ set pageInfo(lastY) $y
+}
+
+proc ConstrainMoveOutline { canvas x y } {
+ global pageInfo
+
+ set dx [expr $x - $pageInfo(lastX)]
+ set dy [expr $y - $pageInfo(lastY)]
+
+ if { $pageInfo(direction) == "undecided" } {
+ if { abs($dx) > abs($dy) } {
+ set pageInfo(direction) x
+ $canvas configure -cursor sb_h_double_arrow
+ } else {
+ set pageInfo(direction) y
+ $canvas configure -cursor sb_v_double_arrow
+ }
+ }
+ switch $pageInfo(direction) {
+ x { set dy 0 ; set pageInfo(lastX) $x }
+ y { set dx 0 ; set pageInfo(lastY) $y }
+ }
+ $canvas move outline $dx $dy
+}
+
+proc EndMove { canvas } {
+ $canvas configure -cursor ""
+
+ set coords [$canvas coords image]
+ set x [lindex $coords 0]
+ set y [lindex $coords 1]
+ set w [$canvas itemcget image -width]
+ set h [$canvas itemcget image -height]
+
+ global pageInfo
+ set pageInfo(xMin) $x
+ set pageInfo(xMin) $y
+ set pageInfo(xMax) [expr $x + $w]
+ set pageInfo(yMax) [expr $y + $h]
+
+ global pageInfo
+ set pageInfo(-padx) [list $pageInfo(xMin) [expr $pageInfo(paperWidth) - $pageInfo(xMax)]]
+ set pageInfo(-pady) [list $pageInfo(yMin) [expr $pageInfo(paperHeight) - $pageInfo(yMax)]]
+}
+
+proc StartResize { canvas grip x y } {
+ global pageInfo
+ $canvas itemconfigure image -quick yes
+ set pageInfo(grip) $grip
+ $canvas itemconfigure $grip -fill red -outline black
+ $canvas raise grip
+ global cursors
+ $canvas configure -cursor $cursors($grip)
+ set pageInfo(lastX) $x
+ set pageInfo(lastY) $y
+}
+
+proc EndResize { canvas grip x y } {
+ $canvas itemconfigure image -quick no
+ ResizeOutline $canvas $x $y
+ $canvas itemconfigure $grip -fill "" -outline ""
+ $canvas configure -cursor ""
+}
+
+proc ResizeOutline { canvas x y } {
+ global pageInfo
+
+ foreach var { gripSize xMin yMin xMax yMax } {
+ set $var $pageInfo($var)
+ }
+ switch $pageInfo(grip) {
+ n {
+ set yMin $y
+ }
+ s {
+ set yMax $y
+ }
+ e {
+ set xMax $x
+ }
+ w {
+ set xMin $x
+ }
+ sw {
+ set xMin $x ; set yMax $y
+ }
+ ne {
+ set xMax $x ; set yMin $y
+ }
+ se {
+ set xMax $x ; set yMax $y
+ }
+ nw {
+ set xMin $x ; set yMin $y
+ }
+ }
+ set width [expr $xMax - $xMin]
+ set height [expr $yMax - $yMin]
+ if { ($width < 1) || ($height < 1) } {
+ return
+ }
+ SetOutline $canvas
+ foreach var { xMin yMin xMax yMax } {
+ set pageInfo($var) [set $var]
+ }
+}
+
+proc ComputePlotGeometry { graph } {
+ global pageInfo
+
+ GetPsOptions $graph
+ set width [winfo width $graph]
+ set height [winfo height $graph]
+ if { $pageInfo(-width) > 0 } {
+ set width $pageInfo(-width)
+ }
+ if { $pageInfo(-height) > 0 } {
+ set height $pageInfo(-height)
+ }
+
+ set left [lindex $pageInfo(-padx) 0]
+ set right [lindex $pageInfo(-padx) 1]
+ set top [lindex $pageInfo(-pady) 0]
+ set bottom [lindex $pageInfo(-pady) 1]
+ set padx [expr $left + $right]
+ set pady [expr $top + $bottom]
+
+ if { $pageInfo(-paperwidth) > 0 } {
+ set paperWidth $pageInfo(-paperwidth)
+ } else {
+ set paperWidth [expr $width + $padx]
+ }
+ if { $pageInfo(-paperheight) > 0 } {
+ set paperHeight $pageInfo(-paperheight)
+ } else {
+ set paperHeight [expr $height + $pady]
+ }
+ if { $pageInfo(-landscape) } {
+ set temp $paperWidth
+ set paperWidth $paperHeight
+ set paperHeight $temp
+ }
+
+ set scale 1.0
+ if { $pageInfo(-maxpect) } {
+ set xScale [expr ($paperWidth - $padx) / double($width)]
+ set yScale [expr ($paperHeight - $pady) / double($height)]
+ set scale [expr min($xScale,$yScale)]
+ set bboxWidth [expr round($width * $scale)]
+ set bboxHeight [expr round($height * $scale)]
+ } else {
+ if { ($width + $padx) > $paperWidth } {
+ set width [expr $paperWidth - $padx]
+ }
+ if { ($height + $pady) > $paperHeight } {
+ set height [expr $paperHeight - $pady]
+ }
+ set bboxWidth $width
+ set bboxHeight $height
+ }
+ set x $left
+ set y $top
+ if { $pageInfo(-center) } {
+ if { $paperWidth > $bboxWidth } {
+ set x [expr ($paperWidth - $bboxWidth) / 2]
+ }
+ if { $paperHeight > $bboxHeight } {
+ set y [expr ($paperHeight - $bboxHeight) / 2]
+ }
+ }
+ set pageInfo(xMin) [expr $x * $pageInfo(scale)]
+ set pageInfo(yMin) [expr $y * $pageInfo(scale)]
+ set pageInfo(xMax) [expr ($x + $bboxWidth) * $pageInfo(scale)]
+ set pageInfo(yMax) [expr ($y + $bboxHeight) * $pageInfo(scale)]
+ set pageInfo(paperHeight) [expr $paperHeight * $pageInfo(scale)]
+ set pageInfo(paperWidth) [expr $paperWidth * $pageInfo(scale)]
+}
+
+proc PsDialog { graph } {
+ global pageInfo
+
+ set pageInfo(graph) $graph
+ set top $graph.top
+ toplevel $top
+ option add *graph.top*Radiobutton.font $pageInfo(radioFont)
+ GetPsOptions $graph
+ ComputePlotGeometry $graph
+ set canvas $top.layout
+ canvas $canvas -confine yes \
+ -width $pageInfo(paperWidth) -height $pageInfo(paperHeight) -bg gray \
+ -bd 2 -relief sunken
+ CreateOutline $canvas
+ SetCanvasOrientation $canvas
+ label $top.titleLabel -text "PostScript Options"
+ table $top \
+ 0,0 $top.titleLabel -cspan 7 \
+ 1,0 $canvas -cspan 7
+
+ set row 2
+ set col 0
+ label $top.paperLabel -text "Paper"
+ radiobutton $top.letter -text "Letter 8 1/2 x 11 in." -value "8.5i 11i" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize i"
+ radiobutton $top.a3 -text "A3 29.7 x 42 cm." -value "28.7c 41c" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize c"
+ radiobutton $top.a4 -text "A4 21 x 29.7 cm." -value "21c 29.7c" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize c"
+ radiobutton $top.a5 -text "A5 14.85 x 21 cm." -value "14.85c 21c" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize c"
+ radiobutton $top.legal -text "Legal 8 1/2 x 14 in." -value "8.5i 14i" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize i"
+ radiobutton $top.large -text "Large 11 x 17 in." -value "11i 17i" \
+ -variable pageInfo(paperSize) \
+ -command "SetPaperSize i"
+ table configure $top r$row -pady { 4 0 }
+ table $top \
+ $row,$col $top.paperLabel -anchor e \
+ $row+0,$col+1 $top.letter -anchor w \
+ $row+1,$col+1 $top.legal -anchor w \
+ $row+2,$col+1 $top.large -anchor w \
+ $row+0,$col+2 $top.a3 -anchor w \
+ $row+1,$col+2 $top.a4 -anchor w \
+ $row+2,$col+2 $top.a5 -anchor w
+
+ incr row 3
+
+ label $top.orientLabel -text "Orientation"
+ radiobutton $top.portrait -text "Portrait" -value "0" \
+ -variable pageInfo(-landscape) -command "ApplyPs"
+ radiobutton $top.landscape -text "Landscape" -value "1" \
+ -variable pageInfo(-landscape) -command "ApplyPs"
+ table configure $top r$row -pady { 4 0 }
+ table $top \
+ $row,$col+0 $top.orientLabel -anchor e \
+ $row,$col+1 $top.portrait -anchor w \
+ $row,$col+2 $top.landscape -anchor w
+
+ incr row 6
+
+ set col 0
+ label $top.plotLabel -text "Plot Options"
+ table $top \
+ $row,$col $top.plotLabel -cspan 3
+ incr row
+ label $top.sizeLabel -text "Size"
+ radiobutton $top.default -text "Default" -value "default" \
+ -variable pageInfo(plotSize) \
+ -command "SetPlotSize"
+ radiobutton $top.maxpect -text "Max Aspect" -value "maxpect" \
+ -variable pageInfo(plotSize) \
+ -command "SetPlotSize"
+ radiobutton $top.resize -text "Resize" -value "resize" \
+ -variable pageInfo(plotSize) \
+ -command "SizeDialog $graph {Adjust Plot Size}"
+ table configure $top r$row -pady { 4 0 }
+ table $top \
+ $row,$col $top.sizeLabel -anchor e \
+ $row,$col+1 $top.default -anchor w \
+ $row+1,$col+1 $top.maxpect -anchor w \
+ $row+2,$col+1 $top.resize -anchor w
+
+ #incr row 4
+
+ set pageInfo(oldPadX) $pageInfo(-padx)
+ set pageInfo(oldPadY) $pageInfo(-pady)
+
+ label $top.posLabel -text "Position"
+ set pageInfo(position) $pageInfo(-center)
+ radiobutton $top.center -text "Center" -value "1" \
+ -variable pageInfo(position) -command {
+ set pageInfo(-center) 1
+ CenterPlot
+ }
+ radiobutton $top.origin -text "Origin" -value "0" \
+ -variable pageInfo(position) -command {
+ set pageInfo(-center) 0
+ ApplyPs
+ }
+ radiobutton $top.move -text "Move" -value "move" \
+ -variable pageInfo(position) -command {
+ set pageInfo(-center) 0
+ MoveDialog
+ }
+ table configure $top r$row -pady { 4 0 }
+ table $top \
+ $row,$col+2 $top.posLabel -anchor e \
+ $row,$col+3 $top.center -anchor w \
+ $row+1,$col+3 $top.origin -anchor w \
+ $row+2,$col+3 $top.move -anchor w
+
+ incr row 4
+ label $top.printLabel -text "Print To"
+ radiobutton $top.toFile -text "File" -value "printFile" \
+ -variable pageInfo(printTo) -command "
+ $top.fileEntry configure -textvariable pageInfo(printFile)
+ "
+ radiobutton $top.toCmd -text "Command" -value "printCmd" \
+ -variable pageInfo(printTo) -command "
+ $top.fileEntry configure -textvariable pageInfo(printCmd)
+ "
+ entry $top.fileEntry
+ table configure $top r$row -pady { 4 0 }
+ table configure $top r[expr $row+1] -pady { 4 0 }
+ table configure $top r[expr $row+2] -pady { 4 0 }
+ table $top \
+ $row,0 $top.printLabel -anchor e \
+ $row,1 $top.toFile -anchor w \
+ $row+1,1 $top.toCmd -anchor w \
+ $row+2,1 $top.fileEntry -anchor w -fill x -cspan 3
+ $top.toFile invoke
+ incr row 3
+ #table configure $top c4 -width .125i
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ button $top.print -text "Done" -command "PrintPs $graph"
+ button $top.advanced -text "Options" -command "MarginDialog $graph"
+ table $top \
+ $row,1 $top.print -width 1i -pady 2 \
+ $row,2 $top.advanced -width 1i -pady 2 \
+ $row,3 $top.cancel -width 1i -pady 2 -anchor w
+
+ SetUnits "inches"
+ foreach label [info commands $top.*Label] {
+ $label configure -font $pageInfo(labelFont) -padx 4
+ }
+}
+
+proc PrintPs { graph } {
+ $graph postscript output "out.ps"
+ puts stdout "wrote file \"out.ps\"."
+ flush stdout
+}
+
+proc ApplyPs { } {
+ global pageInfo
+
+ set graph $pageInfo(graph)
+ foreach option [$graph postscript configure] {
+ set var [lindex $option 0]
+ set old [lindex $option 4]
+ if { [catch {$graph postscript configure $var $pageInfo($var)}] != 0 } {
+ $graph postscript configure $var $old
+ set pageInfo($var) $old
+ }
+ }
+ ComputePlotGeometry $graph
+ foreach var { -paperheight -paperwidth -width -height } {
+ set pageInfo($var) [ConvertUnits $pageInfo($var)]
+ }
+ SetOutline $graph.top.layout
+}
+
+proc StartChange { w delta } {
+ ChangeSize $w $delta
+ global pageInfo
+ set pageInfo(afterId) [after 300 RepeatChange $w $delta]
+}
+
+proc RepeatChange { w delta } {
+ ChangeSize $w $delta
+ global pageInfo
+ set pageInfo(afterId) [after 100 RepeatChange $w $delta]
+}
+
+proc EndChange { w } {
+ global pageInfo
+ after cancel $pageInfo(afterId)
+}
+
+proc ChangeSize { w delta } {
+ set f [winfo parent $w]
+ set value [$f.entry get]
+ set value [expr $value + $delta]
+ if { $value < 0 } {
+ set value 1
+ }
+ $f.entry delete 0 end
+ $f.entry insert 0 $value
+}
+
+proc MakeSizeAdjustor { w label var } {
+ frame $w
+ label $w.label -text $label
+ button $w.plus -text "+" -padx 1 -pady 0 -font \*symbol\*
+ entry $w.entry -width 6 -textvariable "pageInfo($var)"
+ button $w.minus -text "-" -padx 1 -pady 0 -font \*symbol\*
+ label $w.units -text "in"
+ bind $w.plus <ButtonPress-1> { StartChange %W 0.1}
+ bind $w.plus <ButtonRelease-1> { EndChange %W }
+ bind $w.minus <ButtonPress-1> { StartChange %W -0.1}
+ bind $w.minus <ButtonRelease-1> { EndChange %W }
+ table $w \
+ 0,1 $w.label \
+ 1,1 $w.entry -rspan 2 -fill y \
+ 1,0 $w.minus -padx 2 -pady 2 \
+ 2,0 $w.plus -padx 2 -pady { 0 2 } \
+ 1,2 $w.units -rspan 2 -fill y
+
+}
+
+
+proc SizeDialog { graph title } {
+ global pageInfo
+ set top .plotSize
+ if { [winfo exists $top] } {
+ return
+ }
+ toplevel $top
+ label $top.title -text $title
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ button $top.ok -text "Ok" -command "ApplyPs; destroy $top"
+ MakeSizeAdjustor $top.plotWidth "Width" -width
+ MakeSizeAdjustor $top.plotHeight "Height" -height
+ table $top \
+ 0,0 $top.title -cspan 2 \
+ 1,0 $top.plotWidth \
+ 1,1 $top.plotHeight \
+ 2,0 $top.cancel -pady 4 -padx 4 -width 1i \
+ 2,1 $top.ok -pady 4 -padx 4 -width 1i
+ set width [winfo fpixels . $pageInfo(-width)]
+ set height [winfo fpixels . $pageInfo(-height)]
+ if { $width == 0 } {
+ set width [expr ($pageInfo(xMax) - $pageInfo(xMin)) / $pageInfo(scale)]
+ set pageInfo(-width) [ConvertUnits $width]
+ }
+ if { $height == 0 } {
+ set height [expr ($pageInfo(yMax) - $pageInfo(yMin)) / $pageInfo(scale)]
+ set pageInfo(-height) [ConvertUnits $height]
+ }
+ set pageInfo(-maxpect) 0
+}
+
+proc SetPlotSize { } {
+ global pageInfo
+ set graph $pageInfo(graph)
+ switch $pageInfo(plotSize) {
+ default {
+ set pageInfo(-width) 0
+ set pageInfo(-height) 0
+ set pageInfo(-maxpect) 0
+ set pageInfo(-padx) $pageInfo(oldPadX)
+ set pageInfo(-pady) $pageInfo(oldPadY)
+ } maxpect {
+ set pageInfo(-width) 0
+ set pageInfo(-height) 0
+ set pageInfo(-maxpect) 1
+ set pageInfo(-padx) $pageInfo(oldPadX)
+ set pageInfo(-pady) $pageInfo(oldPadY)
+ } resize {
+ set pageInfo(-maxpect) 0
+ }
+ }
+ ApplyPs
+}
+
+
+proc PaperSizeDialog { title } {
+ set top .paperSize
+ if { [winfo exists $top] } {
+ return
+ }
+ toplevel $top
+ label $top.title -text $title
+ MakeSizeAdjustor $top.width "Width" -paperwidth
+ MakeSizeAdjustor $top.height "Height" -paperheight
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ button $top.ok -text "Ok" -command "ApplyPs; destroy $top"
+ table $top \
+ 0,0 $top.title -cspan 2 \
+ 1,0 $top.width \
+ 1,1 $top.height \
+ 2,0 $top.cancel -pady 4 -padx 4 -width 1i \
+ 2,1 $top.ok -pady 4 -padx 4 -width 1i
+}
+
+proc MarginDialog { graph } {
+ set top $graph.top.options
+ if { [winfo exists $top] } {
+ return
+ }
+ toplevel $top
+ set row 0
+ set col 0
+ label $top.modeLabel -text "Printer"
+ radiobutton $top.color -text "Color" -value "color" \
+ -variable pageInfo(-colormode) -command "ApplyPs"
+ radiobutton $top.greyscale -text "Greyscale" -value "greyscale" \
+ -variable pageInfo(-colormode) -command "ApplyPs"
+ table $top \
+ $row,$col $top.modeLabel -anchor e \
+ $row,$col+1 $top.color -anchor w \
+ $row+1,$col+1 $top.greyscale -anchor w
+
+ table configure $top r$row -pady { 4 0 }
+
+ label $top.previewLabel -text "Preview"
+ radiobutton $top.previewYes -text "Yes" -value "1" \
+ -variable pageInfo(-preview) -command "ApplyPs"
+ radiobutton $top.previewNo -text "No" -value "0" \
+ -variable pageInfo(-preview) -command "ApplyPs"
+ set col 2
+ table $top \
+ $row,$col $top.previewLabel -anchor e \
+ $row,$col+1 $top.previewYes -anchor w \
+ $row+1,$col+1 $top.previewNo -anchor w
+ incr row 2
+
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ button $top.ok -text "Done" -command "PrintPs $graph"
+ table $top \
+ $row,0 $top.cancel -pady 4 -padx 4 -width 1i \
+ $row,1 $top.ok -pady 4 -padx 4 -width 1i
+
+}
+
+proc CenterPlot { } {
+ global pageInfo
+
+ set pageInfo(-padx) $pageInfo(oldPadX)
+ set pageInfo(-pady) $pageInfo(oldPadY)
+ ApplyPs
+}
diff --git a/blt/demos/scripts/send.tcl b/blt/demos/scripts/send.tcl
new file mode 100644
index 00000000000..7c57375e566
--- /dev/null
+++ b/blt/demos/scripts/send.tcl
@@ -0,0 +1,115 @@
+
+
+# --------------------------------------------------------------------------
+#
+# SendInit --
+#
+# Creates a "send" proc to replace the former Tk send command.
+# Uses DDE services to simulate the transfer. This must be
+# called before any drag&drop targets are registered. Otherwise
+# they will pick up the wrong application name.
+#
+# The first trick is to determine a unique application name. This
+# is what other applications will use to send to us. Tk used to
+# do this for us.
+#
+# Note that we can generate the same name for two different Tk
+# applications. This can happen if two Tk applications picking
+# names at exactly the same time. [In the future, we should
+# probably generate a name based upon a global system value, such
+# as the handle of the main window ".".] The proc "SendVerify"
+# below will verify that you have only one DDE server registered
+# with this application's name.
+#
+# Arguments:
+# myInterp Sets the application name explicitly to this
+# string. If the argument isn't given, or is the
+# empty string, then the routine picks a name for
+# us.
+#
+# Results:
+# Returns the name of the application.
+#
+# Side Effects:
+# Sets the name of our application. You can call "tk appname" to
+# get the name. A DDE topic using the same name is also created.
+# A send proc is also automatically created. Be careful that you
+# don't overwrite an existing send command.
+#
+# --------------------------------------------------------------------------
+
+proc SendInit { {myInterp ""} } {
+
+ # Load the DDE package.
+ package require dde
+
+ if { $myInterp == "" } {
+
+ # Pick a unique application name, replicating what Tk used to do.
+ # This is what other applications will use to "send" to us. We'll
+ # use DDE topics to represent interpreters.
+
+ set appName [tk appname]
+ set count 0
+ set suffix {}
+
+ # Keep generating interpreter names by suffix-ing the original
+ # application name with " #number". Sooner of later we'll find
+ # one that's not currently use.
+
+ while { 1 } {
+ set myInterp "${appName}${suffix}"
+ set myServer [list TclEval $myInterp]
+ if { [lsearch [dde services TclEval {}] $myServer] < 0 } {
+ break
+ }
+ incr count
+ set suffix " \#$count"
+ }
+ }
+ tk appname $myInterp
+ dde servername $myInterp
+ proc send { interp args } {
+ dde eval $interp $args
+ }
+ return $myInterp
+}
+
+
+# --------------------------------------------------------------------------
+#
+# SendVerify --
+#
+# Verifies that application name picked is uniquely registered
+# as a DDE server. This checks that two Tk applications don't
+# accidently use the same name.
+#
+# Arguments:
+# None Used the current application name.
+#
+# Results:
+# Generates an error if either a server can't be found or more
+# than one server is registered.
+#
+# --------------------------------------------------------------------------
+
+proc SendVerify {} {
+ # Load the DDE package.
+ package require dde
+
+ set count 0
+ set appName [tk appname]
+ foreach server [dde services TclEval {}] {
+ set topic [lindex $server 1]
+ if { [string compare $topic $appName] == 0 } {
+ incr count
+ }
+ }
+ if {$count == 0} {
+ error "Service not found: wrong name registered???"
+ }
+ if { $count > 1 } {
+ error "Duplicate names found for \"[tk appname]\""
+ }
+}
+
diff --git a/blt/demos/scripts/stipples.tcl b/blt/demos/scripts/stipples.tcl
new file mode 100644
index 00000000000..1845d09d919
--- /dev/null
+++ b/blt/demos/scripts/stipples.tcl
@@ -0,0 +1,153 @@
+blt::bitmap define bdiagonal1 {
+#define bdiagonal1_width 8
+#define bdiagonal1_height 8
+static unsigned char bdiagonal1_bits[] = {
+ 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11};
+}
+
+blt::bitmap define bdiagonal2 {
+#define bdiagonal2_width 8
+#define bdiagonal2_height 8
+static unsigned char bdiagonal2_bits[] = {
+ 0x08, 0x04, 0x02, 0x01, 0x80, 0x40, 0x20, 0x10};
+}
+
+blt::bitmap define checker2 {
+#define checker2_width 8
+#define checker2_height 8
+static unsigned char checker2_bits[] = {
+ 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc};
+}
+
+blt::bitmap define checker3 {
+#define checker3_width 8
+#define checker3_height 8
+static unsigned char checker3_bits[] = {
+ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0};
+}
+
+blt::bitmap define cross1 {
+#define cross1_width 8
+#define cross1_height 8
+static unsigned char cross_bits[] = {
+ 0xff, 0xaa, 0xff, 0xaa, 0xff, 0xaa, 0xff, 0xaa};
+}
+
+blt::bitmap define cross2 {
+#define cross2_width 8
+#define cross2_height 8
+static unsigned char cross2_bits[] = {
+ 0xff, 0x88, 0x88, 0x88, 0xff, 0x88, 0x88, 0x88};
+}
+
+blt::bitmap define cross3 {
+#define cross3_width 8
+#define cross3_height 8
+static unsigned char cross3_bits[] = {
+ 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+}
+
+blt::bitmap define crossdiag {
+#define crossdiag_width 8
+#define crossdiag_height 8
+static unsigned char crossdiag2_bits[] = {
+ 0x18, 0x24, 0x42, 0x81, 0x81, 0x42, 0x24, 0x18};
+}
+
+blt::bitmap define dot1 {
+#define dot1_width 8
+#define dot1_height 8
+static unsigned char dot1_bits[] = {
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
+}
+
+blt::bitmap define dot2 {
+#define dot2_width 8
+#define dot2_height 8
+static unsigned char dot2_bits[] = {
+ 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00};
+}
+
+blt::bitmap define dot3 {
+#define dot3_width 8
+#define dot3_height 8
+static unsigned char dot3_bits[] = {
+ 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00};
+}
+
+blt::bitmap define dot4 {
+#define dot4_width 8
+#define dot4_height 8
+static unsigned char dot4_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+}
+
+blt::bitmap define fdiagonal1 {
+#define fdiagonal1_width 8
+#define fdiagonal1_height 8
+static unsigned char fdiagonal1_bits[] = {
+ 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88};
+}
+
+blt::bitmap define fdiagonal2 {
+#define fdiagonal2_width 8
+#define fdiagonal2_height 8
+static unsigned char fdiagonal2_bits[] = {
+ 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08};
+}
+
+blt::bitmap define hline1 {
+#define hline1_width 8
+#define hline1_height 8
+static unsigned char hline1_bits[] = {
+ 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00};
+}
+
+blt::bitmap define hline2 {
+#define hline2_width 8
+#define hline2_height 8
+static unsigned char hline2_bits[] = {
+ 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00};
+}
+
+blt::bitmap define lbottom {
+#define lbottom_width 8
+#define lbottom_height 8
+static unsigned char lbottom_bits[] = {
+ 0x00, 0x11, 0x11, 0x77, 0x00, 0x11, 0x11, 0x77};
+}
+
+blt::bitmap define ltop {
+#define ltop_width 8
+#define ltop_height 8
+static unsigned char ltop_bits[] = {
+ 0xee, 0x88, 0x88, 0x00, 0xee, 0x88, 0x88, 0x00};
+}
+
+blt::bitmap define rbottom {
+#define rbottom_width 8
+#define rbottom_height 8
+static unsigned char rbottom_bits[] = {
+ 0x00, 0x88, 0x88, 0xee, 0x00, 0x88, 0x88, 0xee};
+}
+
+blt::bitmap define rtop {
+#define rtop_width 8
+#define rtop_height 8
+static unsigned char rtop_bits[] = {
+ 0x77, 0x11, 0x11, 0x00, 0x77, 0x11, 0x11, 0x00};
+}
+
+blt::bitmap define vline1 {
+#define vline1_width 8
+#define vline1_height 8
+static unsigned char vline1_bits[] = {
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
+}
+
+blt::bitmap define vline2 {
+#define vline2_width 8
+#define vline2_height 8
+static unsigned char vline2_bits[] = {
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33};
+}
diff --git a/blt/demos/scripts/xcolors.tcl b/blt/demos/scripts/xcolors.tcl
new file mode 100755
index 00000000000..40241c737f8
--- /dev/null
+++ b/blt/demos/scripts/xcolors.tcl
@@ -0,0 +1,271 @@
+#!../bltwish
+#
+# Tk version of xcolors
+#
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+set numCols 0
+set numRows 0
+set maxCols 15
+set cellWidth 40
+set cellHeight 20
+set numCells 0
+set lastCount 0
+set beginInput(0) 0
+set map 0
+set entryCount 0
+set lastTagId {}
+
+scrollbar .xscroll -command { .canvas xview } -orient horizontal
+scrollbar .yscroll -command { .canvas yview }
+
+label .sample \
+ -font -*-new*century*schoolbook*-bold-r-*-*-24-*-*-*-*-*-*-* \
+ -text {"Bisque is Beautiful".}
+
+button .name -font -*-helvetica-medium-r-*-*-18-*-*-*-*-*-*-* \
+ -command "AddSelection name"
+button .rgb -font -*-courier-medium-r-*-*-18-*-*-*-*-*-*-* \
+ -command "AddSelection rgb"
+
+canvas .canvas \
+ -confine 1 \
+ -yscrollcommand { .yscroll set } \
+ -width [expr 16*$cellWidth] -height 400 \
+ -scrollregion [list 0 0 [expr 16*$cellWidth] 800]
+
+frame .border -bd 2 -relief raised
+
+label .status \
+ -anchor w \
+ -font -*-helvetica-medium-r-*-*-14-*-*-*-*-*-*-*
+
+button .quit -text "Quit" -command "exit"
+button .next -text "Next" -command "DisplayColors next"
+button .prev -text "Previous" -command "DisplayColors last"
+
+selection handle .name GetColor
+selection handle .rgb GetValue
+
+bind .name <Enter> {
+ .status config -text \
+ "Press button to write color name into primary selection"
+}
+
+bind .rgb <Enter> {
+ .status config -text \
+ "Press button to write RGB value into primary selection"
+}
+bind .name <Leave> {
+ .status config -text ""
+}
+
+bind .rgb <Leave> {
+ .status config -text ""
+}
+
+bind .canvas <Enter> {
+ .status config -text \
+ "Press button 1 to change background; Button 2 changes foreground"
+}
+
+
+table . \
+ .sample 0,0 -cspan 2 -fill both -reqheight 1i \
+ .name 1,0 -fill both -anchor w \
+ .rgb 1,1 -fill both -anchor w \
+ .canvas 2,0 -cspan 2 -fill both \
+ .yscroll 2,2 -fill y \
+ .border 3,0 -cspan 2 -fill x -reqheight 8 \
+ .status 4,0 -cspan 2 -fill both \
+ .quit 4,1 -anchor e -reqwidth 1i -fill y -padx 10 -pady 4 \
+ .prev 5,0 -anchor e -reqwidth 1i -fill y -padx 10 -pady 4 \
+ .next 5,1 -anchor e -reqwidth 1i -fill y -padx 10 -pady 4
+
+proc AddSelection { what } {
+ selection own .$what
+ if {$what == "name" } {
+ set mesg "Color name written into primary selection"
+ } else {
+ set mesg "RGB value written into primary selection"
+ }
+ .status config -text $mesg
+}
+
+proc GetColor { args } {
+ return [lindex [.name config -text] 4]
+}
+
+proc GetValue { args } {
+ return [lindex [.rgb config -text] 4]
+}
+
+proc ShowInfo { tagId what info } {
+ global lastTagId
+
+ if { $lastTagId != {} } {
+ .canvas itemconfig $lastTagId -width 1
+ }
+ .canvas itemconfig $tagId -width 3
+ set lastTagId $tagId
+
+ set name [lindex $info 3]
+ .name config -text $name
+ set value [format "#%0.2x%0.2x%0.2x" \
+ [lindex $info 0] [lindex $info 1] [lindex $info 2]]
+ .rgb config -text $value
+ .sample config $what $name
+ .status config -bg $name
+}
+
+
+proc MakeCell { info } {
+ global numCols numRows maxCols cellWidth cellHeight numCells
+
+ set x [expr $numCols*$cellWidth]
+ set y [expr $numRows*$cellHeight]
+ set color [lindex $info 3]
+
+ if [catch {winfo rgb . $color}] {
+ return "ok"
+ }
+# if { [tk colormodel .] != "color" } {
+# bind . <Leave> {
+# .status config -text "Color table full after $numCells entries."
+# }
+# .status config -text "Color table full after $numCells entries."
+# return "out of colors"
+# }
+ set id [.canvas create rectangle \
+ $x $y [expr $x+$cellWidth] [expr $y+$cellHeight] \
+ -fill $color -outline black]
+ if { $color == "white" } {
+ global whiteTagId
+ set whiteTagId $id
+ }
+
+ .canvas bind $id <1> [list ShowInfo $id -bg $info]
+ .canvas bind $id <2> [list ShowInfo $id -fg $info]
+
+ incr numCols
+ if { $numCols > $maxCols } {
+ set numCols 0
+ incr numRows
+ }
+ return "ok"
+}
+
+proc DisplayColors { how } {
+ global lastCount numCells cellHeight numRows numCols rgbText
+ global map beginInput
+
+# tk colormodel . color
+ set initialized no
+
+ if { $how == "last" } {
+ if { $map == 0 } {
+ return
+ }
+ set map [expr $map-1]
+ } else {
+ incr map
+ if ![info exists beginInput($map)] {
+ set beginInput($map) $lastCount
+ }
+ }
+
+ set start $beginInput($map)
+
+ if { $numCells > 0 } {
+ .canvas delete all
+ set numRows 0
+ set numCols 0
+ set initialized yes
+ }
+
+ set input [lrange $rgbText $start end]
+ set lineCount $start
+ set entryCount 0
+ foreach i $input {
+ incr lineCount
+ if { [llength $i] == 4 } {
+ if { [MakeCell $i] == "out of colors" } {
+ break
+ }
+ incr entryCount
+ }
+ }
+ if { $entryCount == 0 } {
+ bind . <Leave> {
+ .status config -text "No more entries in RGB database"
+ }
+ .status config -text "No more entries in RGB database"
+ }
+ set lastCount $lineCount
+ proc tkerror {args} {
+ #dummy procedure
+ }
+
+ if { $initialized == "no" } {
+ global cellWidth
+
+ set height [expr $cellHeight*($numRows+1)]
+ .canvas config -scrollregion [list 0 0 [expr 16*$cellWidth] $height]
+ if { $height < 800 } {
+ .canvas config -height $height
+ }
+ global whiteTagId
+ if [info exists whiteTagId] {
+ ShowInfo $whiteTagId -bg {255 255 255 white}
+ }
+ }
+ update idletasks
+ update
+ rename tkerror {}
+}
+
+wm min . 0 0
+
+foreach location {
+ /usr/X11R6
+ /util/X11R6
+ /usr/openwin
+ /usr/dt
+} {
+ set file [file join $location lib X11 rgb.txt]
+ if { [file exists $file] } {
+ break
+ }
+}
+set in [open $file "r"]
+set rgbText [read $in]
+close $in
+set rgbText [split $rgbText \n]
+DisplayColors next
+wm min . 0 0
+
+
diff --git a/blt/demos/spline.tcl b/blt/demos/spline.tcl
new file mode 100755
index 00000000000..8ad330f9684
--- /dev/null
+++ b/blt/demos/spline.tcl
@@ -0,0 +1,84 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+option add *graph.Element.ScaleSymbols true
+
+# test to show spline over-shooting
+
+set tcl_precision 15
+
+# Make and fill small vectors
+vector x y
+x seq 10 0 -0.5
+y expr sin(x^3)
+x expr x*x
+x sort y
+vector x2 y1 y2 y3
+
+# make and fill (x only) large vectors
+x populate x2 10
+
+# natural spline interpolation
+spline natural x y x2 y1
+
+# quadratic spline interpolation
+spline quadratic x y x2 y2
+
+# make plot
+graph .graph
+.graph xaxis configure -title "x^2"
+.graph yaxis configure -title "sin(y^3)"
+
+.graph pen configure activeLine -pixels 5
+.graph element create Original -x x -y y \
+ -color red4 \
+ -fill red \
+ -pixels 5 \
+ -symbol circle
+
+.graph element create Natural -x x2 -y y1 \
+ -color green4 \
+ -fill green \
+ -pixels 3 \
+ -symbol triangle
+
+.graph element create Quadratic -x x2 -y y2 \
+ -color blue4 \
+ -fill orange2 \
+ -pixels 3 \
+ -symbol arrow
+
+table . .graph -fill both
+
+Blt_ZoomStack .graph
+Blt_Crosshairs .graph
+Blt_ActiveLegend .graph
+Blt_ClosestPoint .graph
+Blt_PrintKey .graph
+
+.graph grid on
diff --git a/blt/demos/stripchart1.tcl b/blt/demos/stripchart1.tcl
new file mode 100755
index 00000000000..2dd7e54c568
--- /dev/null
+++ b/blt/demos/stripchart1.tcl
@@ -0,0 +1,405 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+# ----------------------------------------------------------------------
+# EXAMPLE: simple driver for stripchart widget
+# ----------------------------------------------------------------------
+# Michael J. McLennan
+# mmclennan@lucent.com
+# Bell Labs Innovations for Lucent Technologies
+# ======================================================================
+# Copyright (c) 1996 Lucent Technologies
+# ======================================================================
+
+option add *x.range 20.0
+option add *x.shiftBy 15.0
+option add *bufferElements no
+option add *bufferGraph yes
+option add *symbol triangle
+option add *Axis.lineWidth 1
+option add *pixels 1.25m
+#option add *PlotPad 25
+option add *Stripchart.width 6i
+#option add *Smooth quadratic
+#option add *Stripchart.invertXY yes
+#option add *x.descending yes
+
+# ----------------------------------------------------------------------
+# USAGE: random ?<max>? ?<min>?
+#
+# Returns a random number in the range <min> to <max>.
+# If <min> is not specified, the default is 0; if max is not
+# specified, the default is 1.
+# ----------------------------------------------------------------------
+
+proc random {{max 1.0} {min 0.0}} {
+ global randomSeed
+
+ set randomSeed [expr (7141*$randomSeed+54773) % 259200]
+ set num [expr $randomSeed/259200.0*($max-$min)+$min]
+ return $num
+}
+set randomSeed 14823
+
+# ----------------------------------------------------------------------
+
+toplevel .addSource
+wm title .addSource "Add Source"
+wm group .addSource .
+wm withdraw .addSource
+wm protocol .addSource WM_DELETE_WINDOW {.addSource.controls.cancel invoke}
+
+frame .addSource.info
+pack .addSource.info -expand yes -fill both -padx 4 -pady 4
+label .addSource.info.namel -text "Name:"
+entry .addSource.info.name
+label .addSource.info.maxl -text "Maximum:"
+entry .addSource.info.max
+label .addSource.info.minl -text "Minimum:"
+entry .addSource.info.min
+table .addSource.info \
+ .addSource.info.namel 0,0 -anchor e \
+ .addSource.info.name 0,1 -fill x \
+ .addSource.info.maxl 1,0 -anchor e \
+ .addSource.info.max 1,1 -fill x \
+ .addSource.info.minl 2,0 -anchor e \
+ .addSource.info.min 2,1 -fill x
+
+frame .addSource.color
+pack .addSource.color -padx 8 -pady 4
+frame .addSource.color.sample -width 30 -height 30 -borderwidth 2 -relief raised
+pack .addSource.color.sample -side top -fill both
+scale .addSource.color.r -label "Red" -orient vertical \
+ -from 100 -to 0 -command source_color
+pack .addSource.color.r -side left -fill y
+scale .addSource.color.g -label "Green" -orient vertical \
+ -from 100 -to 0 -command source_color
+pack .addSource.color.g -side left -fill y
+scale .addSource.color.b -label "Blue" -orient vertical \
+ -from 100 -to 0 -command source_color
+pack .addSource.color.b -side left -fill y
+
+proc source_color {args} {
+ set r [expr round(2.55*[.addSource.color.r get])]
+ set g [expr round(2.55*[.addSource.color.g get])]
+ set b [expr round(2.55*[.addSource.color.b get])]
+ set color [format "#%2.2x%2.2x%2.2x" $r $g $b]
+ .addSource.color.sample configure -background $color
+}
+source_color
+
+frame .addSource.sep -borderwidth 1 -height 2 -relief sunken
+pack .addSource.sep -fill x -pady 4
+
+frame .addSource.controls
+pack .addSource.controls -fill x -padx 4 -pady 4
+button .addSource.controls.ok -text "OK" -command {
+ wm withdraw .addSource
+ set name [.addSource.info.name get]
+ set color [.addSource.color.sample cget -background]
+ set max [.addSource.info.max get]
+ set min [.addSource.info.min get]
+ if {[catch {source_create $name $color $min $max} err] != 0} {
+ puts "error: $err"
+ }
+}
+pack .addSource.controls.ok -side left -expand yes -padx 4
+button .addSource.controls.cancel -text "Cancel" -command {
+ wm withdraw .addSource
+}
+pack .addSource.controls.cancel -side left -expand yes -padx 4
+
+set useAxes y
+
+proc source_create {name color min max} {
+ global sources
+
+ if {[info exists sources($name-controls)]} {
+ error "source \"$name\" already exists"
+ }
+ if {$max <= $min} {
+ error "bad range: $min - $max"
+ }
+
+ set unique 0
+ set win ".sources.nb.s[incr unique]"
+ while {[winfo exists $win]} {
+ set win ".sources.nb.s[incr unique]"
+ }
+
+ set xvname "xvector$unique"
+ set yvname "yvector$unique"
+ set wvname "wvector$unique"
+ global $xvname $yvname $wvname
+# catch { $xvname delete }
+# catch { $yvname delete }
+# catch { $wvname delete }
+ vector $xvname $yvname $wvname
+
+ if {$xvname == "xvector1"} {
+ $xvname append 0
+ } else {
+ xvector1 variable thisVec
+ $xvname append $thisVec(end)
+ }
+ $yvname append [random $max $min]
+ $wvname append 0
+
+ catch {.sc element delete $name}
+ .sc element create $name -x $xvname -y $yvname -color $color
+ if { $name != "default" } {
+ .sc axis create $name -title $name \
+ -limitscolor $color -limitsformat "%4.4g" -rotate 0 \
+ -titlecolor ${color}
+ .sc element configure $name -mapy $name
+ global useAxes
+ lappend useAxes $name
+ set count 0
+if 1 {
+ set yUse {}
+ set y2Use {}
+ foreach axis $useAxes {
+ if { $count & 1 } {
+ lappend yUse $axis
+ } else {
+ lappend y2Use $axis
+ }
+ incr count
+ }
+ .sc y2axis use $y2Use
+ .sc yaxis use $yUse
+} else {
+ .sc yaxis use $useAxes
+}
+ }
+ .sc axis configure y -rotate 0
+ set cwin .sources.choices.rb$unique
+ radiobutton $cwin -text $name \
+ -variable choices -value $win -command "
+ foreach w \[pack slaves .sources.nb\] {
+ pack forget \$w
+ }
+ pack $win -fill both
+ "
+ pack $cwin -anchor w
+
+ frame $win
+ pack $win -fill x
+ label $win.limsl -text "Limits:"
+ entry $win.lims
+ bind $win.lims <KeyPress-Return> "
+ .sc yaxis configure -limits {%%g}
+ "
+ label $win.smoothl -text "Smooth:"
+ frame $win.smooth
+ radiobutton $win.smooth.linear -text "Linear" \
+ -variable smooth -value linear -command "
+ .sc element configure $name -smooth linear
+ "
+ pack $win.smooth.linear -side left
+ radiobutton $win.smooth.step -text "Step" \
+ -variable smooth -value step -command "
+ .sc element configure $name -smooth step
+ "
+ pack $win.smooth.step -side left
+ radiobutton $win.smooth.natural -text "Natural" \
+ -variable smooth -value natural -command "
+ .sc element configure $name -smooth natural
+ "
+ pack $win.smooth.natural -side left
+ label $win.ratel -text "Sampling Rate:"
+ scale $win.rate -orient horizontal -from 10 -to 1000
+
+ table $win \
+ $win.smoothl 0,0 -anchor e \
+ $win.smooth 0,1 -fill x -padx 4 \
+ $win.limsl 1,0 -anchor e \
+ $win.lims 1,1 -fill x -padx 4 \
+ $win.ratel 2,0 -anchor e \
+ $win.rate 2,1 -fill x -padx 2
+
+ if {$unique != 1} {
+ button $win.del -text "Delete" -command [list source_delete $name]
+ pack $win.del -anchor w
+ table $win $win.del 3,1 -anchor e -padx 4 -pady 4
+ }
+
+ $win.rate set 100
+ catch {$win.smooth.[.sc element cget $name -smooth] invoke} mesg
+
+ set sources($name-choice) $cwin
+ set sources($name-controls) $win
+ set sources($name-stream) [after 100 [list source_event $name 100]]
+ set sources($name-x) $xvname
+ set sources($name-y) $yvname
+ set sources($name-w) $wvname
+ set sources($name-max) $max
+ set sources($name-min) $min
+ set sources($name-steady) [random $max $min]
+
+ $cwin invoke
+}
+
+proc source_delete {name} {
+ global sources
+
+ after cancel $sources($name-stream)
+ destroy $sources($name-choice)
+ destroy $sources($name-controls)
+ unset sources($name-controls)
+
+ set first [lindex [pack slaves .sources.choices] 0]
+ $first invoke
+}
+
+proc source_event {name delay} {
+ global sources
+
+ set xv $sources($name-x)
+ set yv $sources($name-y)
+ set wv $sources($name-w)
+ global $xv $yv $wv
+
+ $xv variable x
+ set x(++end) [expr $x(end) + 0.001 * $delay]
+
+ $yv variable y
+ if {[random] > 0.97} {
+ set y(++end) [random $sources($name-max) $sources($name-min)]
+ } else {
+ set y(++end) [expr $y(end)+0.1*($sources($name-steady)-$y(end))]
+ }
+ set val [random]
+ if {$val > 0.95} {
+ $wv append 2
+ } elseif {$val > 0.8} {
+ $wv append 1
+ } else {
+ $wv append 0
+ }
+ #$wv notify now
+ if { [$xv length] > 100 } {
+ $xv delete 0
+ $yv delete 0
+ $wv delete 0
+ }
+ update
+ set win $sources($name-controls)
+ set delay [$win.rate get]
+ set sources($name-stream) [after $delay [list source_event $name $delay]]
+}
+
+# ----------------------------------------------------------------------
+frame .mbar -borderwidth 2 -relief raised
+pack .mbar -fill x
+
+menubutton .mbar.main -text "Main" -menu .mbar.main.m
+pack .mbar.main -side left -padx 4
+menu .mbar.main.m
+.mbar.main.m add command -label "Add Source..." -command {
+ set x [expr [winfo rootx .]+50]
+ set y [expr [winfo rooty .]+50]
+ wm geometry .addSource +$x+$y
+ wm deiconify .addSource
+}
+.mbar.main.m add separator
+.mbar.main.m add command -label "Quit" -command exit
+
+menubutton .mbar.prefs -text "Preferences" -menu .mbar.prefs.m
+pack .mbar.prefs -side left -padx 4
+
+menu .mbar.prefs.m
+.mbar.prefs.m add cascade -label "Warning Symbol" -menu .mbar.prefs.m.wm
+menu .mbar.prefs.m.wm
+.mbar.prefs.m add cascade -label "Error Symbol" -menu .mbar.prefs.m.em
+menu .mbar.prefs.m.em
+
+foreach sym {square circle diamond plus cross triangle} {
+ .mbar.prefs.m.wm add radiobutton -label $sym \
+ -variable warningsym -value $sym \
+ -command {.sc pen configure "warning" -symbol $warningsym}
+
+ .mbar.prefs.m.em add radiobutton -label $sym \
+ -variable errorsym -value $sym \
+ -command {.sc pen configure "error" -symbol $errorsym}
+}
+catch {.mbar.prefs.m.wm invoke "circle"}
+catch {.mbar.prefs.m.em invoke "cross"}
+
+# ----------------------------------------------------------------------
+stripchart .sc -title "Stripchart"
+pack .sc -expand yes -fill both
+
+.sc xaxis configure -title "Time (s)" -autorange 2.0 -shiftby 0.5
+.sc yaxis configure -title "Samples"
+
+frame .sources
+pack .sources -fill x -padx 10 -pady 4
+frame .sources.nb -borderwidth 2 -relief sunken
+pack .sources.nb -side right -expand yes -fill both -padx 4 -pady 4
+label .sources.title -text "Sources:"
+pack .sources.title -side top -anchor w -padx 4
+frame .sources.choices -borderwidth 2 -relief groove
+pack .sources.choices -expand yes -fill both -padx 4 -pady 4
+
+source_create default red 0 10
+source_create temp blue3 0 10
+source_create pressure green3 0 200
+source_create volume orange3 0 1020
+source_create power yellow3 0 0.01999
+source_create work magenta3 0 10
+
+Blt_ZoomStack .sc
+
+.sc axis bind Y <Enter> {
+ set axis [%W axis get current]
+ set detail [%W axis get detail]
+ if { $detail == "line" } {
+ %W axis configure $axis -background grey
+ }
+}
+.sc axis bind Y <Leave> {
+ set axis [%W axis get current]
+ %W axis configure $axis -background ""
+}
+
+.sc axis bind Y <ButtonPress-1> {
+ set axis [%W axis get current]
+# scan [%W axis limits $axis] "%%g %%g" min max
+# set min [expr $min + (($max - $min) * 0.1)]
+# set max [expr $max - (($max - $min) * 0.1)]
+# %W axis configure $axis -min $min -max $max
+ %W axis configure $axis -logscale yes
+}
+
+.sc axis bind Y <ButtonPress-3> {
+ set axis [%W axis get current]
+# %W axis configure $axis -min {} -max {}
+ %W axis configure $axis -logscale no
+}
+
diff --git a/blt/demos/tabnotebook1.tcl b/blt/demos/tabnotebook1.tcl
new file mode 100755
index 00000000000..599d2bcdf59
--- /dev/null
+++ b/blt/demos/tabnotebook1.tcl
@@ -0,0 +1,86 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+# Create a tabnotebook widget.
+
+tabnotebook .tnb
+
+# The notebook is initially empty. Insert tabs (pages) into the notebook.
+
+foreach label { First Second Third Fourth } {
+ .tnb insert end -text $label
+}
+
+# Tabs are referred to by their index. Tab indices can be one of the
+# following:
+#
+# number Position of tab the notebook's list of tabs.
+# @x,y Tab closest to the specified X-Y screen coordinates.
+# "active" Tab currently under the mouse pointer.
+# "focus" Tab that has focus.
+# "select" The currently selected tab.
+# "right" Next tab from "focus".
+# "left" Previous tab from "focus".
+# "up" Next tab from "focus".
+# "down" Previous tab from "focus".
+# "end" Last tab in list.
+# string Tab identifier. The "insert" operation returns
+# a unique identifier for the new tab (e.g. "tab0").
+# This ID is valid for the life of the tab, even if
+# the tabs are moved or reordered.
+
+# Each tab has a text label and an optional Tk image.
+
+set image [image create photo -file ./images/mini-book1.gif]
+.tnb tab configure 0 -image $image
+
+#
+# How to embed a widget into a page.
+#
+
+# 1. The widget must be a child of the tabnotebook.
+
+set image [image create photo -file ./images/blt98.gif]
+label .tnb.label -image $image -relief sunken -bd 2
+
+# 2. Use the -window option to embed the widget.
+
+.tnb tab configure 0 -window .tnb.label
+
+# The tearoff perforation, displayed on the selected tab, is
+# controlled by the tabnotebook's -tearoff option.
+#
+# If you don't want tearoff pages, configure -tearoff to "no".
+
+.tnb configure -tearoff yes
+
+table . \
+ 0,0 .tnb -fill both
+
+focus .tnb
+
diff --git a/blt/demos/tabnotebook2.tcl b/blt/demos/tabnotebook2.tcl
new file mode 100755
index 00000000000..41e561df9be
--- /dev/null
+++ b/blt/demos/tabnotebook2.tcl
@@ -0,0 +1,80 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+image create photo bgTile -file ./images/smblue_rock.gif
+image create photo label1 -file ./images/mini-book1.gif
+image create photo label2 -file ./images/mini-book2.gif
+image create photo testImage -file ./images/txtrflag.gif
+
+scrollbar .s -command { .t view } -orient horizontal
+tabnotebook .t \
+ -relief sunken -bd 2 \
+ -textside right \
+ -samewidth yes -tiers 2 -slant right \
+ -scrollcommand { .s set } \
+ -tile bgTile
+
+label .t.l -image testImage
+
+set attributes {
+ graph1 "Graph \#1" red .t.graph1
+ graph2 "Graph \#2" green .t.graph2
+ graph3 "Graph \#3" cyan .t.graph3
+ graph5 "Graph \#5" yellow .t.graph5
+ graph6 one orange .t.l
+}
+
+foreach { entry label color window } $attributes {
+ .t insert end -text $label -fill both
+}
+
+foreach label { there bunky another test of a widget } {
+ set id [.t insert end -text $label]
+}
+
+set img [image create photo -file ./images/blt98.gif]
+.t tab configure $id -image label2 -tile $img
+
+table . \
+ .t 0,0 -fill both \
+ .s 1,0 -fill x
+
+table configure . r1 -resize none
+
+set index 0
+foreach file { graph1 graph2 graph3 graph5 } {
+ namespace eval $file {
+ set graph [graph .t.$file]
+ source scripts/$file.tcl
+ .t tab configure $index -window $graph
+ incr index
+ }
+}
+
diff --git a/blt/demos/tabnotebook3.tcl b/blt/demos/tabnotebook3.tcl
new file mode 100755
index 00000000000..54254cfea99
--- /dev/null
+++ b/blt/demos/tabnotebook3.tcl
@@ -0,0 +1,193 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+#bltdebug 100
+
+image create photo label1 -file ./images/mini-book1.gif
+image create photo label2 -file ./images/mini-book2.gif
+image create photo testImage -file ./images/txtrflag.gif
+
+tabnotebook .tnb \
+ -textside right \
+ -slant both \
+ -side right \
+ -samewidth yes \
+ -highlightcolor yellow \
+ -tiers 5 \
+ -scrollcommand { .s set } \
+ -scrollincrement 1
+
+label .tnb.l -image testImage
+
+set attributes {
+ "Graph \#1" pink
+ "Graph \#2" lightblue
+ "Graph \#3" orange
+ "Graph \#5" yellow
+ "Barchart \#2" green
+}
+
+foreach { label color } $attributes {
+ .tnb insert end -text $label \
+ -selectbackground ${color}3 \
+ -background ${color}3 \
+ -activebackground ${color}2
+}
+
+.tnb insert end -selectbackground salmon2 -background salmon3 \
+ -selectbackground salmon3 -activebackground salmon2 -window .tnb.l
+
+set tabLabels {
+ Aarhus Aaron Ababa aback abaft abandon abandoned abandoning
+ abandonment abandons abase abased abasement abasements abases
+ abash abashed abashes abashing abasing abate abated abatement
+ abatements abater abates abating Abba abbe abbey abbeys abbot
+ abbots Abbott abbreviate abbreviated abbreviates abbreviating
+ abbreviation abbreviations Abby abdomen abdomens abdominal
+ abduct abducted abduction abductions abductor abductors abducts
+ Abe abed Abel Abelian Abelson Aberdeen Abernathy aberrant
+ aberration aberrations abet abets abetted abetter abetting
+ abeyance abhor abhorred abhorrent abhorrer abhorring abhors
+ abide abided abides abiding Abidjan Abigail Abilene abilities
+ ability abject abjection abjections abjectly abjectness abjure
+ abjured abjures abjuring ablate ablated ablates ablating
+ ablation ablative ablaze able abler ablest ably Abner abnormal
+ abnormalities abnormality abnormally Abo aboard abode abodes
+ abolish abolished abolisher abolishers abolishes abolishing
+ abolishment abolishments abolition abolitionist abolitionists
+ abominable abominate aboriginal aborigine aborigines abort
+ aborted aborting abortion abortions abortive abortively aborts
+ Abos abound abounded abounding abounds about above aboveboard
+ aboveground abovementioned abrade abraded abrades abrading
+ Abraham Abram Abrams Abramson abrasion abrasions abrasive
+ abreaction abreactions abreast abridge abridged abridges
+ abridging abridgment abroad abrogate abrogated abrogates
+ abrogating abrupt abruptly abruptness abscess abscessed
+ abscesses abscissa abscissas abscond absconded absconding
+ absconds absence absences absent absented absentee
+ absenteeism absentees absentia absenting absently absentminded
+ absents absinthe absolute absolutely absoluteness absolutes
+ absolution absolve absolved absolves absolving absorb
+ absorbed absorbency absorbent absorber absorbing absorbs
+ absorption absorptions absorptive abstain abstained abstainer
+ abstaining abstains abstention abstentions abstinence
+ abstract abstracted abstracting abstraction abstractionism
+ abstractionist abstractions abstractly abstractness
+ abstractor abstractors abstracts abstruse abstruseness
+ absurd absurdities absurdity absurdly Abu abundance abundant
+ abundantly abuse abused abuses abusing abusive abut abutment
+ abuts abutted abutter abutters abutting abysmal abysmally
+ abyss abysses Abyssinia Abyssinian Abyssinians acacia
+ academia academic academically academics academies academy
+ Acadia Acapulco accede acceded accedes accelerate accelerated
+ accelerates accelerating acceleration accelerations
+ accelerator accelerators accelerometer accelerometers accent
+ accented accenting accents accentual accentuate accentuated
+ accentuates accentuating accentuation accept acceptability
+ acceptable acceptably acceptance acceptances accepted
+ accepter accepters accepting acceptor acceptors accepts
+ access accessed accesses accessibility accessible accessibly
+ accessing accession accessions accessories accessors
+ accessory accident accidental accidentally accidently
+ accidents acclaim acclaimed acclaiming acclaims acclamation
+ acclimate acclimated acclimates acclimating acclimatization
+ acclimatized accolade accolades accommodate accommodated
+ accommodates accommodating accommodation accommodations
+ accompanied accompanies accompaniment accompaniments
+ accompanist accompanists accompany accompanying accomplice
+ accomplices accomplish accomplished accomplisher accomplishers
+ accomplishes accomplishing accomplishment accomplishments
+ accord accordance accorded accorder accorders according
+ accordingly accordion accordions accords accost accosted
+ accosting accosts account accountability accountable accountably
+ accountancy accountant accountants accounted accounting
+ accounts Accra accredit accreditation accreditations
+ accredited accretion accretions accrue accrued accrues
+ accruing acculturate acculturated acculturates acculturating
+ acculturation accumulate accumulated accumulates accumulating
+ accumulation accumulations accumulator accumulators
+ accuracies accuracy accurate accurately accurateness accursed
+ accusal accusation accusations accusative accuse accused
+ accuser accuses accusing accusingly accustom accustomed
+ accustoming accustoms ace aces acetate acetone acetylene
+ Achaean Achaeans ache ached aches achievable achieve achieved
+ achievement achievements achiever achievers achieves achieving
+ Achilles aching acid acidic acidities acidity acidly acids
+ acidulous Ackerman Ackley acknowledge acknowledgeable
+ acknowledged acknowledgement acknowledgements acknowledger
+ acknowledgers acknowledges acknowledging acknowledgment
+ acknowledgments acme acne acolyte acolytes acorn acorns
+ acoustic acoustical acoustically acoustician acoustics
+ acquaint acquaintance acquaintances acquainted acquainting
+ acquaints acquiesce acquiesced acquiescence acquiescent
+ acquiesces acquiescing acquirable acquire acquired acquires
+ acquiring acquisition acquisitions
+}
+
+for { set i 0 } { $i < 500 } { incr i } {
+ .tnb insert end -text [lindex $tabLabels $i] -state normal
+}
+
+scrollbar .s -command { .tnb view } -orient horizontal
+radiobutton .left -text "Left" -variable side -value "left" \
+ -command { .tnb configure -side $side -rotate 90 }
+radiobutton .right -text "Right" -variable side -value "right" \
+ -command { .tnb configure -side $side -rotate 270 }
+radiobutton .top -text "Top" -variable side -value "top" \
+ -command { .tnb configure -side $side -rotate 0 }
+radiobutton .bottom -text "Bottom" -variable side -value "bottom" \
+ -command { .tnb configure -side $side -rotate 0 }
+
+table . \
+ .tnb 0,0 -fill both -cspan 2 \
+ .s 1,0 -fill x -cspan 2 \
+ .top 2,0 -cspan 2 \
+ .left 3,0 \
+ .right 3,1 \
+ .bottom 4,0 -cspan 2
+
+table configure . r1 r3 r4 r2 -resize none
+focus .tnb
+
+.tnb focus 0
+
+set filecount 0
+foreach file { graph1 graph2 graph3 graph5 barchart2 } {
+ namespace eval $file {
+ if { [string match graph* $file] } {
+ set graph [graph .tnb.$file]
+ } else {
+ set graph [barchart .tnb.$file]
+ }
+ source scripts/$file.tcl
+ .tnb tab configure $filecount -window $graph -fill both
+ incr filecount
+ }
+}
+
+.top invoke
+
diff --git a/blt/demos/tabset1.tcl b/blt/demos/tabset1.tcl
new file mode 100755
index 00000000000..ce72f34ef0f
--- /dev/null
+++ b/blt/demos/tabset1.tcl
@@ -0,0 +1,57 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+source scripts/demo.tcl
+
+image create photo bgTile -file ./images/chalk.gif
+image create photo label1 -file ./images/mini-book1.gif
+image create photo label2 -file ./images/mini-book2.gif
+
+tabset .t -relief raised \
+ -activebackground yellow \
+ -bg red -borderwidth 0 -highlightthickness 0 \
+ -scrollcommand { .s set } \
+ -width 3i
+
+#option add *iPadX 4
+#option add *iPadY 2
+
+.t insert end First \
+ -image label1 \
+ -anchor center \
+ -selectbackground darkolivegreen2 \
+ Again Next another test of \
+ a -image label2 widget
+
+scrollbar .s -command { .t view } -orient horizontal
+table . \
+ .t 0,0 -fill both \
+ .s 1,0 -fill x
+
+table configure . r1 -resize none
+focus .t
+
diff --git a/blt/demos/tabset2.tcl b/blt/demos/tabset2.tcl
new file mode 100755
index 00000000000..2229af78362
--- /dev/null
+++ b/blt/demos/tabset2.tcl
@@ -0,0 +1,79 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+image create photo bgTile -file ./images/smblue_rock.gif
+image create photo label1 -file ./images/mini-book1.gif
+image create photo label2 -file ./images/mini-book2.gif
+image create photo testImage -file ./images/txtrflag.gif
+
+scrollbar .s -command { .t view } -orient horizontal
+tabset .t \
+ -relief flat \
+ -bd 2 \
+ -textside right \
+ -samewidth yes \
+ -tiers 2 \
+ -slant right \
+ -scrollcommand { .s set } \
+ -tile bgTile \
+ -scrollincrement 1 -selectforeground green2
+
+label .t.l -image testImage
+
+set attributes {
+ graph1 "Graph \#1" red .t.graph1
+ graph2 "Graph \#2" green .t.graph2
+ graph3 "Graph \#3" cyan .t.graph3
+ graph5 "Graph \#5" yellow .t.graph5
+ graph6 one orange .t.l
+}
+
+foreach { entry label color window } $attributes {
+ .t insert end $entry -text $label -fill both
+}
+
+.t insert end \
+ there bunky another test of \
+ a -image label2 widget
+
+table . \
+ .t 0,0 -fill both \
+ .s 1,0 -fill x
+
+table configure . r1 -resize none
+
+foreach file { graph1 graph2 graph3 graph5 } {
+ namespace eval $file {
+ set graph [graph .t.$file]
+ source scripts/$file.tcl
+ .t tab configure $file -window $graph
+ }
+}
+
diff --git a/blt/demos/tabset3.tcl b/blt/demos/tabset3.tcl
new file mode 100755
index 00000000000..c3ad9013355
--- /dev/null
+++ b/blt/demos/tabset3.tcl
@@ -0,0 +1,199 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+#bltdebug 100
+
+image create photo label1 -file ./images/mini-book1.gif
+image create photo label2 -file ./images/mini-book2.gif
+image create photo testImage -file ./images/txtrflag.gif
+
+tabset .t \
+ -textside right \
+ -slant both \
+ -side right \
+ -samewidth yes \
+ -highlightcolor yellow \
+ -tiers 5 \
+ -scrollcommand { .s set } \
+ -scrollincrement 1
+
+label .t.l -image testImage
+
+#option add *Tabset.Tab.font -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*
+#option add *Tabset.Tab.fill both
+
+set attributes {
+ graph1 "Graph \#1" pink
+ graph2 "Graph \#2" lightblue
+ graph3 "Graph \#3" orange
+ graph5 "Graph \#5" yellow
+ barchart2 "Barchart \#2" green
+}
+
+foreach { name label color } $attributes {
+ .t insert end $name -text $label \
+ -selectbackground ${color}3 \
+ -background ${color}3 \
+ -activebackground ${color}2
+}
+
+.t insert end Image -selectbackground salmon2 -background salmon3 \
+ -selectbackground salmon3 -activebackground salmon2 -window .t.l
+
+set tabLabels {
+ Aarhus Aaron Ababa aback abaft abandon abandoned abandoning
+ abandonment abandons abase abased abasement abasements abases
+ abash abashed abashes abashing abasing abate abated abatement
+ abatements abater abates abating Abba abbe abbey abbeys abbot
+ abbots Abbott abbreviate abbreviated abbreviates abbreviating
+ abbreviation abbreviations Abby abdomen abdomens abdominal
+ abduct abducted abduction abductions abductor abductors abducts
+ Abe abed Abel Abelian Abelson Aberdeen Abernathy aberrant
+ aberration aberrations abet abets abetted abetter abetting
+ abeyance abhor abhorred abhorrent abhorrer abhorring abhors
+ abide abided abides abiding Abidjan Abigail Abilene abilities
+ ability abject abjection abjections abjectly abjectness abjure
+ abjured abjures abjuring ablate ablated ablates ablating
+ ablation ablative ablaze able abler ablest ably Abner abnormal
+ abnormalities abnormality abnormally Abo aboard abode abodes
+ abolish abolished abolisher abolishers abolishes abolishing
+ abolishment abolishments abolition abolitionist abolitionists
+ abominable abominate aboriginal aborigine aborigines abort
+ aborted aborting abortion abortions abortive abortively aborts
+ Abos abound abounded abounding abounds about above aboveboard
+ aboveground abovementioned abrade abraded abrades abrading
+ Abraham Abram Abrams Abramson abrasion abrasions abrasive
+ abreaction abreactions abreast abridge abridged abridges
+ abridging abridgment abroad abrogate abrogated abrogates
+ abrogating abrupt abruptly abruptness abscess abscessed
+ abscesses abscissa abscissas abscond absconded absconding
+ absconds absence absences absent absented absentee
+ absenteeism absentees absentia absenting absently absentminded
+ absents absinthe absolute absolutely absoluteness absolutes
+ absolution absolve absolved absolves absolving absorb
+ absorbed absorbency absorbent absorber absorbing absorbs
+ absorption absorptions absorptive abstain abstained abstainer
+ abstaining abstains abstention abstentions abstinence
+ abstract abstracted abstracting abstraction abstractionism
+ abstractionist abstractions abstractly abstractness
+ abstractor abstractors abstracts abstruse abstruseness
+ absurd absurdities absurdity absurdly Abu abundance abundant
+ abundantly abuse abused abuses abusing abusive abut abutment
+ abuts abutted abutter abutters abutting abysmal abysmally
+ abyss abysses Abyssinia Abyssinian Abyssinians acacia
+ academia academic academically academics academies academy
+ Acadia Acapulco accede acceded accedes accelerate accelerated
+ accelerates accelerating acceleration accelerations
+ accelerator accelerators accelerometer accelerometers accent
+ accented accenting accents accentual accentuate accentuated
+ accentuates accentuating accentuation accept acceptability
+ acceptable acceptably acceptance acceptances accepted
+ accepter accepters accepting acceptor acceptors accepts
+ access accessed accesses accessibility accessible accessibly
+ accessing accession accessions accessories accessors
+ accessory accident accidental accidentally accidently
+ accidents acclaim acclaimed acclaiming acclaims acclamation
+ acclimate acclimated acclimates acclimating acclimatization
+ acclimatized accolade accolades accommodate accommodated
+ accommodates accommodating accommodation accommodations
+ accompanied accompanies accompaniment accompaniments
+ accompanist accompanists accompany accompanying accomplice
+ accomplices accomplish accomplished accomplisher accomplishers
+ accomplishes accomplishing accomplishment accomplishments
+ accord accordance accorded accorder accorders according
+ accordingly accordion accordions accords accost accosted
+ accosting accosts account accountability accountable accountably
+ accountancy accountant accountants accounted accounting
+ accounts Accra accredit accreditation accreditations
+ accredited accretion accretions accrue accrued accrues
+ accruing acculturate acculturated acculturates acculturating
+ acculturation accumulate accumulated accumulates accumulating
+ accumulation accumulations accumulator accumulators
+ accuracies accuracy accurate accurately accurateness accursed
+ accusal accusation accusations accusative accuse accused
+ accuser accuses accusing accusingly accustom accustomed
+ accustoming accustoms ace aces acetate acetone acetylene
+ Achaean Achaeans ache ached aches achievable achieve achieved
+ achievement achievements achiever achievers achieves achieving
+ Achilles aching acid acidic acidities acidity acidly acids
+ acidulous Ackerman Ackley acknowledge acknowledgeable
+ acknowledged acknowledgement acknowledgements acknowledger
+ acknowledgers acknowledges acknowledging acknowledgment
+ acknowledgments acme acne acolyte acolytes acorn acorns
+ acoustic acoustical acoustically acoustician acoustics
+ acquaint acquaintance acquaintances acquainted acquainting
+ acquaints acquiesce acquiesced acquiescence acquiescent
+ acquiesces acquiescing acquirable acquire acquired acquires
+ acquiring acquisition acquisitions
+}
+
+for { set i 0 } { $i < 500 } { incr i } {
+ .t insert end [lindex $tabLabels $i] -state normal
+}
+
+scrollbar .s -command { .t view } -orient horizontal
+radiobutton .left -text "Left" -variable side -value "left" \
+ -command { .t configure -side $side -rotate 90 }
+radiobutton .right -text "Right" -variable side -value "right" \
+ -command { .t configure -side $side -rotate 270 }
+radiobutton .top -text "Top" -variable side -value "top" \
+ -command { .t configure -side $side -rotate 0 }
+radiobutton .bottom -text "Bottom" -variable side -value "bottom" \
+ -command { .t configure -side $side -rotate 0 }
+
+table . \
+ .t 0,0 -fill both -cspan 2 \
+ .s 1,0 -fill x -cspan 2 \
+ .top 2,0 -cspan 2 \
+ .left 3,0 \
+ .right 3,1 \
+ .bottom 4,0 -cspan 2
+
+table configure . r1 r3 r4 r2 -resize none
+focus .t
+
+.t focus 0
+
+after 3000 {
+ .t move 0 after 3
+ .t tab configure [.t get 3] -state disabled
+}
+
+foreach file { graph1 graph2 graph3 graph5 barchart2 } {
+ namespace eval $file {
+ if { [string match graph* $file] } {
+ set graph [graph .t.$file]
+ } else {
+ set graph [barchart .t.$file]
+ }
+ source scripts/$file.tcl
+ .t tab configure $file -window $graph -fill both
+ }
+}
+
+.top invoke
+
diff --git a/blt/demos/tabset4.tcl b/blt/demos/tabset4.tcl
new file mode 100755
index 00000000000..cf9249183ce
--- /dev/null
+++ b/blt/demos/tabset4.tcl
@@ -0,0 +1,109 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+#bltdebug 100
+
+source scripts/stipples.tcl
+
+tabset .t \
+ -samewidth yes \
+ -side left \
+ -textside bottom \
+ -textside top \
+ -bg red \
+ -tiers 1 \
+ -scrollincrement 10 \
+ -scrollcommand { .s set } \
+ -rotate 0 \
+ -selectcommand { MakePhoto %W %n }
+
+
+scrollbar .s -command { .t view } -orient horizontal
+
+option clear
+option add *Tabset.Tab.font -*-helvetica-bold-r-*-*-10-*-*-*-*-*-*-*
+
+set files [glob ./images/*.gif]
+set files [lsort $files]
+#set vertFilter sinc
+#set horzFilter sinc
+set vertFilter none
+set horzFilter none
+
+
+proc ResizePhoto { src dest maxSize } {
+ set maxSize [winfo fpixels . $maxSize]
+ set w [image width $src]
+ set h [image height $src]
+ set sw [expr double($maxSize) / $w]
+ set sh [expr double($maxSize) / $h]
+ set s [expr min($sw, $sh)]
+ set w [expr round($s * $w)]
+ set h [expr round($s * $h)]
+ $dest configure -width $w -height $h
+
+ global horzFilter vertFilter
+ winop resample $src $dest $horzFilter $vertFilter
+}
+
+image create photo src
+image create photo dest
+
+label .t.label -image dest -bg purple
+
+proc MakePhoto { w name } {
+ set file ./images/$name.gif
+ src configure -file $file
+
+ set width [$w tab pagewidth]
+ set height [$w tab pageheight]
+ if { $width < $height } {
+ ResizePhoto src dest $width
+ } else {
+ ResizePhoto src dest $height
+ }
+ .t tab dockall
+ .t tab configure $name -window .t.label -padx 4m -pady 4m -fill both
+}
+
+table . \
+ .t 0,0 -fill both \
+ .s 1,0 -fill x
+
+table configure . r1 -resize none
+focus .t
+
+foreach f $files {
+ src configure -file $f
+ set f [file tail [file root $f]]
+ set thumb [image create photo]
+ ResizePhoto src $thumb 0.5i
+ .t insert end $f -image $thumb -fill both
+}
+
+.t focus 0
+.t invoke 0
diff --git a/blt/demos/tour.tcl b/blt/demos/tour.tcl
new file mode 100755
index 00000000000..edc10e79241
--- /dev/null
+++ b/blt/demos/tour.tcl
@@ -0,0 +1,159 @@
+#!../src/bltwish
+
+#package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+option add *Scrollbar.relief flat
+set oldLabel "dummy"
+
+proc RunDemo { program } {
+ if { ![file executable $program] } {
+ return
+ }
+ set cmd [list $program -name "demo:$program" -geom -4000-4000]
+ global programInfo
+ if { [info exists programInfo(lastProgram)] } {
+ set programInfo($programInfo(lastProgram)) 0
+ }
+ eval bgexec programInfo($program) $cmd &
+ set programInfo(lastProgram) $program
+ puts stderr [.top.tab.f1 search -name demo:$program]
+ .top.tab.f1 configure -name demo:$program
+}
+
+frame .top
+hierbox .top.hier -separator "." -xscrollincrement 1 \
+ -yscrollcommand { .top.yscroll set } -xscrollcommand { .top.xscroll set } \
+ -selectcommand {
+ set index [.top.hier curselection]
+ set label [.top.hier entry cget $index -label]
+ .top.title configure -text $label
+ .top.tab tab configure Example -window .top.tab.f1
+ if { $label != $oldLabel } {
+ RunDemo $label
+ }
+ }
+
+
+scrollbar .top.yscroll -command { .top.hier yview }
+scrollbar .top.xscroll -command { .top.hier xview } -orient horizontal
+label .top.mesg -relief groove -borderwidth 2
+label .top.title -text "Synopsis" -highlightthickness 0
+tabset .top.tab -side bottom -relief flat -bd 0 -highlightthickness 0 \
+ -pageheight 4i
+
+.top.tab insert end \
+ "Example" \
+ "See Code" \
+ "Manual"
+
+set pics /DOS/f/gah/Pics
+set pics /home/gah/Pics
+image create photo dummy -file $pics/Ex1.gif
+image create photo graph.img -width 50 -height 50
+winop resample dummy graph.img box box
+
+image create photo dummy -file $pics/Ex11.gif
+image create photo barchart.img -width 50 -height 50
+winop resample dummy barchart.img box box
+
+.top.hier entry configure root -label "BLT"
+.top.hier insert end \
+ "Plotting" \
+ "Plotting.graph" \
+ "Plotting.graph.graph" \
+ "Plotting.graph.graph2" \
+ "Plotting.graph.graph3" \
+ "Plotting.graph.graph4" \
+ "Plotting.graph.graph5" \
+ "Plotting.graph.graph6" \
+ "Plotting.barchart" \
+ "Plotting.barchart.barchart1" \
+ "Plotting.barchart.barchart2" \
+ "Plotting.barchart.barchart3" \
+ "Plotting.barchart.barchart4" \
+ "Plotting.barchart.barchart5" \
+ "Plotting.stripchart" \
+ "Plotting.vector" \
+ "Composition" \
+ "Composition.htext" \
+ "Composition.table" \
+ "Composition.tabset" \
+ "Composition.hierbox" \
+ "Miscellaneous" \
+ "Miscellaneous.busy" \
+ "Miscellaneous.bgexec" \
+ "Miscellaneous.watch" \
+ "Miscellaneous.bltdebug"
+.top.hier open -r root
+.top.hier entry configure root -labelfont *-helvetica*-bold-r-*-18-* \
+ -labelcolor red -labelshadow red3
+.top.hier entry configure "Plotting" "Composition" "Miscellaneous" \
+ -labelfont *-helvetica*-bold-r-*-14-* \
+ -labelcolor blue4 -labelshadow blue2
+
+.top.hier entry configure "Plotting.graph" \
+ -labelfont *-helvetica*-bold-r-*-14-* -label "X-Y Graph"
+.top.hier entry configure "Plotting.barchart" \
+ -labelfont *-helvetica*-bold-r-*-14-* -label "Bar Chart"
+
+.top.hier entry configure "Plotting.stripchart" \
+ -labelfont *-helvetica*-bold-r-*-14-* -label "X-Y Graph"
+.top.hier entry configure "Plotting.stripchart" \
+ -labelfont *-helvetica*-bold-r-*-14-* -label "Strip Chart"
+
+.top.hier entry configure "Plotting.graph" -icon graph.img
+.top.hier entry configure "Plotting.barchart" -icon barchart.img
+
+table .top \
+ 0,0 .top.hier -fill both -rspan 2 \
+ 0,1 .top.yscroll -fill y -rspan 2 \
+ 0,2 .top.mesg -padx 2 -pady { 8 2 } -fill both \
+ 0,2 .top.title -anchor nw -padx { 8 8 } \
+ 1,2 .top.tab -fill both -rspan 2 \
+ 2,0 .top.xscroll -fill x
+
+table configure .top c1 r2 -resize none
+table configure .top c0 -width { 3i {} }
+table configure .top c2 -width { 4i {} }
+table . \
+ .top -fill both
+
+proc DoExit { code } {
+ global progStatus
+ set progStatus 1
+ exit $code
+}
+
+container .top.tab.f1 -relief raised -bd 2 -takefocus 0
+.top.tab tab configure Example -window .top.tab.f1
+
+if 1 {
+ set cmd "xterm -fn fixed -geom +4000+4000"
+ eval bgexec programInfo(xterm) $cmd &
+ set programInfo(lastProgram) xterm
+ .top.tab.f1 configure -command $cmd
+}
+wm protocol . WM_DELETE_WINDOW { destroy . }
diff --git a/blt/demos/treeview1.tcl b/blt/demos/treeview1.tcl
new file mode 100755
index 00000000000..ce5ff20e06d
--- /dev/null
+++ b/blt/demos/treeview1.tcl
@@ -0,0 +1,191 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+proc SortColumn { column } {
+ set old [.t sort cget -column]
+ set decreasing 0
+ if { "$old" == "$column" } {
+ set decreasing [.t sort cget -decreasing]
+ set decreasing [expr !$decreasing]
+ }
+ .t sort configure -decreasing $decreasing -column $column -mode integer
+ .t configure -flat yes
+ .t sort auto yes
+
+ blt::busy hold .t
+ update
+ blt::busy release .t
+}
+
+proc FormatSize { size } {
+ set string ""
+ while { $size > 0 } {
+ set rem [expr $size % 1000]
+ set size [expr $size / 1000]
+ if { $size > 0 } {
+ set rem [format "%03d" $rem]
+ }
+ if { $string != "" } {
+ set string "$rem,$string"
+ } else {
+ set string "$rem"
+ }
+ }
+ return $string
+}
+
+array set modes {
+ 0 ---
+ 1 --x
+ 2 -w-
+ 3 -wx
+ 4 r--
+ 5 r-x
+ 6 rw-
+ 7 rwx
+}
+
+proc FormatMode { mode } {
+ global modes
+
+ set mode [format %o [expr $mode & 07777]]
+ set owner $modes([string index $mode 0])
+ set group $modes([string index $mode 1])
+ set world $modes([string index $mode 2])
+
+ return "${owner}${group}${world}"
+}
+
+proc Find { tree parent dir } {
+ global count
+ set saved [pwd]
+
+ cd $dir
+ foreach f [glob -nocomplain *] {
+ set name [file tail $f]
+ if { [catch { file stat $f info }] != 0 } {
+ set node [$tree insert $parent -label $name]
+ } else {
+ if { $info(type) == "file" } {
+ set info(type) @blt::tv::normalOpenFolder
+ } else {
+ set info(type) ""
+ }
+ set info(mtime) [clock format $info(mtime) -format "%b %d, %Y"]
+ set info(atime) [clock format $info(atime) -format "%b %d, %Y"]
+ set info(ctime) [clock format $info(ctime) -format "%b %d, %Y"]
+ set info(size) [FormatSize $info(size)]
+ set info(mode) [FormatMode $info(mode)]
+ set node [$tree insert $parent -label $name -data [array get info]]
+ }
+ incr count
+ if { [file type $f] == "directory" } {
+ Find $tree $node $f
+ }
+ }
+ cd $saved
+}
+
+
+proc GetAbsolutePath { dir } {
+ set saved [pwd]
+ cd $dir
+ set path [pwd]
+ cd $saved
+ return $path
+}
+
+option add *TreeView.font { Courier 12 }
+#option add *TreeView.Button.background grey95
+#option add *TreeView.Button.activeBackground grey90
+#option add *TreeView.Column.background grey90
+option add *TreeView.Column.titleShadow { grey70 1 }
+#option add *TreeView.Column.titleFont { Helvetica 12 bold }
+option add *TreeView.Column.font { Courier 12 }
+
+set top [GetAbsolutePath ..]
+#set top [GetAbsolutePath /home/gah]
+set trim "$top"
+
+set tree [tree create]
+treeview .t \
+ -width 0 \
+ -yscrollcommand { .vs set } \
+ -xscrollcommand { .hs set } \
+ -yscrollincrement 1 \
+ -selectmode single \
+ -tree $tree
+
+.t column configure treeView -text file
+.t column insert 0 mtime atime gid
+.t column insert end nlink mode type ctime uid ino size dev
+.t column configure uid -background \#eaeaff -font fixed -relief raised -bd 1
+.t column configure mtime -hide no -bg \#ffeaea -relief raised -bd 1
+.t column configure size gid nlink uid ino dev -justify right -edit yes
+.t column configure treeView -hide no -edit no
+.t text configure -selectborderwidth 0
+focus .t
+
+scrollbar .vs -orient vertical -command { .t yview }
+scrollbar .hs -orient horizontal -command { .t xview }
+
+table . \
+ 0,0 .t -fill both \
+ 0,1 .vs -fill y \
+ 1,0 .hs -fill x
+table configure . c1 r1 -resize none
+
+set count 0
+Find $tree root $top
+puts "$count entries"
+
+$tree find root -glob *.c -addtag "c_files"
+$tree find root -glob *.h -addtag "header_files"
+$tree find root -glob *.tcl -addtag "tcl_files"
+
+.t entry configure "c_files" -foreground green4
+.t entry configure "header_files" -foreground cyan4
+.t entry configure "tcl_files" -foreground red4
+
+.t column bind all <ButtonRelease-3> {
+ %W configure -flat no
+}
+
+foreach column [.t column names] {
+ .t column configure $column -command [list SortColumn $column]
+}
+
+# Create a second treeview that shares the same tree.
+if 0 {
+toplevel .top
+treeview .top.t2 -tree $tree -yscrollcommand { .top.sbar set }
+scrollbar .top.sbar -command { .top.t2 yview }
+pack .top.t2 -side left -expand yes -fill both
+pack .top.sbar -side right -fill y
+}
+
diff --git a/blt/demos/winop1.tcl b/blt/demos/winop1.tcl
new file mode 100755
index 00000000000..cf39bee6797
--- /dev/null
+++ b/blt/demos/winop1.tcl
@@ -0,0 +1,62 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+if { [ file exists ./images/sample.gif] } {
+ set src [image create photo -file ./images/sample.gif]
+} else {
+ puts stderr "no image file"
+ exit 0
+}
+
+set width [image width $src]
+set height [image height $src]
+
+option add *Label.font *helvetica*10*
+option add *Label.background white
+label .l0 -image $src
+label .header0 -text "$width x $height"
+label .footer0 -text "100%"
+. configure -bg white
+
+for { set i 2 } { $i <= 10 } { incr i } {
+ set iw [expr $width / $i]
+ set ih [expr $height / $i]
+ set r [format %6g [expr 100.0 / $i]]
+ image create photo r$i -width $iw -height $ih
+ winop resample $src r$i sinc
+ label .header$i -text "$iw x $ih"
+ label .footer$i -text "$r%"
+ label .l$i -image r$i
+ table . \
+ 0,$i .header$i \
+ 1,$i .l$i \
+ 2,$i .footer$i
+ update
+}
+
+
diff --git a/blt/demos/winop2.tcl b/blt/demos/winop2.tcl
new file mode 100755
index 00000000000..add07c73427
--- /dev/null
+++ b/blt/demos/winop2.tcl
@@ -0,0 +1,57 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+source scripts/demo.tcl
+
+set file images/qv100.t.gif
+
+if { [file exists $file] } {
+ set src [image create photo -file $file]
+} else {
+ puts stderr "no image file"
+ exit 0
+}
+
+set width [image width $src]
+set height [image height $src]
+
+option add *Label.font *helvetica*10*
+option add *Label.background white
+
+set i 0
+foreach r { 0 90 180 270 360 45 } {
+ set dest [image create photo]
+ winop image rotate $src $dest $r
+ label .footer$i -text "$r degrees"
+ label .l$i -image $dest
+ table . \
+ 0,$i .l$i \
+ 1,$i .footer$i
+ update
+ incr i
+}
+
+
diff --git a/blt/examples/calendar.tcl b/blt/examples/calendar.tcl
new file mode 100755
index 00000000000..658bc6f2fea
--- /dev/null
+++ b/blt/examples/calendar.tcl
@@ -0,0 +1,141 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+#source scripts/demo.tcl
+
+set file ../demos/images/chalk.gif
+set active ../demos/images/rain.gif
+
+image create photo calendar.texture.1 -file $file
+image create photo calendar.texture.2 -file $active
+
+option add *Tile calendar.texture.1
+
+option add *HighlightThickness 0
+option add *calendar.weekframe*Tile calendar.texture.2
+option add *Calendar.Label.borderWidth 0
+option add *Calendar.Label.relief sunken
+option add *Calendar.Frame.borderWidth 2
+option add *Calendar.Frame.relief raised
+option add *Calendar.Label.font { Helvetica 11 }
+option add *Calendar.Label.foreground navyblue
+option add *button.foreground navyblue
+option add *background grey85
+option add *button.activeForeground red
+option add *button.activeBackground blue4
+option add *Label.ipadX 200
+
+array set monthInfo {
+ Jan { January 31 }
+ Feb { February 28 }
+ Mar { March 31 }
+ Apr { April 30 }
+ May { May 31 }
+ Jun { June 30 }
+ Jul { July 31 }
+ Aug { August 31 }
+ Sep { September 30 }
+ Oct { October 31 }
+ Nov { November 30 }
+ Dec { December 31 }
+}
+
+option add *tile calendar.texture.2
+set abbrDays { Sun Mon Tue Wed Thu Fri Sat }
+
+proc Calendar { weekday day month year } {
+ global monthInfo abbrDays
+
+ set wkdayOffset [lsearch $abbrDays $weekday]
+ if { $wkdayOffset < 0 } {
+ error "Invalid week day \"$weekday\""
+ }
+ set dayOffset [expr ($day-1)%7]
+ if { $wkdayOffset < $dayOffset } {
+ set wkdayOffset [expr $wkdayOffset+7]
+ }
+ set wkday [expr $wkdayOffset-$dayOffset-1]
+ if { [info commands .calendar] == ".calendar" } {
+ destroy .calendar
+ }
+ frame .calendar -class Calendar -width 3i -height 3i
+
+ if ![info exists monthInfo($month)] {
+ error "Invalid month \"$month\""
+ }
+
+ set info $monthInfo($month)
+ label .calendar.month \
+ -text "[lindex $info 0] $year" \
+ -font { Courier 14 bold }
+ table .calendar .calendar.month 1,0 -cspan 7 -pady 10
+
+ set cnt 0
+ frame .calendar.weekframe -relief sunken -bd 1
+ table .calendar .calendar.weekframe 2,0 -columnspan 7 -fill both
+ foreach dayName $abbrDays {
+ set name [string tolower $dayName]
+ label .calendar.$name \
+ -text $dayName \
+ -font { Helvetica 12 }
+ table .calendar .calendar.$name 2,$cnt -pady 2 -padx 2
+ incr cnt
+ }
+ table configure .calendar c* r2 -pad 4
+ set week 0
+ set numDays [lindex $info 1]
+ for { set cnt 1 } { $cnt <= $numDays } { incr cnt } {
+ label .calendar.day${cnt} -text $cnt
+ if { $cnt == $day } {
+ .calendar.day${cnt} configure -relief sunken -bd 1
+ }
+ incr wkday
+ if { $wkday == 7 } {
+ incr week
+ set wkday 0
+ }
+ table .calendar .calendar.day${cnt} $week+3,$wkday \
+ -fill both -ipadx 10 -ipady 4
+ }
+ frame .calendar.quit -bd 1 -relief sunken
+ button .calendar.quit.button -command { exit } -text {Quit} -bd 2
+ table .calendar.quit \
+ .calendar.quit.button -padx 4 -pady 4
+ table .calendar \
+ .calendar.quit $week+4,5 -cspan 2 -pady 4
+ table . \
+ .calendar -fill both
+ table configure .calendar r0 -resize none
+ table configure .calendar c0 c6
+}
+
+set date [clock format [clock seconds] -format {%a %b %d %Y}]
+scan $date { %s %s %d %d } weekday month day year
+
+Calendar $weekday $day $month $year
diff --git a/blt/examples/form.tcl b/blt/examples/form.tcl
new file mode 100755
index 00000000000..0336d219755
--- /dev/null
+++ b/blt/examples/form.tcl
@@ -0,0 +1,1060 @@
+#!../src/bltwish
+
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+#source scripts/demo.tcl
+
+option add *takeFocus 0
+
+set file1 ../demos/images/chalk.gif
+set file2 ../demos/images/tan_paper.gif
+image create photo texture1 -file $file1
+image create photo texture2 -file $file2
+option add *Frame.Tile texture1
+option add *Toplevel.Tile texture1
+option add *Label.Tile texture1
+option add *Scrollbar.tile texture1
+#option add *Scrollbar.activeTile texture2
+option add *Button.tile texture1
+#option add *Button.activeTile texture2
+option add *HighlightThickness 0
+option add *Entry.highlightThickness 2
+
+#
+# Initialization of global variables and Tk resource database
+#
+#
+# Resources available
+#
+# Tk.normalBgColor:
+# Tk.normalFgColor:
+# Tk.focusHighlightColor:
+# Tk.statusFont:
+# Tk.titleFont:
+# Tk.headingFont:
+# Tk.subheadingFont:
+# Tk.entryFont:
+# Tk.textFont:
+#
+
+#debug 50
+bitmap define attlogo { { 60 30 } {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xf8, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x83, 0xf9, 0x87, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xf9, 0x87, 0x7f, 0x00, 0x40, 0x00, 0xf0, 0xc7,
+ 0xc3, 0x38, 0x0c, 0x00, 0xc0, 0xff, 0xff, 0xc7, 0xc3, 0x7c, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0xc2, 0x6c, 0x0c, 0x00, 0x40, 0x00, 0xf8, 0x67,
+ 0xc6, 0x9c, 0x0d, 0x00, 0xc0, 0xff, 0xff, 0xe7, 0xc7, 0xf8, 0x0d, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0xc7, 0xec, 0x0c, 0x00, 0x80, 0x01, 0xfe, 0x33,
+ 0xcc, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x33, 0xcc, 0xb8, 0x0d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+}
+
+bitmap define globe_00 { { 32 32 } {
+ 00 40 02 00 00 1c 3c 00 00 01 fe 00 80 80 fe 03 60 00 ff 07 10 c0 f1 0f
+ 00 80 c0 1f 00 c0 07 3f 00 c0 ff 3f 00 f0 ff 4f 02 f0 ff 5d 00 f0 ff 1b
+ 00 f0 ff 8f 02 f0 ff 0f 06 e0 fc 0f 0e 00 f8 0f 0f 00 f8 07 3f 00 f8 03
+ 7e 00 f0 03 7e 00 f0 03 3e 00 f0 0b 3c 00 f0 09 3c 00 f0 01 18 00 f0 00
+ 18 00 70 00 10 00 00 00 10 00 00 00 20 00 00 00 40 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 1f 00 }
+}
+
+bitmap define globe_01 { { 32 32 } {
+ 00 c0 00 00 00 34 38 00 00 02 e8 00 80 01 fa 03 e0 00 fc 07 30 00 e6 0f
+ 10 00 86 1f 08 00 3e 3c 04 00 ff 3f 04 80 ff 5f 02 80 ff 3f 00 80 ff 2f
+ 00 80 ff 3f 0c 00 ff 3f 1c 00 ee 3f 3c 00 c0 3f 7e 00 c0 1f fe 01 80 1f
+ fc 03 80 1f fc 01 80 1f fc 01 80 2f f8 01 80 0f f0 00 80 17 f0 00 80 03
+ f0 00 80 03 60 00 00 00 60 00 00 00 40 00 00 00 80 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 1e 00 }
+}
+
+bitmap define globe_02 { { 32 32 } {
+ 00 c0 01 00 00 60 30 00 00 04 f0 00 80 07 e0 03 e0 01 f0 07 f0 00 38 0f
+ 30 00 10 1e 18 00 f0 30 04 00 f8 3f 10 00 f8 7f 12 00 fc 7f 02 00 fc 7f
+ 04 00 fc 7f 74 00 f8 7f f0 00 70 7f f8 01 00 7e f8 03 00 7e f8 0f 00 7c
+ f8 1f 00 3c f0 1f 00 3c f0 0f 00 3e e0 0f 00 5e c0 07 00 1c c0 03 00 0e
+ c0 03 00 04 80 01 00 00 80 01 00 00 80 01 00 00 00 01 00 00 00 00 00 00
+ 00 00 00 00 00 10 1c 00 }
+}
+
+bitmap define globe_03 { { 32 32 } {
+ 00 c0 01 00 00 dc 20 00 00 09 c0 00 80 1f a0 03 e0 07 c0 07 f0 01 c0 0c
+ f8 00 40 18 78 00 c0 23 08 00 c0 3f 04 00 e0 7f 54 00 e0 7f 0c 00 c0 7f
+ 10 00 c0 ff d0 01 c0 ff c0 03 80 fb e0 0f 00 f0 e0 1f 00 f0 e0 ff 00 f0
+ e0 ff 00 70 c0 ff 00 70 c0 7f 00 70 00 7f 00 70 00 3f 00 30 00 1f 00 38
+ 00 1f 00 18 00 0e 00 00 00 06 00 00 00 02 00 00 00 04 00 00 00 00 00 00
+ 00 00 00 00 00 20 18 00 }
+}
+
+bitmap define globe_04 { { 32 32 } {
+ 00 c0 03 00 00 7c 03 00 00 13 00 00 80 7f c0 03 c0 1f 00 07 e0 0f 00 0d
+ f0 03 00 10 f0 01 00 0e 38 01 00 3e 10 00 00 7f 50 00 00 7f 30 00 00 7f
+ 40 00 00 ff 00 1e 00 fe 00 3f 00 ec 00 7f 00 c0 00 ff 00 c0 00 ff 07 c0
+ 00 ff 0f c0 00 fe 07 c0 00 fe 07 c0 00 f8 03 40 00 f8 01 60 00 f8 00 20
+ 00 f8 00 20 00 38 00 00 00 18 00 00 00 18 00 00 00 18 00 00 00 00 00 00
+ 00 00 00 00 00 40 10 00 }
+}
+
+bitmap define globe_05 { { 32 32 } {
+ 00 c0 03 00 00 bc 06 00 00 cf 00 00 80 ff 01 02 c0 7f 00 06 c0 3f 00 0e
+ e0 1f 00 14 e0 0f 00 18 e0 00 00 38 60 00 00 78 40 08 00 78 c0 01 00 78
+ 00 02 00 f8 00 f0 00 f0 00 f0 01 b0 00 f8 07 80 00 f8 0f 80 00 f8 3f 00
+ 00 f8 7f 00 00 f0 3f 80 00 f0 3f 80 00 c0 1f 00 00 c0 0f 00 00 c0 07 40
+ 00 c0 07 00 00 c0 01 00 00 e0 00 00 00 60 00 00 00 40 00 00 00 00 00 00
+ 00 00 00 00 00 80 10 00 }
+}
+
+bitmap define globe_06 { { 32 32 } {
+ 00 80 07 00 00 7c 0d 00 00 9f 03 00 00 ff 07 02 00 ff 03 04 80 ff 00 08
+ c0 7f 00 00 80 3f 00 30 80 07 00 20 00 03 00 60 00 03 00 60 00 0e 00 60
+ 00 10 00 e0 00 80 07 c0 00 80 0f c0 00 80 3f 00 00 c0 7f 00 00 c0 ff 01
+ 00 c0 ff 03 00 80 ff 01 00 80 ff 01 00 00 fe 00 00 00 7e 00 00 00 3e 00
+ 00 00 1f 00 00 00 0f 00 00 00 03 00 00 00 03 00 00 00 01 00 00 00 00 00
+ 00 00 00 00 00 00 01 00 }
+}
+
+bitmap define globe_07 { { 32 32 } {
+ 00 80 07 00 00 fc 1a 00 00 7d 02 00 00 fe 1f 00 00 fe 0f 00 00 fe 07 00
+ 00 ff 03 00 00 fe 01 20 00 1c 01 00 00 1c 00 40 00 18 00 40 00 70 00 00
+ 00 80 00 80 00 00 39 80 00 00 7c 00 00 00 fc 01 00 00 fe 03 00 00 fe 0f
+ 00 00 fc 0f 00 00 fc 0f 00 00 f8 07 00 00 f0 07 00 00 f0 03 00 00 f0 01
+ 00 00 f8 00 00 00 38 00 00 00 18 00 00 00 0c 00 00 00 04 00 00 00 00 00
+ 00 00 00 00 00 00 02 00 }
+}
+
+bitmap define globe_08 { { 32 32 } {
+ 00 00 07 00 00 fc 25 00 00 f8 19 00 00 f8 7f 00 00 f8 3f 00 00 f8 1f 00
+ 00 f8 1f 00 00 f8 0f 00 00 f0 08 00 00 f0 00 00 00 c0 04 00 00 80 03 00
+ 00 00 0c 00 00 00 c8 01 00 00 e0 03 00 00 e0 0f 00 00 e0 0f 00 00 f0 3f
+ 00 00 e0 3f 00 00 e0 3f 00 00 c0 1f 00 00 80 1f 00 00 80 0f 00 00 c0 07
+ 00 00 c0 03 00 00 c0 01 00 00 60 00 00 00 30 00 00 00 10 00 00 00 00 00
+ 00 00 00 00 00 00 04 00 }
+}
+
+bitmap define globe_09 { { 32 32 } {
+ 00 00 03 00 00 fc 27 00 00 f0 13 00 00 e0 ff 00 00 e0 ff 01 00 e0 7f 00
+ 00 e0 7f 00 00 c0 7f 00 00 80 47 00 00 80 07 00 00 00 26 00 00 00 1c 00
+ 00 00 60 00 00 00 40 0e 00 00 00 1f 00 00 00 3f 00 00 00 3f 00 00 00 7f
+ 00 00 00 7f 00 00 00 7f 00 00 00 7e 00 00 00 3c 00 00 00 3e 00 00 00 1e
+ 00 00 00 0f 00 00 00 07 00 00 80 01 00 00 80 00 00 00 40 00 00 00 00 00
+ 00 00 00 00 00 00 08 00 }
+}
+
+bitmap define globe_10 { { 32 32 } {
+ 00 00 06 00 00 f4 2f 00 00 c8 4f 00 00 80 ff 01 00 80 ff 01 00 80 ff 01
+ 00 00 ff 01 00 00 fe 01 00 00 3c 00 00 00 3c 00 00 00 30 04 00 00 e0 00
+ 00 00 00 01 00 00 00 3a 00 00 00 38 00 00 00 78 00 00 00 f8 00 00 00 fc
+ 00 00 00 f8 00 00 00 f8 00 00 00 f8 00 00 00 70 00 00 00 70 00 00 00 38
+ 00 00 00 18 00 00 00 0c 00 00 00 06 00 00 00 03 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 10 00 }
+}
+
+bitmap define globe_11 { { 32 32 } {
+ 00 00 06 00 00 ec 1f 00 00 91 9f 00 00 00 fe 03 00 00 fc 07 00 00 fc 07
+ 00 00 fc 07 00 00 f0 07 00 00 f0 01 00 00 e0 00 00 00 80 05 00 00 00 07
+ 00 00 00 08 00 00 00 60 00 00 00 e0 00 00 00 e0 00 00 00 e0 00 00 00 e0
+ 01 00 00 e0 00 00 00 e0 00 00 00 e0 00 00 00 40 00 00 00 60 00 00 00 60
+ 00 00 00 30 00 00 00 10 40 00 00 08 40 00 00 04 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 10 00 }
+}
+
+bitmap define globe_12 { { 32 32 } {
+ 00 00 04 00 00 dc 3f 00 00 42 7e 00 00 00 f8 03 20 00 f0 07 10 00 f0 0f
+ 00 00 e0 0f 00 00 c0 0f 00 00 00 07 00 00 00 06 00 00 00 14 00 00 00 18
+ 00 00 00 20 00 00 00 80 00 00 00 80 00 00 00 80 00 00 00 80 01 00 00 80
+ 00 00 00 80 00 00 00 80 02 00 00 80 02 00 00 00 04 00 00 40 04 00 00 40
+ 08 00 00 20 08 00 00 00 00 00 00 10 00 00 00 08 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 }
+}
+
+bitmap define globe_13 { { 32 32 } {
+ 00 00 04 00 00 bc 3f 00 00 01 79 00 80 00 e0 03 60 00 c0 07 10 00 80 0f
+ 00 00 80 1f 08 00 00 1e 00 00 00 1c 00 00 00 58 00 00 00 10 00 00 00 20
+ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 06 00 00 00
+ 04 00 00 00 00 00 00 00 02 00 00 00 0e 00 00 00 0c 00 00 00 1c 00 00 00
+ 18 00 00 00 30 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 10 00 00 }
+}
+
+bitmap define globe_14 { { 32 32 } {
+ 00 00 00 00 00 fc 3f 00 00 03 e6 00 80 01 c0 03 60 00 00 07 30 00 00 0f
+ 00 00 00 1e 00 00 00 38 04 00 00 30 00 00 00 30 02 00 00 00 00 00 00 40
+ 00 00 00 80 00 00 00 00 02 00 00 00 01 00 00 00 01 00 00 00 18 00 00 00
+ 00 00 00 00 00 00 00 00 14 00 00 00 3c 00 00 00 3c 00 00 00 7c 00 00 00
+ 78 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 30 00 00 }
+}
+
+bitmap define globe_15 { { 32 32 } {
+ 00 00 00 00 00 fc 3d 00 00 27 c8 00 80 13 00 03 e0 01 00 06 70 00 00 0c
+ 10 00 00 18 18 00 00 30 0c 00 00 20 0c 00 00 40 02 00 00 00 02 00 00 00
+ 00 00 00 00 00 00 00 00 01 00 00 00 03 00 00 00 13 00 00 00 64 00 00 00
+ c0 00 00 00 00 00 00 00 30 00 00 00 f8 00 00 00 f8 01 00 00 f8 03 00 00
+ f0 03 00 00 80 03 00 00 00 80 00 00 00 40 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 70 00 00
+ }
+}
+
+bitmap define globe_16 { { 32 32 } {
+ 00 00 00 00 00 fc 3b 00 00 9f a0 00 80 4f 00 02 e0 0f 00 04 f0 01 00 08
+ 70 00 00 10 38 00 00 20 3c 00 00 00 1c 00 00 00 06 00 00 00 04 00 00 00
+ 04 00 00 00 00 00 00 00 20 00 00 00 0a 00 00 00 0a 00 00 00 00 03 00 00
+ 28 06 00 00 00 00 00 00 c0 02 00 00 e0 07 00 00 f0 0f 00 00 e0 1f 00 00
+ e0 1f 00 00 00 0c 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 00 00 }
+}
+
+bitmap define globe_17 { { 32 32 } {
+ 00 00 00 00 00 fc 37 00 00 3f 42 00 80 3f 01 02 e0 1f 00 00 f0 07 00 00
+ f0 11 00 00 f8 04 00 00 fc 00 00 00 7c 00 00 00 1a 00 00 00 9a 00 00 00
+ 18 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 28 00 00 00 08 18 00 00
+ 00 30 00 00 00 10 00 00 00 17 00 00 00 3f 00 00 c0 7f 00 00 80 7f 00 00
+ 80 7f 00 00 00 70 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 01 00
+ }
+}
+
+bitmap define globe_18 { { 32 32 } {
+ 00 00 00 00 00 fc 2f 00 00 ff 84 00 80 ff 04 00 e0 7f 00 00 f0 9f 00 00
+ f0 97 00 00 f8 27 00 00 fc 07 00 00 fc 03 00 00 6c 00 00 00 64 00 00 00
+ 60 04 00 00 40 00 00 00 20 00 00 00 20 01 00 00 a0 01 00 00 00 c0 05 00
+ 00 88 00 00 00 00 00 00 00 38 01 00 00 fc 01 00 00 fe 03 00 00 fe 03 00
+ 00 fc 03 00 00 80 03 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 03 00
+ }
+}
+
+bitmap define globe_19 { { 32 32 } {
+ 00 40 00 00 00 fc 3f 00 00 ff 13 00 80 ff 13 00 e0 ff 03 00 f0 ff 00 00
+ f0 9f 00 00 f8 3f 00 00 fc 3f 00 00 f8 1f 00 00 ba 07 00 00 98 23 00 00
+ 08 03 00 00 08 00 00 00 00 00 00 00 80 09 00 00 00 0d 01 00 00 21 0e 00
+ 00 00 1c 00 00 00 00 00 00 c0 09 00 00 e0 0f 00 00 f0 1f 00 00 f0 1f 00
+ 00 f0 1f 00 00 00 0e 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 07 00
+ }
+}
+
+bitmap define globe_20 { { 32 32 } {
+ 00 00 00 00 00 fc 3f 00 00 ff 07 00 80 ff 0f 00 e0 ff 0f 00 f0 ff 13 00
+ f0 ff 10 00 f8 ff 00 00 fc ff 01 00 f4 ff 00 00 e6 1e 00 00 62 1c 01 00
+ 20 18 00 00 20 10 00 00 01 80 00 00 01 cc 00 00 01 68 08 00 00 00 60 00
+ 00 00 c0 00 00 00 00 00 00 00 5c 00 00 00 7e 00 00 80 ff 00 00 80 ff 00
+ 00 80 ff 00 00 00 70 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 0f 00
+ }
+}
+
+bitmap define globe_21 { { 32 32 } {
+ 00 80 00 00 00 fc 3f 00 00 ff 1f 00 80 ff bf 00 e0 ff 3f 00 f0 ff 1f 00
+ f8 ff 17 00 f8 ff 27 00 ec ff 0f 00 8c ff 07 00 9e f7 00 00 0e e3 00 00
+ 06 c1 00 00 06 81 10 00 03 40 04 00 03 20 06 00 03 40 06 00 01 80 00 03
+ 01 00 00 02 02 00 00 00 02 00 e0 02 02 00 f0 03 00 00 fc 03 00 00 fc 03
+ 00 00 fc 03 00 00 c0 01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_22 { { 32 32 } {
+ 00 00 01 00 00 fc 3f 00 00 ff 3f 00 80 ff 7f 00 e0 ff ff 00 f0 ff 7f 00
+ f0 ff 1f 00 e0 ff 3f 00 fc ff 3f 00 34 fe 3f 00 76 bc 07 00 36 1c 07 00
+ 0e 08 0e 00 1e 08 80 00 0f 00 02 00 0f 00 20 00 07 00 36 00 07 00 04 08
+ 07 00 00 18 06 00 00 00 16 00 00 0b 16 00 80 0f 04 00 e0 0f 04 00 e0 0f
+ 08 00 e0 0f 00 00 00 06 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_23 { { 32 32 } {
+ 00 00 00 00 00 fc 3f 00 00 ff 7f 00 80 ff ff 01 e0 ff ff 01 e0 ff ff 01
+ e8 ff ff 00 c0 ff ff 00 fc fe ff 01 dc f2 ff 01 de e3 3d 00 de e1 38 02
+ 7e 40 70 00 fe 40 00 04 7f 00 00 00 3e 00 30 01 3e 00 a0 01 1e 00 20 20
+ 1e 00 00 20 1c 00 00 00 9c 00 00 3c 1c 00 00 3e 1c 00 00 3f 18 00 80 3f
+ 10 00 00 1f 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_24 { { 32 32 } {
+ 00 00 02 00 00 fc 3f 00 00 fe ff 00 80 ff ff 01 c0 ff ff 03 80 ff ff 03
+ e0 ff ff 03 18 ff ff 03 fc ff ff 07 7c 87 ff 07 fe 1f ef 01 fe 0e c6 01
+ fe 01 82 03 fe 03 02 00 ff 03 00 08 fc 01 80 09 fc 00 00 0d fc 00 00 00
+ f8 00 00 80 f8 00 00 00 f8 00 00 20 78 02 00 70 70 02 00 7c 70 00 00 3c
+ 60 00 00 3c 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_25 { { 32 32 } {
+ 00 00 00 00 00 f0 3f 00 00 fc ff 00 80 ff ff 03 80 ff ff 07 a0 ff ff 07
+ 10 ff ff 07 30 f8 ff 0f f8 df ff 1f fc 3b fc 1f fc fb 78 07 fe 77 30 0e
+ fe 1f 30 0c fe 3f 00 48 fe 1f 00 00 f0 0f 00 24 f0 07 00 a0 f0 07 00 08
+ e0 07 00 00 e0 07 00 00 e0 27 00 c0 e0 13 00 40 c0 13 00 70 c0 03 00 70
+ 80 01 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_26 { { 32 32 } {
+ 00 40 00 00 00 e0 3f 00 00 e8 ff 00 00 fc ff 03 00 ff ff 07 c0 fe ff 0f
+ 40 f0 ff 1f e0 e0 ff 1f f0 ff fe 3f f8 df e1 3f f8 df c7 1b fc bf 83 19
+ fc ff 80 30 fc ff 01 20 f8 ff 00 00 c0 ff 00 00 c0 7f 00 e0 80 3f 00 20
+ 80 3f 00 00 80 3f 00 00 80 3f 01 80 80 9f 00 00 00 9f 00 40 00 0f 00 60
+ 00 0e 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 f0 1f 00 }
+}
+
+bitmap define globe_27 { { 32 32 } {
+ 00 80 00 00 00 c4 3f 00 00 f0 ff 00 00 fe ff 03 00 fe ff 07 00 eb ff 0f
+ 80 c9 ff 1f 80 07 ff 3f c0 ff f7 3f e0 ff 0e 7f f0 ff 3e 6e f0 ff 1d 64
+ f0 ff 07 44 f0 ff 0f 00 60 ff 0f 00 00 fe 07 40 00 fe 03 00 01 fc 01 00
+ 01 fc 01 00 00 fc 01 00 00 fc 09 00 02 fc 08 00 00 f8 04 00 00 78 00 40
+ 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 e0 1f 00 }
+}
+
+bitmap define globe_28 { { 32 32 } {
+ 00 00 00 00 00 88 3f 00 00 40 ff 00 00 e8 ff 03 00 f8 ff 07 00 8c ff 0f
+ 00 06 fe 1f 00 1e f8 3f 00 ff bf 3f 80 ff 77 7c 80 ff ff 79 c0 ff ef 10
+ c0 ff 3f 90 c0 ff 7f 00 81 fb 7f 00 01 f0 3f 00 01 f0 1f 00 03 e0 1f 00
+ 07 e0 0f 00 02 c0 1f 00 02 e0 5f 00 06 e0 47 00 04 c0 27 00 04 c0 03 00
+ 00 80 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 c0 1f 00 }
+}
+
+bitmap define globe_29 { { 32 32 } {
+ 00 40 01 00 00 0c 3f 00 00 80 fd 00 00 a0 ff 03 20 e0 ff 07 00 30 fd 0f
+ 00 10 f4 1f 00 f8 c0 3f 00 f8 ff 3f 00 fc bf 73 00 fe ff 67 00 fe 7f 47
+ 00 fe ff 41 00 fe ff 03 01 dc ff 03 03 00 ff 01 07 80 ff 00 0f 00 ff 00
+ 1f 00 7e 00 0e 00 fe 00 0e 00 ff 02 0e 00 3f 01 0c 00 3e 01 0c 00 1e 00
+ 08 00 1c 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 80 1f 00 }
+}
+
+# ----------------------------------------------------------------
+#
+# SetOption --
+#
+# Sets the option array associated with the resource. It
+# first check to see if the resource exists in the option
+# data base, otherwise it uses the default value given.
+#
+#
+# Arguments
+# name -- Name of the resource. Used as index into
+# the option array.
+# value -- default value given.
+# Globals
+# pq_dict -- Associative array where the option resources
+# are stored.
+#
+# ----------------------------------------------------------------
+
+proc SetOption { name value } {
+ global pq_dict
+ set widgetOption [option get . $name Tk]
+ if { $widgetOption != "" } {
+ set value $widgetOption
+ }
+ set pq_dict($name) $value
+}
+
+set pq_dict(textIndex) {}
+
+set pq_dict(entryNames) {
+ last first middle
+ area exch ext
+ com org tl
+ room oldloc loc
+ street
+ city state zip
+ ema
+}
+
+set pq_dict(numEntries) [llength $pq_dict(entryNames)]
+set pq_dict(index) 0
+set pq_dict(defaults) {}
+
+set cnt 0
+foreach name $pq_dict(entryNames) {
+ if { $cnt > 0 } {
+ set pq_dict(format) $pq_dict(format):%$name
+ } else {
+ set pq_dict(format) %$name
+ }
+ incr cnt
+}
+
+set visual [winfo screenvisual .]
+if { ($visual == "staticgray") || ($visual == "grayscale") } {
+ option add *Entry.background white
+ option add *Text.background white
+ set pq_dict(visual) mono
+} else {
+ set depth [winfo screendepth .]
+ if { $depth < 8 } {
+ SetOption normalBgColor grey
+ SetOption normalFgColor black
+ SetOption focusHighlightColor white
+ } else {
+#fff4de
+ SetOption normalBgColor grey90
+ SetOption normalFgColor #da5f5f
+ SetOption normalFgColor navyblue
+ SetOption focusHighlightColor #fffdf8
+ }
+ option add *Entry.background $pq_dict(normalBgColor) widgetDefault
+ option add *Text.background $pq_dict(normalBgColor) widgetDefault
+ option add *Label.foreground $pq_dict(normalFgColor) widgetDefault
+ option add *Button.foreground $pq_dict(normalFgColor) widgetDefault
+ set pq_dict(visual) color
+}
+
+SetOption statusFont -*-Helvetica-Medium-R-*-*-14-*-*-*-*-*-*-*
+SetOption titleFont -*-Helvetica-Bold-R-*-*-14-*-*-*-*-*-*-*
+SetOption headingFont -*-Helvetica-Medium-R-*-*-14-*-*-*-*-*-*-*
+SetOption subheadingFont -*-Helvetica-Medium-R-*-*-12-*-*-*-*-*-*-*
+SetOption entryFont -*-Courier-Medium-R-*-*-12-*-*-*-*-*-*-*
+SetOption textFont -*-Courier-Bold-R-*-*-12-*-*-*-*-*-*-*
+#SetOption entryFont fixed
+#SetOption textFont fixed
+
+option add *Label.borderWidth 0 widgetDefault
+option add *Entry.relief sunken widgetDefault
+option add *Entry.width 11 widgetDefault
+option add *Entry.borderWidth 2 widgetDefault
+option add *Entry.font $pq_dict(entryFont) widgetDefault
+option add *Text.font $pq_dict(textFont) widgetDefault
+option add *Text.width 35 widgetDefault
+option add *Text.height 10 widgetDefault
+option add *Scrollbar.relief flat widgetDefault
+option add *Scrollbar.minSlider 10 widgetDefault
+option add *Button.padY 6
+option add *Text.relief sunken widgetDefault
+option add *Text.borderWidth 2 widgetDefault
+
+foreach name $pq_dict(entryNames) {
+ option add *${name}_label.font $pq_dict(subheadingFont) widgetDefault
+}
+
+option add *Label.Font $pq_dict(subheadingFont)
+option add *status_label.font $pq_dict(statusFont) widgetDefault
+option add *name_label.font $pq_dict(headingFont) widgetDefault
+option add *tel_label.font $pq_dict(headingFont) widgetDefault
+option add *office_label.font $pq_dict(headingFont) widgetDefault
+option add *addr_label.font $pq_dict(headingFont) widgetDefault
+option add *loc_title.font $pq_dict(headingFont) widgetDefault
+option add *org_title.font $pq_dict(headingFont) widgetDefault
+
+option add *overall_label.text "Customer Database"
+option add *name_label.text "Name"
+option add *tel_label.text "Telephone"
+option add *addr_label.text "Address"
+option add *last_label.text "last"
+option add *first_label.text "first"
+option add *middle_label.text "middle"
+option add *com_label.text "company"
+option add *org_label.text "organization"
+option add *tl_label.text "title"
+option add *ext_label.text "extension"
+option add *exch_label.text "exchange"
+option add *area_label.text "area code"
+option add *loc_label.text "extension"
+option add *oldloc_label.text "exchange"
+option add *room_label.text "area code"
+option add *street_label.text "street address"
+option add *ema_label.text "e-mail"
+option add *city_label.text "city"
+option add *state_label.text "state"
+option add *zip_label.text "zip code"
+option add *org_title.text "Organization"
+option add *loc_title.text "Fax"
+
+option add *clear_button.text "Clear"
+option add *quit_button.text "Quit"
+option add *cancel_button.text "Cancel"
+
+# --------------------------------------------------------------------------
+#
+# Procedures to perform post queries
+#
+
+# ----------------------------------------------------------------
+#
+# StopQuery --
+#
+# Stops any current "pq" request by setting the variable
+# associated with the background subprocesses.
+#
+# Arguments
+# None.
+#
+# Globals
+# postOutput -- variable where collected output from
+# pq command will be stored
+#
+# ----------------------------------------------------------------
+
+proc StopQuery {} {
+ global postOutput
+ set postOutput {}
+}
+
+
+# ----------------------------------------------------------------
+#
+# PostQuery --
+#
+# Collects the data from the entry widget fields and
+# executes a "pq" request. The "pq" command is executed
+# in the background and a "wait" is setup to wait for the
+# command to finish. This allows the animation routine
+# to operate and exposure events to be handled properly.
+#
+# Arguments
+# None.
+#
+# Globals
+# postOutput -- variable where collected output from
+# pq command will be stored
+# pq_dict(entryNames) -- list of entry widget names
+# pq_dict(textIndex) -- starting index of highlighted information
+# (a line in the text widget)
+#
+# ----------------------------------------------------------------
+
+proc PostQuery {} {
+ global pq_dict
+
+ .status_label configure -text {}
+ set cnt 0
+ foreach name $pq_dict(entryNames) {
+ set value [.${name}_entry get]
+ if { $value != "" } {
+ set value [split $value "|"]
+ foreach i $value {
+ if { $cnt > 0 } {
+ set query $query/$name=[list $i]
+ } else {
+ set query $name=[list $i]
+ }
+ incr cnt
+ }
+ }
+ }
+ if { $cnt == 0 } {
+ return
+ }
+ set fmt {%^24pn\t%10org\t%6loc\t%area-%exch-%ext\t%ema}
+ global postOutput postError
+ set postOutput {}
+ set postError {}
+ bgexec postStatus -error postError -output postOutput \
+ pq -o $fmt $query &
+ Animate on
+ tkwait variable postStatus
+ if { $postOutput != "" } {
+ .text configure -state normal
+ .text delete 0.0 end
+ .text insert 0.0 $postOutput
+ .text configure -state disabled
+ .status_label configure -text "Post query successful"
+ } else {
+ .status_label configure -text "Post query failed"
+ }
+ set pq_dict(textIndex) {}
+ Animate off
+ if { $postError != "" } {
+ tkerror $postError
+ }
+}
+
+
+# ----------------------------------------------------------------
+#
+# ClearFields --
+#
+# Clears the all the entry fields.
+#
+# Arguments
+# None.
+#
+# Globals
+# pq_dict(entryNames) -- list of entry widget names
+# pq_dict(textIndex) -- starting index of highlighted information
+# (a line in the text widget)
+#
+# ----------------------------------------------------------------
+
+proc ClearFields {} {
+ global pq_dict
+
+ busy hold . ; update
+ foreach name $pq_dict(entryNames) {
+ .${name}_entry delete 0 end
+ }
+ set pq_dict(textIndex) {}
+ .status_label configure -text "Cleared query fields"
+ busy release .
+}
+
+
+# ----------------------------------------------------------------
+#
+# FillFields --
+#
+# Makes a post query based upon the highlighted line in
+# the text widget to fill in all post entry fields.
+#
+# Arguments
+# x x screen coordinate
+# y y screen coordinate
+#
+# Globals
+# postOutput variable where collected output from pq
+# command will be stored
+# pq_dict(format) standard query format to collect data for
+# all entry fields
+# pq_dict(entryNames) list of entry widget names
+#
+# ----------------------------------------------------------------
+
+proc FillFields { x y } {
+ global pq_dict
+
+ set info [.text get [list @$x,$y linestart] [list @$x,$y lineend]]
+ set info [split $info \t]
+ if { [llength $info] == 0 } {
+ return
+ }
+ set name [string trim [lindex $info 0]]
+ set name [split $name ,]
+ set last [lindex $name 0]
+ set name [split [lindex $name 1]]
+ set first [lindex $name 0]
+ set middle [lindex $name 1]
+ set org [string trim [lindex $info 1]]
+ set loc [string trim [lindex $info 2]]
+ set tel [string trim [lindex $info 3]]
+ set query last=$last/first=$first/middle=$middle/org=$org/loc=$loc/tel=[list $tel]
+ global postOutput
+ set postOutput {}
+ bgexec postStatus -output postOutput \
+ pq -o $pq_dict(format) $query &
+ Animate on
+ tkwait variable postStatus
+
+ if { $postOutput == "" } {
+ # Try again with out the telephone number
+ set query last=$last/first=$first/middle=$middle/org=$org/loc=$loc
+ set postStatus {}
+ bgexec postStatus -output postOutput \
+ pq -o $pq_dict(format) $query &
+ tkwait variable postStatus
+ }
+ Animate off
+ if { $postOutput == "" } {
+ .status_label configure -text "Post query failed"
+ } else {
+ .status_label configure -text "Post database fields found"
+ set postOutput [split $postOutput : ]
+ set cnt 0
+ foreach name $pq_dict(entryNames) {
+ .${name}_entry delete 0 end
+ .${name}_entry insert 0 [lindex $postOutput $cnt]
+ incr cnt
+ }
+ }
+}
+
+
+# ----------------------------------------------------------------
+#
+# HighlightText --
+#
+# Highlight the text under the current line (as based upon
+# the given screen coordinates. Only highlight the line if
+# pointer has been moved to the another line.
+#
+# Arguments
+# x x screen coordinate
+# y y screen coordinate
+#
+# Globals
+# pq_dict(visual) either "mono" or "color"; indicates if
+# color screen features can be used
+# pq_dict(textIndex) starting index of highlighted information
+# pq_dict(normalFgColor) color to use for highlighted region
+#
+# ----------------------------------------------------------------
+
+proc HighlightText { x y } {
+ global pq_dict
+
+ set newIndex [.text index [list @$x,$y linestart]]
+ if { $newIndex != $pq_dict(textIndex) } {
+ catch { .text tag delete highlight }
+ .text tag add highlight $newIndex [list $newIndex lineend]
+ if { $pq_dict(visual) == "color" } {
+ .text tag configure highlight \
+ -foreground $pq_dict(normalFgColor) -underline on
+ } else {
+ .text tag configure highlight -underline on
+ }
+ set pq_dict(textIndex) $newIndex
+ }
+}
+
+
+# ----------------------------------------------------------------
+#
+# ChangeFocus --
+#
+# Change the keyboard focus to the next/last entry widget.
+#
+# Arguments
+# direction either "next" or "last"; indicates in
+# which direction to change focus
+#
+# Globals
+# pq_dict(entryNames) list of entry widget names
+# pq_dict(index) current index in list of entry widget
+# names of the keyboard focus. An index
+# of -1 indicates there is no focus.
+# pq_dict(numEntries) number of names in entry widget list
+#
+# ----------------------------------------------------------------
+
+proc ChangeFocus direction {
+ global pq_dict
+
+ case $direction {
+ next {
+ incr pq_dict(index)
+ if { $pq_dict(index) == $pq_dict(numEntries) } {
+ set pq_dict(index) 0
+ }
+ }
+ last {
+ set pq_dict(index) [expr $pq_dict(index)-1]
+ if { $pq_dict(index) < 0 } {
+ set pq_dict(index) [expr $pq_dict(numEntries)-1]
+ }
+ }
+ }
+ focus .[lindex $pq_dict(entryNames) $pq_dict(index)]_entry
+ update idletasks
+ update
+}
+
+
+# ----------------------------------------------------------------
+#
+# ColorFocus --
+#
+# Change background color of entry widget with active
+# keyboard focus
+#
+# Arguments
+# w name of entry widget to change
+# bool either "on" or "off"; indicates if
+# the focus highlight should turned on
+# or off.
+#
+# Globals
+# pq_dict(entryNames) list of entry widget names
+# pq_dict(index) current index in list of entry widget
+# names of the keyboard focus. An index
+# of -1 indicates there is no focus.
+# pq_dict(visual) either "mono" or "color"; indicates if
+# color screen features can be used
+#
+# ----------------------------------------------------------------
+
+proc ColorFocus { w bool } {
+ global pq_dict
+
+ regexp {\.([a-z]+)_entry} $w dummy name
+ if { $pq_dict(visual) == "color" && [info commands $w] == $w } {
+ if { $bool == "on" } {
+ set pq_dict(index) [lsearch $pq_dict(entryNames) $name]
+ $w configure -background $pq_dict(focusHighlightColor)
+ } else {
+ $w configure -background $pq_dict(normalBgColor)
+ }
+ }
+}
+
+# ----------------------------------------------------------------
+#
+# Animate --
+#
+# Activates/deactivates an animated bitmap and busy window.
+# A cancel button is mapped and raised so that it is unaffected
+# by the busy window.
+#
+# Arguments
+# option either "on", "off", or "continue";
+# indicates whether animation should
+# be started, stoped or continued.
+#
+# Globals
+# pq_dict(entryNames) list of entry widget names
+# pq_dict(index) current index in list of entry widget
+# names of the keyboard focus. An index
+# of -1 indicates there is no focus.
+# pq_dict(visual) either "mono" or "color"; indicates if
+# color screen features can be used
+#
+# ----------------------------------------------------------------
+
+set pq_dict(curBitmap) 0
+set pq_dict(lastBitmap) 0
+
+proc Animate option {
+ global pq_dict
+
+ case $option {
+ on {
+ busy hold .
+ .status_label configure -text "Searching..."
+ global topLevel
+ table $topLevel .cancel_button 18,8 -anchor e -reqwidth .70i
+ winop raise .cancel_button
+ .quit_button configure -state disabled
+ .clear_button configure -state disabled
+ winop raise .cancel_button
+ set pq_dict(lastFocus) [focus]
+ focus -force .
+ set pq_dict(curBitmap) $pq_dict(lastBitmap)
+ update
+ }
+ off {
+ table forget .cancel_button
+ .quit_button configure -state normal
+ .clear_button configure -state normal
+ .trademark configure -bitmap attlogo
+ set pq_dict(lastBitmap) $pq_dict(curBitmap)
+ set pq_dict(curBitmap) -1
+ focus $pq_dict(lastFocus)
+ busy release .
+ }
+ }
+ #
+ # Continue with next bitmap
+ #
+ if { $pq_dict(curBitmap) >= 0 } {
+ set bmap [format globe_%0.2d $pq_dict(curBitmap)]
+ .trademark configure -bitmap $bmap
+ incr pq_dict(curBitmap)
+ if { $pq_dict(curBitmap) >= 29 } {
+ set pq_dict(curBitmap) 0
+ }
+ after 100 Animate continue
+ }
+}
+
+
+# --------------------------------------------------------------------------
+#
+# main body of program
+#
+frame .frame
+set topLevel .frame
+
+label .overall_label -font -*-Helvetica-Bold-R-*-*-18-*-*-*-*-*-*-*
+label .name_label -font $pq_dict(titleFont)
+label .tel_label -font $pq_dict(titleFont)
+label .addr_label -font $pq_dict(titleFont)
+label .org_title -font $pq_dict(titleFont)
+label .loc_title -font $pq_dict(titleFont)
+
+foreach name $pq_dict(entryNames) {
+ label .${name}_label
+ entry .${name}_entry
+}
+if [info exists env(POST_DEFAULTS)] {
+ set pq_dict(defaults) [split $env(POST_DEFAULTS) ":"]
+}
+foreach i $pq_dict(defaults) {
+ set i [split $i "="]
+ if { [llength $i] == 2 } {
+ set name [lindex $i 0]
+ if { [lsearch $pq_dict(entryNames) $name] >= 0 } {
+ .${name}_entry insert 0 [lindex $i 1]
+ }
+ }
+}
+label .orders_title -text "Current Orders" \
+ -font -*-Helvetica-Bold-R-*-*-16-*-*-*-*-*-*-*
+
+set font -*-Helvetica-Bold-R-*-*-14-*-*-*-*-*-*-*
+button .clear_button -command ClearFields -font $font
+button .quit_button -command { exit } -font $font
+button .search_button -text "Search" -font $font
+
+label .status_label
+button .cancel_button -command StopQuery
+#-relief raised
+label .trademark -bitmap attlogo
+text .text -yscrollcommand { .vscroll set } -state disabled
+scrollbar .vscroll -command { .text yview }
+
+table $topLevel \
+ .overall_label 0,1 -cspan 10 -pady 5 \
+ .name_label 1,2 \
+ .last_entry 2,2 -cspan 2 \
+ .first_entry 2,4 \
+ .middle_entry 2,5 \
+ .last_label 3,2 \
+ .first_label 3,4 \
+ .middle_label 3,5 \
+ .tel_label 1,7 \
+ .area_entry 2,7 \
+ .exch_entry 2,8 \
+ .ext_entry 2,9 \
+ .area_label 3,7 \
+ .exch_label 3,8 \
+ .ext_label 3,9 \
+ .org_title 4,2 \
+ .com_entry 5,2 \
+ .org_entry 5,3 \
+ .tl_entry 5,4 \
+ .com_label 6,2 \
+ .org_label 6,3 \
+ .tl_label 6,4 \
+ .loc_title 4,7 \
+ .room_entry 5,7 \
+ .oldloc_entry 5,8 \
+ .loc_entry 5,9 \
+ .room_label 6,7 \
+ .oldloc_label 6,8 \
+ .loc_label 6,9 \
+ .addr_label 8,2 \
+ .street_entry 9,2 \
+ .ema_entry 9,7 -cspan 2 \
+ .street_label 10,2 \
+ .city_entry 11,2 -cspan 2 \
+ .state_entry 11,4 \
+ .zip_entry 11,5 \
+ .ema_label 10,7 -cspan 2 \
+ .city_label 12,2 -cspan 2 \
+ .state_label 12,4 \
+ .zip_label 12,5 \
+ .orders_title 16,2 -pady { 4 0 } \
+ .text 17,2 -cspan 8 -fill both -padx 2 \
+ .vscroll 17,10 -anchor center -fill both \
+ .status_label 18,4 -cspan 6 -reqwidth {0 4i} \
+ .search_button 18,3 -reqwidth .9i -anchor center -pady 8\
+ .clear_button 18,5 -reqwidth .9i -anchor center \
+ .quit_button 18,8 -reqwidth .9i -anchor center
+
+eval table configure $topLevel \
+ [info command .*_label] [info commands .*_title] \
+ -anchor w -padx 2 -ipadx 2
+eval table configure $topLevel [info command .*_entry] \
+ -fill both -padx 2
+eval table configure $topLevel .name_label .tel_label .org_title \
+ .com_label .addr_label .street_entry .street_label \
+ -cspan 3
+eval table configure $topLevel .last_entry .ema_entry .city_entry \
+ .ema_label .city_label -cspan 2
+
+table configure $topLevel .overall_label -anchor center
+table configure $topLevel r16 -pady { 5 5 } -resize both
+table configure $topLevel c0 -width .vscroll
+table configure $topLevel c0 c10 -resize none
+table configure $topLevel r3 r6 r10 r12 -resize none
+table configure $topLevel r17 -height { 40 {} }
+table configure $topLevel r16 r18 -resize none
+table configure $topLevel c6 -pad { 5 5 }
+
+if { $topLevel == ".frame" } {
+ table . \
+ $topLevel 0,0 -fill both
+}
+
+bind .text <Button-2> {
+ FillFields %x %y
+ continue
+}
+
+bind .text <Motion> {
+ HighlightText %x %y
+ continue
+}
+
+bind .text <Enter> {
+ set pq_dict(textIndex) {}
+ HighlightText %x %y
+ set info [.text get [list 0.0 linestart] [list 0.0 lineend]]
+ if { $info != "" } {
+ .status_label configure -text "Query individual with button-2"
+ }
+ continue
+}
+
+bind .text <Leave> {
+ if { [busy isbusy .] != "." } {
+ .text tag delete highlight
+ .status_label configure -text ""
+ }
+ continue
+}
+
+
+bind EntryFocus <Tab> {
+ ChangeFocus next
+ break
+}
+
+bind EntryFocus <Shift-Tab> {
+ ChangeFocus last
+ break
+}
+
+if { $pq_dict(visual) == "color" } {
+ bind EntryFocus <FocusIn> {
+ ColorFocus %W on
+ }
+ bind EntryFocus <FocusOut> {
+ ColorFocus %W off
+ }
+}
+
+bind Entry <Return> PostQuery
+
+foreach name $pq_dict(entryNames) {
+ set w .${name}_entry
+ bindtags $w [list EntryFocus $w Entry all]
+}
+
+focus .last_entry
+
diff --git a/blt/examples/pareto.tcl b/blt/examples/pareto.tcl
new file mode 100755
index 00000000000..51ee84497a9
--- /dev/null
+++ b/blt/examples/pareto.tcl
@@ -0,0 +1,139 @@
+#!../src/bltwish
+
+package require BLT
+
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+if { $tcl_version >= 8.0 } {
+ namespace import blt::*
+ namespace import -force blt::tile::*
+}
+
+
+# Example of a pareto chart.
+#
+# The pareto chart mixes line and bar elements in the same graph.
+# Each processing operating is represented by a bar element. The
+# total accumulated defects is displayed with a single line element.
+
+barchart .b \
+ -title "Defects Found During Inspection" \
+ -font {Helvetica 12} \
+ -plotpady { 12 4 } \
+ -width 6i \
+ -height 5i
+table . .b -fill both
+
+set data {
+ "Spot Weld" 82 yellow
+ "Lathe" 49 orange
+ "Gear Cut" 38 green
+ "Drill" 24 blue
+ "Grind" 17 red
+ "Lapping" 12 brown
+ "Press" 8 purple
+ "De-burr" 4 pink
+ "Packaging" 3 cyan
+ "Other" 12 magenta
+}
+
+# Create an X-Y graph line element to trace the accumulated defects.
+.b line create accum -label "" -symbol none -color red
+
+# Define a bitmap to be used to stipple the background of each bar.
+bitmap define pattern1 { {4 4} {01 02 04 08} }
+
+# For each process, create a bar element to display the magnitude.
+set count 0
+set sum 0
+set ydata 0
+set xdata 0
+foreach { label value color } $data {
+ incr count
+ .b element create $label \
+ -xdata $count \
+ -ydata $value \
+ -fg $color \
+ -relief solid \
+ -borderwidth 1 \
+ -stipple pattern1 \
+ -bg lightblue
+
+ set labels($count) $label
+ # Get the total number of defects.
+ set sum [expr $value + $sum]
+ lappend ydata $sum
+ lappend xdata $count
+}
+
+# Configure the coordinates of the accumulated defects,
+# now that we know what they are.
+.b line configure accum -xdata $xdata -ydata $ydata
+
+# Add text markers to label the percentage of total at each point.
+foreach x $xdata y $ydata {
+ set percent [expr ($y * 100.0) / $sum]
+ if { $x == 0 } {
+ set text " 0%"
+ } else {
+ set text [format %.1f $percent]
+ }
+ .b marker create text \
+ -coords "$x $y" \
+ -text $text \
+ -font {Helvetica 10} \
+ -fg red4 \
+ -anchor c \
+ -yoffset -5
+}
+
+# Display an auxillary y-axis for percentages.
+.b axis configure y2 \
+ -hide no \
+ -min 0.0 \
+ -max 100.0 \
+ -title "Percentage"
+
+# Title the y-axis
+.b axis configure y -title "Defects"
+
+# Configure the x-axis to display the process names, instead of numbers.
+.b axis configure x \
+ -title "Process" \
+ -command FormatLabels \
+ -rotate 90 \
+ -subdivisions 0
+
+proc FormatLabels { widget value } {
+ global labels
+ set value [expr round($value)]
+ if {[info exists labels($value)] } {
+ return $labels($value)
+ }
+ return $value
+}
+
+# No legend needed.
+.b legend configure -hide yes
+
+# Configure the grid lines.
+.b grid configure -mapx x -color lightblue
+
diff --git a/blt/html/BLT.html b/blt/html/BLT.html
new file mode 100644
index 00000000000..03317c2bae6
--- /dev/null
+++ b/blt/html/BLT.html
@@ -0,0 +1,161 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>graph(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+BLT - Introduction to the BLT library
+<H2><A NAME="sect1" HREF="#toc1">Description</A></H2>
+BLT
+is a library of extensions to the Tk library. It adds new commands and
+variables to the application's interpreter. <P>
+
+<H2><A NAME="sect2" HREF="#toc2">Commands</A></H2>
+The following commands
+are added to the interpreter from the BLT library:
+<DL>
+
+<DT><B>table</B> </DT>
+<DD>A table geometry
+manager for Tk. You specify window placements as table row,column positions
+and windows can also span multiple rows or columns. It also has many options
+for setting and/or bounding window sizes. </DD>
+
+<DT><B>graph</B> </DT>
+<DD>A 2D plotting widget. Plots
+two variable data in a window with an optional legend and annotations.
+ It has of several components; coordinate axes, crosshairs, a legend,
+and a collection of elements and tags. </DD>
+
+<DT><B>barchart</B> </DT>
+<DD>A barchart widget. Plots
+two-variable data as rectangular bars in a window. The x-coordinate values
+designate the position of the bar along the x-axis, while the y-coordinate
+values designate the magnitude. The <B>barchart</B> widget has of several components;
+coordinate axes, crosshairs, a legend, and a collection of elements and
+tags. </DD>
+
+<DT><B>vector</B> </DT>
+<DD>Creates a vector of floating point values. The vector's components
+can be manipulated in three ways: through a Tcl array variable, a Tcl command,
+or the C API. </DD>
+
+<DT><B>spline</B> </DT>
+<DD>Computes a spline fitting a set of data points (x and
+y vectors) and produces a vector of the interpolated images (y-coordinates)
+at a given set of x-coordinates. </DD>
+
+<DT><B>bgexec</B> </DT>
+<DD>Like Tcl's <B>exec</B> command, <B>bgexec</B> runs
+a pipeline of Unix commands in the background. Unlike <B>exec</B>, the output
+of the last process is collected and a global Tcl variable is set upon
+its completion. <B>bgexec</B> can be used with <B>tkwait</B> to wait for Unix commands
+to finish while still handling expose events. Intermediate output is also
+available while the pipeline is active. </DD>
+
+<DT><B>busy</B> </DT>
+<DD>Creates a "busy window" which
+prevents user-interaction when an application is busy. The busy window also
+provides an easy way to have temporary busy cursors (such as a watch or
+hourglass). </DD>
+
+<DT><B>bitmap</B> </DT>
+<DD>Reads and writes bitmaps from Tcl. New X bitmaps can
+be defined on-the-fly from Tcl, obviating the need to copy around bitmap
+files. Other options query loaded X bitmap's dimensions and data. </DD>
+
+<DT><B>drag&amp;drop</B>
+ </DT>
+<DD>Provides a drag-and-drop facility for Tk. Information (represented by a
+token window) can be dragged to and from any Tk window, including those
+of another Tk application. <B>drag&amp;drop</B> acts as a coordinator, directing Tk
+<B>send</B> commands between (or within) TCL/Tk applications. </DD>
+
+<DT><B>htext</B> </DT>
+<DD>A simple
+hypertext widget. Combines text and Tk widgets into a single scroll-able
+window. Tcl commands can be embedded into text, which are invoked as the
+text is parsed. In addition, Tk widgets can be appended to the window at
+the current point in the text. <B>Htext</B> can be also used to create scrolled
+windows of Tk widgets. </DD>
+
+<DT><B>winop</B> </DT>
+<DD>Raise, lower, map, or, unmap any window. The
+raise and lower functions are useful for stacking windows above or below
+"busy windows". </DD>
+
+<DT><B>watch</B> </DT>
+<DD>Arranges for Tcl procedures to be called before and/or
+after the execution of every Tcl command. This command may be used in the
+logging, profiling, or tracing of Tcl code. </DD>
+
+<DT><B>bltdebug</B> </DT>
+<DD>A simple Tcl command
+tracing facility useful for debugging Tcl code. Displays each Tcl command
+before and after substitution along its level in the interpreter on standard
+error. </DD>
+</DL>
+
+<H2><A NAME="sect3" HREF="#toc3">Variables</A></H2>
+<P>
+The following Tcl variables are either set or used by BLT
+at various times in its execution:
+<DL>
+
+<DT><B>blt_library</B> </DT>
+<DD>This variable contains the
+name of a directory containing a library of Tcl scripts and other files
+related to BLT. Currently, this directory contains the <B>drag&amp;drop</B> protocol
+scripts and the PostScript prolog used by <B>graph</B> and <B>barchart</B>. The value
+of this variable is taken from the BLT_LIBRARY environment variable, if
+one exists, or else from a default value compiled into the <B>BLT</B> library.
+</DD>
+
+<DT><B>blt_versions</B> </DT>
+<DD>This variable is set in the interpreter for each application.
+It is an array of the current version numbers for each of the BLT commands
+in the form <I>major</I>.<I>minor</I>. <I>Major</I> and <I>minor</I> are integers. The major version
+number increases in any command that includes changes that are not backward
+compatible (i.e. whenever existing applications and scripts may have to change
+to work with the new release). The minor version number increases with
+each new release of a command, except that it resets to zero whenever the
+major version number changes. The array is indexed by the individual command
+name. </DD>
+</DL>
+
+<H2><A NAME="sect4" HREF="#toc4">Adding Blt to Your Applications</A></H2>
+It's easy to add BLT to an existing
+Tk application. BLT requires no patches or edits to the Tcl or Tk libraries.
+ To add BLT, simply add the following code snippet to your application's
+tkAppInit.c file. <BR>
+<CODE>if (Blt_Init(interp) != TCL_OK) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+</CODE><P>Recompile and link with the BLT library (libBLT.a) and that's it. <P>
+Alternately,
+you can dynamically load BLT, simply by invoking the command <BR>
+<CODE>package require BLT<BR>
+</CODE><P>from your Tcl script.
+<H2><A NAME="sect5" HREF="#toc5">Bugs</A></H2>
+Send bug reports, requests, suggestions, etc. to
+ gah@siliconmetrics.com or gah@myfirstlink.net
+<H2><A NAME="sect6" HREF="#toc6">Keywords</A></H2>
+BLT <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Description</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Commands</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Variables</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Adding Blt to Your Applications</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Bugs</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/Makefile.cyg b/blt/html/Makefile.cyg
new file mode 100644
index 00000000000..4a09ef198c4
--- /dev/null
+++ b/blt/html/Makefile.cyg
@@ -0,0 +1,32 @@
+# ------------------------------------------------------------------------
+# Makefile for HTML files
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+INSTALL_DATA = install -m 0644
+
+RM = rm -f
+SHELL = bash
+srcdir = .
+
+instdirs = $(prefix) $(libdir) $(scriptdir) $(scriptdir)/html
+
+all:
+
+install: install-dirs install-html
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else mkdir "$$i" ; fi ; \
+ done
+
+install-html: install-dirs
+ for i in $(srcdir)/*.html ; do \
+ $(INSTALL_DATA) $(srcdir)/$$i $(scriptdir)/html ; \
+ done
+
+clean:
+
+distclean: clean
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"* Makefile
diff --git a/blt/html/Makefile.vc b/blt/html/Makefile.vc
new file mode 100644
index 00000000000..a63f359da15
--- /dev/null
+++ b/blt/html/Makefile.vc
@@ -0,0 +1,33 @@
+# ------------------------------------------------------------------------
+# Makefile for HTML files
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+srcdir = .
+
+instdirs = $(prefix) $(libdir) $(scriptdir) $(scriptdir)/html
+
+all:
+
+install: install-dirs install-html
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else \
+ echo "mkdir $$i" ; \
+ mkdir "$$i" ; \
+ fi ; \
+ done
+
+install-html: install-dirs
+ for i in $(srcdir)/*.html ; do \
+ $(RM) $(scriptdir)/html/$$i ; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(scriptdir)/html ; \
+ done
+
+clean:
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) Makefile
diff --git a/blt/html/barchart.html b/blt/html/barchart.html
new file mode 100644
index 00000000000..fd58741c0fc
--- /dev/null
+++ b/blt/html/barchart.html
@@ -0,0 +1,2240 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>barchat(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+barchart - Bar chart for plotting X-Y coordinate
+data.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>barchart<I> <I>pathName </I></I></B>?<I>option value</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>barchart</B> command
+creates a bar chart for plotting two-dimensional data (X-Y coordinates). A
+bar chart is a graphic means of comparing numbers by displaying bars of
+lengths proportional to the y-coordinates of the points they represented.
+ The bar chart has many configurable components: coordinate axes, elements,
+legend, grid lines, cross hairs, etc. They allow you to customize the look
+and feel of the graph.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The <B>barchart</B> command creates a new window
+for plotting two-dimensional data (X-Y coordinates), using bars of various
+lengths to represent the data points. The bars are drawn in a rectangular
+area displayed in the center of the new window. This is the <I>plotting area</I>.
+ The coordinate axes are drawn in the margins surrounding the plotting
+area. By default, the legend is drawn in the right margin. The title is
+displayed in top margin. <P>
+A <B>barchart</B> widget has several configurable components:
+coordinate axes, data elements, legend, grid, cross hairs, pens, postscript,
+and annotation markers. Each component can be queried or modified.
+<DL>
+
+<DT><I>axis</I>
+ </DT>
+<DD><P>
+ Up to four coordinate axes (two X-coordinate and two Y-coordinate axes)
+can be displayed, but you can create and use any number of axes. Axes control
+what region of data is displayed and how the data is scaled. Each axis consists
+of the axis line, title, major and minor ticks, and tick labels. Tick labels
+display the value at each major tick. </DD>
+
+<DT><I>crosshairs</I> </DT>
+<DD>Cross hairs are used to
+position the mouse pointer relative to the X and Y coordinate axes. Two
+perpendicular lines, intersecting at the current location of the mouse,
+extend across the plotting area to the coordinate axes. </DD>
+
+<DT><I>element</I> </DT>
+<DD>An element
+represents a set of data to be plotted. It contains an x and y vector of
+values representing the data points. Each data point is displayed as a
+bar where the length of the bar is proportional to the ordinate (Y-coordinate)
+of the data point. The appearance of the bar, such as its color, stipple,
+or relief is configurable. <P>
+A special case exists when two or more data points
+have the same abscissa (X-coordinate). By default, the bars are overlayed,
+one on top of the other. The bars are drawn in the order of the element
+display list. But you can also configure the bars to be displayed in two
+other ways. They may be displayed as a stack, where each bar (with the
+same abscissa) is stacked on the previous. Or they can be drawn side-by-side
+as thin bars. The width of each bar is a function of the number of data
+points with the same abscissa. </DD>
+
+<DT><I>grid</I> </DT>
+<DD>Extends the major and minor ticks of
+the X-axis and/or Y-axis across the plotting area. </DD>
+
+<DT><I>legend</I> </DT>
+<DD>The legend displays
+the name and symbol of each data element. The legend can be drawn in any
+margin or in the plotting area. </DD>
+
+<DT><I>marker</I> </DT>
+<DD>Markers are used annotate or highlight
+areas of the graph. For example, you could use a text marker to label a
+particular data point. Markers come in various forms: text strings, bitmaps,
+connected line segments, images, polygons, or embedded widgets. </DD>
+
+<DT><I>pen</I> </DT>
+<DD>Pens
+define attributes for elements. Data elements use pens to specify how they
+should be drawn. A data element may use many pens at once. Here the particular
+pen used for a data point is determined from each element's weight vector
+(see the element's <B>-weight</B> and <B>-style</B> options). </DD>
+
+<DT><I>postscript</I> </DT>
+<DD>The widget can generate
+encapsulated PostScript output. This component has several options to configure
+how the PostScript is generated. </DD>
+</DL>
+
+<H2><A NAME="sect4" HREF="#toc4">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>barchart <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>barchart</B> command creates a new window <I>pathName</I> and makes it into a
+<B>barchart</B> widget. At the time this command is invoked, there must not exist
+a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options
+may be specified on the command line or in the option database to configure
+aspects of the graph such as its colors and font. See the <B>configure</B> operation
+below for the exact details about what <I>option</I> and <I>value</I> pairs are valid.
+<P>
+If successful, <B>barchart</B> returns the path name of the widget. It also creates
+a new Tcl command by the same name. You can use this command to invoke
+various operations that query or modify the graph. The general form is:
+<BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for the graph are described in the <FONT SIZE=-1><B>BARCHART
+OPERATIONS</B></FONT>
+ section. <P>
+The command can also be used to access components of
+the graph. <BR>
+<P>
+<CODE><I>pathName component operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>The operation, now located after the name of the component, is the function
+to be performed on that component. Each component has its own set of operations
+that manipulate that component. They will be described below in their own
+sections.
+<H2><A NAME="sect5" HREF="#toc5">Example</A></H2>
+The <B>barchart</B> command creates a new bar chart. <BR>
+<CODE># Create a new bar chart. Plotting area is black.<BR>
+barchart .b -plotbackground black<BR>
+</CODE><P>A new Tcl command <I>.b</I> is created. This command can be used to query and modify
+the bar chart. For example, to change the title of the graph to "My Plot",
+you use the new command and the <B>configure</B> operation. <BR>
+<CODE># Change the title.<BR>
+.b configure -title "My Plot"<BR>
+</CODE><P>To add data elements, you use the command and the <B>element</B> component. <BR>
+<CODE># Create a new element named "e1"<BR>
+.b element create e1 \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-xdata { 1 2 3 4 5 6 7 8 9 10 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;155.85 166.60 175.38 }<BR>
+</CODE><P>The element's X-Y coordinates are specified using lists of numbers. Alternately,
+BLT vectors could be used to hold the X-Y coordinates. <BR>
+<CODE># Create two vectors and add them to the barchart.<BR>
+vector xVector yVector<BR>
+xVector set { 1 2 3 4 5 6 7 8 9 10 }<BR>
+yVector set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;166.60 175.38 }<BR>
+n.b element create e1 -xdata xVector -ydata yVector<BR>
+</CODE><P>The advantage of using vectors is that when you modify one, the graph is
+automatically redrawn to reflect the new values. <BR>
+<CODE># Change the y coordinate of the first point.<BR>
+set yVector(0) 25.18<BR>
+</CODE><P>An element named <I>e1</I> is now created in <I>.b</I>. It is automatically added to
+the display list of elements. You can use this list to control in what
+order elements are displayed. To query or reset the element display list,
+you use the element's <B>show</B> operation. <BR>
+<CODE># Get the current display list <BR>
+set elemList [.b element show]<BR>
+# Remove the first element so it won't be displayed.<BR>
+.b element show [lrange $elemList 0 end]<BR>
+</CODE><P>The element will be displayed by as many bars as there are data points
+(in this case there are ten). The bars will be drawn centered at the x-coordinate
+of the data point. All the bars will have the same attributes (colors,
+stipple, etc). The width of each bar is by default one unit. You can change
+this with using the <B>-barwidth</B> option. <BR>
+<CODE># Change the scale of the x-coordinate data <BR>
+xVector set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }<BR>
+# Make sure we change the bar width too.<BR>
+.b configure -barwidth 0.2<BR>
+</CODE><P>The height of each bar is proportional to the ordinate (Y-coordinate) of
+the data point. <P>
+If two or more data points have the same abscissa (X-coordinate
+value), the bars representing those data points may be drawn in various
+ways. The default is to overlay the bars, one on top of the other. The ordering
+is determined from the of element display list. If the stacked mode is
+selected (using the <B>-barmode</B> configuration option), the bars are stacked,
+each bar above the previous. <BR>
+<CODE># Display the elements as stacked.<BR>
+.b configure -barmode stacked<BR>
+</CODE><P>If the aligned mode is selected, the bars having the same x-coordinates
+are displayed side by side. The width of each bar is a fraction of its
+normal width, based upon the number of bars with the same x-coordinate. <BR>
+<CODE># Display the elements side-by-side.<BR>
+.b configure -barmode aligned<BR>
+</CODE><P>By default, the element's label in the legend will be also <I>e1</I>. You can change
+the label, or specify no legend entry, again using the element's <B>configure</B>
+operation. <BR>
+<CODE># Don't display "e1" in the legend.<BR>
+.b element configure e1 -label ""<BR>
+</CODE><P>You can configure more than just the element's label. An element has many
+attributes such as stipple, foreground and background colors, relief, etc.
+<BR>
+<CODE>.b element configure e1 -fg red -bg pink \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-stipple gray50<BR>
+</CODE><P>Four coordinate axes are automatically created: <I>x</I>, <I>x2</I>, <I>y</I>, and <I>y2</I>. And by
+default, elements are mapped onto the axes <I>x</I> and <I>y</I>. This can be changed
+with the <B>-mapx</B> and <B>-mapy</B> options. <BR>
+<CODE># Map "e1" on the alternate y axis "y2".<BR>
+.b element configure e1 -mapy y2<BR>
+</CODE><P>Axes can be configured in many ways too. For example, you change the scale
+of the Y-axis from linear to log using the <B>axis</B> component. <BR>
+<CODE># Y-axis is log scale.<BR>
+.b axis configure y -logscale yes<BR>
+</CODE><P>One important way axes are used is to zoom in on a particular data region.
+ Zooming is done by simply specifying new axis limits using the <B>-min</B> and
+<B>-max</B> configuration options. <BR>
+<CODE>.b axis configure x -min 1.0 -max 1.5<BR>
+.b axis configure y -min 12.0 -max 55.15<BR>
+</CODE><P>To zoom interactively, you link the<B>axis configure</B> operations with some
+user interaction (such as pressing the mouse button), using the <B>bind</B> command.
+ To convert between screen and graph coordinates, use the <B>invtransform</B>
+operation. <BR>
+<CODE># Click the button to set a new minimum <BR>
+bind .b &lt;ButtonPress-1&gt; { <BR>
+ %W axis configure x -min [%W axis invtransform x %x]<BR>
+ %W axis configure x -min [%W axis invtransform x %y]<BR>
+}<BR>
+</CODE><P>By default, the limits of the axis are determined from data values. To reset
+back to the default limits, set the <B>-min</B> and <B>-max</B> options to the empty value.
+<BR>
+<CODE># Reset the axes to autoscale again.<BR>
+.b axis configure x -min {} -max {}<BR>
+.b axis configure y -min {} -max {}<BR>
+</CODE><P>By default, the legend is drawn in the right margin. You can change this
+or any legend configuration options using the <B>legend</B> component. <BR>
+<CODE># Configure the legend font, color, and relief<BR>
+.b legend configure -position left -relief raised \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-font fixed -fg blue<BR>
+</CODE><P>To prevent the legend from being displayed, turn on the <B>-hide</B> option. <BR>
+<CODE># Don't display the legend.<BR>
+.b legend configure -hide yes<BR>
+</CODE><P>The <B>barchart</B> has simple drawing procedures called markers. They can be
+used to highlight or annotate data in the graph. The types of markers available
+are bitmaps, polygons, lines, or windows. Markers can be used, for example,
+to mark or brush points. For example there may be a line marker which indicates
+some low-water value. Markers are created using the <B>marker</B> operation. <BR>
+<CODE># Create a line represent the low water mark at 10.0<BR>
+.b marker create line -name "low_water" \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-coords { -Inf 10.0 Inf 10.0 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-dashes { 2 4 2 } -fg red -bg blue <BR>
+</CODE><P>This creates a line marker named <I>low_water</I>. It will display a horizontal
+line stretching across the plotting area at the y-coordinate 10.0. The coordinates
+"-Inf" and "Inf" indicate the relative minimum and maximum of the axis (in
+this case the x-axis). By default, markers are drawn last, on top of the
+bars. You can change this with the <B>-under</B> option. <BR>
+<CODE># Draw the marker before elements are drawn.<BR>
+.b marker configure low_water -under yes<BR>
+</CODE><P>You can add cross hairs or grid lines using the <B>crosshairs</B> and <B>grid</B> components.
+<BR>
+<CODE># Display both cross hairs and grid lines.<BR>
+.b crosshairs configure -hide no -color red<BR>
+.b grid configure -hide no -dashes { 2 2 }<BR>
+</CODE><P>Finally, to get hardcopy of the graph, use the <B>postscript</B> component. <BR>
+<CODE># Print the bar chart into file "file.ps"<BR>
+.b postscript output file.ps -maxpect yes -decorations no<BR>
+</CODE><P>This generates a file <I>file.ps</I> containing the encapsulated PostScript of
+the graph. The option <B>-maxpect</B> says to scale the plot to the size of the
+page. Turning off the <B>-decorations</B> option denotes that no borders or color
+backgrounds should be drawn (i.e. the background of the margins, legend,
+and plotting area will be white).
+<H2><A NAME="sect6" HREF="#toc6">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>barchart <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>barchart</B> command creates a new window <I>pathName</I> and makes it into a
+barchart widget. At the time this command is invoked, there must not exist
+a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options
+may may be specified on the command line or in the option database to configure
+aspects of the bar chart such as its colors and font. See the <B>configure</B>
+operation below for the exact details as to what <I>option</I> and <I>value</I> pairs
+are valid. <P>
+If successful, <B>barchart</B> returns <I>pathName</I>. It also creates a new
+Tcl command <I>pathName</I>. This command may be used to invoke various operations
+to query or modify the bar chart. It has the general form: <BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for the bar chart are described in the following
+section.
+<H2><A NAME="sect7" HREF="#toc7">Barchart Operations</A></H2>
+
+<DL>
+
+<DT><I>pathName <B>bar <I>elemName </I></B></I>?<I>option value</I>?... </DT>
+<DD>Creates
+a new barchart element <I>elemName</I>. It's an error if an element <I>elemName</I> already
+exists. See the manual for <B>barchart</B> for details about what <I>option</I> and
+<I>value</I> pairs are valid. </DD>
+
+<DT><I>pathName <B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value of
+the configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries
+or modifies the configuration options of the graph. If <I>option</I> isn't specified,
+a list describing the current options for <I>pathName</I> is returned. If <I>option</I>
+is specified, but not <I>value</I>, then a list describing <I>option</I> is returned.
+If one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair,
+the option <I>option</I> is set to <I>value</I>. The following options are valid. <blockquote></DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color. This includes the margins and legend, but
+not the plotting area. </DD>
+
+<DT><B>-barmode <I>mode</I></B> </DT>
+<DD>Indicates how related bar elements
+will be drawn. Related elements have data points with the same abscissas
+(X-coordinates). <I>Mode</I> indicates how those segments should be drawn. <I>Mode</I> can
+be <I>infront</I>, <I>aligned</I>, <I>overlap</I>, or <I>stacked</I>. The default mode is <I>infront</I>. <blockquote></DD>
+
+<DT><I>infront</I>
+</DT>
+<DD>Each successive segment is drawn in front of the previous. </DD>
+
+<DT><I>stacked</I> </DT>
+<DD>Each
+successive segment is stacked vertically on top of the previous. </DD>
+
+<DT><I>aligned</I>
+</DT>
+<DD>Segments is displayed aligned from right-to-left. </DD>
+
+<DT><I>overlap</I> </DT>
+<DD>Like <I>aligned</I> but
+segments slightly overlap each other. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-barwidth <I>value</I></B> </DT>
+<DD>Specifies the width
+of the bars. This value can be overrided by the individual elements using
+their <B>-barwidth</B> configuration option. <I>Value</I> is the width in terms of graph
+coordinates. The default width is <I>1.0</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the 3-D border around the outside edge of the widget. The <B>-relief</B> option
+determines if the border is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-bottommargin
+<I>pixels</I></B> </DT>
+<DD>Specifies the size of the margin below the X-coordinate axis. If
+<I>pixels</I> is <I>0</I>, the size of the margin is selected automatically. The default
+is <I>0</I>. </DD>
+
+<DT><B>-bufferelements <I>boolean</I></B> </DT>
+<DD>Indicates whether an internal pixmap to buffer
+the display of data elements should be used. If <I>boolean</I> is true, data elements
+are drawn to an internal pixmap. This option is especially useful when
+the graph is redrawn frequently while the remains data unchanged (for example,
+moving a marker across the plot). See the <FONT SIZE=-1><B>SPEED TIPS</B></FONT>
+ section. The default
+is <I>1</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is
+<I>crosshair</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of the graph title. The default
+is <I>*-Helvetica-Bold-R-Normal-*-18-180-*</I>. </DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a maximum distance
+to consider when searching for the closest data point (see the element's
+<B>closest</B> operation below). Data points further than <I>pixels</I> away are ignored.
+ The default is <I>0.5i</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget.
+ The default is <I>4i</I>. </DD>
+
+<DT><B>-invertxy <I>boolean</I></B> </DT>
+<DD>Indicates whether the placement X-axis
+and Y-axis should be inverted. If <I>boolean</I> is true, the X and Y axes are
+swapped. The default is <I>0</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the title should
+be justified. This matters only when the title contains more than one line
+of text. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-leftmargin
+<I>pixels</I></B> </DT>
+<DD>Sets the size of the margin from the left edge of the window to
+ the Y-coordinate axis. If <I>pixels</I> is <I>0</I>, the size is calculated automatically.
+ The default is <I>0</I>. </DD>
+
+<DT><B>-plotbackground <I>color</I></B> </DT>
+<DD>Specifies the background color of
+the plotting area. The default is <I>white</I>. </DD>
+
+<DT><B>-plotborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the
+width of the 3-D border around the plotting area. The <B>-plotrelief</B> option
+determines if a border is drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-plotpadx <I>pad</I></B> </DT>
+<DD>Sets the
+amount of padding to be added to the left and right sides of the plotting
+area. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has two
+elements, the left side of the plotting area entry is padded by the first
+distance and the right side by the second. If <I>pad</I> is just one distance,
+both the left and right sides are padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotpady
+<I>pad</I></B> </DT>
+<DD>Sets the amount of padding to be added to the top and bottom of the
+plotting area. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I>
+has two elements, the top of the plotting area is padded by the first distance
+and the bottom by the second. If <I>pad</I> is just one distance, both the top
+and bottom are padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotrelief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect for the plotting area. <I>Relief</I> specifies how the interior
+of the plotting area should appear relative to rest of the graph; for example,
+<I>raised</I> means the plot should appear to protrude from the graph, relative
+to the surface of the graph. The default is <I>sunken</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect for the barchart widget. <I>Relief</I> specifies how the graph should
+appear relative to widget it is packed into; for example, <I>raised</I> means
+the graph should appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-rightmargin <I>pixels</I></B>
+</DT>
+<DD>Sets the size of margin from the plotting area to the right edge of the
+window. By default, the legend is drawn in this margin. If <I>pixels</I> is than
+1, the margin size is selected automatically. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides
+information used when moving the focus from window to window via keyboard
+traversal (e.g., Tab and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window
+should be skipped entirely during keyboard traversal. <I>1</I> means that the
+this window should always receive the input focus. An empty value means
+that the traversal scripts make the decision whether to focus on the window.
+The default is <I>""</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies a tiled background for the widget.
+ If <I>image</I> isn't <I>""</I>, the background is tiled using <I>image</I>. Otherwise, the normal
+background color is drawn (see the <B>-background</B> option). <I>Image</I> must be an
+image created using the Tk <B>image</B> command. The default is <I>""</I>. </DD>
+
+<DT><B>-title <I>text</I></B>
+ </DT>
+<DD>Sets the title to <I>text</I>. If <I>text</I> is <I>""</I>, no title will be displayed. </DD>
+
+<DT><B>-topmargin
+<I>pixels</I></B> </DT>
+<DD>Specifies the size of the margin above the x2 axis. If <I>pixels</I> is
+<I>0</I>, the margin size is calculated automatically. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the
+requested width of the widget. The default is <I>5i</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>crosshairs <I>operation
+</I></B></I>?<I>arg</I>? </DT>
+<DD>See the <FONT SIZE=-1><B>CROSSHAIRS COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>element <I>operation
+</I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>ELEMENT COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>extents <I>item</I></B></I> </DT>
+<DD>Returns
+the size of a particular item in the graph. <I>Item</I> must be either <I>leftmargin</I>,
+<I>rightmargin</I>, <I>topmargin</I>, <I>bottommargin</I>, <I>plotwidth</I>, or <I>plotheight</I>. </DD>
+
+<DT><I>pathName
+<B>grid <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>GRID COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>invtransform
+<I>winX winY</I></B></I> </DT>
+<DD>Performs an inverse coordinate transformation, mapping window
+coordinates back to graph coordinates, using the standard X-axis and Y-axis.
+Returns a list of containing the X-Y graph coordinates. </DD>
+
+<DT><I>pathName <B>inside <I>x
+y</I></B></I> </DT>
+<DD>Returns <I>1</I> is the designated screen coordinate (<I>x</I> and <I>y</I>) is inside the
+plotting area and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>legend <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the
+<FONT SIZE=-1><B>LEGEND COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>line<B> operation arg</B></B></I>... </DT>
+<DD>The operation is
+the same as <B>element</B>. </DD>
+
+<DT><I>pathName <B>marker <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>MARKER COMPONENTS</B></FONT>
+
+ section. </DD>
+
+<DT><I>pathName</I> <B>metafile</B> ?<I>fileName</I>? </DT>
+<DD><I>This operation is for Window platforms
+only</I>. Creates a Windows enhanced metafile of the barchart. If present,
+<I>fileName</I> is the file name of the new metafile. Otherwise, the metafile is
+automatically added to the clipboard. </DD>
+
+<DT><I>pathName <B>postscript <I>operation </I></B></I>?<I>arg</I>?...
+</DT>
+<DD>See the <FONT SIZE=-1><B>POSTSCRIPT COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>snap <I>photoName</I></B></I> </DT>
+<DD>Takes a
+snapshot of the graph and stores the contents in the photo image <I>photoName</I>.
+ <I>PhotoName</I> is the name of a Tk photo image that must already exist. </DD>
+
+<DT><I>pathName
+<B>transform <I>x y</I></B></I> </DT>
+<DD>Performs a coordinate transformation, mapping graph coordinates
+to window coordinates, using the standard X-axis and Y-axis. Returns a list
+containing the X-Y screen coordinates. </DD>
+
+<DT><I>pathName <B>xaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName
+<B>x2axis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>yaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>y2axis
+<I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+</DL>
+
+<H2><A NAME="sect8" HREF="#toc8">Barchart Components</A></H2>
+A
+graph is composed of several components: coordinate axes, data elements,
+legend, grid, cross hairs, postscript, and annotation markers. Instead of
+one big set of configuration options and operations, the graph is partitioned,
+where each component has its own configuration options and operations that
+specifically control that aspect or part of the graph.
+<H3><A NAME="sect9" HREF="#toc9">Axis Components</A></H3>
+Four
+coordinate axes are automatically created: two X-coordinate axes (<I>x</I> and
+<I>x2</I>) and two Y-coordinate axes (<I>y</I>, and <I>y2</I>). By default, the axis <I>x</I> is located
+in the bottom margin, <I>y</I> in the left margin, <I>x2</I> in the top margin, and <I>y2</I>
+in the right margin. <P>
+An axis consists of the axis line, title, major and
+minor ticks, and tick labels. Major ticks are drawn at uniform intervals
+along the axis. Each tick is labeled with its coordinate value. Minor ticks
+are drawn at uniform intervals within major ticks. <P>
+The range of the axis
+controls what region of data is plotted. Data points outside the minimum
+and maximum limits of the axis are not plotted. By default, the minimum
+and maximum limits are determined from the data, but you can reset either
+limit. <P>
+You can create and use several axes. To create an axis, invoke the
+axis component and its create operation. <BR>
+<CODE># Create a new axis called "temperature"<BR>
+.b axis create temperature<BR>
+</CODE><P>You map data elements to an axis using the element's -mapy and -mapx configuration
+options. They specify the coordinate axes an element is mapped onto. <BR>
+<CODE># Now map the temperature data to this axis.<BR>
+.b element create "temp" -xdata $x -ydata $tempData \<BR>
+ -mapy temperature<BR>
+</CODE><P>While you can have many axes, only four axes can be displayed simultaneously.
+ They are drawn in each of the margins surrounding the plotting area. The
+axes <I>x</I> and <I>y</I> are drawn in the bottom and left margins. The axes <I>x2</I> and <I>y2</I>
+are drawn in top and right margins. Only <I>x</I> and <I>y</I> are shown by default. Note
+that the axes can have different scales. <P>
+To display a different axis, you
+invoke one of the following components: <B>xaxis</B>, <B>yaxis</B>, <B>x2axis</B>, and <B>y2axis</B>.
+The <B>use</B> operation designates the axis to be drawn in the corresponding
+margin: <B>xaxis</B> in the bottom, <B>yaxis</B> in the left, <B>x2axis</B> in the top, and
+<B>y2axis</B> in the right. <BR>
+<CODE># Display the axis temperature in the left margin.<BR>
+.b yaxis use temperature<BR>
+<P>
+</CODE><P>You can configure axes in many ways. The axis scale can be linear or logarithmic.
+ The values along the axis can either monotonically increase or decrease.
+ If you need custom tick labels, you can specify a Tcl procedure to format
+the label any way you wish. You can control how ticks are drawn, by changing
+the major tick interval or the number of minor ticks. You can define non-uniform
+tick intervals, such as for time-series plots. <P>
+
+<DL>
+
+<DT><I>pathName <B>axis <B>cget <I>axisName
+<I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option given by <I>option</I> for <I>axisName</I>.
+ <I>Option</I> may be any option described below for the axis <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>axis <B>configure <I>axisName </I></B></B></I>?<I>axisName</I>?... ?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options of <I>axisName</I>. Several axes can be changed. If <I>option</I>
+isn't specified, a list describing all the current options for <I>axisName</I>
+is returned. If <I>option</I> is specified, but not <I>value</I>, then a list describing
+<I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are specified,
+then for each pair, the axis option <I>option</I> is set to <I>value</I>. The following
+options are valid for axes. <blockquote></DD>
+
+<DT><B>-autorange <I>range</I></B> </DT>
+<DD>Sets the range of values for
+the axis to <I>range</I>. The axis limits are automatically reset to display the
+most recent data points in this range. If <I>range</I> is 0.0, the range is determined
+from the limits of the data. If <B>-min</B> or <B>-max</B> are specified, they override
+this option. The default is <I>0.0</I>. </DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the axis and
+tick labels. The default is <I>black</I>. </DD>
+
+<DT><B>-command <I>prefix</I></B> </DT>
+<DD>Specifies a Tcl command
+to be invoked when formatting the axis tick labels. <I>Prefix</I> is a string containing
+the name of a Tcl proc and any extra arguments for the procedure. This
+command is invoked for each major tick on the axis. Two additional arguments
+are passed to the procedure: the pathname of the widget and the current
+the numeric value of the tick. The procedure returns the formatted tick
+label. If <I>""</I> is returned, no label will appear next to the tick. You can
+get the standard tick labels again by setting <I>prefix</I> to <I>""</I>. The default
+is <I>""</I>. <P>
+Please note that this procedure is invoked while the bar chart is
+redrawn. You may query the widget's configuration options. But do not reset
+options, because this can have unexpected results. </DD>
+
+<DT><B>-descending <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the values along the axis are monotonically increasing or decreasing.
+ If <I>boolean</I> is true, the axis values will be decreasing. The default is
+<I>0</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the axis is displayed. </DD>
+
+<DT><B>-justify <I>justify</I></B>
+</DT>
+<DD>Specifies how the axis title should be justified. This matters only when
+the axis title contains more than one line of text. <I>Justify</I> must be <I>left</I>,
+<I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-limits <I>formatStr</I></B> </DT>
+<DD>Specifies a printf-like
+description to format the minimum and maximum limits of the axis. The limits
+are displayed at the top/bottom or left/right sides of the plotting area.
+ <I>FormatStr</I> is a list of one or two format descriptions. If one description
+is supplied, both the minimum and maximum limits are formatted in the same
+way. If two, the first designates the format for the minimum limit, the
+second for the maximum. If <I>""</I> is given as either description, then the
+that limit will not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets
+the width of the axis and tick lines. The default is <I>1</I> pixel. </DD>
+
+<DT><B>-logscale <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the scale of the axis is logarithmic or linear. If <I>boolean</I>
+is true, the axis is logarithmic. The default scale is linear. </DD>
+
+<DT><B>-loose <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the limits of the axis should fit the data points tightly,
+at the outermost data points, or loosely, at the outer tick intervals. This
+is relevant only when the axis limit is automatically calculated. If <I>boolean</I>
+is true, the axis range is "loose". The default is <I>0</I>. </DD>
+
+<DT><B>-majorticks <I>majorList</I></B>
+</DT>
+<DD>Specifies where to display major axis ticks. You can use this option to
+display ticks at non-uniform intervals. <I>MajorList</I> is a list of axis coordinates
+designating the location of major ticks. No minor ticks are drawn. If <I>majorList</I>
+is <I>""</I>, major ticks will be automatically computed. The default is <I>""</I>. </DD>
+
+<DT><B>-max
+<I>value</I></B> </DT>
+<DD>Sets the maximum limit of <I>axisName</I>. Any data point greater than
+<I>value</I> is not displayed. If <I>value</I> is <I>""</I>, the maximum limit is calculated
+using the largest data value. The default is <I>""</I>. </DD>
+
+<DT><B>-min <I>value</I></B> </DT>
+<DD>Sets the minimum
+limit of <I>axisName</I>. Any data point less than <I>value</I> is not displayed. If
+<I>value</I> is <I>""</I>, the minimum limit is calculated using the smallest data value.
+The default is <I>""</I>. </DD>
+
+<DT><B>-minorticks <I>minorList</I></B> </DT>
+<DD>Specifies where to display minor
+axis ticks. You can use this option to display minor ticks at non-uniform
+intervals. <I>MinorList</I> is a list of real values, ranging from 0.0 to 1.0, designating
+the placement of a minor tick. No minor ticks are drawn if the <B>-majortick</B>
+option is also set. If <I>minorList</I> is <I>""</I>, minor ticks will be automatically
+computed. The default is <I>""</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the how many degrees
+to rotate the axis tick labels. <I>Theta</I> is a real value representing the number
+of degrees to rotate the tick labels. The default is <I>0.0</I> degrees. </DD>
+
+<DT><B>-shiftby
+<I>value</I></B> </DT>
+<DD>Specifies how much to automatically shift the range of the axis. When
+the new data exceeds the current axis maximum, the maximum is increased
+in increments of <I>value</I>. You can use this option to prevent the axis limits
+from being recomputed at each new time point. If <I>value</I> is 0.0, then no automatic
+shifting is down. The default is <I>0.0</I>. </DD>
+
+<DT><B>-showticks <I>boolean</I></B> </DT>
+<DD>Indicates whether
+axis ticks should be drawn. If <I>boolean</I> is true, ticks are drawn. If false,
+only the axis line is drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-stepsize <I>value</I></B> </DT>
+<DD>Specifies the
+interval between major axis ticks. If <I>value</I> isn't a valid interval (must
+be less than the axis range), the request is ignored and the step size
+is automatically calculated. </DD>
+
+<DT><B>-subdivisions <I>number</I></B> </DT>
+<DD>Indicates how many minor
+axis ticks are to be drawn. For example, if <I>number</I> is two, only one minor
+tick is drawn. If <I>number</I> is one, no minor ticks are displayed. The default
+is <I>2</I>. </DD>
+
+<DT><B>-tickfont <I>fontName</I></B> </DT>
+<DD>Specifies the font for axis tick labels. The default
+is <I>*-Courier-Bold-R-Normal-*-100-*</I>. </DD>
+
+<DT><B>-ticklength <I>pixels</I></B> </DT>
+<DD>Sets the length of major
+and minor ticks (minor ticks are half the length of major ticks). If <I>pixels</I>
+is less than zero, the axis will be inverted with ticks drawn pointing
+towards the plot. The default is <I>0.1i</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title of the axis.
+If <I>text</I> is <I>""</I>, no axis title will be displayed. </DD>
+
+<DT><B>-titlecolor <I>color</I></B> </DT>
+<DD>Sets
+the color of the axis title. The default is <I>black</I>. </DD>
+
+<DT><B>-titlefont <I>fontName</I></B> </DT>
+<DD>Specifies
+the font for axis title. The default is <I>*-Helvetica-Bold-R-Normal-*-14-140-*</I>. </DD>
+</DL>
+<P>
+Axis
+configuration options may be also be set by the <B>option</B> command. The resource
+class is <I>Axis</I>. The resource names are the names of the axes (such as <I>x</I>
+or <I>x2</I>). <BR>
+<CODE>option add *Barchart.Axis.Color blue<BR>
+option add *Barchart.x.LogScale true<BR>
+option add *Barchart.x2.LogScale false<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>axis <B>create <I>axisName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new axis by the
+name <I>axisName</I>. No axis by the same name can already exist. <I>Option</I> and <I>value</I>
+are described in above in the axis <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>axis <B>delete
+</B></B></I>?<I>axisName</I>?... </DT>
+<DD>Deletes the named axes. An axis is not really deleted until it
+is not longer in use, so it's safe to delete axes mapped to elements. </DD>
+
+<DT><I>pathName
+<B>axis invtransform <I>axisName value</I></B></I> </DT>
+<DD>Performs the inverse transformation, changing
+the screen coordinate <I>value</I> to a graph coordinate, mapping the value mapped
+to <I>axisName</I>. Returns the graph coordinate. </DD>
+
+<DT><I>pathName <B>axis limits <I>axisName</I></B></I>
+</DT>
+<DD>Returns a list of the minimum and maximum limits for <I>axisName</I>. The order
+of the list is <I>min max</I>. </DD>
+
+<DT><I>pathName <B>axis names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of
+axes matching zero or more patterns. If no <I>pattern</I> argument is give, the
+names of all axes are returned. </DD>
+
+<DT><I>pathName <B>axis transform <I>axisName value</I></B></I> </DT>
+<DD>Transforms
+the coordinate <I>value</I> to a screen coordinate by mapping the it to <I>axisName</I>.
+ Returns the transformed screen coordinate. </DD>
+</DL>
+<P>
+Only four axes can be displayed
+simultaneously. By default, they are <I>x</I>, <I>y</I>, <I>x2</I>, and <I>y2</I>. You can swap in
+a different axis with <B>use</B> operation of the special axis components: <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B>. <BR>
+<CODE>.g create axis temp<BR>
+.g create axis time<BR>
+...<BR>
+.g xaxis use temp<BR>
+.g yaxis use time<BR>
+</CODE><P>Only the axes specified for use are displayed on the screen. <P>
+The <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B> components operate on an axis location rather
+than a specific axis like the more general <B>axis</B> component does. The <B>xaxis</B>
+component manages the X-axis located in the bottom margin (whatever axis
+that happens to be). Likewise, <B>yaxis</B> uses the Y-axis in the left margin,
+<B>x2axis</B> the top X-axis, and <B>y2axis</B> the right Y-axis. <P>
+They implicitly control
+the axis that is currently using to that location. By default, <B>xaxis</B> uses
+the <I>x</I> axis, <B>yaxis</B> uses <I>y</I>, <B>x2axis</B> uses <I>x2</I>, and <B>y2axis</B> uses <I>y2</I>. These components
+can be more convenient to use than always determining what axes are current
+being displayed by the graph. <P>
+The following operations are available for
+axes. They mirror exactly the operations of the <B>axis</B> component. The <I>axis</I>
+argument must be <B>xaxis</B>, <B>x2axis</B>, <B>yaxis</B>, or <B>y2axis</B>.
+<DL>
+
+<DT><I>pathName <I>axis <B>cget <I>option</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>configure </B></I></I>?<I>option value</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> invtransform <I>value</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>limits</B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> transform <I>value</I></B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> use </B></I></I>?<I>axisName</I>?
+ </DT>
+<DD>Designates the axis <I>axisName</I> is to be displayed at this location. <I>AxisName</I>
+can not be already in use at another location. This command returns the
+name of the axis currently using this location. </DD>
+</DL>
+
+<H3><A NAME="sect10" HREF="#toc10">Crosshairs Component</A></H3>
+Cross
+hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position the
+mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives. This
+means that they can be quickly drawn and erased without redrawing the entire
+widget. <P>
+The following operations are available for cross hairs:
+<DL>
+
+<DT><I>pathName
+<B>crosshairs cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the cross hairs configuration
+option given by <I>option</I>. <I>Option</I> may be any option described below for the
+cross hairs <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>crosshairs configure </B></I>?<I>option value</I>?...
+ </DT>
+<DD>Queries or modifies the configuration options of the cross hairs. If
+<I>option</I> isn't specified, a list describing all the current options for the
+cross hairs is returned. If <I>option</I> is specified, but not <I>value</I>, then a
+list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs
+are specified, then for each pair, the cross hairs option <I>option</I> is set
+to <I>value</I>. The following options are available for cross hairs. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B>
+ </DT>
+<DD>Sets the color of the cross hairs. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the cross hairs. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the cross
+hair lines. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the
+cross hairs will be solid lines. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether cross hairs
+are drawn. If <I>boolean</I> is true, cross hairs are not drawn. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Set the width of the cross hair lines. The default
+is <I>1</I>. </DD>
+
+<DT><B>-position <I>pos</I></B> </DT>
+<DD>Specifies the screen position where the cross hairs
+intersect. <I>Pos</I> must be in the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window
+coordinates of the intersection. </DD>
+</DL>
+<P>
+Cross hairs configuration options may be
+also be set by the <B>option</B> command. The resource name and class are <I>crosshairs</I>
+and <I>Crosshairs</I> respectively. <BR>
+<CODE>option add *Barchart.Crosshairs.LineWidth 2<BR>
+option add *Barchart.Crosshairs.Color red<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>crosshairs off</B></I> </DT>
+<DD>Turns off the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs
+on</B></I> </DT>
+<DD>Turns on the display of the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs toggle</B></I>
+</DT>
+<DD>Toggles the current state of the cross hairs, alternately mapping and unmapping
+the cross hairs. </DD>
+</DL>
+
+<H2><A NAME="sect11" HREF="#toc11">Elements</A></H2>
+A data element represents a set of data. It contains
+x and y vectors which are the coordinates of the data points. Elements
+are displayed as bars where the length of the bar is proportional to the
+ordinate of the data point. Elements also control the appearance of the
+data, such as the color, stipple, relief, etc. <P>
+When new data elements are
+created, they are automatically added to a list of displayed elements.
+ The display list controls what elements are drawn and in what order.
+<P>
+The following operations are available for elements.
+<DL>
+
+<DT><I>pathName <B>element activate
+<I>elemName </I></B></I>?<I>index</I>?... </DT>
+<DD>Specifies the data points of element <I>elemName</I> to be drawn
+using active foreground and background colors. <I>ElemName</I> is the name of
+the element and <I>index</I> is a number representing the index of the data point.
+If no indices are present then all data points become active. </DD>
+
+<DT><I>pathName <B>element
+bind <I>tagName</I></B></I> ?<I>sequence</I>? ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such
+that whenever the event sequence given by <I>sequence</I> occurs for an element
+with this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B>
+command except that it operates on graph elements, rather than widgets.
+See the <B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>element cget <I>elemName
+<I>option</I></I></B></I> </DT>
+<DD>Returns the current value of the element configuration option given
+by <I>option</I>. <I>Option</I> may be any of the options described below for the element
+<B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>element closest <I>x y</I></B></I> ?<I>option value</I>?... ?<I>elemName</I>?...
+</DT>
+<DD>Finds the data point representing the bar closest to the window coordinates
+<I>x</I> and <I>y</I> in the element <I>elemName</I>. <I>ElemName</I> is the name of an element, which
+must be displayed. If no elements are specified, then all displayed elements
+are searched. It returns a list containing the name of the closest element,
+the index of its closest point, and the graph coordinates of the point.
+If no data point within the threshold distance can be found, <I>""</I> is returned.
+ The following <I>option</I>-<I>value</I> pairs are available. <blockquote></DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a
+threshold distance where selected data points are ignored. <I>Pixels</I> is a valid
+screen distance, such as <I>2</I> or <I>1.2i</I>. If this option isn't specified, then it
+defaults to the value of the <B>barchart</B>'s <B>-halo</B> option. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>element configure
+<I>elemName </I></B></I>?<I>elemName</I>... ?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration
+options for elements. Several elements can be modified at the same time.
+If <I>option</I> isn't specified, a list describing all the current options for
+<I>elemName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a list
+describing the option <I>option</I> is returned. If one or more <I>option</I> and <I>value</I>
+pairs are specified, then for each pair, the element option <I>option</I> is set
+to <I>value</I>. The following options are valid for elements. <blockquote></DD>
+
+<DT><B>-activepen <I>penName</I></B>
+</DT>
+<DD>Specifies pen to use to draw active element. If <I>penName</I> is <I>""</I>, no active
+elements will be drawn. The default is <I>activeLine</I>. </DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies
+the binding tags for the element. <I>TagList</I> is a list of binding tag names.
+ The tags and their order will determine how events for elements. Each
+tag in the list matching the current event sequence will have its Tcl command
+executed. Implicitly the name of the element is always the first tag in
+the list. The default value is <I>all</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the the color
+of the border around each bar. The default is <I>white</I>. </DD>
+
+<DT><B>-barwidth <I>value</I></B> </DT>
+<DD>Specifies
+the width the bars drawn for the element. <I>Value</I> is the width in X-coordinates.
+ If this option isn't specified, the width of each bar is the value of the
+widget's <B>-barwidth</B> option. </DD>
+
+<DT><B>-baseline <I>value</I></B> </DT>
+<DD>Specifies the baseline of the bar
+segments. This affects how bars are drawn since bars are drawn from their
+respective y-coordinate the baseline. By default the baseline is <I>0.0</I>. </DD>
+
+<DT><B>-borderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the border width of the 3-D border drawn around the outside
+of each bar. The <B>-relief</B> option determines if such a border is drawn. <I>Pixels</I>
+must be a valid screen distance like <I>2</I> or <I>0.25i</I>. The default is <I>2</I>. </DD>
+
+<DT><B>-data <I>coordList</I></B>
+</DT>
+<DD>Specifies the X-Y coordinates of the data. <I>CoordList</I> is a list of numeric
+expressions representing the X-Y coordinate pairs of each data point. </DD>
+
+<DT><B>-foreground
+<I>color</I></B> </DT>
+<DD>Sets the color of the interior of the bars. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the element is displayed. The default is <I>no</I>. </DD>
+
+<DT><B>-label <I>text</I></B> </DT>
+<DD>Sets the
+element's label in the legend. If <I>text</I> is <I>""</I>, the element will have no entry
+in the legend. The default label is the element's name. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Selects
+the X-axis to map the element's X-coordinates onto. <I>XAxis</I> must be the name
+of an axis. The default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Selects the Y-axis to map the element's
+Y-coordinates onto. <I>YAxis</I> must be the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-relief
+<I>string</I></B> </DT>
+<DD>Specifies the 3-D effect desired for bars. <I>Relief</I> indicates how the
+interior of the bar should appear relative to the surface of the chart;
+for example, <I>raised</I> means the bar should appear to protrude from the surface
+of the plotting area. The default is <I>raised</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies a
+stipple pattern with which to draw the bars. If <I>bitmap</I> is <I>""</I>, then the
+bar is drawn in a solid fashion. </DD>
+
+<DT><B>-xdata <I>xVector</I></B> </DT>
+<DD>Specifies the x-coordinate
+vector of the data. <I>XVector</I> is the name of a BLT vector or a list of numeric
+expressions. </DD>
+
+<DT><B>-ydata <I>yVector</I></B> </DT>
+<DD>Specifies the y-coordinate vector of the data.
+<I>YVector</I> is the name of a BLT vector or a list of numeric expressions.
+</DD>
+</DL>
+<P>
+Element configuration options may also be set by the <B>option</B> command. The
+resource names in the option database are prefixed by <I>elem</I>. <BR>
+<CODE>option add *Barchart.Element.background blue<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>element create <I>elemName</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a new element <I>elemName</I>.
+ Element names must be unique, so an element <I>elemName</I> may not already exist.
+ If additional arguments are present, they specify any of the element options
+valid for element <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>element deactivate <I>pattern</I></B></I>...
+</DT>
+<DD>Deactivates all the elements matching <I>pattern</I> for the graph. Elements
+whose names match any of the patterns given are redrawn using their normal
+colors. </DD>
+
+<DT><I>pathName <B>element delete</B></I> ?<I>pattern</I>?... </DT>
+<DD>Deletes all the elements matching
+<I>pattern</I> for the graph. Elements whose names match any of the patterns
+given are deleted. The graph will be redrawn without the deleted elements.
+ </DD>
+
+<DT><I>pathName <B>element exists <I>elemName</I></B></I> </DT>
+<DD>Returns <I>1</I> if an element <I>elemName</I> currently
+exists and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>element names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns the elements
+matching one or more pattern. If no <I>pattern</I> is given, the names of all
+elements is returned. </DD>
+
+<DT><I>pathName <B>element show</B></I> ?<I>nameList</I>? </DT>
+<DD>Queries or modifies
+the element display list. The element display list designates the elements
+drawn and in what order. <I>NameList</I> is a list of elements to be displayed
+in the order they are named. If there is no <I>nameList</I> argument, the current
+display list is returned. </DD>
+
+<DT><I>pathName <B>element type</B></I> <I>elemName</I> </DT>
+<DD>Returns the type
+of <I>elemName</I>. If the element is a bar element, the commands returns the
+string <I>"bar"</I>, otherwise it returns <I>"line"</I>. </DD>
+</DL>
+
+<H3><A NAME="sect12" HREF="#toc12"></CODE><P>Grid Component</A></H3>
+Grid lines extend
+from the major and minor ticks of each axis horizontally or vertically
+across the plotting area. The following operations are available for grid
+lines.
+<DL>
+
+<DT><I>pathName <B>grid cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the grid line
+configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the grid <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>grid configure</B></I> ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options for grid lines. If
+<I>option</I> isn't specified, a list describing all the current grid options for
+<I>pathName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a list
+describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are
+specified, then for each pair, the grid line option <I>option</I> is set to <I>value</I>.
+ The following options are valid for grid lines. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color
+of the grid lines. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style
+of the grid lines. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the grid lines. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the grid will be solid lines.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid should be drawn. If <I>boolean</I> is true,
+grid lines are not shown. The default is <I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of grid lines. The default width is <I>1</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis to
+display grid lines. <I>XAxis</I> must be the name of an axis or <I>""</I> for no grid
+lines. The default is <I>""</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to display grid
+lines. <I>YAxis</I> must be the name of an axis or <I>""</I> for no grid lines. The default
+is <I>y</I>. </DD>
+
+<DT><B>-minor <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid lines should be drawn for
+minor ticks. If <I>boolean</I> is true, the lines will appear at minor tick intervals.
+ The default is <I>1</I>. </DD>
+</DL>
+<P>
+Grid configuration options may also be set by the <B>option</B>
+command. The resource name and class are <I>grid</I> and <I>Grid</I> respectively. <BR>
+<CODE>option add *Barchart.grid.LineWidth 2<BR>
+option add *Barchart.Grid.Color black<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>grid off</B></I> </DT>
+<DD>Turns off the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid on</B></I>
+</DT>
+<DD>Turns on the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid toggle</B></I> </DT>
+<DD>Toggles the display
+of the grid. </DD>
+</DL>
+
+<H3><A NAME="sect13" HREF="#toc13">Legend Component</A></H3>
+The legend displays a list of the data elements.
+ Each entry consists of the element's symbol and label. The legend can appear
+in any margin (the default location is in the right margin). It can also
+be positioned anywhere within the plotting area. <P>
+The following operations
+are valid for the legend.
+<DL>
+
+<DT><I>pathName <B>legend activate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend
+entries to be drawn using the active legend colors and relief. All entries
+whose element names match <I>pattern</I> are selected. To be selected, the element
+name must match only one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend bind <I>tagName</I></B></I> ?<I>sequence</I>?
+ ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the event
+sequence given by <I>sequence</I> occurs for a legend entry with this tag, <I>command</I>
+will be invoked. Implicitly the element names in the entry are tags. The
+syntax is similar to the <B>bind</B> command except that it operates on legend
+entries, rather than widgets. See the <B>bind</B> manual entry for complete details
+on <I>sequence</I> and the substitutions performed on <I>command</I> before invoking
+it. <P>
+If all arguments are specified then a new binding is created, replacing
+ any existing binding for the same <I>sequence</I> and <I>tagName</I>. If the first character
+of <I>command</I> is <I>+</I> then <I>command</I> augments an existing binding rather than
+replacing it. If no <I>command</I> argument is provided then the command currently
+associated with <I>tagName</I> and <I>sequence</I> (it's an error occurs if there's no
+such binding) is returned. If both <I>command</I> and <I>sequence</I> are missing then
+a list of all the event sequences for which bindings have been defined
+for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>legend cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of a
+legend configuration option. <I>Option</I> may be any option described below in
+the legend <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>legend configure </B></I>?<I>option value</I>?...
+</DT>
+<DD>Queries or modifies the configuration options for the legend. If <I>option</I>
+isn't specified, a list describing the current legend options for <I>pathName</I>
+is returned. If <I>option</I> is specified, but not <I>value</I>, then a list describing
+<I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are specified,
+then for each pair, the legend option <I>option</I> is set to <I>value</I>. The following
+options are valid for the legend. <blockquote></DD>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets the background
+color for active legend entries. All legend entries marked active (see
+the legend <B>activate</B> operation) are drawn using this background color. </DD>
+
+<DT><B>-activeborderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the outside edge of the active
+legend entries. The default is <I>2</I>. </DD>
+
+<DT><B>-activeforeground <I>color</I></B> </DT>
+<DD>Sets the foreground
+color for active legend entries. All legend entries marked as active (see
+the legend <B>activate</B> operation) are drawn using this foreground color. </DD>
+
+<DT><B>-activerelief
+<I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect desired for active legend entries. <I>Relief</I>
+denotes how the interior of the entry should appear relative to the legend;
+for example, <I>raised</I> means the entry should appear to protrude from the
+legend, relative to the surface of the legend. The default is <I>flat</I>. </DD>
+
+<DT><B>-anchor
+<I>anchor</I></B> </DT>
+<DD>Tells how to position the legend relative to the positioning point
+for the legend. This is dependent on the value of the <B>-position</B> option.
+The default is <I>center</I>. <blockquote></DD>
+
+<DT><I>left</I> or <I>right</I> </DT>
+<DD>The anchor describes how to position
+the legend vertically. </DD>
+
+<DT><I>top</I> or <I>bottom</I> </DT>
+<DD>The anchor describes how to position
+the legend horizontally. </DD>
+
+<DT><I>@x,y</I> </DT>
+<DD>The anchor specifies how to position the
+legend relative to the positioning point. For example, if <I>anchor</I> is <I>center</I>
+then the legend is centered on the point; if <I>anchor</I> is <I>n</I> then the legend
+will be drawn such that the top center point of the rectangular region
+occupied by the legend will be at the positioning point. </DD>
+
+<DT><I>plotarea</I> </DT>
+<DD>The anchor
+specifies how to position the legend relative to the plotting area. For
+example, if <I>anchor</I> is <I>center</I> then the legend is centered in the plotting
+area; if <I>anchor</I> is <I>ne</I> then the legend will be drawn such that occupies
+the upper right corner of the plotting area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background
+color of the legend. If <I>color</I> is <I>""</I>, the legend background with be transparent.
+</DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for legend entries. <I>TagList</I>
+is a list of binding tag names. The tags and their order will determine
+how events for legend entries. Each tag in the list matching the current
+ event sequence will have its Tcl command executed. The default value is
+<I>all</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the outside
+edge of the legend (if such border is being drawn; the <B>relief</B> option determines
+this). The default is <I>2</I> pixels. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD><I>FontName</I> specifies a font
+to use when drawing the labels of each element into the legend. The default
+is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the foreground color
+of the text drawn for the element's label. The default is <I>black</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the legend should be displayed. If <I>boolean</I> is true, the
+legend will not be draw. The default is <I>no</I>. </DD>
+
+<DT><B>-ipadx <I>pad</I></B> </DT>
+<DD>Sets the amount of
+internal padding to be added to the width of each legend entry. <I>Pad</I> can
+be a list of one or two screen distances. If <I>pad</I> has two elements, the
+left side of the legend entry is padded by the first distance and the right
+side by the second. If <I>pad</I> is just one distance, both the left and right
+sides are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-ipady <I>pad</I></B> </DT>
+<DD>Sets an amount of internal
+padding to be added to the height of each legend entry. <I>Pad</I> can be a list
+of one or two screen distances. If <I>pad</I> has two elements, the top of the
+entry is padded by the first distance and the bottom by the second. If
+<I>pad</I> is just one distance, both the top and bottom of the entry are padded
+evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the padding to the left and right
+exteriors of the legend. <I>Pad</I> can be a list of one or two screen distances.
+ If <I>pad</I> has two elements, the left side of the legend is padded by the
+first distance and the right side by the second. If <I>pad</I> has just one distance,
+both the left and right sides are padded evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-pady
+<I>pad</I></B> </DT>
+<DD>Sets the padding above and below the legend. <I>Pad</I> can be a list of one
+or two screen distances. If <I>pad</I> has two elements, the area above the legend
+is padded by the first distance and the area below by the second. If <I>pad</I>
+is just one distance, both the top and bottom areas are padded evenly.
+The default is <I>0</I>. </DD>
+
+<DT><B>-position <I>pos</I></B> </DT>
+<DD>Specifies where the legend is drawn. The <B>-anchor</B>
+option also affects where the legend is positioned. If <I>pos</I> is <I>left</I>, <I>left</I>,
+<I>top</I>, or <I>bottom</I>, the legend is drawn in the specified margin. If <I>pos</I> is
+<I>plotarea</I>, then the legend is drawn inside the plotting area at a particular
+anchor. If <I>pos</I> is in the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window coordinates,
+the legend is drawn in the plotting area at the specified coordinates.
+The default is <I>right</I>. </DD>
+
+<DT><B>-raised <I>boolean</I></B> </DT>
+<DD>Indicates whether the legend is above
+or below the data elements. This matters only if the legend is in the plotting
+area. If <I>boolean</I> is true, the legend will be drawn on top of any elements
+that may overlap it. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the border around the legend. <I>Relief</I> specifies how the interior of the
+legend should appear relative to the bar chart; for example, <I>raised</I> means
+the legend should appear to protrude from the bar chart, relative to the
+surface of the bar chart. The default is <I>sunken</I>. </DD>
+</DL>
+<P>
+Legend configuration options
+may also be set by the <B>option</B> command. The resource name and class are
+<I>legend</I> and <I>Legend</I> respectively. <BR>
+<CODE>option add *Barchart.legend.Foreground blue<BR>
+option add *Barchart.Legend.Relief raised<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>legend deactivate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend entries to be drawn using
+the normal legend colors and relief. All entries whose element names match
+<I>pattern</I> are selected. To be selected, the element name must match only
+one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend get <I>pos</I></B></I> </DT>
+<DD>Returns the name of the element whose
+entry is at the screen position <I>pos</I> in the legend. <I>Pos</I> must be in the form
+"<I>@x,y</I>", where <I>x</I> and <I>y</I> are window coordinates. If the given coordinates
+do not lie over a legend entry, <I>""</I> is returned. </DD>
+</DL>
+
+<H3><A NAME="sect14" HREF="#toc14">Pen Components</A></H3>
+Pens define
+attributes for elements. Pens mirror the configuration options of data elements
+that pertain to how symbols and lines are drawn. Data elements use pens
+to determine how they are drawn. A data element may use several pens at
+once. In this case, the pen used for a particular data point is determined
+from each element's weight vector (see the element's <B>-weight</B> and <B>-style</B> options).
+<P>
+One pen, called <I>activeBar</I>, is automatically created. It's used as the default
+active pen for elements. So you can change the active attributes for all
+elements by simply reconfiguring this pen. <BR>
+<CODE>.g pen configure "activeBar" -fg green -bg green4<BR>
+</CODE><P>You can create and use several pens. To create a pen, invoke the pen component
+and its create operation. <BR>
+<CODE>.g pen create myPen<BR>
+</CODE><P>You map pens to a data element using either the element's <B>-pen</B> or <B>-activepen</B>
+options. <BR>
+<CODE>.g element create "e1" -xdata $x -ydata $tempData \<BR>
+ -pen myPen<BR>
+</CODE><P>An element can use several pens at once. This is done by specifying the
+name of the pen in the element's style list (see the <B>-styles</B> option). <BR>
+<CODE>.g element configure "e1" -styles { myPen 2.0 3.0 }<BR>
+</CODE><P>This says that any data point with a weight between 2.0 and 3.0 is to be
+drawn using the pen <I>myPen</I>. All other points are drawn with the element's
+default attributes. <P>
+The following operations are available for pen components.
+<P>
+
+<DL>
+
+<DT><I>pathName <B>pen <B>cget <I>penName <I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option
+given by <I>option</I> for <I>penName</I>. <I>Option</I> may be any option described below for
+the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>configure <I>penName </I></B></B></I>?<I>penName</I>... ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options of <I>penName</I>. Several
+pens can be modified at once. If <I>option</I> isn't specified, a list describing
+the current options for <I>penName</I> is returned. If <I>option</I> is specified, but
+not <I>value</I>, then a list describing <I>option</I> is returned. If one or more <I>option</I>
+and <I>value</I> pairs are specified, then for each pair, the pen option <I>option</I>
+is set to <I>value</I>. The following options are valid for pens. <blockquote></DD>
+
+<DT><B>-background <I>color</I></B>
+</DT>
+<DD>Sets the the color of the border around each bar. The default is <I>white</I>.
+</DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the border width of the 3-D border drawn around
+the outside of each bar. The <B>-relief</B> option determines if such a border
+is drawn. <I>Pixels</I> must be a valid screen distance like <I>2</I> or <I>0.25i</I>. The default
+is <I>2</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the color of the interior of the bars. </DD>
+
+<DT><B>-relief
+<I>string</I></B> </DT>
+<DD>Specifies the 3-D effect desired for bars. <I>Relief</I> indicates how the
+interior of the bar should appear relative to the surface of the chart;
+for example, <I>raised</I> means the bar should appear to protrude from the bar
+chart, relative to the surface of the plotting area. The default is <I>raised</I>.
+</DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies a stipple pattern with which to draw the bars.
+ If <I>bitmap</I> is <I>""</I>, then the bar is drawn in a solid fashion. </DD>
+
+<DT><B>-type <I>elemType</I></B>
+ </DT>
+<DD>Specifies the type of element the pen is to be used with. This option should
+only be employed when creating the pen. This is for those that wish to
+mix different types of elements (bars and lines) on the same graph. The
+default type is "bar". </DD>
+</DL>
+<P>
+Pen configuration options may be also be set by the
+<B>option</B> command. The resource class is <I>Pen</I>. The resource names are the names
+of the pens. <BR>
+<CODE>option add *Barchart.Pen.Foreground<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp; blue<BR>
+option add *Barchart.activeBar.foreground green<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>pen <B>create <I>penName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new pen by the name
+<I>penName</I>. No pen by the same name can already exist. <I>Option</I> and <I>value</I> are
+described in above in the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>delete
+</B></B></I>?<I>penName</I>?... </DT>
+<DD>Deletes the named pens. A pen is not really deleted until it is
+not longer in use, so it's safe to delete pens mapped to elements. </DD>
+
+<DT><I>pathName
+<B>pen names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of pens matching zero or more patterns.
+ If no <I>pattern</I> argument is give, the names of all pens are returned. </DD>
+</DL>
+
+<H3><A NAME="sect15" HREF="#toc15">PostScript
+Component</A></H3>
+The barchart can generate encapsulated PostScript output. There
+are several configuration options you can specify to control how the plot
+will be generated. You can change the page dimensions and borders. The
+plot itself can be scaled, centered, or rotated to landscape. The PostScript
+output can be written directly to a file or returned through the interpreter.
+<P>
+The following postscript operations are available.
+<DL>
+
+<DT><I>pathName <B>postscript cget
+<I>option</I></B></I> </DT>
+<DD>Returns the current value of the postscript option given by <I>option</I>.
+ <I>Option</I> may be any option described below for the postscript <B>configure</B>
+operation. </DD>
+
+<DT><I>pathName <B>postscript configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options for PostScript generation. If <I>option</I> isn't specified,
+a list describing the current postscript options for <I>pathName</I> is returned.
+ If <I>option</I> is specified, but not <I>value</I>, then a list describing <I>option</I> is
+returned. If one or more <I>option</I> and <I>value</I> pairs are specified, then for
+each pair, the postscript option <I>option</I> is set to <I>value</I>. The following
+postscript options are available. <blockquote></DD>
+
+<DT><B>-center <I>boolean</I></B> </DT>
+<DD>Indicates whether the plot
+should be centered on the PostScript page. If <I>boolean</I> is false, the plot
+will be placed in the upper left corner of the page. The default is <I>1</I>. </DD>
+
+<DT><B>-colormap
+<I>varName</I></B> </DT>
+<DD><I>VarName</I> must be the name of a global array variable that specifies
+a color mapping from the X color name to PostScript. Each element of <I>varName</I>
+must consist of PostScript code to set a particular color value (e.g. ``<I>1.0
+1.0 0.0 setrgbcolor</I>''). When generating color information in PostScript, the
+array variable <I>varName</I> is checked if an element of the name as the color
+exists. If so, it uses its value as the PostScript command to set the color.
+ If this option hasn't been specified, or if there isn't an entry in <I>varName</I>
+for a given color, then it uses the red, green, and blue intensities from
+the X color. </DD>
+
+<DT><B>-colormode <I>mode</I></B> </DT>
+<DD>Specifies how to output color information. <I>Mode</I>
+must be either <I>color</I> (for full color output), <I>gray</I> (convert all colors
+to their gray-scale equivalents) or <I>mono</I> (convert foreground colors to black
+and background colors to white). The default mode is <I>color</I>. </DD>
+
+<DT><B>-fontmap <I>varName</I></B>
+</DT>
+<DD><I>VarName</I> must be the name of a global array variable that specifies a font
+mapping from the X font name to PostScript. Each element of <I>varName</I> must
+consist of a Tcl list with one or two elements; the name and point size
+of a PostScript font. When outputting PostScript commands for a particular
+font, the array variable <I>varName</I> is checked to see if an element by the
+ specified font exists. If there is such an element, then the font information
+contained in that element is used in the PostScript output. (If the point
+size is omitted from the list, the point size of the X font is used). Otherwise
+the X font is examined in an attempt to guess what PostScript font to use.
+ This works only for fonts whose foundry property is <I>Adobe</I> (such as Times,
+Helvetica, Courier, etc.). If all of this fails then the font defaults to
+<I>Helvetica-Bold</I>. </DD>
+
+<DT><B>-decorations <I>boolean</I></B> </DT>
+<DD>Indicates whether PostScript commands
+to generate color backgrounds and 3-D borders will be output. If <I>boolean</I>
+is false, the graph will background will be white and no 3-D borders will
+be generated. The default is <I>1</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Sets the height of the plot.
+ This lets you print the bar chart with a height different from the one
+drawn on the screen. If <I>pixels</I> is 0, the height is the same as the widget's
+height. The default is <I>0</I>. </DD>
+
+<DT><B>-landscape <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, this specifies
+the printed area is to be rotated 90 degrees. In non-rotated output the
+X-axis of the printed area runs along the short dimension of the page (``portrait''
+orientation); in rotated output the X-axis runs along the long dimension
+of the page (``landscape'' orientation). Defaults to <I>0</I>. </DD>
+
+<DT><B>-maxpect <I>boolean</I></B> </DT>
+<DD>Indicates
+to scale the plot so that it fills the PostScript page. The aspect ratio
+of the barchart is still retained. The default is <I>0</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the horizontal
+padding for the left and right page borders. The borders are exterior to
+the plot. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has
+two elements, the left border is padded by the first distance and the right
+border by the second. If <I>pad</I> has just one distance, both the left and right
+borders are padded evenly. The default is <I>1i</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the vertical
+padding for the top and bottom page borders. The borders are exterior to
+the plot. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has
+two elements, the top border is padded by the first distance and the bottom
+border by the second. If <I>pad</I> has just one distance, both the top and bottom
+borders are padded evenly. The default is <I>1i</I>. </DD>
+
+<DT><B>-paperheight <I>pixels</I></B> </DT>
+<DD>Sets the
+height of the postscript page. This can be used to select between different
+page sizes (letter, A4, etc). The default height is <I>11.0i</I>. </DD>
+
+<DT><B>-paperwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the postscript page. This can be used to select between
+different page sizes (letter, A4, etc). The default width is <I>8.5i</I>. </DD>
+
+<DT><B>-width
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the plot. This lets you generate a plot of a width
+different from that of the widget. If <I>pixels</I> is 0, the width is the same
+as the widget's width. The default is <I>0</I>. </DD>
+</DL>
+<P>
+Postscript configuration options
+may be also be set by the <B>option</B> command. The resource name and class are
+<I>postscript</I> and <I>Postscript</I> respectively. <BR>
+<CODE>option add *Barchart.postscript.Decorations false<BR>
+option add *Barchart.Postscript.Landscape true<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>postscript output </B></I>?<I>fileName</I>? ?<I>option value</I>?... </DT>
+<DD>Outputs a file of
+encapsulated PostScript. If a <I>fileName</I> argument isn't present, the command
+returns the PostScript. If any <I>option-value</I> pairs are present, they set configuration
+options controlling how the PostScript is generated. <I>Option</I> and <I>value</I> can
+be anything accepted by the postscript <B>configure</B> operation above. </DD>
+</DL>
+
+<H3><A NAME="sect16" HREF="#toc16">Marker
+Components</A></H3>
+Markers are simple drawing procedures used to annotate or highlight
+areas of the graph. Markers have various types: text strings, bitmaps,
+images, connected lines, windows, or polygons. They can be associated with
+a particular element, so that when the element is hidden or un-hidden, so
+is the marker. By default, markers are the last items drawn, so that data
+elements will appear in behind them. You can change this by configuring
+the <B>-under</B> option. <P>
+Markers, in contrast to elements, don't affect the scaling
+of the coordinate axes. They can also have <I>elastic</I> coordinates (specified
+by <I>-Inf</I> and <I>Inf</I> respectively) that translate into the minimum or maximum
+limit of the axis. For example, you can place a marker so it always remains
+in the lower left corner of the plotting area, by using the coordinates
+<I>-Inf</I>,<I>-Inf</I>. <P>
+The following operations are available for markers.
+<DL>
+
+<DT><I>pathName <B>marker
+after <I>markerId</I></B></I> ?<I>afterId</I>? </DT>
+<DD>Changes the order of the markers, drawing the
+first marker after the second. If no second <I>afterId</I> argument is specified,
+the marker is placed at the end of the display list. This command can be
+used to control how markers are displayed since markers are drawn in the
+order of this display list. </DD>
+
+<DT><I>pathName <B>marker before <I>markerId</I></B></I> ?<I>beforeId</I>? </DT>
+<DD>Changes
+the order of the markers, drawing the first marker before the second. If
+no second <I>beforeId</I> argument is specified, the marker is placed at the beginning
+of the display list. This command can be used to control how markers are
+displayed since markers are drawn in the order of this display list. </DD>
+
+<DT><I>pathName
+<B>marker bind <I>tagName</I></B></I> ?<I>sequence</I>? ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I>
+such that whenever the event sequence given by <I>sequence</I> occurs for a marker
+with this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B>
+command except that it operates on graph markers, rather than widgets.
+See the <B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>marker cget <I>option</I></B></I> </DT>
+<DD>Returns
+the current value of the marker configuration option given by <I>option</I>. <I>Option</I>
+may be any option described below in the <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>marker
+configure <I>markerId</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration
+options for markers. If <I>option</I> isn't specified, a list describing the current
+options for <I>markerId</I> is returned. If <I>option</I> is specified, but not <I>value</I>,
+then a list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I>
+pairs are specified, then for each pair, the marker option <I>option</I> is set
+to <I>value</I>. <P>
+The following options are valid for all markers. Each type of marker
+also has its own type-specific options. They are described in the sections
+below. <blockquote></DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for the marker. <I>TagList</I>
+is a list of binding tag names. The tags and their order will determine
+how events for markers are handled. Each tag in the list matching the
+current event sequence will have its Tcl command executed. Implicitly
+the name of the marker is always the first tag in the list. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-coords <I>coordList</I></B> </DT>
+<DD>Specifies the coordinates of the marker.
+<I>CoordList</I> is a list of graph coordinates. The number of coordinates required
+is dependent on the type of marker. Text, image, and window markers need
+only two coordinates (an X-Y coordinate). Bitmap markers can take either
+two or four coordinates (if four, they represent the corners of the bitmap).
+Line markers need at least four coordinates, polygons at least six. If <I>coordList</I>
+is <I>""</I>, the marker will not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-element <I>elemName</I></B>
+</DT>
+<DD>Links the marker with the element <I>elemName</I>. The marker is drawn only if
+the element is also currently displayed (see the element's <B>show</B> operation).
+ If <I>elemName</I> is <I>""</I>, the marker is always drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-hide
+<I>boolean</I></B> </DT>
+<DD>Indicates whether the marker is drawn. If <I>boolean</I> is true, the
+marker is not drawn. The default is <I>no</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis
+to map the marker's X-coordinates onto. <I>XAxis</I> must the name of an axis. The
+default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to map the marker's Y-coordinates
+onto. <I>YAxis</I> must the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-name <I>markerId</I></B> </DT>
+<DD>Changes
+the identifier for the marker. The identifier <I>markerId</I> can not already
+be used by another marker. If this option isn't specified, the marker's name
+is uniquely generated. </DD>
+
+<DT><B>-under <I>boolean</I></B> </DT>
+<DD>Indicates whether the marker is drawn
+below/above data elements. If <I>boolean</I> is true, the marker is be drawn underneath
+the data elements. Otherwise, the marker is drawn on top of the element.
+ The default is <I>0</I>. </DD>
+
+<DT><B>-xoffset <I>pixels</I></B> </DT>
+<DD>Specifies a screen distance to offset
+the marker horizontally. <I>Pixels</I> is a valid screen distance, such as <I>2</I> or
+<I>1.2i</I>. The default is <I>0</I>. </DD>
+
+<DT><B>-yoffset <I>pixels</I></B> </DT>
+<DD>Specifies a screen distance to offset
+the markers vertically. <I>Pixels</I> is a valid screen distance, such as <I>2</I> or
+<I>1.2i</I>. The default is <I>0</I>. </DD>
+</DL>
+<P>
+Marker configuration options may also be set by the
+<B>option</B> command. The resource class is either <I>BitmapMarker</I>, <I>ImageMarker</I>,
+ <I>LineMarker</I>, <I>PolygonMarker</I>, <I>TextMarker</I>, or <I>WindowMarker</I>, depending on the
+type of marker. The resource name is the name of the marker. <BR>
+<CODE>option add *Barchart.TextMarker.Foreground white<BR>
+option add *Barchart.BitmapMarker.Foreground white<BR>
+option add *Barchart.m1.Background blue<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>marker create <I>type</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a marker of the selected
+type. <I>Type</I> may be either <I>text</I>, <I>line</I>, <I>bitmap</I>, <I>image</I>, <I>polygon</I>, or <I>window</I>.
+This command returns the marker identifier, used as the <I>markerId</I> argument
+in the other marker-related commands. If the <B>-name</B> option is used, this overrides
+the normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old. </DD>
+
+<DT><I>pathName <B>marker delete</B></I>
+?<I>name</I>?... </DT>
+<DD>Removes one of more markers. The graph will automatically be redrawn
+without the marker.. </DD>
+
+<DT><I>pathName <B>marker exists <I>markerId</I></B></I> </DT>
+<DD>Returns <I>1</I> if the
+marker <I>markerId</I> exists and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>marker names</B></I> ?<I>pattern</I>?
+ </DT>
+<DD>Returns the names of all the markers that currently exist. If <I>pattern</I>
+is supplied, only those markers whose names match it will be returned. </DD>
+
+<DT><I>pathName
+<B>marker type <I>markerId</I></B></I> </DT>
+<DD>Returns the type of the marker given by <I>markerId</I>,
+such as <I>line</I> or <I>text</I>. If <I>markerId</I> is not a valid a marker identifier, <I>""</I>
+is returned. </DD>
+</DL>
+
+<H3><A NAME="sect17" HREF="#toc17">Bitmap Markers</A></H3>
+A bitmap marker displays a bitmap. The size of
+the bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the bitmap.
+ The bitmap retains its normal width and height. If four coordinates, the
+first and second pairs of coordinates represent the corners of the bitmap.
+ The bitmap will be stretched or reduced as necessary to fit into the bounding
+rectangle. <P>
+Bitmap markers are created with the marker's <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create bitmap </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration options
+for the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's
+<B>configure</B> operation. <P>
+The following options are specific to bitmap markers:
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Same as the <B>-fill</B> option. </DD>
+
+<DT><B>-bitmap <I>bitmap</I></B> </DT>
+<DD>Specifies the bitmap
+to be displayed. If <I>bitmap</I> is <I>""</I>, the marker will not be displayed. The
+default is <I>""</I>. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background color of the bitmap. If <I>color</I>
+is the empty string, no background will be transparent. The default background
+color is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Same as the <B>-outline</B> option. </DD>
+
+<DT><B>-mask <I>mask</I></B> </DT>
+<DD>Specifies
+a mask for the bitmap to be displayed. This mask is a bitmap itself, denoting
+the pixels that are transparent. If <I>mask</I> is <I>""</I>, all pixels of the bitmap
+will be drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the foreground color
+of the bitmap. The default value is <I>black</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Sets the rotation
+of the bitmap. <I>Theta</I> is a real number representing the angle of rotation
+in degrees. The marker is first rotated and then placed according to its
+anchor position. The default rotation is <I>0.0</I>. </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Image Markers</A></H3>
+A image marker
+displays an image. Image markers are created with the marker's <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create image </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to image markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the image relative to the positioning point
+for the image. For example, if <I>anchor</I> is <I>center</I> then the image is centered
+on the point; if <I>anchor</I> is <I>n</I> then the image will be drawn such that the
+top center point of the rectangular region occupied by the image will be
+at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-image <I>image</I></B> </DT>
+<DD>Specifies
+the image to be drawn. If <I>image</I> is <I>""</I>, the marker will not be drawn. The
+default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Line Markers</A></H3>
+A line marker displays one or more connected
+line segments. Line markers are created with marker's <B>create</B> operation in
+the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create line </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to line markers:
+<DL>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the line. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the line.
+ Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the marker line
+will be solid. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background color of the line. This color
+is used with striped lines (see the <B>-fdashesR option). If <I>color</I></B> is the empty
+string, no background color is drawn (the line will be dashed, not striped).
+ The default background color is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the lines. The default width is <I>0</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the foreground color
+of the line. The default value is <I>black</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies a stipple
+pattern used to draw the line, rather than a solid line. <I>Bitmap</I> specifies
+a bitmap to use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then the line
+is drawn in a solid fashion. The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Polygon Markers</A></H3>
+A polygon
+marker displays a closed region described as two or more connected line
+segments. It is assumed the first and last points are connected. Polygon
+markers are created using the marker <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create polygon </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the <B>marker configure</B>
+command to change the marker's configuration. The following options are supported
+for polygon markers:
+<DL>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline
+of the polygon. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the outline. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the outline will be a solid
+line. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the fill color of the polygon. If <I>color</I> is <I>""</I>, then
+the interior of the polygon is transparent. The default is <I>white</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the outline of the polygon. If <I>pixels</I> is zero,
+ no outline is drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color of the
+outline of the polygon. If the polygon is stippled (see the <B>-stipple</B> option),
+then this represents the foreground color of the stipple. The default is
+<I>black</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies that the polygon should be drawn with a
+stippled pattern rather than a solid color. <I>Bitmap</I> specifies a bitmap to
+use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then the polygon is filled
+with a solid color (if the <B>-fill</B> option is set). The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Text
+Markers</A></H3>
+A text marker displays a string of characters on one or more lines
+of text. Embedded newlines cause line breaks. They may be used to annotate
+regions of the graph. Text markers are created with the <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create text </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option
+for the text marker. These same <I>option</I>-<I>value</I> pairs may be used with the
+ marker's <B>configure</B> operation. <P>
+The following options are specific to text
+markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B> </DT>
+<DD><I>Anchor</I> tells how to position the text relative to
+the positioning point for the text. For example, if <I>anchor</I> is <I>center</I> then
+the text is centered on the point; if <I>anchor</I> is <I>n</I> then the text will be
+drawn such that the top center point of the rectangular region occupied
+by the text will be at the positioning point. This default is <I>center</I>. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Same as the <B>-fill</B> option. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of the text.
+ The default is <I>*-Helvetica-Bold-R-Normal-*-120-*</I>. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background
+color of the text. If <I>color</I> is the empty string, no background will be
+transparent. The default background color is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Same as
+the <B>-outline</B> option. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the text should be justified.
+ This matters only when the marker contains more than one line of text.
+<I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-outline <I>color</I></B>
+</DT>
+<DD>Sets the color of the text. The default value is <I>black</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the
+padding to the left and right exteriors of the text. <I>Pad</I> can be a list of
+one or two screen distances. If <I>pad</I> has two elements, the left side of
+the text is padded by the first distance and the right side by the second.
+ If <I>pad</I> has just one distance, both the left and right sides are padded
+evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and below the
+text. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has two
+elements, the area above the text is padded by the first distance and the
+area below by the second. If <I>pad</I> is just one distance, both the top and
+bottom areas are padded evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies
+the number of degrees to rotate the text. <I>Theta</I> is a real number representing
+the angle of rotation. The marker is first rotated along its center and
+is then drawn according to its anchor position. The default is <I>0.0</I>. </DD>
+
+<DT><B>-text <I>text</I></B>
+</DT>
+<DD>Specifies the text of the marker. The exact way the text is displayed may
+be affected by other options such as <B>-anchor</B> or <B>-rotate</B>. </DD>
+</DL>
+
+<H3><A NAME="sect22" HREF="#toc22">Window Markers</A></H3>
+A window
+marker displays a widget at a given position. Window markers are created
+with the marker's <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create window </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+command. <P>
+The following options are specific to window markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the widget relative to the positioning point
+for the widget. For example, if <I>anchor</I> is <I>center</I> then the widget is centered
+on the point; if <I>anchor</I> is <I>n</I> then the widget will be displayed such that
+the top center point of the rectangular region occupied by the widget will
+be at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B>
+</DT>
+<DD>Specifies the height to assign to the marker's window. If this option isn't
+specified, or if it is specified as <I>""</I>, then the window is given whatever
+height the widget requests internally. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the width
+to assign to the marker's window. If this option isn't specified, or if it
+is specified as <I>""</I>, then the window is given whatever width the widget
+requests internally. </DD>
+
+<DT><B>-window <I>pathName</I></B> </DT>
+<DD>Specifies the widget to be managed
+by the barchart. <I>PathName</I> must be a child of the <B>barchart</B> widget. </DD>
+</DL>
+
+<H2><A NAME="sect23" HREF="#toc23">Graph
+Component Bindings</A></H2>
+Specific barchart components, such as elements, markers
+and legend entries, can have a command trigger when event occurs in them,
+much like canvas items in Tk's canvas widget. Not all event sequences are
+valid. The only binding events that may be specified are those related
+to the mouse and keyboard (such as <B>Enter</B>, <B>Leave</B>, <B>ButtonPress</B>, <B>Motion</B>, and
+<B>KeyPress</B>). <P>
+Only one element or marker can be picked during an event. This
+means, that if the mouse is directly over both an element and a marker,
+only the uppermost component is selected. This isn't true for legend entries.
+ Both a legend entry and an element (or marker) binding commands will
+be invoked if both items are picked. <P>
+It is possible for multiple bindings
+to match a particular event. This could occur, for example, if one binding
+is associated with the element name and another is associated with one
+of the element's tags (see the <B>-bindtags</B> option). When this occurs, all of
+the matching bindings are invoked. A binding associated with the element
+name is invoked first, followed by one binding for each of the element's
+ bindtags. If there are multiple matching bindings for a single tag, then
+only the most specific binding is invoked. A continue command in a binding
+script terminates that script, and a break command terminates that script
+and skips any remaining scripts for the event, just as for the bind command.
+<P>
+The <B>-bindtags</B> option for these components controls addition tag names which
+can be matched. Implicitly elements and markers always have tags matching
+their names. Setting the value of the <B>-bindtags</B> option doesn't change this.
+
+<H2><A NAME="sect24" HREF="#toc24">C Language API</A></H2>
+You can manipulate data elements from the C language. There
+may be situations where it is too expensive to translate the data values
+from ASCII strings. Or you might want to read data in a special file format.
+<P>
+Data can manipulated from the C language using BLT vectors. You specify
+the X-Y data coordinates of an element as vectors and manipulate the vector
+from C. The barchart will be redrawn automatically after the vectors are
+updated. <P>
+From Tcl, create the vectors and configure the element to use them.
+<BR>
+<CODE>vector X Y<BR>
+.g element configure line1 -xdata X -ydata Y<BR>
+</CODE><P>To set data points from C, you pass the values as arrays of doubles using
+the <B>Blt_ResetVector</B> call. The vector is reset with the new data and at
+the next idle point (when Tk re-enters its event loop), the graph will be
+redrawn automatically. <BR>
+<CODE>#include &lt;tcl.h&gt;<BR>
+#include &lt;blt.h&gt;<BR>
+<P>
+register int i;<BR>
+Blt_Vector *xVec, *yVec;<BR>
+double x[50], y[50];<BR>
+<P>
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */<BR>
+if ((Blt_GetVector(interp, "X", 50, &amp;xVec) != TCL_OK) ||<BR>
+ (Blt_GetVector(interp, "Y", 50, &amp;yVec) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+<P>
+for (i = 0; i &lt; 50; i++) {<BR>
+ x[i] = i * 0.02;<BR>
+ y[i] = sin(x[i]);<BR>
+}<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<BR>
+<P>
+/* Put the data into BLT vectors */<BR>
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||<BR>
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+</CODE><P>See the <B>vector</B> manual page for more details.
+<H2><A NAME="sect25" HREF="#toc25">Speed Tips</A></H2>
+There may be cases
+where the bar chart needs to be drawn and updated as quickly as possible.
+ If drawing speed becomes a big problem, here are a few tips to speed up
+displays.
+<UL>
+&#183;<LI>Try to minimize the number of data points. The more data points
+ looked at, the more work the bar chart must do. </LI>&#183;<LI>If your data is generated
+as floating point values, the time required to convert the data values
+to and from ASCII strings can be significant, especially when there any
+many data points. You can avoid the redundant string-to-decimal conversions
+using the C API to BLT vectors. </LI>&#183;<LI>Don't stipple or dash the element. Solid
+bars are much faster. </LI>&#183;<LI>If you update data elements frequently, try turning
+off the widget's <B>-bufferelements</B> option. When the bar chart is first displayed,
+it draws data elements into an internal pixmap. The pixmap acts as a cache,
+so that when the bar chart needs to be redrawn again, and the data elements
+or coordinate axes haven't changed, the pixmap is simply copied to the screen.
+ This is especially useful when you are using markers to highlight points
+and regions on the bar chart. But if the bar chart is updated frequently,
+changing either the element data or coordinate axes, the buffering becomes
+redundant. </LI>
+</UL>
+
+<H2><A NAME="sect26" HREF="#toc26">Limitations</A></H2>
+Auto-scale routines do not use requested min/max limits
+as boundaries when the axis is logarithmically scaled. <P>
+The PostScript
+output generated for polygons with more than 1500 points may exceed the
+limits of some printers (See PostScript Language Reference Manual, page
+568). The work-around is to break the polygon into separate pieces.
+<H2><A NAME="sect27" HREF="#toc27">Keywords</A></H2>
+bar
+chart, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Syntax</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Example</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Syntax</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Barchart Operations</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Barchart Components</A></LI>
+<UL>
+<LI><A NAME="toc9" HREF="#sect9">Axis Components</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Crosshairs Component</A></LI>
+</UL>
+<LI><A NAME="toc11" HREF="#sect11">Elements</A></LI>
+<UL>
+<LI><A NAME="toc12" HREF="#sect12">Grid Component</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Legend Component</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Pen Components</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">PostScript Component</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Marker Components</A></LI>
+<LI><A NAME="toc17" HREF="#sect17">Bitmap Markers</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Image Markers</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Line Markers</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Polygon Markers</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Text Markers</A></LI>
+<LI><A NAME="toc22" HREF="#sect22">Window Markers</A></LI>
+</UL>
+<LI><A NAME="toc23" HREF="#sect23">Graph Component Bindings</A></LI>
+<LI><A NAME="toc24" HREF="#sect24">C Language API</A></LI>
+<LI><A NAME="toc25" HREF="#sect25">Speed Tips</A></LI>
+<LI><A NAME="toc26" HREF="#sect26">Limitations</A></LI>
+<LI><A NAME="toc27" HREF="#sect27">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/beep.html b/blt/html/beep.html
new file mode 100644
index 00000000000..a3544c6daeb
--- /dev/null
+++ b/blt/html/beep.html
@@ -0,0 +1,41 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>beep(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+beep - ring the bell
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>beep</B> ?<I>percent</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The
+<B>beep</B> command rings the keyboard bell. <I>Percent</I> is relative to the base volume
+of the keyboard bell and can range from -100 to 100 inclusive. <P>
+If <I>percent</I>
+is nonnegative then the bell volume is: <BR>
+<CODE>base - [(base * <I>percent</I>) / 100] + <I>percent</I><BR>
+</CODE><P>If <I>percent</I> is negative then the bell volume is: <BR>
+<CODE>base + [(base * <I>percent</I>) / 100]<BR>
+</CODE><P>The default <I>percent</I> is 50.
+<H2><A NAME="sect3" HREF="#toc3">Example</A></H2>
+<BR>
+<CODE>beep<BR>
+
+<H2><A NAME="sect4" HREF="#toc4"></CODE><P>Keywords</A></H2>
+bell, beep <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Example</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/bgexec.html b/blt/html/bgexec.html
new file mode 100644
index 00000000000..efceca8048b
--- /dev/null
+++ b/blt/html/bgexec.html
@@ -0,0 +1,271 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>bgexec(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+bgexec - Run programs in the background while
+handling Tk events.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>blt::bgexec <I>varName</I></B> ?<I>option value</I>?... <I>program</I> ?<I>arg</I>?...
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+<P>
+The <B>bgexec</B> command executes programs in the background, allowing
+Tk to handle events. A global Tcl variable <I>varName</I> is set when the program
+has completed.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Tcl's <B>exec</B> command is very useful for gathering
+information from the operating system. It runs a program and returns the
+output as its result. This works well for Tcl-only applications. But for
+Tk applications, a problem occurs when the program takes time to process.
+ Let's say we want the get the disk usage of a directory. We'll use the Unix
+program <I>du</I> to get the summary. <BR>
+<CODE>set out [exec du -s $dir]<BR>
+puts "Disk usage for $dir is $out"<BR>
+</CODE><P>While <I>du</I> is running, scrollbars won't respond. None of the Tk widgets will
+be redrawn properly. The <B>send</B> command won't work. And the worst part is that
+the application appears hung up or dead. The problem is that while <B>exec</B>
+is waiting for <I>du</I> to finish, Tk is not able to handle X events. <P>
+The <B>bgexec</B>
+command performs the same functions as <B>exec</B>, but also allows Tk to handle
+events. You can execute a long-running program and the Tk widgets will behave
+normally. When the program finishes, its output and the exit status are
+written to Tcl variables. This makes it easy to monitor and save the output
+of a program.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+Here is the disk usage example again, this time using
+<B>bgexec</B>. The syntax to invoke "du" is exactly the same as the previous example,
+when we used <B>exec</B>. <BR>
+<CODE>global myStatus myOutput<BR>
+blt::bgexec myStatus -output myOutput du -s $dir<BR>
+puts "Disk usage for $dir is $myOutput"<BR>
+</CODE><P>Two global variables, <I>myStatus</I> and <I>myOutput</I>, will be set by <B>bgexec</B> when
+<I>du</I> has completed. <I>MyStatus</I> will contain the program's exit status. <I>MyOutput</I>,
+specified by the <B>-output</B> option, will store the output of the program. <P>
+You
+can also terminate the program by setting the variable <I>myStatus</I>. If <I>myStatus</I>
+is set before <I>du</I> has completed, the process is killed. Under Unix, this
+is done sending by a configurable signal (by default it's SIGKILL). Under
+Win32, this is done by calling <B>TerminateProcess</B>. It makes no difference
+what <I>myStatus</I> is set to. <BR>
+<CODE>set myStatus {}<BR>
+</CODE><P>There are several <B>bgexec</B> options to collect different types of information.
+<BR>
+<CODE>global myStatus myOutput myErrs<BR>
+blt::bgexec myStatus -output myOutput -error myErrs du -s $dir<BR>
+</CODE><P>The <B>-error</B> option is similar to <B>-output</B>. It sets a global variable when the
+program completes. The variable will contain any data written to stderr
+by the program. <P>
+The <B>-output</B> and <B>-error</B> variables are set only after the program
+completes. But if the program takes a long time, to run you may want to
+receive its partial output. You can gather data as it becomes available
+using the <B>-onoutput</B> option. It specifies a Tcl command prefix. Whenever
+new data is available, this command is executed, with the data appended
+as an argument to the command. <BR>
+<CODE>proc GetInfo { data } {<BR>
+ puts $data<BR>
+}<BR>
+blt::bgexec myStatus -onoutput GetInfo du -s $dir<BR>
+</CODE><P>When output is available, the procedure <I>GetInfo</I> is called. The <B>-onerror</B> option
+performs a similar function for the stderr data stream. <P>
+Like <B>exec</B>, <B>bgexec</B>
+returns an error if the exit code of the program is not zero. If you think
+you may get a non-zero exit code, you might want to invoke <B>bgexec</B> from within
+a <B>catch</B>. <BR>
+<CODE>catch { blt::bgexec myStatus -output myOutput du -s $dir }<BR>
+</CODE><P>By default, <B>bgexec</B> will wait for the program to finish. But you can detach
+the program making ampersand (&amp;) the last argument on the command line. <BR>
+<CODE>global myStatus myOutput<BR>
+blt::bgexec myStatus -output myOutput du -s $dir &amp;<BR>
+</CODE><P><B>Bgexec</B> will return immediately and its result will be a list of the spawned
+process ids. If at some point you need to wait for the program to finish
+up, you can use <B>tkwait</B>. When the program finishes, the variable <I>myStatus</I>
+will be written to, breaking out the <B>tkwait</B> command. <BR>
+<CODE>global myStatus myOutput<BR>
+blt::bgexec myStatus -output myOutput du -s $dir &amp;<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;...<BR>
+tkwait variable myStatus<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Syntax</A></H2>
+The <B>bgexec</B> command takes the following form: <P>
+<B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;blt::bgexec <I>varName</I></B>
+?<I>option value</I>?... <I>program</I> ?<I>arg</I>?... <P>
+<I>VarName</I> is the name of a global variable which
+is set when <I>program</I> has finished executing. The exit status of will be
+stored in <I>varName</I>. The exit status is a list of a status token, the process-id
+of the program, the exit code, and a status message. You can also prematurely
+terminate the program by setting <I>varName</I>. Under Unix, the program will
+be sent a signal to terminate it (by default the signal is a SIGKILL; see
+the <B>-killsignal</B> option). <P>
+<I>Program</I> is the name of the program to be executed
+and <I>args</I> are any extra arguments for <I>program</I>. The syntax of <I>program</I> and
+<I>args</I> is the same as the <B>exec</B> command. So you can redirect I/O, execute pipelines,
+etc. (see the <B>exec</B> manual for further information) just like <B>exec</B>. If the
+last argument is an ampersand (&amp;), the program will be run detached, and
+<B>bgexec</B> will return immediately. <I>VarName</I> will still be set with the return
+status when <I>program</I> completes.
+<H2><A NAME="sect6" HREF="#toc6">Options</A></H2>
+<I>Option</I> refers to the switch name always
+beginning with a dash (-). <I>Value</I> is the value of the option. Option-value
+pairs are terminated either by the program name, or double dashes (--). The
+following options are available for <B>bgexec</B>:
+<DL>
+
+<DT><B>-decodeerror <I>encodingName</I></B> </DT>
+<DD><BR>
+Specifies the encoding of the stderr channel. This affects only data returned
+to the Tcl interpreter. No translation is done on file redirection. <BR>
+For example if data is to be converted from Unicode for use in Tcl, you
+would use the "unicode" encoding. The default is that no tranlation is
+performed. </DD>
+
+<DT><B>-decodeoutput <I>encodingName</I></B> </DT>
+<DD><BR>
+Specifies the encoding of the stdout channels. This affects only data returned
+to the Tcl interpreter. No translation is done on file redirection. <BR>
+For example if data is to be converted from Unicode for use in Tcl, you
+would use the "unicode" encoding. The default is that no tranlation is
+performed. </DD>
+
+<DT><B>-error <I>varName</I></B> </DT>
+<DD><BR>
+Specifies that a global variable <I>varName</I> is to be set with the contents
+of stderr after the program has completed. </DD>
+
+<DT><B>-keepnewline <I>boolean</I></B> </DT>
+<DD>Specifies
+that a trailing newline should be retained in the output. If <I>boolean</I> is
+true, the trailing newline is truncated from the output of the <B>-onoutput</B>
+and <B>-output</B> variables. The default value is <I>true</I>. </DD>
+
+<DT><B>-killsignal <I>signal</I></B> </DT>
+<DD>Specifies
+the signal to be sent to the program when terminating. This is available
+only under Unix. <I>Signal</I> can either be a number (typically 1-32) or a mnemonic
+(such as SIGINT). If <I>signal</I> is the empty string, then no signal is sent.
+ The default signal is <I>9</I> (SIGKILL). </DD>
+
+<DT><B>-lasterror <I>varName</I></B> </DT>
+<DD>Specifies a variable
+<I>varName</I> that is updated whenever data becomes available from standard error
+of the program. <I>VarName</I> is a global variable. Unlike the <B>-error</B> option, data
+is available as soon as it arrives. </DD>
+
+<DT><B>-lastoutput <I>varName</I></B> </DT>
+<DD>Specifies a variable
+<I>varName</I> that is updated whenever data becomes available from standard output
+of the program. <I>VarName</I> is a global variable. Unlike the <B>-output</B> option, data
+is available as soon as it arrives. </DD>
+
+<DT><B>-linebuffered <I>boolean</I></B> </DT>
+<DD>Specifies that
+updates should be made on a line-by-line basis. Normally when new data is
+available <B>bgexec</B> will set the variable (<B>-lastoutput</B> and <B>-lasterror</B> options)
+or invoke the command (<B>-onoutput</B> and <B>-onerror</B> options) delivering all the
+new data currently available. If <I>boolean</I> is true, only one line at a time
+will be delivered. This can be useful when you want to process the output
+on a line-by-line basis. The default value is <I>false</I>. </DD>
+
+<DT><B>-output <I>varName</I></B> </DT>
+<DD><BR>
+Specifies that a global variable <I>varName</I> is to be set with the output of
+the program, once it has completed. If this option is not set, no output
+will be accumulated. </DD>
+
+<DT><B>-onerror <I>command</I></B> </DT>
+<DD>Specifies the start of a Tcl command
+that will be executed whenever new data is available from standard error.
+The data is appended to the command as an extra argument before it is executed.
+</DD>
+
+<DT><B>-onoutput <I>command</I></B> </DT>
+<DD>Specifies the start of a Tcl command that will be executed
+whenever new data is available from standard output. The data is appended
+to the command as an extra argument before it is executed. </DD>
+
+<DT><B>-update <I>varName</I></B>
+ </DT>
+<DD>Deprecated. This option is replaced by <B>-lasterror</B>. </DD>
+
+<DT><B>--</B> </DT>
+<DD>This marks the end of
+the options. The following argument will be considered the name of a program
+even if it starts with a dash (<B>-</B>). </DD>
+</DL>
+
+<H2><A NAME="sect7" HREF="#toc7">Preemption</A></H2>
+Because <B>bgexec</B> allows Tk to
+handle events while a program is running, it's possible for an application
+to preempt itself with further user-interactions. Let's say your application
+has a button that runs the disk usage example. And while the <I>du</I> program
+is running, the user accidently presses the button again. A second <B>bgexec</B>
+program will preempt the first. What this means is that the first program
+can not finish until the second program has completed. <P>
+Care must be taken
+to prevent an application from preempting itself by blocking further user-interactions
+(such as button clicks). The BLT <B>busy</B> command is very useful for just these
+situations. See the <B>busy</B> manual for details.
+<H2><A NAME="sect8" HREF="#toc8">Differences with Fileevent</A></H2>
+Since
+Tk 4.0, a subset of <B>bgexec</B> can be also achieved using the <B>fileevent</B> command.
+ The steps for running a program in the background are: <P>
+Execute the program
+with the <B>open</B> command (using the "|" syntax) and save the file handle. <BR>
+<CODE>global fileId <BR>
+set fileId [open "|du -s $dir" r]<BR>
+</CODE><P>Next register a Tcl code snippet with <B>fileevent</B> to be run whenever output
+is available on the file handle. The code snippet will read from the file
+handle and save the output in a variable. <BR>
+<CODE>fileevent fileId readable { <BR>
+ if { [gets $fileId line] &lt; 0 } {<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;close $fileId<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;set output $temp<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;unset fileId temp<BR>
+ } else {<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;append temp $line<BR>
+ }<BR>
+}<BR>
+<P>
+</CODE><P>The biggest advantage of <B>bgexec</B> is that, unlike <B>fileevent</B>, it requires
+no additional Tcl code to run a program. It's simpler and less error prone.
+ You don't have to worry about non-blocking I/O. It's handled tranparently
+for you. <P>
+<B>Bgexec</B> runs programs that <B>fileevent</B> can not. <B>Fileevent</B> assumes that
+the when stdout is closed the program has completed. But some programs,
+like the Unix <I>compress</I> program, reopen stdout, fooling <B>fileevent</B> into thinking
+the program has terminated. In the example above, we assume that the program
+will write and flush its output line-by-line. However running another program,
+your application may block in the <B>gets</B> command reading a partial line. <P>
+<B>Bgexec</B>
+lets you get back the exit status of the program. It also allows you to
+collect data from both stdout and stderr simultaneously. Finally, since
+data collection is handled in C code, <B>bgexec</B> is faster. You get back to
+the Tk event loop more quickly, making your application seem more responsive.
+
+<H2><A NAME="sect9" HREF="#toc9">See Also</A></H2>
+busy, exec, tkwait
+<H2><A NAME="sect10" HREF="#toc10">Keywords</A></H2>
+exec, background, busy <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Options</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Preemption</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Differences with Fileevent</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">See Also</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/bitmap.html b/blt/html/bitmap.html
new file mode 100644
index 00000000000..d23c7385c9b
--- /dev/null
+++ b/blt/html/bitmap.html
@@ -0,0 +1,208 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>bitmap(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+bitmap - Define a new bitmap from a Tcl script
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>bitmap define <I>bitmapName data</I></B> ?<I>option value</I>?... <P>
+<B>bitmap compose <I>bitmapName
+text</I></B> ?<I>option value</I>?... <P>
+<B>bitmap exists <I>bitmapName</I></B> <P>
+<B>bitmap source <I>bitmapName</I></B> <P>
+<B>bitmap
+data <I>bitmapName</I></B> <P>
+<B>bitmap height <I>bitmapName</I></B> <P>
+<B>bitmap width <I>bitmapName</I></B>
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The
+<B>bitmap</B> command lets you create new bitmaps directly from your Tcl script.
+ The bitmap can be specified as a list of data or a text string which is
+converted into a bitmap. You can arbitrarily scale or rotate the bitmap
+too.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Bitmaps are commonly used within Tk. In label and button
+widgets, you display bitmaps them instead of text strings and in the canvas
+and text widgets, they're used for stippling. But Tk let's you can create
+new bitmaps only by reading the bitmap data from a file. This makes bitmaps
+cumbersome to manage, especially in packaging the program as a <B>wish</B> script,
+since each bitmap must be its own file. It would be nicer if you could
+create new bitmaps directly from your Tcl script. <P>
+The <B>bitmap</B> command lets
+you do just that. You can specify the bitmap as in various formats (such
+as the X11 bitmap format). You can also compose a bitmap from a text string.
+ The <B>bitmap</B> command also lets you and arbitrarily rotate or scale the bitmap.
+ For example, you could use this to create button widgets with the text
+label rotated 90 degrees.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+&lt;&lt;&lt;&lt;&lt;&lt;&lt; bitmap.mann You can define a new bitmap
+with the <B>define</B> operation. For example, let's say you are using the X11
+bitmap "gray1". Normally to use it, you would specify the location of the
+file. <BR>
+<CODE>label .l -bitmap @/usr/X11R6/include/X11/bitmaps/gray1<BR>
+</CODE><P>But you can simply cut and paste the contents of "gray1" into the <B>bitmap</B>
+command. <BR>
+<CODE>bitmap define gray1 {<BR>
+ #define gray1_width 2<BR>
+ #define gray1_height 2<BR>
+ static char gray1_bits[] = {<BR>
+ 0x01, 0x02};<BR>
+}<BR>
+label .l -bitmap gray1<BR>
+</CODE><P>Tk will recognize "gray1" as a bitmap which can now be used with any widget
+that accepts bitmaps. <BR>
+<CODE></CODE><P>The bitmap data can be specified in a mulitude of forms. The following commands
+are all equivalent. <BR>
+<CODE>bitmap define gray1 {<BR>
+ #define gray1_width 2<BR>
+ #define gray1_height 2<BR>
+ static char gray1_bits[] = {<BR>
+ 0x01, 0x02};<BR>
+}<BR>
+bitmap define gray1 { { 2 2 } { 0x01, 0x02 } }<BR>
+bitmap define gray1 { { 2 2 } { 0x01 0x02 } }<BR>
+bitmap define gray1 { { 2 2 } { 1 2 } }<BR>
+</CODE><P>Either the data is in the standard X11 bitmap form, or it's a list of two
+lists. The first list contains the height and width of the bitmap. The second
+list is the bitmap source data. Each element of that list is an hexadecimal
+number specifying which pixels are <A HREF="foreground.1.html">foreground (1)</A>
+ and which are background
+(0) of the bitmap. Note that the format of the source data is exactly that
+of the XBM format. <P>
+You can scale or rotate the bitmap as you create it,
+by using the <B>-scale</B> or<B>-rotate</B> options. <BR>
+<CODE>bitmap define gray1 {<BR>
+ #define gray1_width 2<BR>
+ #define gray1_height 2<BR>
+ static char gray1_bits[] = {<BR>
+ 0x01, 0x02};<BR>
+} -scale 2.0 -rotate 90.0<BR>
+</CODE><P>In addition, you can compose bitmaps from text strings. This makes it easy
+to create rotated buttons or labels. The text string can have multi-line.
+ <BR>
+<CODE>bitmap compose rot_text "This is rotated\ntext" \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-rotate 90.0 -font fixed<BR>
+</CODE><P>There are also a number of ways to query bitmaps. This isn't limited to
+bitmaps that you create, but any bitmap. <BR>
+<CODE>bitmap exists rot_text<BR>
+bitmap width rot_text<BR>
+bitmap height rot_text<BR>
+bitmap data rot_text<BR>
+bitmap source rot_text<BR>
+</CODE><P>The <B>exists</B> operation indicates if a bitmap by that name is defined. You
+can query the dimensions of the bitmap using the <B>width</B> and <B>height</B> operations.
+The <B>data</B> operation returns the list of the data used to create the bitmap.
+ For example, you could query the data of a bitmap and <B>send</B> it across
+the network to another Tk application. <BR>
+<CODE>set data [bitmap data @/usr/X11R6/include/X11/bitmaps/ghost.xbm]<BR>
+send {wish #2} bitmap define ghost $data<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Operations</A></H2>
+The following operations are available for <B>bitmap</B>:
+<DL>
+
+<DT><B>bitmap compose
+<I>bitmapName text </I></B>?<I>option value</I>?... </DT>
+<DD>Creates a bitmap <I>bitmapName</I> from the text
+string <I>text</I>. A bitmap <I>bitmapName</I> can not already exist. The following options
+are available. <blockquote></DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies a font to use when drawing text
+into the bitmap. If this option isn't specified then <I>fontName</I> defaults to
+ <I>*-Helvetica-Bold-R-Normal-*-140-*</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the angle of rotation
+of the text in the bitmap. <I>Theta</I> is a real number representing the angle
+in degrees. It defaults to <I>0.0</I> degrees. </DD>
+
+<DT><B>-scale <I>value</I></B> </DT>
+<DD>Specifies the scale of
+the bitmap. <I>Value</I> is a real number representing the scale. A scale of 1.0
+indicates no scaling is necessary, while 2.0 would double the size of the
+bitmap. There is no way to specify differents scales for the width and
+height of the bitmap. The default scale is <I>1.0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>bitmap data <I>bitmapName</I></B> </DT>
+<DD>Returns
+a list of both the dimensions of the bitmap <I>bitmapName</I> and its source data.
+</DD>
+
+<DT><B>bitmap define <I>bitmapName data</I></B> ?<I>option value</I>?... </DT>
+<DD>Associates <I>bitmapName</I> with
+in-memory bitmap data so that <I>bitmapName</I> can be used in later calls to <B>Tk_GetBitmap</B>.
+The <I>bitmapName</I> argument is the name of the bitmap; it must not previously
+have been defined in either a call to Tk_DefineBitmap or <B>bitmap</B>. The argument
+<I>data</I> describes the bitmap to be created. It is either the X11 bitmap format
+(a C structure) or a list of two lists: the dimensions and source data.
+ The dimensions are a list of two numbers which are the width and height
+of the bitmap. The source data is a list of hexadecimal values in a format
+similar to the X11 or X10 bitmap format. The values may be optionally separated
+by commas and do not need to be prefixed with "0x". The following options
+are available. <blockquote></DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies how many degrees to rotate the bitmap.
+<I>Theta</I> is a real number representing the angle. The default is <I>0.0</I> degrees.
+</DD>
+
+<DT><B>-scale <I>value</I></B> </DT>
+<DD>Specifies how to scale the bitmap. <I>Value</I> is a real number representing
+the scale. A scale of 1.0 indicates no scaling is necessary, while 2.0 would
+double the size of the bitmap. There is no way to specify differents scales
+for the width and height of the bitmap. The default scale is <I>1.0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>bitmap exists
+<I>bitmapName</I></B> </DT>
+<DD>Returns <I>1</I> if a bitmap <I>bitmapName</I> exists, otherwise <I>0</I>. </DD>
+
+<DT><B>bitmap
+height <I>bitmapName</I></B> </DT>
+<DD>Returns the height in pixels of the bitmap <I>bitmapName</I>.
+</DD>
+
+<DT><B>bitmap source <I>bitmapName</I></B> </DT>
+<DD>Returns the source data of the bitmap <I>bitmapName</I>.
+The source data is a list of the hexadecimal values. </DD>
+
+<DT><B>bitmap width <I>bitmapName</I></B>
+ </DT>
+<DD>Returns the width in pixels of the bitmap <I>bitmapName</I>. </DD>
+</DL>
+
+<H2><A NAME="sect6" HREF="#toc6">Limitations</A></H2>
+Tk currently
+offers no way of destroying bitmaps. Once a bitmap is created, it exists
+until the application terminates.
+<H2><A NAME="sect7" HREF="#toc7">Keywords</A></H2>
+bitmap <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Operations</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Limitations</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/bltdebug.html b/blt/html/bltdebug.html
new file mode 100644
index 00000000000..6ff395317ce
--- /dev/null
+++ b/blt/html/bltdebug.html
@@ -0,0 +1,34 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>bltdebug(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+bltdebug - print Tcl commands before execution
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>bltdebug</B>
+?<I>level</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>bltdebug</B> command is a simple tracing facility for
+Tcl commands. Each command line is printed before it is executed on standard
+error. The output consists of the command line both before and after substitutions
+have occurred. <I>Level</I> indicates at what level to stop tracing commands.
+If <I>level</I> is <I>0</I>, no tracing is performed. This is the default. If no <I>level</I>
+argument is given, the current level is printed.
+<H2><A NAME="sect3" HREF="#toc3">Keywords</A></H2>
+debug <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/busy.html b/blt/html/busy.html
new file mode 100644
index 00000000000..c588faf252c
--- /dev/null
+++ b/blt/html/busy.html
@@ -0,0 +1,218 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>busy(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+busy - Make Tk widgets busy, temporarily blocking
+user interactions.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>busy hold <I>window</I></B> ?<I>option value</I>?... <P>
+<B>busy release
+<I>window</I></B> ?<I>window</I>?... <P>
+<B>busy configure <I>window</I></B> ?<I>option value</I>?... <P>
+<B>busy forget <I>window</I></B>
+?<I>window</I>?... <P>
+<B>busy isbusy </B>?<I>pattern</I>? <P>
+<B>busy names </B>?<I>pattern</I>? <P>
+<B>busy status <I>window</I></B>
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+<P>
+The <B>busy</B> command provides a simple means to block keyboard,
+button, and pointer events from Tk widgets, while overriding the widget's
+cursor with a configurable busy cursor.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+<P>
+There are many times
+in applications where you want to temporarily restrict what actions the
+user can take. For example, an application could have a "run" button that
+when pressed causes some processing to occur. But while the application
+is busy processing, you probably don't want the the user to be able to click
+the "run" button again. You may also want restrict the user from other
+tasks such as clicking a "print" button. <P>
+The <B>busy</B> command lets you make
+Tk widgets busy. This means that user interactions such as button clicks,
+moving the mouse, typing at the keyboard, etc. are ignored by the widget.
+ You can set a special cursor (like a watch) that overrides the widget's
+normal cursor, providing feedback that the application (widget) is temporarily
+busy. <P>
+When a widget is made busy, the widget and all of its descendents
+will ignore events. It's easy to make an entire panel of widgets busy. You
+can simply make the toplevel widget (such as ".") busy. This is easier and
+far much more efficient than recursively traversing the widget hierarchy,
+disabling each widget and re-configuring its cursor. <P>
+Often, the busy command
+can be used instead of Tk's <B>grab</B> command. Unlike <B>grab</B> which restricts all
+user interactions to one widget, with the busy command you can have more
+than one widget active (for example, a "cancel" dialog and a "help" button).
+
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+You can make several widgets busy by simply making its ancestor
+widget busy using the <B>hold</B> operation. <BR>
+<CODE>frame .top<BR>
+button .top.button; canvas .top.canvas<BR>
+pack .top.button .top.canvas<BR>
+pack .top<BR>
+ . . .<BR>
+busy hold .top<BR>
+update<BR>
+</CODE><P>All the widgets within <I>.top</I> (including <I>.top</I>) are now busy. Using <B>update</B>
+insures that <B>busy</B> command will take effect before any other user events
+can occur. <P>
+When the application is no longer busy processing, you can allow
+user interactions again by the <B>release</B> operation. <BR>
+<PRE><I><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;busy release .top </I>
+</PRE>The busy window has a configurable cursor. You can change the busy cursor
+using the <B>configure</B> operation. <BR>
+<PRE><I><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;busy configure .top -cursor "watch"</I>
+</PRE>Finally, when you no longer need to the busy window, invoke the <B>forget</B>
+operation to free any resources it allocated. <BR>
+<PRE><I><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;busy forget .top </I>
+</PRE>Destroying the widget will also clean up any resources allocated by the
+busy command. <P>
+
+<H2><A NAME="sect5" HREF="#toc5">Operations</A></H2>
+The following operations are available for the <B>busy</B>
+command:
+<DL>
+
+<DT><B>busy hold <I>window</I></B> ?<I>option value</I>?... </DT>
+<DD>Makes the widget <I>window</I> (and its
+descendants in the Tk window hierarchy) busy. <I>Window</I> must be a valid path
+name of a Tk widget. The busy window is mapped the next time idle tasks
+are processed, and the widget and its descendants will be blocked from
+user interactions. All events in the widget window and its descendants are
+ignored. Normally <B>update</B> should be called immediately afterward to insure
+that the <B>hold</B> operation is in effect <I>before</I> the application starts its
+processing. The following configuration options are valid: <blockquote></DD>
+
+<DT><B>-cursor <I>cursorName</I></B>
+</DT>
+<DD>Specifies the cursor to be displayed when the widget is made busy. <I>CursorName</I>
+can be in any form accepted by <B>Tk_GetCursor</B>. The default cursor is <I>watch</I>.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>busy configure <I>window</I></B> ?<I>option value</I>?... </DT>
+<DD>Queries or modifies the <B>busy</B> command
+configuration options for <I>window</I>. <I>Window</I> must be the path name of a widget
+previously made busy by the <B>hold</B> operation. If no options are specified,
+a list describing all of the available options for <I>window</I> (see <B>Tk_ConfigureInfo</B>
+for information on the format of this list) is returned. If <I>option</I> is specified
+with no <I>value</I>, then the command returns a list describing the one named
+option (this list will be identical to the corresponding sublist of the
+value returned if no <I>option</I> is specified). If one or more <I>option-value</I> pairs
+are specified, then the command modifies the given widget option(s) to
+have the given value(s); in this case the command returns the empty string.
+ <I>Option</I> may have any of the values accepted by the <B>hold</B> operation. <P>
+Please
+note that the option database is referenced through <I>window</I>. For example,
+if the widget <I>.frame</I> is to be made busy, the busy cursor can be specified
+for it by either <B>option</B> command: <BR>
+<PRE><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<I>option add *frame.busyCursor gumby</I>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<I>option add *Frame.BusyCursor gumby</I>
+</PRE></DD>
+
+<DT><B>busy forget <I>window</I></B> ?<I>window</I>?... </DT>
+<DD>Releases resources allocated by the busy command
+for <I>window</I>, including the busy window. User events will again be received
+again by <I>window</I>. Resources are also released when <I>window</I> is destroyed.
+<I>Window</I> must be the name of a widget specified in the <B>hold</B> operation, otherwise
+an error is reported. </DD>
+
+<DT><B>busy isbusy </B>?<I>pattern</I>? </DT>
+<DD>Returns the pathnames of all
+widgets that are currently busy. If a <I>pattern</I> is given, the path names of
+busy widgets matching <I>pattern</I> are returned. </DD>
+
+<DT><B>busy names </B>?<I>pattern</I>? </DT>
+<DD>Returns
+the pathnames of all widgets that have previously been made busy (i.e. a
+busy window is allocated and associated with the widget). It makes no difference
+if the window is currently busy or not. If a <I>pattern</I> is given, the path
+names of busy widgets matching <I>pattern</I> are returned. </DD>
+
+<DT><B>busy release <I>window</I></B>
+?<I>window</I>?... </DT>
+<DD>Restores user interactions to the widget <I>window</I> again. This differs
+from the <B>forget</B> operation in that the busy window is not destroyed, but
+simply unmapped. <I>Window</I> must be the name of a widget specified in a <B>hold</B>
+operation, otherwise an error is reported. </DD>
+
+<DT><B>busy status <I>window</I></B> </DT>
+<DD>Returns the
+status of a widget <I>window</I> previously made busy. An error is reported if
+<I>window</I> was never made busy, or the <B>forget</B> operation was invoked (i.e. does
+not currently have a busy window associated with it). If <I>window</I> is presently
+can not receive user interactions, <I>1</I> is returned, otherwise <I>0</I>. <P>
+</DD>
+</DL>
+
+<H2><A NAME="sect6" HREF="#toc6">Bindings</A></H2>
+The
+event blocking feature is implemented by creating and mapping a transparent
+window that completely covers the widget. When the busy window is mapped,
+it invisibly shields the widget and its hierarchy from all events that
+may be sent. Like Tk widgets, busy windows have widget names in the Tk
+window hierarchy. This means that you can use the <B>bind</B> command, to handle
+events in the busy window. <BR>
+<CODE>busy hold .frame.canvas<BR>
+bind .frame.canvas_Busy &lt;Enter&gt; { ... } <BR>
+<P>
+</CODE><P>Normally the busy window is a sibling of the widget. The name of the busy
+window is "<I>widget<I>_Busy</I></I>" where <I>widget</I> is the name of the widget to be made
+busy. In the previous example, the pathname of the busy window is "<I>.frame.canvas_Busy</I>"
+The exception is when the widget is a toplevel widget (such as ".") where
+the busy window can't be made a sibling. The busy window is then a child
+of the widget named "<I>widget<I>._Busy</I></I>" where <I>widget</I> is the name of the toplevel
+widget. In the following example, the pathname of the busy window is "<I>._Busy</I>"
+<BR>
+<CODE>busy hold .<BR>
+bind ._Busy &lt;Enter&gt; { ... } <BR>
+
+<H2><A NAME="sect7" HREF="#toc7"></CODE><P>Enter/Leave Events</A></H2>
+Mapping and unmapping busy windows generates Enter/Leave
+events for all widgets they cover. Please note this if you are tracking
+Enter/Leave events in widgets.
+<H2><A NAME="sect8" HREF="#toc8">Keyboard Events</A></H2>
+When a widget is made busy,
+the widget is prevented from gaining the keyboard focus by the busy window.
+But if the widget already had focus, it still may received keyboard events.
+ To prevent this, you must move focus to another window. <BR>
+<CODE>busy hold .frame<BR>
+label .dummy<BR>
+focus .dummy<BR>
+update<BR>
+</CODE><P>The above example moves the focus from .frame immediately after invoking
+the <B>hold</B> so that no keyboard events will be sent to <I>.frame</I> or any of its
+descendants.
+<H2><A NAME="sect9" HREF="#toc9">Keywords</A></H2>
+busy, keyboard events, pointer events, window, cursor
+<P>
+ <P>
+ <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Operations</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Bindings</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Enter/Leave Events</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Keyboard Events</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/container.html b/blt/html/container.html
new file mode 100644
index 00000000000..40cb470cbb6
--- /dev/null
+++ b/blt/html/container.html
@@ -0,0 +1,272 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>container(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+container - Widget to contain a foreign window.
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>container</B> <I>pathName </I>?<I>options</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>container</B> widget lets
+you embed an X11 window from a foreign application into your Tk application.
+ The foreign window is reparented inside of the widget. You can then place
+and arrange the container just as you would any Tk widget.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Notebooks
+are a popular graphical paradigm. They allow you to organize many windows
+in a single widget. For example, you might have an application the displays
+several X-Y graphs at the same time. Typically, you can't pack the graphs
+into the same <B>frame</B> because they are too large. The other alternative is
+to pack the graphs into several <B>toplevel</B> widgets, allowing them to overlap
+on the screen. The problem is that all the different toplevel windows clutter
+the screen and are difficult to manage. <P>
+The <B>container</B> widget lets organize
+your application by displaying each graph as a page in a folder of a notebook.
+ Only one page is visible at a time. When you click on a tab, the folder
+(graph) corresponding to the tab is displayed in the <B>container</B> widget.
+The container also lets you temporarily tear pages out of the notebook
+into a separate toplevel widget, and put them back in the container later.
+ For example, you could compare two graphs side-by-side by tearing them out,
+and then replace them when you are finished. <P>
+A container may contain an
+unlimited number of folders. If there are too many tabs to view, you can
+arrange them as multiple tiers or scroll the tabs. The container uses the
+conventional Tk scrollbar syntax, so you can attach a scrollbar too.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+You
+create a container widget with the <B>container</B> command. <BR>
+<CODE># Create a new container<BR>
+container .c<BR>
+</CODE><P>A new Tcl command <I>.c</I> is also created. This command can be used to query
+and modify the container. For example, to change the default borderwidth,
+you use the new command and the container's <B>configure</B> operation. <BR>
+<CODE># Change the default font.<BR>
+.c configure -borderwidth 2<BR>
+</CODE><P>You can then add folders using the <B>insert</B> operation. <BR>
+<CODE># Create a new folder "f1"<BR>
+.c coinsert 0 "f1"<BR>
+</CODE><P>This inserts the new tab named "f1" into the container. The index <I>0</I> indicates
+location to insert the new tab. You can also use the index <I>end</I> to append
+a tab to the end of the container. By default, the text of the tab is the
+name of the tab. You can change this by configuring the <B>-text</B> option. <BR>
+<CODE># Change the label of "f1"<BR>
+.ts tab configure "f1" -label "Tab #1" <BR>
+</CODE><P>The <B>insert</B> operation lets you add one or more folders at a time. <BR>
+<CODE>.ts insert end "f2" -label "Tab #2" "f3" "f4" <BR>
+</CODE><P>The tab on each folder contains a label. A label may display both an image
+and a text string. You can reconfigure the tab's attributes (foreground/background
+colors, font, rotation, etc) using the <B>tab configure</B> operation. <BR>
+<CODE># Add an image to the label of "f1"<BR>
+set image [image create photo -file stopsign.gif]<BR>
+.ts tab configure "f1" -image $image<BR>
+.ts tab configure "f2" -rotate 90<BR>
+</CODE><P>Each folder may contain an embedded widget to represent its contents. The
+widget to be embedded must be a child of the container widget. Using the
+<B>-window</B> option, you specify the name of widget to be embedded. But don't
+pack the widget, the container takes care of placing and arranging the
+widget for you. <BR>
+<CODE>graph .ts.graph<BR>
+.ts tab configure "f1" -window ".ts.graph" \<BR>
+ -fill both -padx 0.25i -pady 0.25i<BR>
+</CODE><P>The size of the folder is determined the sizes of the Tk widgets embedded
+inside each folder. The folder will be as wide as the widest widget in
+any folder. The tallest determines the height. You can use the tab's <B>-pagewidth</B>
+and <B>-pageheight</B> options override this. <P>
+Other options control how the widget
+appears in the folder. The <B>-fill</B> option says that you wish to have the widget
+stretch to fill the available space in the folder. <BR>
+<CODE>.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i<BR>
+<P>
+</CODE><P>Now when you click the left mouse button on "f1", the graph will be displayed
+in the folder. It will be automatically hidden when another folder is selected.
+ If you click on the right mouse button, the embedded widget will be moved
+into a toplevel widget of its own. Clicking again on the right mouse button
+puts it back into the folder. <P>
+If you want to share a page between two different
+folders, the <B>-command</B> option lets you specify a Tcl command to be invoked
+whenever the folder is selected. You can reset the <B>-window</B> option for the
+tab whenever it's clicked. <BR>
+<CODE>.ts tab configure "f2" -command { <BR>
+ .ts tab configure "f2" -window ".ts.graph"<BR>
+}<BR>
+.ts tab configure "f1" -command { <BR>
+ .ts tab configure "f1" -window ".ts.graph"<BR>
+}<BR>
+</CODE><P>If you have many folders, you may wish to stack tabs in multiple tiers.
+ The container's <B>-tiers</B> option requests a maximum number of tiers. The default
+is one tier. <BR>
+<CODE>.ts configure -tiers 2<BR>
+</CODE><P>If the tabs can fit in less tiers, the widget will use that many. Whenever
+there are more tabs than can be displayed in the maximum number of tiers,
+the container will automatically let you scroll the tabs. You can even
+attach a scrollbar to the container. <BR>
+<CODE>.ts configure -scrollcommand { .sbar set } -scrollincrement 20<BR>
+.sbar configure -orient horizontal -command { .ts view }<BR>
+</CODE><P>By default tabs are along the top of the container from left to right.
+ But tabs can be placed on any side of the container using the <B>-side</B> option.
+<BR>
+<CODE># Arrange tabs along the right side of the container. <BR>
+.ts configure -side right -rotate 270<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Syntax</A></H2>
+The <B>container</B> command creates a new window using the <I>pathName</I> argument
+and makes it into a container widget. <BR>
+<CODE><B>container <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>Additional options may be specified on the command line or in the option
+database to configure aspects of the container such as its colors, font,
+text, and relief. The <B>container</B> command returns its <I>pathName</I> argument.
+At the time this command is invoked, there must not exist a window named
+<I>pathName</I>, but <I>pathName</I>'s parent must exist. <P>
+When first created, a new container
+contains no tabs. Tabs are added or deleted using widget operations described
+below. It is not necessary for all the tabs to be displayed in the container
+window at once; commands described below may be used to change the view
+in the window. Containers allow scrolling of tabs using the <B>-scrollcommand</B>
+option. They also support scanning (see the <B>scan</B> operation). Tabs may be
+arranged along any side of the container window using the <B>-side</B> option. <P>
+The
+size of the container window is determined the number of tiers of tabs
+and the sizes of the Tk widgets embedded inside each folder. The widest
+widget determines the width of the folder. The tallest determines the height.
+ If no folders contain an embedded widget, the size is detemined solely
+by the size of the tabs. <P>
+You can override either dimension with the container's
+<B>-width</B> and <B>-height</B> options.
+<H2><A NAME="sect6" HREF="#toc6">Container Operations</A></H2>
+All <B>container</B> operations are
+invoked by specifying the widget's pathname, the operation, and any arguments
+that pertain to that operation. The general form is: <P>
+<BR>
+<CODE><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<I>pathName operation </I>?<I>arg arg ...</I>?<BR>
+<P>
+</CODE><P><I>Operation</I> and the <I>arg</I>s determine the exact behavior of the command. The
+following operations are available for container widgets:
+<DL>
+
+<DT><I>pathName <B>cget</B></I>
+<I>option</I> </DT>
+<DD>Returns the current value of the configuration option given by <I>option</I>.
+<I>Option</I> may have any of the values accepted by the <B>configure</B> operation described
+below. </DD>
+
+<DT><I>pathName <B>configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the configuration options of the widget. If no <I>option</I> is specified, returns
+a list describing all the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B>
+for information on the format of this list). If <I>option</I> is specified with
+no <I>value</I>, then the command returns a list describing the one named option
+(this list will be identical to the corresponding sublist of the value
+returned if no <I>option</I> is specified). If one or more <I>option-value</I> pairs are
+specified, then the command modifies the given widget option(s) to have
+the given value(s); in this case the command returns an empty string. <I>Option</I>
+and <I>value</I> are described below: <blockquote></DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the border color of
+the container. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the outside edge of the widget. The <B>-relief</B> option determines how the border
+is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-command <I>pattern</I></B> </DT>
+<DD>Specifies to search for
+a window whose <I>WM_COMMAND</I> property matches the given pattern. If no windows,
+or more than one window, matches the pattern, an error is generated. If
+<I>pattern</I> is the empty string, then no command search is performed. The default
+is <I>""</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is
+<I>""</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget. If <I>pixels</I> is
+0, then the height is height the embedded window plus the specified borderwidth.
+The default is <I>0</I>. </DD>
+
+<DT><B>-highlightbackground <I>color</I></B> </DT>
+<DD>Sets the color to display in
+the traversal highlight region when the container does not have the input
+focus. </DD>
+
+<DT><B>-highlightcolor <I>color</I></B> </DT>
+<DD>Sets the color to use for the traversal highlight
+rectangle that is drawn around the widget when it has the input focus.
+The default is <I>black</I>. </DD>
+
+<DT><B>-highlightthickness <I>pixels</I></B> </DT>
+<DD>Sets the width of the highlight
+rectangle to draw around the outside of the widget when it has the input
+focus. <I>Pixels</I> is a non-negative value and may have any of the forms acceptable
+to <B>Tk_GetPixels</B>. If the value is zero, no focus highlight is drawn around
+the widget. The default is <I>2</I>. </DD>
+
+<DT><B>-name <I>pattern</I></B> </DT>
+<DD>Specifies to search for a window
+whose <I>WM_NAME</I> property matches the given pattern. If no windows, or more
+than one window, matches the pattern, an error is generated. If <I>pattern</I>
+is the empty string, then no name search is performed. The default is <I>""</I>.
+</DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect for the container widget. <I>Relief</I>
+specifies how the container should appear relative to widget that it is
+packed into; for example, <I>raised</I> means the container should appear to protrude.
+ The default is <I>sunken</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides information used when
+moving the focus from window to window via keyboard traversal (e.g., Tab
+and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window should be skipped
+entirely during keyboard traversal. <I>1</I> means that the this window should
+always receive the input focus. An empty value means that the traversal
+scripts decide whether to focus on the window. The default is <I>1</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B>
+ </DT>
+<DD>Specifies the requested width of the widget. If <I>pixels</I> is 0, then the
+width is the width the embedded window and the specified borderwidth. The
+default is <I>0</I>. </DD>
+
+<DT><B>-window <I>id</I></B> </DT>
+<DD>Specifies the foreign embedded using its X window
+id. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>find <B>-command</B></B></I>|<B>-name</B> <I>pattern</I> </DT>
+<DD>Searches for all windows that match
+the given pattern. If the <B>-command</B> switch is given, all windows whose WWM_COMMAND
+property match <I>pattern</I> are returned in a list. If the <B>-name</B> switch is given,
+all windows whose WWM_NAME property match <I>pattern</I> are returned in a list.
+ The list returned will contains pairs of the window id and the matching
+property. </DD>
+</DL>
+
+<H2><A NAME="sect7" HREF="#toc7">Keywords</A></H2>
+container, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Container Operations</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/cutbuffer.html b/blt/html/cutbuffer.html
new file mode 100644
index 00000000000..9661d8061b8
--- /dev/null
+++ b/blt/html/cutbuffer.html
@@ -0,0 +1,57 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>cutbuffer(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+cutbuffer - Manipulate X cut buffer properties
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>cutbuffer<I>
+get ?number?</I></B> <BR>
+<B>cutbuffer<I> rotate ?count?</I></B> <BR>
+<B>cutbuffer<I> set value ?number?</I></B>
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+<P>
+The <B>cutbuffer</B> command allows you
+to read or modify the eight X cut buffer properties. You can also rotate
+the buffers properties.
+<H2><A NAME="sect3" HREF="#toc3">Operations</A></H2>
+The following operations are available
+for the <B>cutbuffer</B> command:
+<DL>
+
+<DT><B>cutbuffer get <I>?number?</I></B> </DT>
+<DD>Returns the value of
+a cutbuffer <I>number</I>. <I>Number</I> must be a number between 0 and 7. The default
+is 0. The cutbuffer is returned exactly, except that NUL bytes are converted
+to '@' characters. If a cut buffer <I>number</I> does not exist, then <I>""</I> is returned.
+</DD>
+
+<DT><B>cutbuffer rotate <I>?count?</I></B> </DT>
+<DD>Rotates the cut buffers by <I>count</I>. <I>Count</I> must be
+a number between -7 and 7. The default is 1. </DD>
+
+<DT><B>cutbuffer set <I>value</I></B> ?<I>number</I>?
+ </DT>
+<DD>Sets the cutbuffer <I>number</I> to <I>value</I>. <I>Number</I> must be a number between 0
+and 7. The default is 0. </DD>
+</DL>
+
+<H2><A NAME="sect4" HREF="#toc4">Keywords</A></H2>
+cut buffer, property <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Operations</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/dragdrop.html b/blt/html/dragdrop.html
new file mode 100644
index 00000000000..9773d9b685d
--- /dev/null
+++ b/blt/html/dragdrop.html
@@ -0,0 +1,479 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>drag&drop(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+drag&amp;drop - facilities for handling drag&amp;drop data
+transfers
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>drag&amp;drop source </B><BR>
+<B>drag&amp;drop source <I>window </I></B>?<I>options</I>? <BR>
+<B>drag&amp;drop source <I>window <B>handler </B></I></B>?<I>dataType</I>? ?<I>command arg arg...</I>? <P>
+<B>drag&amp;drop target
+</B><BR>
+<B>drag&amp;drop target <I>window <B>handler </B></I></B>?<I>dataType command arg arg...</I>? <P>
+<B>drag&amp;drop target
+<I>window <B>handle <I>dataType</I></B></I></B> ?<I>value</I>? <P>
+<B>drag&amp;drop token <I>window </I></B><P>
+<B>drag&amp;drop drag <I>window
+x y </I></B><BR>
+<B>drag&amp;drop drop <I>window x y </I></B><BR>
+<B>drag&amp;drop active </B><BR>
+<B>drag&amp;drop errors </B>?<I>proc</I>? <BR>
+<B>drag&amp;drop location </B>?<I>x y</I>? <P>
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+<P>
+The <B>drag&amp;drop</B> command provides access
+to a set of facilities for managing drag-and-drop data transfers. Any of
+the usual Tk widgets can be registered to participate in the drag-and-drop
+process. Widgets registered as a drag&amp;drop <I>source</I> can export data to other
+widgets registered as a drag&amp;drop <I>target</I>. Note that a particular widget
+can be registered as a source, as a target, or as both. <P>
+The drag-and-drop
+process begins when the user clicks and holds a mouse button in a source
+window; a token window appears with an icon or message to represent the
+data being transferred. As the user moves the mouse pointer, the token
+window follows along, acting as a movable packet of data. Whenever the mouse
+pointer falls on a valid target window, the border of the token window
+is changed to a raised (active) state. When the mouse button is released
+over the target window, a Tcl routine is invoked to send the data to the
+desired application, and the target window is asked to "handle" the data.
+ If this communication process fails, a rejection symbol (a circle with
+a line through it) is displayed on the token window to indicate failure.
+<P>
+The details of the communication process are fully configurable by the
+application developer. In the simplest case, the value that is sent to
+the target window is a simple string. The target window is simply asked
+to "handle" that string value. In general, the source window can have a
+special "handler" procedure to transfer a particular data type by issuing
+a series of "send" commands. After this, the target window is again asked
+to "handle" the result. <P>
+Both sources and targets can have a list of "handlers"
+for different data types. As a token window is dragged from its source
+to various targets, each target is checked to see if it recognizes a handler
+offered by the source. If it does, it is treated as a valid target. Otherwise,
+it is ignored. This scheme allows the same source to interact with many
+different kinds of targets. For example, a source for RGB color samples
+might have "color" and "string" handlers. This would allow it to communicate
+with "color" targets (sending RGB data) as well as entry widgets (sending
+strings of the form "#rrggbb"). <P>
+This introduction was presented as a brief
+overview of the communication process; further details are presented below:
+
+<DL>
+
+<DT><B>drag&amp;drop source</B> </DT>
+<DD>Returns a list of path names for widgets registered as
+drag&amp;drop sources. Returns an empty string if no widgets have been registered.
+</DD>
+
+<DT><B>drag&amp;drop source <I>window </I></B>?<I>options</I>? </DT>
+<DD>Registers a new drag&amp;drop source window
+with the given options, or modifies the options for an existing window:
+<blockquote></DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>buttonBinding</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>ButtonBinding</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-button</B> <I>n</I>
+</PRE>
+<DL>
+
+<DT>Specifies the mouse button (integer 1-5) that will invoke the drag&amp;drop </DT>
+<DD>operation
+on the source window. This causes the following bindings to be added to
+the widget: <P>
+<BR>
+<PRE><I>bind <I>win</I></I> &lt;ButtonPress-<I>n</I>&gt; {drag&amp;drop drag %W %X %Y}
+<I>bind <I>win</I></I> &lt;B<I>n</I>-Motion&gt; {drag&amp;drop drag %W %X %Y}
+<I>bind <I>win</I></I> &lt;ButtonRelease-<I>n</I>&gt; {drag&amp;drop drop %W %X %Y}
+</PRE><P>
+The default value is button 3. If the value "0" is specified, then no bindings
+are added; this enables the user to establish bindings manually. <P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>packageCommand</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Command</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-packagecmd <I>command</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies a Tcl command used to establish the appearance of the token </DT>
+<DD>window
+at the start of each drag&amp;drop operation. This command is automatically
+invoked by the <B>drag&amp;drop drag</B> command whenever the token window is about
+to be mapped for a drag operation. It should update the appearance of the
+token window to represent the data that is being moved. </DD>
+</DL>
+<P>
+The following substitutions
+are made in the <I>command</I> string before it is executed: <blockquote>
+<DL>
+
+<DT><B>%t</B> </DT>
+<DD>Replaced with
+the window path name for the token which represents the data being dragged.
+</DD>
+
+<DT><B>%W</B> </DT>
+<DD>Replaced with the window path name for the drag&amp;drop source. </DD>
+</DL>
+</blockquote>
+<P>
+The return
+value from the package command represents the data being transferred. If
+the package command returns an empty string, the drag operation is quietly
+aborted. This can be used to disallow drag&amp;drop operations from certain
+parts of a widget, if the drag position is inappropriate. <P>
+For example, the
+following package routine will select an item from a listbox and configure
+the token window to display the selected string. It uses the <B>drag&amp;drop location</B>
+command to determine the entry in the listbox that the user has selected
+and it returns this as the data value: <P>
+<BR>
+<PRE><I>proc package_list_item {lbox token} {</I>
+ set xy [drag&amp;drop location]
+ set y [expr [lindex $xy 1]-[winfo rooty $lbox]]
+ set str [$lbox get [$lbox nearest $y]]
+ $token.value configure -text $str
+ return $str
+}
+</PRE><P>
+The return value is available later when the source and target communicate.
+ If the source has a command associated with its data handler, then this
+value is substituted in place of "%v" in the source handler. Otherwise,
+it is substituted in place of "%v" in the target handler. <P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>rejectBackground</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Background</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-rejectbg <I>color</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies the color used to draw the background of the rejection symbol
+</DT>
+<DD>on the token window. The rejection symbol (a circle with a line through
+it--the international "no") appears whenever communication fails. </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>rejectForeground</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Foreground</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-rejectfg <I>color</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies the color used to draw the foreground of the rejection symbol
+</DT>
+<DD>on the token window. </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>rejectStipple</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Stipple</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-rejectstipple <I>pattern</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies a stipple pattern used to draw the foreground of the rejection
+</DT>
+<DD>symbol on the token window. Any of the forms acceptable to Tk_GetBitmap
+can be used. </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>selfTarget</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>SelfTarget</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-selftarget <I>boolean</I></B>
+</PRE>
+<DL>
+
+<DT>If the <I>boolean</I> value is true, and if a source widget is also </DT>
+<DD>registered
+as a compatible target, then the source will be able to transmit to itself
+during drag&amp;drop operations. This is primarily useful for complex sources
+such as a canvas widget, where items may be moved from place to place within
+the same widget. By default, this option is disabled. </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>send</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Send</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-send <I>list</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies a <I>list</I> of <I>dataTypes</I> enabled for communication. Only </DT>
+<DD><I>dataTypes</I>
+defined by commands of the form "<B>drag&amp;drop source <I>window <B>handler </B></I></B>?<I>dataType</I>
+?<I>command arg arg...</I>?" are allowed. This list also determines the priority
+of the various <I>dataTypes</I>. When a token window is over a potential drag&amp;drop
+target, this list is searched from start to finish for a <I>dataType</I> that
+is also recognized by the target. The first matching <I>dataType</I> found determines
+the value that will be sent if the token is dropped. If no matching <I>dataType</I>
+is found, then the target is incompatible, and is ignored. By default,
+this option has the value "all", indicating that all <I>dataTypes</I> should be
+considered in the order that they were defined for the source. </DD>
+</DL>
+<P>
+Note that
+this option makes it easy to control a drag&amp;drop source. Setting the value
+to an empty string disables the source; setting the value back to "all"
+restores communication. <P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>siteCommand</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Command</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-sitecmd <I>command</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies a Tcl command used to update the appearance of the token window.
+</DT>
+<DD>If specified, this command is automatically invoked by the <B>drag&amp;drop drag</B>
+command whenever the token window is over a compatible drag&amp;drop target.
+</DD>
+</DL>
+<P>
+The following substitutions are made in the <I>command</I> string before it is
+executed: <blockquote>
+<DL>
+
+<DT><B>%s</B> </DT>
+<DD>Replaced with "1" if the token window is over a compatible
+target, and "0" otherwise. </DD>
+
+<DT><B>%t</B> </DT>
+<DD>Replaced with the window path name for the
+token which represents the data being dragged. </DD>
+</DL>
+</blockquote>
+<P>
+Regardless of this command,
+border of the token window will become raised whenever the token is over
+a valid target. This command can be used to display other visual cues. <P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tokenAnchor</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Anchor</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-tokenanchor <I>anchor</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies how the token window is positioned relative to the mouse </DT>
+<DD>pointer
+coordinates passed to the <B>drag&amp;drop drag</B> command. Must be one of the values
+n, s, e, w, center, nw, ne, sw or se. For example, "nw" means to position
+the token such that its upper-left corner is at the mouse pointer. The default
+value is "center". </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tokenBackground</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Background</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-tokenbg <I>color</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies the color used to draw the background of the token window. </DT>
+<DD></DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tokenBorderWidth</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>BorderWidth</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-tokenborderwidth <I>size</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies the width in pixels of the border around the token window. </DT>
+<DD>This
+border becomes raised to indicate when the token is over a compatible drag&amp;drop
+target site. The value may have any of the forms acceptable to Tk_GetPixels.
+ The default value is "3". </DD>
+</DL>
+<P>
+<BR>
+<PRE>Name:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tokenCursor</B>
+Class:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>Cursor</B>
+Switch:<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>-tokencursor <I>cursor</I></B>
+</PRE>
+<DL>
+
+<DT>Specifies the cursor used when a token window is active. The value </DT>
+<DD>may
+have any of the forms acceptable to Tk_GetCursor. The default value is
+"center_ptr". </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>drag&amp;drop source <I>window <B>handler </B></I></B>?<I>dataType</I>? ?<I>command arg arg...</I>?
+</DT>
+<DD>With no extra arguments, this command returns a list of all <I>dataType</I> names
+that have been registered for the source <I>window</I>. If only the <I>dataType</I> is
+specified, then the <I>dataType</I> is created if necessary, and the command associated
+with the <I>dataType</I> is returned. Otherwise, it concatenates the <I>command</I> and
+any extra <I>arg</I> strings, and registers a new <I>dataType</I> with this command. </DD>
+</DL>
+<P>
+The
+following substitutions are made in the <I>command</I> string before it is executed:
+<blockquote>
+<DL>
+
+<DT><B>%i</B> </DT>
+<DD>Replaced with the name of the interpreter for the target application.
+</DD>
+
+<DT><B>%v</B> </DT>
+<DD>Replaced with the value returned from the "-packagecmd" command. </DD>
+
+<DT><B>%w</B> </DT>
+<DD>Replaced
+with the window path name for the target window. </DD>
+</DL>
+</blockquote>
+<P>
+A typical source handler
+contains one or more "send" commands which transfer data to the remote
+application. The target window is then asked to handle the new data. Whatever
+value is returned by the source <I>command</I> handler is automatically substituted
+into the "%v" fields of the target handler. <P>
+This separation between the
+transfer and the handling of the data is important. It allows the same
+source handler to transfer data for many different targets, and it allows
+each of the targets to handle the incoming data differently. If an error
+is encountered during the communication process, the rejection symbol is
+posted on the token window to indicate failure. </blockquote>
+<P>
+
+<DL>
+
+<DT><B>drag&amp;drop target</B> </DT>
+<DD>Returns
+a list of path names for widgets registered as drag&amp;drop targets. Returns
+an empty string if no widgets have been registered. </DD>
+
+<DT><B>drag&amp;drop target <I>window
+<B>handler </B></I></B>?<I>dataType command arg arg...</I>? </DT>
+<DD>Registers a new drag&amp;drop target window
+with a given handler, or modifies the handlers for an existing window.
+If no <I>dataType</I> is specified, this command returns the current list of recognized
+<I>dataType</I> strings. Each <I>dataType</I> is a symbolic name representing a form
+of data, and the corresponding <I>command</I> is a Tcl command that specifies
+how the target will make use of the data. This command is invoked indirectly
+after a source has transferred data to a target application. </DD>
+</DL>
+<P>
+The following
+substitutions are made in the <I>command</I> string before it is executed: <blockquote>
+<DL>
+
+<DT><B>%v</B>
+</DT>
+<DD>In the simplest case, the source window does not have a handler command
+for the selected <I>dataType</I>, and this field is replaced with the result from
+the "-packagecmd" command. When the source does have a handler command,
+the result from the "-packagecmd" command is substituted into its "%v" field,
+and the result from this command is substituted into this field in the
+target command. </DD>
+
+<DT><B>%W</B> </DT>
+<DD>Replaced with the window path name for the target window.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>drag&amp;drop target <I>window </I></B>handle <I>dataType</I> ?<I>value</I>? </DT>
+<DD>Searches for the given <I>dataType</I>
+name among the handlers registered for the target <I>window</I>, and invokes the
+appropriate <I>command</I>. If a <I>value</I> is specified, it is substituted into any
+"%v" fields in the handler command associated with the <I>dataType</I>. If the
+<I>dataType</I> name is not recognized, this command returns an error. This command
+is invoked automatically by the drag&amp;drop facility when data is being transferred
+from a source to a target. </DD>
+
+<DT><B>drag&amp;drop token <I>window</I></B> </DT>
+<DD>Returns the token window
+associated with a drag&amp;drop source <I>window</I>. The token window is used to represent
+data as it is being dragged from the source to a target. When a source
+is first established, its token window must be filled with widgets to display
+the source data. For example, <P>
+<BR>
+<PRE><I>drag&amp;drop source .foo</I>
+set win [drag&amp;drop token .foo]
+label $win.label -text "Data"
+pack $win.label
+</PRE><P>
+
+<DL>
+
+<DT><B>drag&amp;drop drag <I>window x y</I></B> </DT>
+<DD>Marks the start of (or movement during) a drag&amp;drop
+operation. If the token window is unmapped when this command is invoked,
+then the <B>-packagecmd</B> for the source <I>window</I> is executed. If this command
+is successful and returns a non-null string, the token window is mapped.
+ On subsequent calls, the token window is moved to the new <I>x y</I> location.
+ Unless the "<B>-button 0</B>" option is specified for the source, this command
+is automatically bound to &lt;ButtonPress-<I>n</I>&gt; and &lt;B<I>n</I>-Motion&gt; events for "<B>-button
+<I>n</I></B>" of the source widget. </DD>
+
+<DT><B>drag&amp;drop drop <I>window x y</I></B> </DT>
+<DD>Marks the end of a drag&amp;drop
+operation. If the mouse pointer is over a compatible target window, then
+the appropriate send handler for the first compatible <I>dataType</I> is invoked
+to handle the data transfer. If the data transfer is successful, then the
+token window is unmapped; otherwise, a rejection symbol is drawn on the
+token window, and the window is unmapped after a small delay. Unless the
+"<B>-button 0</B>" option is specified for the source, this command is automatically
+bound to the &lt;ButtonRelease-<I>n</I>&gt; event for "<B>-button <I>n</I></B>" of the source widget. </DD>
+
+<DT><B>drag&amp;drop
+active</B> </DT>
+<DD>Returns "1" if a drag&amp;drop operation is in progress, and "0" otherwise.
+A drag&amp;drop operation officially starts after the package command has been
+executed successfully, and ends after the send handler has been executed
+(successfully or otherwise). </DD>
+
+<DT><B>drag&amp;drop errors </B>?<I>proc</I>? </DT>
+<DD>Specifies a Tcl <I>proc</I>
+used to handle errors encountered during drag&amp;drop operations. If a <I>proc</I>
+is not specified, this command returns the current error handler. By default,
+all errors are sent to the usual <B>tkerror</B> command, and therefore appear
+in a dialog box to the user. This behavior is quite useful when debugging
+communication protocols, but may not be desirable in a finished application.
+ Errors can be suppressed entirely (leaving the rejection symbol as the
+only error indicator) by specifying a null string in place of the <I>proc</I>
+name. </DD>
+
+<DT><B>drag&amp;drop location </B>?<I>x y</I>? </DT>
+<DD>Used to set or query the pointer location
+during a drag&amp;drop operation. The <I>x y</I> arguments specify the current location;
+if these arguments are missing, then the last reported (x,y) location is
+returned as a list with two elements. This command is issued automatically
+within the <B>drag&amp;drop drag</B> and <B>drag&amp;drop drop</B> commands, to keep track of pointer
+movement. <P>
+ </DD>
+</DL>
+
+<H2><A NAME="sect3" HREF="#toc3">Keywords</A></H2>
+drag&amp;drop, send, bind, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/eps.html b/blt/html/eps.html
new file mode 100644
index 00000000000..58d5c7053f7
--- /dev/null
+++ b/blt/html/eps.html
@@ -0,0 +1,1231 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>graph(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+eps - Encapsulated PostScript canvas item.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<I>canvas<B>
+create eps <I>x y </I></B></I>?<I>option value</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>eps</B> canvas item lets you place
+encapulated PostScript (EPS) on a canvas, controlling its size and placement.
+ The EPS item is displayed either as a solid rectangle or a preview image.
+ The preview image is designated in one of two ways: 1) the EPS file contains
+an ASCII hexidecimal preview, or 2) a Tk photo image. When the canvas generates
+PostScript output, the EPS will be inserted with the proper translation
+and scaling to match that of the EPS item. So can use the canvas widget
+as a page layout tool.
+<H2><A NAME="sect3" HREF="#toc3">Example</A></H2>
+Let's say you have for PostScript files of
+four graphs which you want to tile two-by-two on a single page. Maybe you'd
+like to annotate the graphs by putting a caption at the bottom of each
+graph. <P>
+Normally, you would have to resort to an external tool or write your
+own PostScript program. The <B>eps</B> canvas item lets you do this through Tk's
+canvas widget. An <B>eps</B> item displays an image (or rectangle) representing
+the encapsulated PostScript file. It also scales and translates the EPS
+file when the canvas is printed. <P>
+
+<H2><A NAME="sect4" HREF="#toc4">Syntax</A></H2>
+<BR>
+<P>
+<CODE><I>canvas <B>create eps <I>x y </I></B></I>?<I>option value</I>?...<BR>
+</CODE><P>The <B>eps</B> item creates a new canvas item. <I>Canvas</I> is the name of a <B>canvas</B> widget.
+ You must supply the X-Y coordinate of the new eps item. How the coordinate
+is exactly interpretered is controlled by the <B>-anchor</B> option (see below).
+<P>
+Additional options may be specified on the command line to configure aspects
+of the eps item such as its color, stipple, and font. The following <I>option</I>
+and <I>value</I> pairs are valid.
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B> </DT>
+<DD>Tells how to position the EPS item
+relative to its X-Y coordinate. The default is <I>center</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets
+the background color of the EPS rectangle. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the 3-D border around the outside edge of the item. The <B>-relief</B> option
+determines if the border is to be drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-file <I>fileName</I></B>
+</DT>
+<DD>Specifies the name of the EPS file. The first line of an EPS file must
+start with "%!PS" and contain a "EPS" version specification. The other
+requirement is that there be a "%%BoundingBox:" entry which contains four
+integers representing the lower-left and upper-right coordinates of the area
+bounding the EPS. The default is <I>""</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of
+the title. The default is <I>*-Helvetica-Bold-R-Normal-*-18-180-*</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B>
+</DT>
+<DD>Specifies the foreground color of the EPS rectangle. The option matters
+only when the <B>-stipple</B> option is set. The default is <I>white</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B>
+</DT>
+<DD>Specifies the height EPS item. If <I>pixels</I> is <I>0</I>, then the height is determined
+from the PostScript "BoundingBox:" entry in the EPS file. The default is
+<I>0</I>. </DD>
+
+<DT><B>-image <I>photo</I></B> </DT>
+<DD>Specifies the name of a Tk photo image to be displayed as
+in the item as a preview image. This option overrides any preview specification
+found in the EPS file. The default is <I>""</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the
+title should be justified. This matters only when the title contains more
+than one line of text. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default
+is <I>center</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect for the EPS item. <I>Relief</I>
+specifies how the item should appear relative to canvas; for example,
+<I>raised</I> means the item should appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-shadowcolor
+<I>color</I></B> </DT>
+<DD>Specifies the color of the drop shadow used for the title. The option
+with the <B>-shadowoffset</B> option control how the title's drop shadow appears.
+The default is <I>grey</I>. </DD>
+
+<DT><B>-shadowoffset <I>pixels</I></B> </DT>
+<DD>Specifies the offset of the drop
+shadow from the title's text. If <I>pixels</I> is <I>0</I>, no shadow will be seen. The
+default is <I>0</I>. </DD>
+
+<DT><B>-showimage <I>boolean</I></B> </DT>
+<DD>Indicates whether to display the image preview
+(if one exists), or a simple rectangle. The default is <I>yes</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B>
+</DT>
+<DD>Specifies a bitmap to used to stipple the rectangle representing the EPS
+item. The default is <I>""</I>. </DD>
+
+<DT><B>-title <I>string</I></B> </DT>
+<DD>Sets the title of the EPS item. If
+<I>string</I> is <I>""</I>, then the title specified by the PostScript "Title:" entry
+is used. You can set the string a single space to display no title. The
+default is <I>""</I>. </DD>
+
+<DT><B>-titleanchor <I>anchor</I></B> </DT>
+<DD>Tells how to position the title within
+EPS item. The default is <I>n</I>. </DD>
+
+<DT><B>-titlecolor <I>color</I></B> </DT>
+<DD>Specifies the color of the title.
+ The default is <I>white</I>. </DD>
+
+<DT><B>-titlerotate <I>degrees</I></B> </DT>
+<DD>Sets the rotation of the title.
+ <I>Degrees</I> is a real number representing the angle of rotation. The title
+is first rotated in space and then placed according to the <B>-titleanchor</B>
+position. The default rotation is <I>0.0</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the width EPS
+item. If <I>pixels</I> is <I>0</I>, then the width is determined from the PostScript
+"BoundingBox:" entry in the EPS file. The default is <I>0</I>. <I>5i</I>. </DD>
+</DL>
+
+<H2><A NAME="sect5" HREF="#toc5">Example</A></H2>
+The <B>graph</B>
+command creates a new graph. <BR>
+<CODE># Create a new graph. Plotting area is black.<BR>
+graph .g -plotbackground black<BR>
+</CODE><P>A new Tcl command <I>.g</I> is also created. This command can be used to query
+and modify the graph. For example, to change the title of the graph to
+"My Plot", you use the new command and the graph's <B>configure</B> operation. <BR>
+<CODE># Change the title.<BR>
+.g configure -title "My Plot"<BR>
+</CODE><P>A graph has several components. To access a particular component you use
+the component's name. For example, to add data elements, you use the new
+command and the <B>element</B> component. <BR>
+<CODE># Create a new element named "line1"<BR>
+.g element create line1 \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-xdata { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;155.85 166.60 175.38 }<BR>
+</CODE><P>The element's X and Y coordinates are specified using lists of numbers.
+Alternately, BLT vectors could be used to hold the X-Y coordinates. <BR>
+<CODE># Create two vectors and add them to the graph.<BR>
+vector xVec yVec<BR>
+xVec set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }<BR>
+yVec set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;166.60 175.38 }<BR>
+.g element create line1 -xdata xVec -ydata yVec<BR>
+</CODE><P>The advantage of using vectors is that when you modify one, the graph is
+automatically redrawn to display the new values. <BR>
+<CODE># Change the X-Y coordinates of the first point.<BR>
+set xVec(0) 0.18<BR>
+set yVec(0) 25.18<BR>
+</CODE><P>An element named <I>line1</I> is now created in <I>.g</I>. By default, the element's label
+in the legend will be also <I>line1</I>. You can change the label, or specify no
+legend entry, again using the element's <B>configure</B> operation. <BR>
+<CODE># Don't display "line1" in the legend.<BR>
+.g element configure line1 -label ""<BR>
+</CODE><P>You can configure more than just the element's label. An element has many
+attributes such as symbol type and size, dashed or solid lines, colors,
+line width, etc. <BR>
+<CODE>.g element configure line1 -symbol square -color red \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-dashes { 2 4 2 } -linewidth 2 -pixels 2c<BR>
+</CODE><P>Four coordinate axes are automatically created: <I>x</I>, <I>x2</I>, <I>y</I>, and <I>y2</I>. And by
+default, elements are mapped onto the axes <I>x</I> and <I>y</I>. This can be changed
+with the <B>-mapx</B> and <B>-mapy</B> options. <BR>
+<CODE># Map "line1" on the alternate Y-axis "y2".<BR>
+.g element configure line1 -mapy y2<BR>
+</CODE><P>Axes can be configured in many ways too. For example, you change the scale
+of the Y-axis from linear to log using the <B>axis</B> component. <BR>
+<CODE># Y-axis is log scale.<BR>
+.g axis configure y -logscale yes<BR>
+</CODE><P>One important way axes are used is to zoom in on a particular data region.
+ Zooming is done by simply specifying new axis limits using the <B>-min</B> and
+<B>-max</B> configuration options. <BR>
+<CODE>.g axis configure x -min 1.0 -max 1.5<BR>
+.g axis configure y -min 12.0 -max 55.15<BR>
+</CODE><P>To zoom interactively, you link the axis <B>configure</B> operations with some
+user interaction (such as pressing the mouse button), using the <B>bind</B> command.
+ To convert between screen and graph coordinates, use the <B>invtransform</B>
+operation. <BR>
+<CODE># Click the button to set a new minimum <BR>
+bind .g &lt;ButtonPress-1&gt; { <BR>
+ %W axis configure x -min [%W axis invtransform x %x]<BR>
+ %W axis configure x -min [%W axis invtransform x %y]<BR>
+}<BR>
+</CODE><P>By default, the limits of the axis are determined from data values. To reset
+back to the default limits, set the <B>-min</B> and <B>-max</B> options to the empty value.
+<BR>
+<CODE># Reset the axes to autoscale again.<BR>
+.g axis configure x -min {} -max {}<BR>
+.g axis configure y -min {} -max {}<BR>
+</CODE><P>By default, the legend is drawn in the right margin. You can change this
+or any legend configuration options using the <B>legend</B> component. <BR>
+<CODE># Configure the legend font, color, and relief<BR>
+.g legend configure -position left -relief raised \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-font fixed -fg blue<BR>
+</CODE><P>To prevent the legend from being displayed, turn on the <B>-hide</B> option. <BR>
+<CODE># Don't display the legend.<BR>
+.g legend configure -hide yes<BR>
+</CODE><P>The <B>graph</B> widget has simple drawing procedures called markers. They can
+be used to highlight or annotate data in the graph. The types of markers
+available are bitmaps, images, polygons, lines, or windows. Markers can
+be used, for example, to mark or brush points. In this example, is a text
+marker that labels the data first point. Markers are created using the
+<B>marker</B> component. <BR>
+<CODE># Create a label for the first data point of "line1".<BR>
+.g marker create text -name first_marker -coords { 0.2 26.18 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-text "start" -anchor se -xoffset -10 -yoffset -10<BR>
+</CODE><P>This creates a text marker named <I>first_marker</I>. It will display the text
+"start" near the coordinates of the first data point. The <B>-anchor</B>, <B>-xoffset</B>,
+and <B>-yoffset</B> options are used to display the marker above and to the left
+of the data point, so that the data point isn't covered by the marker. By
+default, markers are drawn last, on top of data. You can change this with
+the <B>-under</B> option. <BR>
+<CODE># Draw the label before elements are drawn.<BR>
+.g marker configure first_marker -under yes<BR>
+</CODE><P>You can add cross hairs or grid lines using the <B>crosshairs</B> and <B>grid</B> components.
+<BR>
+<CODE># Display both cross hairs and grid lines.<BR>
+.g crosshairs configure -hide no -color red<BR>
+.g grid configure -hide no -dashes { 2 2 }<BR>
+</CODE><P>Finally, to get hardcopy of the graph, use the <B>postscript</B> component. <BR>
+<CODE># Print the graph into file "file.ps"<BR>
+.g postscript output file.ps -maxpect yes -decorations no<BR>
+</CODE><P>This generates a file <I>file.ps</I> containing the encapsulated PostScript of
+the graph. The option <B>-maxpect</B> says to scale the plot to the size of the
+page. Turning off the <B>-decorations</B> option denotes that no borders or color
+backgrounds should be drawn (i.e. the background of the margins, legend,
+and plotting area will be white).
+<H2><A NAME="sect6" HREF="#toc6">Graph Operations</A></H2>
+
+<DL>
+
+<DT><I>pathName <B>axis <I>operation
+</I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>bar <I>elemName </I></B></I>?<I>option value</I>?...
+</DT>
+<DD>Creates a new barchart element <I>elemName</I>. It's an error if an element <I>elemName</I>
+already exists. See the manual for <B>barchart</B> for details about what <I>option</I>
+and <I>value</I> pairs are valid. </DD>
+
+<DT><I>pathName <B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries
+or modifies the configuration options of the graph. If <I>option</I> isn't specified,
+a list describing the current options for <I>pathName</I> is returned. If <I>option</I>
+is specified, but not <I>value</I>, then a list describing <I>option</I> is returned.
+If one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair,
+the option <I>option</I> is set to <I>value</I>. The following options are valid. <blockquote></DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color. This includes the margins and legend, but
+not the plotting area. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border
+around the outside edge of the widget. The <B>-relief</B> option determines if
+the border is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-bottommargin <I>pixels</I></B> </DT>
+<DD>Specifies
+the size of the margin below the X-coordinate axis. If <I>pixels</I> is <I>0</I>, the
+size of the margin is selected automatically. The default is <I>0</I>. </DD>
+
+<DT><B>-bufferelements
+<I>boolean</I></B> </DT>
+<DD>Indicates whether an internal pixmap to buffer the display of data
+elements should be used. If <I>boolean</I> is true, data elements are drawn to
+an internal pixmap. This option is especially useful when the graph is
+redrawn frequently while the remains data unchanged (for example, moving
+a marker across the plot). See the <FONT SIZE=-1><B>SPEED TIPS</B></FONT>
+ section. The default is <I>1</I>.
+</DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is <I>crosshair</I>.
+</DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of the graph title. The default is <I>*-Helvetica-Bold-R-Normal-*-18-180-*</I>.
+</DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a maximum distance to consider when searching for
+the closest data point (see the element's <B>closest</B> operation below). Data
+points further than <I>pixels</I> away are ignored. The default is <I>0.5i</I>. </DD>
+
+<DT><B>-height
+<I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget. The default is <I>4i</I>. </DD>
+
+<DT><B>-invertxy
+<I>boolean</I></B> </DT>
+<DD>Indicates whether the placement X-axis and Y-axis should be inverted.
+ If <I>boolean</I> is true, the X and Y axes are swapped. The default is <I>0</I>. </DD>
+
+<DT><B>-justify
+<I>justify</I></B> </DT>
+<DD>Specifies how the title should be justified. This matters only
+when the title contains more than one line of text. <I>Justify</I> must be <I>left</I>,
+<I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-leftmargin <I>pixels</I></B> </DT>
+<DD>Sets the size
+of the margin from the left edge of the window to the Y-coordinate axis.
+ If <I>pixels</I> is <I>0</I>, the size is calculated automatically. The default is <I>0</I>.
+</DD>
+
+<DT><B>-plotbackground <I>color</I></B> </DT>
+<DD>Specifies the background color of the plotting area.
+ The default is <I>white</I>. </DD>
+
+<DT><B>-plotborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border
+around the plotting area. The <B>-plotrelief</B> option determines if a border
+is drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-plotpadx <I>pad</I></B> </DT>
+<DD>Sets the amount of padding to be
+added to the left and right sides of the plotting area. <I>Pad</I> can be a list
+of one or two screen distances. If <I>pad</I> has two elements, the left side
+of the plotting area entry is padded by the first distance and the right
+side by the second. If <I>pad</I> is just one distance, both the left and right
+sides are padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotpady <I>pad</I></B> </DT>
+<DD>Sets the amount
+of padding to be added to the top and bottom of the plotting area. <I>Pad</I>
+can be a list of one or two screen distances. If <I>pad</I> has two elements,
+the top of the plotting area is padded by the first distance and the bottom
+by the second. If <I>pad</I> is just one distance, both the top and bottom are
+padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the plotting area. <I>Relief</I> specifies how the interior of the plotting
+area should appear relative to rest of the graph; for example, <I>raised</I> means
+the plot should appear to protrude from the graph, relative to the surface
+of the graph. The default is <I>sunken</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the graph widget. <I>Relief</I> specifies how the graph should appear relative
+to widget it is packed into; for example, <I>raised</I> means the graph should
+appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-rightmargin <I>pixels</I></B> </DT>
+<DD>Sets the size
+of margin from the plotting area to the right edge of the window. By default,
+the legend is drawn in this margin. If <I>pixels</I> is than 1, the margin size
+is selected automatically. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides information used when
+moving the focus from window to window via keyboard traversal (e.g., Tab
+and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window should be skipped
+entirely during keyboard traversal. <I>1</I> means that the this window should
+always receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window. The default is
+<I>""</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies a tiled background for the widget. If <I>image</I> isn't
+<I>""</I>, the background is tiled using <I>image</I>. Otherwise, the normal background
+color is drawn (see the <B>-background</B> option). <I>Image</I> must be an image created
+using the Tk <B>image</B> command. The default is <I>""</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title
+to <I>text</I>. If <I>text</I> is <I>""</I>, no title will be displayed. </DD>
+
+<DT><B>-topmargin <I>pixels</I></B> </DT>
+<DD>Specifies
+the size of the margin above the x2 axis. If <I>pixels</I> is <I>0</I>, the margin size
+is calculated automatically. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the requested width
+of the widget. The default is <I>5i</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>crosshairs <I>operation </I></B></I>?<I>arg</I>? </DT>
+<DD>See
+the <FONT SIZE=-1><B>CROSSHAIRS COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>element <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See
+the <FONT SIZE=-1><B>ELEMENT COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>extents <I>item</I></B></I> </DT>
+<DD>Reports the size
+of a particular items in the graph. <I>Item</I> must be either <I>leftmargin</I>, <I>rightmargin</I>,
+<I>topmargin</I>, <I>bottommargin</I>, <I>plotwidth</I>, or <I>plotheight</I>. </DD>
+
+<DT><I>pathName <B>grid <I>operation
+</I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>GRID COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>invtransform <I>winX winY</I></B></I>
+ </DT>
+<DD>Performs an inverse coordinate transformation, mapping window coordinates
+back to graph coordinates, using the standard X-axis and Y-axis. Returns a
+list of containing the X-Y y graph coordinates. </DD>
+
+<DT><I>pathName <B>inside <I>x y</I></B></I> </DT>
+<DD>Returns
+<I>1</I> is the designated screen coordinate (<I>x</I> and <I>y</I>) is inside the plotting
+area and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>legend <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>LEGEND COMPONENT</B></FONT>
+
+ section. </DD>
+
+<DT><I>pathName <B>line<B> operation arg</B></B></I>... </DT>
+<DD>The operation is the same as <B>element</B>.
+</DD>
+
+<DT><I>pathName <B>marker <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>MARKER COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName
+<B>postscript <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>POSTSCRIPT COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName
+<B>snap <I>photoName</I></B></I> </DT>
+<DD>Takes a snapshot of the graph and stores the contents in
+the photo image <I>photoName</I>. <I>PhotoName</I> is the name of a Tk photo image that
+must already exist. </DD>
+
+<DT><I>pathName <B>transform <I>x y</I></B></I> </DT>
+<DD>Performs a coordinate transformation,
+mapping graph coordinates to window coordinates, using the standard X-axis
+and Y-axis. Returns a list containing the X-Y screen coordinates. </DD>
+
+<DT><I>pathName
+<B>xaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>x2axis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>yaxis <I>operation</I></B></I>
+?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>y2axis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section.
+</DD>
+</DL>
+
+<H2><A NAME="sect7" HREF="#toc7">Graph Components</A></H2>
+A graph is composed of several components: coordinate axes,
+data elements, legend, grid, cross hairs, postscript, and annotation markers.
+Instead of one big set of configuration options and operations, the graph
+is partitioned, where each component has its own configuration options
+and operations that specifically control that aspect or part of the graph.
+
+<H3><A NAME="sect8" HREF="#toc8">Axis Components</A></H3>
+Four coordinate axes are automatically created: two X-coordinate
+axes (<I>x</I> and <I>x2</I>) and two Y-coordinate axes (<I>y</I>, and <I>y2</I>). By default, the axis
+<I>x</I> is located in the bottom margin, <I>y</I> in the left margin, <I>x2</I> in the top
+margin, and <I>y2</I> in the right margin. <P>
+An axis consists of the axis line, title,
+major and minor ticks, and tick labels. Major ticks are drawn at uniform
+intervals along the axis. Each tick is labeled with its coordinate value.
+ Minor ticks are drawn at uniform intervals within major ticks. <P>
+The range
+of the axis controls what region of data is plotted. Data points outside
+the minimum and maximum limits of the axis are not plotted. By default,
+the minimum and maximum limits are determined from the data, but you can
+reset either limit. <P>
+You can create and use several axes. To create an axis,
+invoke the axis component and its create operation. <BR>
+<CODE># Create a new axis called "tempAxis"<BR>
+.g axis create tempAxis<BR>
+</CODE><P>You map data elements to an axis using the element's -mapy and -mapx configuration
+options. They specify the coordinate axes an element is mapped onto. <BR>
+<CODE># Now map the tempAxis data to this axis.<BR>
+.g element create "e1" -xdata $x -ydata $y -mapy tempAxis<BR>
+</CODE><P>While you can create many axes, only four can be displayed simultaneously.
+They are drawn in each of the margins surrounding the plotting area. The
+axes <I>x</I> and <I>y</I> are drawn in the bottom and left margins. The axes <I>x2</I> and <I>y2</I>
+are drawn in top and right margins. Only <I>x</I> and <I>y</I> are shown by default. Note
+that the axes can have different scales. <P>
+To display a different axis, you
+invoke one of the following components: <B>xaxis</B>, <B>yaxis</B>, <B>x2axis</B>, and <B>y2axis</B>.
+The <B>use</B> operation designates the axis to be drawn in the corresponding
+margin: <B>xaxis</B> in the bottom, <B>yaxis</B> in the left, <B>x2axis</B> in the top, and
+<B>y2axis</B> in the right. <BR>
+<CODE># Display the axis tempAxis in the left margin.<BR>
+.g yaxis use tempAxis<BR>
+<P>
+</CODE><P>You can configure axes in many ways. The axis scale can be linear or logarithmic.
+ The values along the axis can either monotonically increase or decrease.
+ If you need custom tick labels, you can specify a Tcl procedure to format
+the label any way you wish. You can control how ticks are drawn, by changing
+the major tick interval or the number of minor ticks. You can define non-uniform
+tick intervals, such as for time-series plots. <P>
+
+<DL>
+
+<DT><I>pathName <B>axis <B>cget <I>axisName
+<I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option given by <I>option</I> for <I>axisName</I>.
+ <I>Option</I> may be any option described below for the axis <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>axis <B>configure <I>axisName </I></B></B></I>?<I>axisName</I>?... ?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options of <I>axisName</I>. Several axes can be changed. If <I>option</I>
+isn't specified, a list describing all the current options for <I>axisName</I>
+is returned. If <I>option</I> is specified, but not <I>value</I>, then a list describing
+<I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are specified,
+then for each pair, the axis option <I>option</I> is set to <I>value</I>. The following
+options are valid for axes. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the axis and tick
+labels. The default is <I>black</I>. </DD>
+
+<DT><B>-command <I>prefix</I></B> </DT>
+<DD>Specifies a Tcl command to be
+invoked when formatting the axis tick labels. <I>Prefix</I> is a string containing
+the name of a Tcl proc and any extra arguments for the procedure. This
+command is invoked for each major tick on the axis. Two additional arguments
+are passed to the procedure: the pathname of the widget and the current
+the numeric value of the tick. The procedure returns the formatted tick
+label. If <I>""</I> is returned, no label will appear next to the tick. You can
+get the standard tick labels again by setting <I>prefix</I> to <I>""</I>. The default
+is <I>""</I>. <P>
+Please note that this procedure is invoked while the graph is redrawn.
+You may query configuration options. But do not them, because this can
+have unexpected results. </DD>
+
+<DT><B>-descending <I>boolean</I></B> </DT>
+<DD>Indicates whether the values
+along the axis are monotonically increasing or decreasing. If <I>boolean</I> is
+true, the axis values will be decreasing. The default is <I>0</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the axis is displayed. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how
+the axis title should be justified. This matters only when the axis title
+contains more than one line of text. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>.
+ The default is <I>center</I>. </DD>
+
+<DT><B>-limits <I>formatStr</I></B> </DT>
+<DD>Specifies a printf-like description
+to format the minimum and maximum limits of the axis. The limits are displayed
+at the top/bottom or left/right sides of the plotting area. <I>FormatStr</I> is
+a list of one or two format descriptions. If one description is supplied,
+both the minimum and maximum limits are formatted in the same way. If two,
+the first designates the format for the minimum limit, the second for the
+maximum. If <I>""</I> is given as either description, then the that limit will
+not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the axis and tick lines. The default is <I>1</I> pixel. </DD>
+
+<DT><B>-logscale <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the scale of the axis is logarithmic or linear. If <I>boolean</I> is true,
+the axis is logarithmic. The default scale is linear. </DD>
+
+<DT><B>-loose <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the limits of the axis should fit the data points tightly, at the
+outermost data points, or loosely, at the outer tick intervals. This is
+relevant only when the axis limit is automatically calculated. If <I>boolean</I>
+is true, the axis range is "loose". The default is <I>0</I>. </DD>
+
+<DT><B>-majorticks <I>majorList</I></B>
+</DT>
+<DD>Specifies where to display major axis ticks. You can use this option to
+display ticks at non-uniform intervals. <I>MajorList</I> is a list of axis coordinates
+designating the location of major ticks. No minor ticks are drawn. If <I>majorList</I>
+is <I>""</I>, major ticks will be automatically computed. The default is <I>""</I>. </DD>
+
+<DT><B>-max
+<I>value</I></B> </DT>
+<DD>Sets the maximum limit of <I>axisName</I>. Any data point greater than
+<I>value</I> is not displayed. If <I>value</I> is <I>""</I>, the maximum limit is calculated
+using the largest data value. The default is <I>""</I>. </DD>
+
+<DT><B>-min <I>value</I></B> </DT>
+<DD>Sets the minimum
+limit of <I>axisName</I>. Any data point less than <I>value</I> is not displayed. If
+<I>value</I> is <I>""</I>, the minimum limit is calculated using the smallest data value.
+The default is <I>""</I>. </DD>
+
+<DT><B>-minorticks <I>minorList</I></B> </DT>
+<DD>Specifies where to display minor
+axis ticks. You can use this option to display minor ticks at non-uniform
+intervals. <I>MinorList</I> is a list of real values, ranging from 0.0 to 1.0, designating
+the placement of a minor tick. No minor ticks are drawn if the <B>-majortick</B>
+option is also set. If <I>minorList</I> is <I>""</I>, minor ticks will be automatically
+computed. The default is <I>""</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the how many degrees
+to rotate the axis tick labels. <I>Theta</I> is a real value representing the number
+of degrees to rotate the tick labels. The default is <I>0.0</I> degrees. </DD>
+
+<DT><B>-showticks
+<I>boolean</I></B> </DT>
+<DD>Indicates whether axis ticks should be drawn. If <I>boolean</I> is true,
+ticks are drawn. If false, only the axis line is drawn. The default is <I>1</I>.
+</DD>
+
+<DT><B>-stepsize <I>value</I></B> </DT>
+<DD>Specifies the interval between major axis ticks. If <I>value</I>
+isn't a valid interval (must be less than the axis range), the request
+is ignored and the step size is automatically calculated. </DD>
+
+<DT><B>-subdivisions <I>number</I></B>
+ </DT>
+<DD>Indicates how many minor axis ticks are to be drawn. For example, if <I>number</I>
+is two, only one minor tick is drawn. If <I>number</I> is one, no minor ticks
+are displayed. The default is <I>2</I>. </DD>
+
+<DT><B>-tickfont <I>fontName</I></B> </DT>
+<DD>Specifies the font for
+axis tick labels. The default is <I>*-Courier-Bold-R-Normal-*-100-*</I>. </DD>
+
+<DT><B>-ticklength <I>pixels</I></B>
+</DT>
+<DD>Sets the length of major and minor ticks (minor ticks are half the length
+of major ticks). If <I>pixels</I> is less than zero, the axis will be inverted
+with ticks drawn pointing towards the plot. The default is <I>0.1i</I>. </DD>
+
+<DT><B>-title <I>text</I></B>
+</DT>
+<DD>Sets the title of the axis. If <I>text</I> is <I>""</I>, no axis title will be displayed.
+ </DD>
+
+<DT><B>-titlecolor <I>color</I></B> </DT>
+<DD>Sets the color of the axis title. The default is <I>black</I>.
+</DD>
+
+<DT><B>-titlefont <I>fontName</I></B> </DT>
+<DD>Specifies the font for axis title. The default is <I>*-Helvetica-Bold-R-Normal-*-14-140-*</I>.
+</DD>
+</DL>
+<P>
+Axis configuration options may be also be set by the <B>option</B> command. The
+resource class is <I>Axis</I>. The resource names are the names of the axes (such
+as <I>x</I> or <I>x2</I>). <BR>
+<CODE>option add *Graph.Axis.Color blue<BR>
+option add *Graph.x.LogScale true<BR>
+option add *Graph.x2.LogScale false<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>axis <B>create <I>axisName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new axis by the
+name <I>axisName</I>. No axis by the same name can already exist. <I>Option</I> and <I>value</I>
+are described in above in the axis <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>axis <B>delete
+</B></B></I>?<I>axisName</I>?... </DT>
+<DD>Deletes the named axes. An axis is not really deleted until it
+is not longer in use, so it's safe to delete axes mapped to elements. </DD>
+
+<DT><I>pathName
+<B>axis invtransform <I>axisName value</I></B></I> </DT>
+<DD>Performs the inverse transformation, changing
+the screen coordinate <I>value</I> to a graph coordinate, mapping the value mapped
+to <I>axisName</I>. Returns the graph coordinate. </DD>
+
+<DT><I>pathName <B>axis limits <I>axisName</I></B></I>
+</DT>
+<DD>Returns a list of the minimum and maximum limits for <I>axisName</I>. The order
+of the list is <I>min max</I>. </DD>
+
+<DT><I>pathName <B>axis names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of
+axes matching zero or more patterns. If no <I>pattern</I> argument is give, the
+names of all axes are returned. </DD>
+
+<DT><I>pathName <B>axis transform <I>axisName value</I></B></I> </DT>
+<DD>Transforms
+the coordinate <I>value</I> to a screen coordinate by mapping the it to <I>axisName</I>.
+ Returns the transformed screen coordinate. </DD>
+</DL>
+<P>
+Only four axes can be displayed
+simultaneously. By default, they are <I>x</I>, <I>y</I>, <I>x2</I>, and <I>y2</I>. You can swap in
+a different axis with <B>use</B> operation of the special axis components: <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B>. <BR>
+<CODE>.g create axis temp<BR>
+.g create axis time<BR>
+...<BR>
+.g xaxis use temp<BR>
+.g yaxis use time<BR>
+</CODE><P>Only the axes specified for use are displayed on the screen. <P>
+The <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B> components operate on an axis location rather
+than a specific axis like the more general <B>axis</B> component does. The <B>xaxis</B>
+component manages the X-axis located in the bottom margin (whatever axis
+that happens to be). Likewise, <B>yaxis</B> uses the Y-axis in the left margin,
+<B>x2axis</B> the top X-axis, and <B>y2axis</B> the right Y-axis. <P>
+They implicitly control
+the axis that is currently using to that location. By default, <B>xaxis</B> uses
+the <I>x</I> axis, <B>yaxis</B> uses <I>y</I>, <B>x2axis</B> uses <I>x2</I>, and <B>y2axis</B> uses <I>y2</I>. These components
+can be more convenient to use than always determining what axes are current
+being displayed by the graph. <P>
+The following operations are available for
+axes. They mirror exactly the operations of the <B>axis</B> component. The <I>axis</I>
+argument must be <B>xaxis</B>, <B>x2axis</B>, <B>yaxis</B>, or <B>y2axis</B>.
+<DL>
+
+<DT><I>pathName <I>axis <B>cget <I>option</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>configure </B></I></I>?<I>option value</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> invtransform <I>value</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>limits</B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> transform <I>value</I></B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> use </B></I></I>?<I>axisName</I>?
+ </DT>
+<DD>Designates the axis <I>axisName</I> is to be displayed at this location. <I>AxisName</I>
+can not be already in use at another location. This command returns the
+name of the axis currently using this location. </DD>
+</DL>
+
+<H3><A NAME="sect9" HREF="#toc9">Crosshairs Component</A></H3>
+Cross
+hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position the
+mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives. This
+means that they can be quickly drawn and erased without redrawing the entire
+graph. <P>
+The following operations are available for cross hairs:
+<DL>
+
+<DT><I>pathName
+<B>crosshairs cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the cross hairs configuration
+option given by <I>option</I>. <I>Option</I> may be any option described below for the
+cross hairs <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>crosshairs configure </B></I>?<I>option value</I>?...
+ </DT>
+<DD>Queries or modifies the configuration options of the cross hairs. If
+<I>option</I> isn't specified, a list describing all the current options for the
+cross hairs is returned. If <I>option</I> is specified, but not <I>value</I>, then a
+list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs
+are specified, then for each pair, the cross hairs option <I>option</I> is set
+to <I>value</I>. The following options are available for cross hairs. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B>
+ </DT>
+<DD>Sets the color of the cross hairs. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the cross hairs. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the cross
+hair lines. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the
+cross hairs will be solid lines. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether cross hairs
+are drawn. If <I>boolean</I> is true, cross hairs are not drawn. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Set the width of the cross hair lines. The default
+is <I>1</I>. </DD>
+
+<DT><B>-position <I>pos</I></B> </DT>
+<DD>Specifies the screen position where the cross hairs
+intersect. <I>Pos</I> must be in the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window
+coordinates of the intersection. </DD>
+</DL>
+<P>
+Cross hairs configuration options may be
+also be set by the <B>option</B> command. The resource name and class are <I>crosshairs</I>
+and <I>Crosshairs</I> respectively. <BR>
+<CODE>option add *Graph.Crosshairs.LineWidth 2<BR>
+option add *Graph.Crosshairs.Color red<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>crosshairs off</B></I> </DT>
+<DD>Turns off the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs
+on</B></I> </DT>
+<DD>Turns on the display of the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs toggle</B></I>
+</DT>
+<DD>Toggles the current state of the cross hairs, alternately mapping and unmapping
+the cross hairs. </DD>
+</DL>
+
+<H3><A NAME="sect10" HREF="#toc10">Element Components</A></H3>
+A data element represents a set of data.
+ It contains x and y vectors containing the coordinates of the data points.
+ Elements can be displayed with a symbol at each data point and lines connecting
+the points. Elements also control the appearance of the data, such as the
+symbol type, line width, color etc. <P>
+When new data elements are created,
+they are automatically added to a list of displayed elements. The display
+list controls what elements are drawn and in what order. <P>
+The following
+operations are available for elements.
+<DL>
+
+<DT><I>pathName <B>element activate <I>elemName
+</I></B></I>?<I>index</I>?... </DT>
+<DD>Specifies the data points of element <I>elemName</I> to be drawn using
+active foreground and background colors. <I>ElemName</I> is the name of the element
+and <I>index</I> is a number representing the index of the data point. If no indices
+are present then all data points become active. </DD>
+
+<DT><I>pathName <B>element cget <I>elemName
+<I>option</I></I></B></I> </DT>
+<DD>Returns the current value of the element configuration option given
+by <I>option</I>. <I>Option</I> may be any of the options described below for the element
+<B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>element closest <I>x y</I></B></I> <I>varName</I> ?<I>option value</I>?...
+?<I>elemName</I>?... </DT>
+<DD>Finds the data point closest to the window coordinates <I>x</I> and
+<I>y</I> in the element <I>elemName</I>. <I>ElemName</I> is the name of an element, that must
+not be hidden. If no elements are specified, then all visible elements
+are searched. It returns via the array variable <I>varName</I> the name of the
+closest element, the index of its closest point, and the graph coordinates
+of the point. Returns <I>0</I>, if no data point within the threshold distance
+can be found, otherwise <I>1</I> is returned. The following <I>option</I>-<I>value</I> pairs
+are available. <blockquote></DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a threshold distance where selected
+data points are ignored. <I>Pixels</I> is a valid screen distance, such as <I>2</I> or
+<I>1.2i</I>. If this option isn't specified, then it defaults to the value of the
+graph's <B>-halo</B> option. </DD>
+
+<DT><B>-interpolate <I>boolean</I></B> </DT>
+<DD>Indicates that both the data points
+and interpolated points along the line segment formed should be considered.
+ If <I>boolean</I> is true, the closest line segment will be selected instead
+of the closest point. If this option isn't specified, <I>boolean</I> defaults to
+<I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>element configure <I>elemName </I></B></I>?<I>elemName</I>... ?<I>option value</I>?... </DT>
+<DD>Queries
+or modifies the configuration options for elements. Several elements can
+be modified at the same time. If <I>option</I> isn't specified, a list describing
+all the current options for <I>elemName</I> is returned. If <I>option</I> is specified,
+but not <I>value</I>, then a list describing the option <I>option</I> is returned. If
+one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair, the
+element option <I>option</I> is set to <I>value</I>. The following options are valid
+for elements. <blockquote></DD>
+
+<DT><B>-activepen <I>penName</I></B> </DT>
+<DD>Specifies pen to use to draw active element.
+ If <I>penName</I> is <I>""</I>, no active elements will be drawn. The default is <I>activeLine</I>.
+</DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the traces connecting the data points.
+</DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of element line. <I>DashList</I> is a list
+of up to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the element line. Each number must be between 1 and 255. If
+<I>dashList</I> is <I>""</I>, the lines will be solid. </DD>
+
+<DT><B>-data <I>coordList</I></B> </DT>
+<DD>Specifies the X-Y
+coordinates of the data. <I>CoordList</I> is a list of numeric expressions representing
+the X-Y coordinate pairs of each data point. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the interior
+color of symbols. If <I>color</I> is <I>""</I>, then the interior of the symbol is transparent.
+ If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the element is
+displayed. The default is <I>no</I>. </DD>
+
+<DT><B>-label <I>text</I></B> </DT>
+<DD>Sets the element's label in the
+legend. If <I>text</I> is <I>""</I>, the element will have no entry in the legend. The
+default label is the element's name. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the connecting lines between data points. If <I>pixels</I> is <I>0</I>, no connecting
+lines will be drawn between symbols. The default is <I>0</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Selects
+the X-axis to map the element's X-coordinates onto. <I>XAxis</I> must be the name
+of an axis. The default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Selects the Y-axis to map the element's
+Y-coordinates onto. <I>YAxis</I> must be the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-offdash
+<I>color</I></B> </DT>
+<DD>Sets the color of the stripes when traces are dashed (see the <B>-dashes</B>
+option). If <I>color</I> is <I>""</I>, then the "off" pixels will represent gaps instead
+of stripes. If <I>color</I> is <I>defcolor</I>, then the color will be the same as the
+<B>-color</B> option. The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color or
+the outline around each symbol. If <I>color</I> is <I>""</I>, then no outline is drawn.
+If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outlinewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the outline
+bordering each symbol. If <I>pixels</I> is <I>0</I>, no outline will be drawn. The default
+is <I>1</I>. </DD>
+
+<DT><B>-pixels <I>pixels</I></B> </DT>
+<DD>Sets the size of symbols. If <I>pixels</I> is <I>0</I>, no symbols
+will be drawn. The default is <I>0.125i</I>. </DD>
+
+<DT><B>-scalesymbols <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is
+true, the size of the symbols drawn for <I>elemName</I> will change with scale
+of the X-axis and Y-axis. At the time this option is set, the current ranges
+of the axes are saved as the normalized scales (i.e scale factor is 1.0)
+and the element is drawn at its designated size (see the <B>-pixels</B> option).
+ As the scale of the axes change, the symbol will be scaled according to
+the smaller of the X-axis and Y-axis scales. If <I>boolean</I> is false, the element's
+symbols are drawn at the designated size, regardless of axis scales. The
+default is <I>0</I>. </DD>
+
+<DT><B>-smooth <I>smooth</I></B> </DT>
+<DD>Specifies how connecting line segments are
+drawn between data points. <I>Smooth</I> can be either <I>linear</I>, <I>step</I>, <I>natural</I>, or
+<I>quadratic</I>. If <I>smooth</I> is <I>linear</I>, a single line segment is drawn, connecting
+both data points. When <I>smooth</I> is <I>step</I>, two line segments are drawn. The first
+is a horizontal line segment that steps the next X-coordinate. The second
+is a vertical line, moving to the next Y-coordinate. Both <I>natural</I> and <I>quadratic</I>
+generate multiple segments between data points. If <I>natural</I>, the segments
+are generated using a cubic spline. If <I>quadratic</I>, a quadratic spline is
+used. The default is <I>linear</I>. </DD>
+
+<DT><B>-styles <I>styleList</I></B> </DT>
+<DD>Specifies what pen to use
+based on the range of weights given. <I>StyleList</I> is a list of style specifications.
+Each style specification, in turn, is a list consisting of a pen name,
+and optionally a minimum and maximum range. Data points whose weight (see
+the <B>-weight</B> option) falls in this range, are drawn with this pen. If no
+range is specified it defaults to the index of the pen in the list. Note
+that this affects only symbol attributes. Line attributes, such as line
+width, dashes, etc. are ignored. </DD>
+
+<DT><B>-symbol <I>symbol</I></B> </DT>
+<DD>Specifies the symbol for
+data points. <I>Symbol</I> can be either <I>square</I>, <I>circle</I>, <I>diamond</I>, <I>plus</I>, <I>cross</I>,
+<I>splus</I>, <I>scross</I>, <I>triangle</I>, <I>""</I> (where no symbol is drawn), or a bitmap. Bitmaps
+are specified as "<I>source</I> ?<I>mask</I>?", where <I>source</I> is the name of the bitmap,
+and <I>mask</I> is the bitmap's optional mask. The default is <I>circle</I>. </DD>
+
+<DT><B>-trace <I>direction</I></B>
+ </DT>
+<DD>Indicates whether connecting lines between data points (whose X-coordinate
+values are either increasing or decreasing) are drawn. <I>Direction</I> must
+be <I>increasing</I>, <I>decreasing</I>, or <I>both</I>. For example, if <I>direction</I> is <I>increasing</I>,
+connecting lines will be drawn only between those data points where X-coordinate
+values are monotonically increasing. If <I>direction</I> is <I>both</I>, connecting lines
+will be draw between all data points. The default is <I>both</I>. </DD>
+
+<DT><B>-weights <I>wVec</I></B>
+ </DT>
+<DD>Specifies the weights of the individual data points. This, with the list
+pen styles (see the <B>-styles</B> option), controls how data points are drawn.
+ <I>WVec</I> is the name of a BLT vector or a list of numeric expressions representing
+the weights for each data point. </DD>
+
+<DT><B>-xdata <I>xVec</I></B> </DT>
+<DD>Specifies the X-coordinates
+of the data. <I>XVec</I> is the name of a BLT vector or a list of numeric expressions.
+</DD>
+
+<DT><B>-ydata <I>yVec</I></B> </DT>
+<DD>Specifies the Y-coordinates of the data. <I>YVec</I> is the name of
+a BLT vector or a list of numeric expressions. </DD>
+</DL>
+<P>
+Element configuration options
+may also be set by the <B>option</B> command. The resource class is <I>Element</I>. The
+resource name is the name of the element. <BR>
+<CODE>option add *Graph.Element.symbol line<BR>
+option add *Graph.e1.symbol line<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>element create <I>elemName</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a new element <I>elemName</I>.
+ It's an error is an element <I>elemName</I> already exists. If additional arguments
+are present, they specify options valid for the element <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>element deactivate <I>elemName</I></B></I> ?<I>elemName</I>?... </DT>
+<DD>Deactivates all the elements
+matching <I>pattern</I>. Elements whose names match any of the patterns given are
+redrawn using their normal colors. </DD>
+
+<DT><I>pathName <B>element delete</B></I> ?<I>elemName</I>?... </DT>
+<DD>Deletes
+all the named elements. The graph is automatically redrawn. </DD>
+
+<DT><I>pathName <B>element
+exists <I>elemName</I></B></I> </DT>
+<DD>Returns <I>1</I> if an element <I>elemName</I> currently exists and <I>0</I>
+otherwise. </DD>
+
+<DT><I>pathName <B>element names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns the elements matching
+one or more pattern. If no <I>pattern</I> is given, the names of all elements
+is returned. </DD>
+
+<DT><I>pathName <B>element show</B></I> ?<I>nameList</I>? </DT>
+<DD>Queries or modifies the
+element display list. The element display list designates the elements
+drawn and in what order. <I>NameList</I> is a list of elements to be displayed
+in the order they are named. If there is no <I>nameList</I> argument, the current
+display list is returned. </DD>
+
+<DT><I>pathName <B>element type</B></I> <I>elemName</I> </DT>
+<DD>Returns the type
+of <I>elemName</I>. If the element is a bar element, the commands returns the
+string <I>"bar"</I>, otherwise it returns <I>"line"</I>. </DD>
+</DL>
+
+<H3><A NAME="sect11" HREF="#toc11"></CODE><P>Grid Component</A></H3>
+Grid lines extend
+from the major and minor ticks of each axis horizontally or vertically
+across the plotting area. The following operations are available for grid
+lines.
+<DL>
+
+<DT><I>pathName <B>grid cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the grid line
+configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the grid <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>grid configure</B></I> ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options for grid lines. If
+<I>option</I> isn't specified, a list describing all the current grid options for
+<I>pathName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a list
+describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are
+specified, then for each pair, the grid line option <I>option</I> is set to <I>value</I>.
+ The following options are valid for grid lines. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color
+of the grid lines. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style
+of the grid lines. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the grid lines. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the grid will be solid lines.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid should be drawn. If <I>boolean</I> is true,
+grid lines are not shown. The default is <I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of grid lines. The default width is <I>1</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis to
+display grid lines. <I>XAxis</I> must be the name of an axis. The default is <I>x</I>.
+</DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to display grid lines. <I>YAxis</I> must be the
+name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-minor <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid
+lines should be drawn for minor ticks. If <I>boolean</I> is true, the lines will
+appear at minor tick intervals. The default is <I>1</I>. </DD>
+</DL>
+<P>
+</blockquote>
+
+<H2><A NAME="sect12" HREF="#toc12">Speed Tips</A></H2>
+There may be
+cases where the graph needs to be drawn and updated as quickly as possible.
+ If drawing speed becomes a big problem, here are a few tips to speed up
+displays.
+<UL>
+&#183;<LI>Try to minimize the number of data points. The more data points
+the looked at, the more work the graph must do. </LI>&#183;<LI>If your data is generated
+as floating point values, the time required to convert the data values
+to and from ASCII strings can be significant, especially when there any
+many data points. You can avoid the redundant string-to-decimal conversions
+using the C API to BLT vectors. </LI>&#183;<LI>Data elements without symbols are drawn
+faster than with symbols. Set the data element's <B>-symbol</B> option to <I>none</I>. If
+you need to draw symbols, try using the simple symbols such as <I>splus</I> and
+<I>scross</I>. </LI>&#183;<LI>Don't stipple or dash the element. Solid lines are much faster. </LI>&#183;<LI>If
+you update data elements frequently, try turning off the widget's <B>-bufferelements</B>
+option. When the graph is first displayed, it draws data elements into
+an internal pixmap. The pixmap acts as a cache, so that when the graph
+needs to be redrawn again, and the data elements or coordinate axes haven't
+changed, the pixmap is simply copied to the screen. This is especially
+useful when you are using markers to highlight points and regions on the
+graph. But if the graph is updated frequently, changing either the element
+data or coordinate axes, the buffering becomes redundant. </LI>
+</UL>
+
+<H2><A NAME="sect13" HREF="#toc13">Limitations</A></H2>
+Auto-scale
+routines do not use requested min/max limits as boundaries when the axis
+is logarithmically scaled. <P>
+The PostScript output generated for polygons
+with more than 1500 points may exceed the limits of some printers (See
+PostScript Language Reference Manual, page 568). The work-around is to break
+the polygon into separate pieces.
+<H2><A NAME="sect14" HREF="#toc14">Future Incompatibility</A></H2>
+The <B>-mapped</B> options
+are obsoleted and will be removed. You can achieve the same results using
+the <B>-hide</B> option instead. <BR>
+<CODE># Works for now.<BR>
+.g legend configure -mapped no<BR>
+<P>
+# Instead use this.<BR>
+.g legend configure -hide yes <BR>
+
+<H2><A NAME="sect15" HREF="#toc15"></CODE><P>Keywords</A></H2>
+graph, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Example</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Syntax</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Example</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Graph Operations</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Graph Components</A></LI>
+<UL>
+<LI><A NAME="toc8" HREF="#sect8">Axis Components</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Crosshairs Component</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Element Components</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Grid Component</A></LI>
+</UL>
+<LI><A NAME="toc12" HREF="#sect12">Speed Tips</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Limitations</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Future Incompatibility</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/graph.html b/blt/html/graph.html
new file mode 100644
index 00000000000..3d40e3ac293
--- /dev/null
+++ b/blt/html/graph.html
@@ -0,0 +1,2311 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>graph(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+graph - 2D graph for plotting X-Y coordinate data.
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>graph<I> <I>pathName </I></I></B>?<I>option value</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>graph</B> command creates
+a graph for plotting two-dimensional data (X-Y coordinates). It has many configurable
+components: coordinate axes, elements, legend, grid lines, cross hairs,
+etc. They allow you to customize the look and feel of the graph.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The
+<B>graph</B> command creates a new window for plotting two-dimensional data (X-Y
+coordinates). Data points are plotted in a rectangular area displayed in
+the center of the new window. This is the <I>plotting area</I>. The coordinate
+axes are drawn in the margins around the plotting area. By default, the
+legend is displayed in the right margin. The title is displayed in top
+margin. <P>
+The <B>graph</B> widget is composed of several components: coordinate axes,
+data elements, legend, grid, cross hairs, pens, postscript, and annotation
+markers.
+<DL>
+
+<DT><I>axis</I> </DT>
+<DD>The graph has four standard axes (<I>x</I>, <I>x2</I>, <I>y</I>, and <I>y2</I>), but
+you can create and display any number of axes. Axes control what region
+of data is displayed and how the data is scaled. Each axis consists of the
+axis line, title, major and minor ticks, and tick labels. Tick labels display
+the value at each major tick. </DD>
+
+<DT><I>crosshairs</I> </DT>
+<DD>Cross hairs are used to position
+the mouse pointer relative to the X and Y coordinate axes. Two perpendicular
+lines, intersecting at the current location of the mouse, extend across
+the plotting area to the coordinate axes. </DD>
+
+<DT><I>element</I> </DT>
+<DD>An element represents
+a set of data points. Elements can be plotted with a symbol at each data
+point and lines connecting the points. The appearance of the element, such
+as its symbol, line width, and color is configurable. </DD>
+
+<DT><I>grid</I> </DT>
+<DD>Extends the
+major and minor ticks of the X-axis and/or Y-axis across the plotting area.
+ </DD>
+
+<DT><I>legend</I> </DT>
+<DD>The legend displays the name and symbol of each data element.
+The legend can be drawn in any margin or in the plotting area. </DD>
+
+<DT><I>marker</I> </DT>
+<DD>Markers
+are used annotate or highlight areas of the graph. For example, you could
+use a polygon marker to fill an area under a curve, or a text marker to
+label a particular data point. Markers come in various forms: text strings,
+bitmaps, connected line segments, images, polygons, or embedded widgets.
+</DD>
+
+<DT><I>pen</I> </DT>
+<DD>Pens define attributes (both symbol and line style) for elements.
+Data elements use pens to specify how they should be drawn. A data element
+may use many pens at once. Here, the particular pen used for a data point
+is determined from each element's weight vector (see the element's <B>-weight</B>
+and <B>-style</B> options). </DD>
+
+<DT><I>postscript</I> </DT>
+<DD>The widget can generate encapsulated PostScript
+output. This component has several options to configure how the PostScript
+is generated. </DD>
+</DL>
+
+<H2><A NAME="sect4" HREF="#toc4">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>graph <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>graph</B> command creates a new window <I>pathName</I> and makes it into a <B>graph</B>
+widget. At the time this command is invoked, there must not exist a window
+named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options may
+be specified on the command line or in the option database to configure
+aspects of the graph such as its colors and font. See the <B>configure</B> operation
+below for the exact details about what <I>option</I> and <I>value</I> pairs are valid.
+<P>
+If successful, <B>graph</B> returns the path name of the widget. It also creates
+a new Tcl command by the same name. You can use this command to invoke
+various operations that query or modify the graph. The general form is:
+<BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for the graph are described in the <FONT SIZE=-1><B>GRAPH OPERATIONS</B></FONT>
+
+ section. <P>
+The command can also be used to access components of the graph.
+<BR>
+<P>
+<CODE><I>pathName component operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>The operation, now located after the name of the component, is the function
+to be performed on that component. Each component has its own set of operations
+that manipulate that component. They will be described below in their own
+sections.
+<H2><A NAME="sect5" HREF="#toc5">Example</A></H2>
+The <B>graph</B> command creates a new graph. <BR>
+<CODE># Create a new graph. Plotting area is black.<BR>
+graph .g -plotbackground black<BR>
+</CODE><P>A new Tcl command <I>.g</I> is also created. This command can be used to query
+and modify the graph. For example, to change the title of the graph to
+"My Plot", you use the new command and the graph's <B>configure</B> operation. <BR>
+<CODE># Change the title.<BR>
+.g configure -title "My Plot"<BR>
+</CODE><P>A graph has several components. To access a particular component you use
+the component's name. For example, to add data elements, you use the new
+command and the <B>element</B> component. <BR>
+<CODE># Create a new element named "line1"<BR>
+.g element create line1 \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-xdata { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;155.85 166.60 175.38 }<BR>
+</CODE><P>The element's X-Y coordinates are specified using lists of numbers. Alternately,
+BLT vectors could be used to hold the X-Y coordinates. <BR>
+<CODE># Create two vectors and add them to the graph.<BR>
+vector xVec yVec<BR>
+xVec set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }<BR>
+yVec set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;166.60 175.38 }<BR>
+.g element create line1 -xdata xVec -ydata yVec<BR>
+</CODE><P>The advantage of using vectors is that when you modify one, the graph is
+automatically redrawn to reflect the new values. <BR>
+<CODE># Change the y coordinate of the first point.<BR>
+set yVector(0) 25.18<BR>
+</CODE><P>An element named <I>e1</I> is now created in <I>.b</I>. It is automatically added to
+the display list of elements. You can use this list to control in what
+order elements are displayed. To query or reset the element display list,
+you use the element's <B>show</B> operation. <BR>
+<CODE># Get the current display list <BR>
+set elemList [.b element show]<BR>
+# Remove the first element so it won't be displayed.<BR>
+.b element show [lrange $elemList 0 end]<BR>
+</CODE><P>The element will be displayed by as many bars as there are data points
+(in this case there are ten). The bars will be drawn centered at the x-coordinate
+of the data point. All the bars will have the same attributes (colors,
+stipple, etc). The width of each bar is by default one unit. You can change
+this with using the <B>-barwidth</B> option. <BR>
+<CODE># Change the X-Y coordinates of the first point.<BR>
+set xVec(0) 0.18<BR>
+set yVec(0) 25.18<BR>
+</CODE><P>An element named <I>line1</I> is now created in <I>.g</I>. By default, the element's label
+in the legend will be also <I>line1</I>. You can change the label, or specify no
+legend entry, again using the element's <B>configure</B> operation. <BR>
+<CODE># Don't display "line1" in the legend.<BR>
+.g element configure line1 -label ""<BR>
+</CODE><P>You can configure more than just the element's label. An element has many
+attributes such as symbol type and size, dashed or solid lines, colors,
+line width, etc. <BR>
+<CODE>.g element configure line1 -symbol square -color red \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-dashes { 2 4 2 } -linewidth 2 -pixels 2c<BR>
+</CODE><P>Four coordinate axes are automatically created: <I>x</I>, <I>x2</I>, <I>y</I>, and <I>y2</I>. And by
+default, elements are mapped onto the axes <I>x</I> and <I>y</I>. This can be changed
+with the <B>-mapx</B> and <B>-mapy</B> options. <BR>
+<CODE># Map "line1" on the alternate Y-axis "y2".<BR>
+.g element configure line1 -mapy y2<BR>
+</CODE><P>Axes can be configured in many ways too. For example, you change the scale
+of the Y-axis from linear to log using the <B>axis</B> component. <BR>
+<CODE># Y-axis is log scale.<BR>
+.g axis configure y -logscale yes<BR>
+</CODE><P>One important way axes are used is to zoom in on a particular data region.
+ Zooming is done by simply specifying new axis limits using the <B>-min</B> and
+<B>-max</B> configuration options. <BR>
+<CODE>.g axis configure x -min 1.0 -max 1.5<BR>
+.g axis configure y -min 12.0 -max 55.15<BR>
+</CODE><P>To zoom interactively, you link the <B>axis configure</B> operations with some
+user interaction (such as pressing the mouse button), using the <B>bind</B> command.
+ To convert between screen and graph coordinates, use the <B>invtransform</B>
+operation. <BR>
+<CODE># Click the button to set a new minimum <BR>
+bind .g &lt;ButtonPress-1&gt; { <BR>
+ %W axis configure x -min [%W axis invtransform x %x]<BR>
+ %W axis configure x -min [%W axis invtransform x %y]<BR>
+}<BR>
+</CODE><P>By default, the limits of the axis are determined from data values. To reset
+back to the default limits, set the <B>-min</B> and <B>-max</B> options to the empty value.
+<BR>
+<CODE># Reset the axes to autoscale again.<BR>
+.g axis configure x -min {} -max {}<BR>
+.g axis configure y -min {} -max {}<BR>
+</CODE><P>By default, the legend is drawn in the right margin. You can change this
+or any legend configuration options using the <B>legend</B> component. <BR>
+<CODE># Configure the legend font, color, and relief<BR>
+.g legend configure -position left -relief raised \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-font fixed -fg blue<BR>
+</CODE><P>To prevent the legend from being displayed, turn on the <B>-hide</B> option. <BR>
+<CODE># Don't display the legend.<BR>
+.g legend configure -hide yes<BR>
+</CODE><P>The <B>graph</B> widget has simple drawing procedures called markers. They can
+be used to highlight or annotate data in the graph. The types of markers
+available are bitmaps, images, polygons, lines, or windows. Markers can
+be used, for example, to mark or brush points. In this example, is a text
+marker that labels the data first point. Markers are created using the
+<B>marker</B> component. <BR>
+<CODE># Create a label for the first data point of "line1".<BR>
+.g marker create text -name first_marker -coords { 0.2 26.18 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-text "start" -anchor se -xoffset -10 -yoffset -10<BR>
+</CODE><P>This creates a text marker named <I>first_marker</I>. It will display the text
+"start" near the coordinates of the first data point. The <B>-anchor</B>, <B>-xoffset</B>,
+and <B>-yoffset</B> options are used to display the marker above and to the left
+of the data point, so that the data point isn't covered by the marker. By
+default, markers are drawn last, on top of data. You can change this with
+the <B>-under</B> option. <BR>
+<CODE># Draw the label before elements are drawn.<BR>
+.g marker configure first_marker -under yes<BR>
+</CODE><P>You can add cross hairs or grid lines using the <B>crosshairs</B> and <B>grid</B> components.
+<BR>
+<CODE># Display both cross hairs and grid lines.<BR>
+.g crosshairs configure -hide no -color red<BR>
+.g grid configure -hide no -dashes { 2 2 }<BR>
+# Set up a binding to reposition the crosshairs.<BR>
+bind .g &lt;Motion&gt; {<BR>
+ .g crosshairs configure -position @%x,%y<BR>
+}<BR>
+</CODE><P>The crosshairs are repositioned as the mouse pointer is moved in the graph.
+ The pointer X-Y coordinates define the center of the crosshairs. <P>
+Finally,
+to get hardcopy of the graph, use the <B>postscript</B> component. <BR>
+<CODE># Print the graph into file "file.ps"<BR>
+.g postscript output file.ps -maxpect yes -decorations no<BR>
+</CODE><P>This generates a file <I>file.ps</I> containing the encapsulated PostScript of
+the graph. The option <B>-maxpect</B> says to scale the plot to the size of the
+page. Turning off the <B>-decorations</B> option denotes that no borders or color
+backgrounds should be drawn (i.e. the background of the margins, legend,
+and plotting area will be white).
+<H2><A NAME="sect6" HREF="#toc6">Graph Operations</A></H2>
+
+<DL>
+
+<DT><I>pathName <B>axis <I>operation
+</I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>bar <I>elemName </I></B></I>?<I>option value</I>?...
+</DT>
+<DD>Creates a new barchart element <I>elemName</I>. It's an error if an element <I>elemName</I>
+already exists. See the manual for <B>barchart</B> for details about what <I>option</I>
+and <I>value</I> pairs are valid. </DD>
+
+<DT><I>pathName <B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries
+or modifies the configuration options of the graph. If <I>option</I> isn't specified,
+a list describing the current options for <I>pathName</I> is returned. If <I>option</I>
+is specified, but not <I>value</I>, then a list describing <I>option</I> is returned.
+If one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair,
+the option <I>option</I> is set to <I>value</I>. The following options are valid. <blockquote></DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color. This includes the margins and legend, but
+not the plotting area. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border
+around the outside edge of the widget. The <B>-relief</B> option determines if
+the border is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-bottommargin <I>pixels</I></B> </DT>
+<DD>If non-zero,
+overrides the computed size of the margin extending below the X-coordinate
+axis. If <I>pixels</I> is <I>0</I>, the automatically computed size is used. The default
+is <I>0</I>. </DD>
+
+<DT><B>-bufferelements <I>boolean</I></B> </DT>
+<DD>Indicates whether an internal pixmap to buffer
+the display of data elements should be used. If <I>boolean</I> is true, data elements
+are drawn to an internal pixmap. This option is especially useful when
+the graph is redrawn frequently while the remains data unchanged (for example,
+moving a marker across the plot). See the <FONT SIZE=-1><B>SPEED TIPS</B></FONT>
+ section. The default
+is <I>1</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is
+<I>crosshair</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of the graph title. The default
+is <I>*-Helvetica-Bold-R-Normal-*-18-180-*</I>. </DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a maximum distance
+to consider when searching for the closest data point (see the element's
+<B>closest</B> operation below). Data points further than <I>pixels</I> away are ignored.
+ The default is <I>0.5i</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget.
+ The default is <I>4i</I>. </DD>
+
+<DT><B>-invertxy <I>boolean</I></B> </DT>
+<DD>Indicates whether the placement X-axis
+and Y-axis should be inverted. If <I>boolean</I> is true, the X and Y axes are
+swapped. The default is <I>0</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the title should
+be justified. This matters only when the title contains more than one line
+of text. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-leftmargin
+<I>pixels</I></B> </DT>
+<DD>If non-zero, overrides the computed size of the margin extending
+ from the left edge of the window to the Y-coordinate axis. If <I>pixels</I> is
+<I>0</I>, the automatically computed size is used. The default is <I>0</I>. </DD>
+
+<DT><B>-plotbackground
+<I>color</I></B> </DT>
+<DD>Specifies the background color of the plotting area. The default
+is <I>white</I>. </DD>
+
+<DT><B>-plotborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the plotting area. The <B>-plotrelief</B> option determines if a border is drawn.
+ The default is <I>2</I>. </DD>
+
+<DT><B>-plotpadx <I>pad</I></B> </DT>
+<DD>Sets the amount of padding to be added to
+the left and right sides of the plotting area. <I>Pad</I> can be a list of one
+or two screen distances. If <I>pad</I> has two elements, the left side of the
+plotting area entry is padded by the first distance and the right side
+by the second. If <I>pad</I> is just one distance, both the left and right sides
+are padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotpady <I>pad</I></B> </DT>
+<DD>Sets the amount of padding
+to be added to the top and bottom of the plotting area. <I>Pad</I> can be a list
+of one or two screen distances. If <I>pad</I> has two elements, the top of the
+plotting area is padded by the first distance and the bottom by the second.
+ If <I>pad</I> is just one distance, both the top and bottom are padded evenly.
+ The default is <I>8</I>. </DD>
+
+<DT><B>-plotrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect for the plotting
+area. <I>Relief</I> specifies how the interior of the plotting area should appear
+relative to rest of the graph; for example, <I>raised</I> means the plot should
+appear to protrude from the graph, relative to the surface of the graph.
+ The default is <I>sunken</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect for the graph
+widget. <I>Relief</I> specifies how the graph should appear relative to widget
+it is packed into; for example, <I>raised</I> means the graph should appear to
+protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-rightmargin <I>pixels</I></B> </DT>
+<DD>If non-zero, overrides
+the computed size of the margin extending from the plotting area to the
+right edge of the window. By default, the legend is drawn in this margin.
+ If <I>pixels</I> is <I>0</I>, the automatically computed size is used. The default
+is <I>0</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides information used when moving the focus from
+window to window via keyboard traversal (e.g., Tab and Shift-Tab). If <I>focus</I>
+is <I>0</I>, this means that this window should be skipped entirely during keyboard
+traversal. <I>1</I> means that the this window should always receive the input
+focus. An empty value means that the traversal scripts make the decision
+whether to focus on the window. The default is <I>""</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies
+a tiled background for the widget. If <I>image</I> isn't <I>""</I>, the background is
+tiled using <I>image</I>. Otherwise, the normal background color is drawn (see
+the <B>-background</B> option). <I>Image</I> must be an image created using the Tk <B>image</B>
+command. The default is <I>""</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title to <I>text</I>. If <I>text</I> is
+<I>""</I>, no title will be displayed. </DD>
+
+<DT><B>-topmargin <I>pixels</I></B> </DT>
+<DD>If non-zero, overrides
+the computed size of the margin above the x2 axis. If <I>pixels</I> is <I>0</I>, the
+automatically computed size is used. The default is <I>0</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies
+the requested width of the widget. The default is <I>5i</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>crosshairs
+<I>operation </I></B></I>?<I>arg</I>? </DT>
+<DD>See the <FONT SIZE=-1><B>CROSSHAIRS COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>element
+<I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>ELEMENT COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>extents <I>item</I></B></I>
+ </DT>
+<DD>Returns the size of a particular item in the graph. <I>Item</I> must be either
+<I>leftmargin</I>, <I>rightmargin</I>, <I>topmargin</I>, <I>bottommargin</I>, <I>plotwidth</I>, or <I>plotheight</I>.
+</DD>
+
+<DT><I>pathName <B>grid <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>GRID COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName
+<B>invtransform <I>winX winY</I></B></I> </DT>
+<DD>Performs an inverse coordinate transformation,
+mapping window coordinates back to graph coordinates, using the standard
+X-axis and Y-axis. Returns a list of containing the X-Y graph coordinates. </DD>
+
+<DT><I>pathName
+<B>inside <I>x y</I></B></I> </DT>
+<DD>Returns <I>1</I> is the designated screen coordinate (<I>x</I> and <I>y</I>) is inside
+the plotting area and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>legend <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the
+ <FONT SIZE=-1><B>LEGEND COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>line<B> operation arg</B></B></I>... </DT>
+<DD>The operation is
+the same as <B>element</B>. </DD>
+
+<DT><I>pathName <B>marker <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>MARKER COMPONENTS</B></FONT>
+
+ section. </DD>
+
+<DT><I>pathName</I> <B>metafile</B> ?<I>fileName</I>? </DT>
+<DD><I>This operation is for Window platforms
+only</I>. Creates a Windows enhanced metafile of the graph. If present, <I>fileName</I>
+is the file name of the new metafile. Otherwise, the metafile is automatically
+added to the clipboard. </DD>
+
+<DT><I>pathName <B>postscript <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>POSTSCRIPT
+COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>snap <I>photoName</I></B></I> </DT>
+<DD>Takes a snapshot of the graph
+and stores the contents in the photo image <I>photoName</I>. <I>PhotoName</I> is the
+name of a Tk photo image that must already exist. </DD>
+
+<DT><I>pathName <B>transform <I>x y</I></B></I>
+ </DT>
+<DD>Performs a coordinate transformation, mapping graph coordinates to window
+coordinates, using the standard X-axis and Y-axis. Returns a list containing
+the X-Y screen coordinates. </DD>
+
+<DT><I>pathName <B>xaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>x2axis
+<I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>yaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>y2axis <I>operation</I></B></I>
+?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+</DL>
+
+<H2><A NAME="sect7" HREF="#toc7">Graph Components</A></H2>
+A graph is composed
+of several components: coordinate axes, data elements, legend, grid, cross
+hairs, postscript, and annotation markers. Instead of one big set of configuration
+options and operations, the graph is partitioned, where each component
+has its own configuration options and operations that specifically control
+that aspect or part of the graph.
+<H3><A NAME="sect8" HREF="#toc8">Axis Components</A></H3>
+Four coordinate axes are
+automatically created: two X-coordinate axes (<I>x</I> and <I>x2</I>) and two Y-coordinate
+axes (<I>y</I>, and <I>y2</I>). By default, the axis <I>x</I> is located in the bottom margin,
+<I>y</I> in the left margin, <I>x2</I> in the top margin, and <I>y2</I> in the right margin.
+<P>
+An axis consists of the axis line, title, major and minor ticks, and tick
+labels. Major ticks are drawn at uniform intervals along the axis. Each
+tick is labeled with its coordinate value. Minor ticks are drawn at uniform
+intervals within major ticks. <P>
+The range of the axis controls what region
+of data is plotted. Data points outside the minimum and maximum limits of
+the axis are not plotted. By default, the minimum and maximum limits are
+determined from the data, but you can reset either limit. <P>
+You can have several
+axes. To create an axis, invoke the axis component and its create operation.
+<BR>
+<CODE># Create a new axis called "tempAxis"<BR>
+.g axis create tempAxis<BR>
+</CODE><P>You map data elements to an axis using the element's -mapy and -mapx configuration
+options. They specify the coordinate axes an element is mapped onto. <BR>
+<CODE># Now map the tempAxis data to this axis.<BR>
+.g element create "e1" -xdata $x -ydata $y -mapy tempAxis<BR>
+</CODE><P>Any number of axes can be displayed simultaneously. They are drawn in the
+margins surrounding the plotting area. The default axes <I>x</I> and <I>y</I> are drawn
+in the bottom and left margins. The axes <I>x2</I> and <I>y2</I> are drawn in top and
+right margins. By default, only <I>x</I> and <I>y</I> are shown. Note that the axes can
+have different scales. <P>
+To display a different axis or more than one axis,
+you invoke one of the following components: <B>xaxis</B>, <B>yaxis</B>, <B>x2axis</B>, and <B>y2axis</B>.
+ Each component has a <B>use</B> operation that designates the axis (or axes)
+to be drawn in that corresponding margin: <B>xaxis</B> in the bottom, <B>yaxis</B> in
+the left, <B>x2axis</B> in the top, and <B>y2axis</B> in the right. <BR>
+<CODE># Display the axis tempAxis in the left margin.<BR>
+.g yaxis use tempAxis<BR>
+</CODE><P>The <B>use</B> operation takes a list of axis names as its last argument. This
+is the list of axes to be drawn in this margin. <P>
+You can configure axes in
+many ways. The axis scale can be linear or logarithmic. The values along
+the axis can either monotonically increase or decrease. If you need custom
+tick labels, you can specify a Tcl procedure to format the label any way
+you wish. You can control how ticks are drawn, by changing the major tick
+interval or the number of minor ticks. You can define non-uniform tick intervals,
+such as for time-series plots. <P>
+
+<DL>
+
+<DT><I>pathName <B>axis bind <I>tagName</I></B></I> ?<I>sequence</I>? ?<I>command</I>?
+ </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the event sequence
+given by <I>sequence</I> occurs for an axis with this tag, <I>command</I> will be invoked.
+ The syntax is similar to the <B>bind</B> command except that it operates on
+graph axes, rather than widgets. See the <B>bind</B> manual entry for complete
+details on <I>sequence</I> and the substitutions performed on <I>command</I> before
+invoking it. <P>
+If all arguments are specified then a new binding is created,
+replacing any existing binding for the same <I>sequence</I> and <I>tagName</I>. If the
+first character of <I>command</I> is <I>+</I> then <I>command</I> augments an existing binding
+rather than replacing it. If no <I>command</I> argument is provided then the command
+currently associated with <I>tagName</I> and <I>sequence</I> (it's an error occurs if
+there's no such binding) is returned. If both <I>command</I> and <I>sequence</I> are
+missing then a list of all the event sequences for which bindings have
+been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>axis <B>cget <I>axisName <I>option</I></I></B></B></I> </DT>
+<DD>Returns the
+current value of the option given by <I>option</I> for <I>axisName</I>. <I>Option</I> may be
+any option described below for the axis <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>axis
+<B>configure <I>axisName </I></B></B></I>?<I>axisName</I>?... ?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration
+options of <I>axisName</I>. Several axes can be changed. If <I>option</I> isn't specified,
+a list describing all the current options for <I>axisName</I> is returned. If
+<I>option</I> is specified, but not <I>value</I>, then a list describing <I>option</I> is returned.
+ If one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair,
+the axis option <I>option</I> is set to <I>value</I>. The following options are valid
+for axes. <blockquote></DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for the axis. <I>TagList</I>
+is a list of binding tag names. The tags and their order will determine
+how events for axes are handled. Each tag in the list matching the current
+event sequence will have its Tcl command executed. Implicitly the name
+of the element is always the first tag in the list. The default value is
+<I>all</I>. </DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the axis and tick labels. The default
+is <I>black</I>. </DD>
+
+<DT><B>-command <I>prefix</I></B> </DT>
+<DD>Specifies a Tcl command to be invoked when formatting
+the axis tick labels. <I>Prefix</I> is a string containing the name of a Tcl proc
+and any extra arguments for the procedure. This command is invoked for
+each major tick on the axis. Two additional arguments are passed to the
+procedure: the pathname of the widget and the current the numeric value
+of the tick. The procedure returns the formatted tick label. If <I>""</I> is returned,
+no label will appear next to the tick. You can get the standard tick labels
+again by setting <I>prefix</I> to <I>""</I>. The default is <I>""</I>. <P>
+Please note that this
+procedure is invoked while the graph is redrawn. You may query configuration
+options. But do not them, because this can have unexpected results. </DD>
+
+<DT><B>-descending
+<I>boolean</I></B> </DT>
+<DD>Indicates whether the values along the axis are monotonically
+increasing or decreasing. If <I>boolean</I> is true, the axis values will be decreasing.
+ The default is <I>0</I>. </DD>
+
+<DT><B>-hide <I>string</I></B> </DT>
+<DD>Indicates if the axis and all the elements
+mapped to it will be displayed. The valid values for <I>string</I> are shown
+below. The default value is <I>0</I>. <blockquote></DD>
+
+<DT><I>false</I> </DT>
+<DD>The axis and its data elements are
+displayed. </DD>
+
+<DT><I>true</I> </DT>
+<DD>The axis is hidden, but the data elements mapped to it are
+displayed. </DD>
+
+<DT><I>all</I> </DT>
+<DD>The axis and its data elements are hidden. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-justify <I>justify</I></B>
+</DT>
+<DD>Specifies how the axis title should be justified. This matters only when
+the axis title contains more than one line of text. <I>Justify</I> must be <I>left</I>,
+<I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-limits <I>formatStr</I></B> </DT>
+<DD>Specifies a printf-like
+description to format the minimum and maximum limits of the axis. The limits
+are displayed at the top/bottom or left/right sides of the plotting area.
+ <I>FormatStr</I> is a list of one or two format descriptions. If one description
+is supplied, both the minimum and maximum limits are formatted in the same
+way. If two, the first designates the format for the minimum limit, the
+second for the maximum. If <I>""</I> is given as either description, then the
+that limit will not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets
+the width of the axis and tick lines. The default is <I>1</I> pixel. </DD>
+
+<DT><B>-logscale <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the scale of the axis is logarithmic or linear. If <I>boolean</I>
+is true, the axis is logarithmic. The default scale is linear. </DD>
+
+<DT><B>-loose <I>boolean</I></B>
+</DT>
+<DD>Indicates whether the limits of the axis should fit the data points tightly,
+at the outermost data points, or loosely, at the outer tick intervals. If
+the axis limit is set with the -min or -max option, the axes are displayed
+tightly. If <I>boolean</I> is true, the axis range is "loose". The default is <I>0</I>.
+</DD>
+
+<DT><B>-majorticks <I>majorList</I></B> </DT>
+<DD>Specifies where to display major axis ticks. You can
+use this option to display ticks at non-uniform intervals. <I>MajorList</I> is
+a list of axis coordinates designating the location of major ticks. No
+minor ticks are drawn. If <I>majorList</I> is <I>""</I>, major ticks will be automatically
+computed. The default is <I>""</I>. </DD>
+
+<DT><B>-max <I>value</I></B> </DT>
+<DD>Sets the maximum limit of <I>axisName</I>.
+ Any data point greater than <I>value</I> is not displayed. If <I>value</I> is <I>""</I>,
+the maximum limit is calculated using the largest data value. The default
+is <I>""</I>. </DD>
+
+<DT><B>-min <I>value</I></B> </DT>
+<DD>Sets the minimum limit of <I>axisName</I>. Any data point less
+than <I>value</I> is not displayed. If <I>value</I> is <I>""</I>, the minimum limit is calculated
+using the smallest data value. The default is <I>""</I>. </DD>
+
+<DT><B>-minorticks <I>minorList</I></B> </DT>
+<DD>Specifies
+where to display minor axis ticks. You can use this option to display minor
+ticks at non-uniform intervals. <I>MinorList</I> is a list of real values, ranging
+from 0.0 to 1.0, designating the placement of a minor tick. No minor ticks
+are drawn if the <B>-majortick</B> option is also set. If <I>minorList</I> is <I>""</I>, minor
+ticks will be automatically computed. The default is <I>""</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies
+the how many degrees to rotate the axis tick labels. <I>Theta</I> is a real value
+representing the number of degrees to rotate the tick labels. The default
+is <I>0.0</I> degrees. </DD>
+
+<DT><B>-showticks <I>boolean</I></B> </DT>
+<DD>Indicates whether axis ticks should be
+drawn. If <I>boolean</I> is true, ticks are drawn. If false, only the axis line
+is drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-stepsize <I>value</I></B> </DT>
+<DD>Specifies the interval between
+major axis ticks. If <I>value</I> isn't a valid interval (must be less than the
+axis range), the request is ignored and the step size is automatically
+calculated. </DD>
+
+<DT><B>-subdivisions <I>number</I></B> </DT>
+<DD>Indicates how many minor axis ticks are
+to be drawn. For example, if <I>number</I> is two, only one minor tick is drawn.
+ If <I>number</I> is one, no minor ticks are displayed. The default is <I>2</I>. </DD>
+
+<DT><B>-tickfont
+<I>fontName</I></B> </DT>
+<DD>Specifies the font for axis tick labels. The default is <I>*-Courier-Bold-R-Normal-*-100-*</I>.
+</DD>
+
+<DT><B>-ticklength <I>pixels</I></B> </DT>
+<DD>Sets the length of major and minor ticks (minor ticks
+are half the length of major ticks). If <I>pixels</I> is less than zero, the axis
+will be inverted with ticks drawn pointing towards the plot. The default
+is <I>0.1i</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title of the axis. If <I>text</I> is <I>""</I>, no axis title
+will be displayed. </DD>
+
+<DT><B>-titlecolor <I>color</I></B> </DT>
+<DD>Sets the color of the axis title. The
+default is <I>black</I>. </DD>
+
+<DT><B>-titlefont <I>fontName</I></B> </DT>
+<DD>Specifies the font for axis title.
+The default is <I>*-Helvetica-Bold-R-Normal-*-14-140-*</I>. </DD>
+</DL>
+<P>
+Axis configuration options
+may be also be set by the <B>option</B> command. The resource class is <I>Axis</I>. The
+resource names are the names of the axes (such as <I>x</I> or <I>x2</I>). <BR>
+<CODE>option add *Graph.Axis.Color blue<BR>
+option add *Graph.x.LogScale true<BR>
+option add *Graph.x2.LogScale false<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>axis <B>create <I>axisName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new axis by the
+name <I>axisName</I>. No axis by the same name can already exist. <I>Option</I> and <I>value</I>
+are described in above in the axis <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>axis <B>delete
+</B></B></I>?<I>axisName</I>?... </DT>
+<DD>Deletes the named axes. An axis is not really deleted until it
+is not longer in use, so it's safe to delete axes mapped to elements. </DD>
+
+<DT><I>pathName
+<B>axis invtransform <I>axisName value</I></B></I> </DT>
+<DD>Performs the inverse transformation, changing
+the screen coordinate <I>value</I> to a graph coordinate, mapping the value mapped
+to <I>axisName</I>. Returns the graph coordinate. </DD>
+
+<DT><I>pathName <B>axis limits <I>axisName</I></B></I>
+</DT>
+<DD>Returns a list of the minimum and maximum limits for <I>axisName</I>. The order
+of the list is <I>min max</I>. </DD>
+
+<DT><I>pathName <B>axis names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of
+axes matching zero or more patterns. If no <I>pattern</I> argument is give, the
+names of all axes are returned. </DD>
+
+<DT><I>pathName <B>axis transform <I>axisName value</I></B></I> </DT>
+<DD>Transforms
+the coordinate <I>value</I> to a screen coordinate by mapping the it to <I>axisName</I>.
+ Returns the transformed screen coordinate. </DD>
+</DL>
+<P>
+The default axes are <I>x</I>, <I>y</I>, <I>x2</I>,
+and <I>y2</I>. But you can display more than four axes simultaneously. You can
+also swap in a different axis with <B>use</B> operation of the special axis components:
+<B>xaxis</B>, <B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B>. <BR>
+<CODE>.g create axis temp<BR>
+.g create axis time<BR>
+...<BR>
+.g xaxis use temp<BR>
+.g yaxis use time<BR>
+</CODE><P>Only the axes specified for use are displayed on the screen. <P>
+The <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B> components operate on an axis location rather
+than a specific axis like the more general <B>axis</B> component does. They implicitly
+control the axis that is currently using to that location. By default,
+<B>xaxis</B> uses the <I>x</I> axis, <B>yaxis</B> uses <I>y</I>, <B>x2axis</B> uses <I>x2</I>, and <B>y2axis</B> uses <I>y2</I>.
+ When more than one axis is displayed in a margin, it represents the first
+axis displayed. <P>
+The following operations are available for axes. They mirror
+exactly the operations of the <B>axis</B> component. The <I>axis</I> argument must be
+<B>xaxis</B>, <B>x2axis</B>, <B>yaxis</B>, or <B>y2axis</B>. This feature is deprecated since more
+than one axis can now be used a margin. You should only use the <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B> components with the <B>use</B> operation. For all other
+operations, use the general <B>axis</B> component instead.
+<DL>
+
+<DT><I>pathName <I>axis <B>cget <I>option</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>configure </B></I></I>?<I>option value</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> invtransform <I>value</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>limits</B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> transform <I>value</I></B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> use </B></I></I>?<I>axisName</I>?
+ </DT>
+<DD>Designates the axis <I>axisName</I> is to be displayed at this location. <I>AxisName</I>
+can not be already in use at another location. This command returns the
+name of the axis currently using this location. </DD>
+</DL>
+
+<H3><A NAME="sect9" HREF="#toc9">Crosshairs Component</A></H3>
+Cross
+hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position the
+mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives. This
+means that they can be quickly drawn and erased without redrawing the entire
+graph. <P>
+The following operations are available for cross hairs:
+<DL>
+
+<DT><I>pathName
+<B>crosshairs cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the cross hairs configuration
+option given by <I>option</I>. <I>Option</I> may be any option described below for the
+cross hairs <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>crosshairs configure </B></I>?<I>option value</I>?...
+ </DT>
+<DD>Queries or modifies the configuration options of the cross hairs. If
+<I>option</I> isn't specified, a list describing all the current options for the
+cross hairs is returned. If <I>option</I> is specified, but not <I>value</I>, then a
+list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs
+are specified, then for each pair, the cross hairs option <I>option</I> is set
+to <I>value</I>. The following options are available for cross hairs. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B>
+ </DT>
+<DD>Sets the color of the cross hairs. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the cross hairs. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the cross
+hair lines. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the
+cross hairs will be solid lines. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether cross hairs
+are drawn. If <I>boolean</I> is true, cross hairs are not drawn. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Set the width of the cross hair lines. The default
+is <I>1</I>. </DD>
+
+<DT><B>-position <I>pos</I></B> </DT>
+<DD>Specifies the screen position where the cross hairs
+intersect. <I>Pos</I> must be in the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window
+coordinates of the intersection. </DD>
+</DL>
+<P>
+Cross hairs configuration options may be
+also be set by the <B>option</B> command. The resource name and class are <I>crosshairs</I>
+and <I>Crosshairs</I> respectively. <BR>
+<CODE>option add *Graph.Crosshairs.LineWidth 2<BR>
+option add *Graph.Crosshairs.Color red<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>crosshairs off</B></I> </DT>
+<DD>Turns off the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs
+on</B></I> </DT>
+<DD>Turns on the display of the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs toggle</B></I>
+</DT>
+<DD>Toggles the current state of the cross hairs, alternately mapping and unmapping
+the cross hairs. </DD>
+</DL>
+
+<H3><A NAME="sect10" HREF="#toc10">Element Components</A></H3>
+A data element represents a set of data.
+ It contains x and y vectors containing the coordinates of the data points.
+ Elements can be displayed with a symbol at each data point and lines connecting
+the points. Elements also control the appearance of the data, such as the
+symbol type, line width, color etc. <P>
+When new data elements are created,
+they are automatically added to a list of displayed elements. The display
+list controls what elements are drawn and in what order. <P>
+The following
+operations are available for elements.
+<DL>
+
+<DT><I>pathName <B>element activate <I>elemName
+</I></B></I>?<I>index</I>?... </DT>
+<DD>Specifies the data points of element <I>elemName</I> to be drawn using
+active foreground and background colors. <I>ElemName</I> is the name of the element
+and <I>index</I> is a number representing the index of the data point. If no indices
+are present then all data points become active. </DD>
+
+<DT><I>pathName <B>element bind <I>tagName</I></B></I>
+?<I>sequence</I>? ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever
+the event sequence given by <I>sequence</I> occurs for an element with this tag,
+<I>command</I> will be invoked. The syntax is similar to the <B>bind</B> command except
+that it operates on graph elements, rather than widgets. See the <B>bind</B> manual
+entry for complete details on <I>sequence</I> and the substitutions performed
+on <I>command</I> before invoking it. <P>
+If all arguments are specified then a
+new binding is created, replacing any existing binding for the same <I>sequence</I>
+and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I> augments
+an existing binding rather than replacing it. If no <I>command</I> argument is
+provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>element cget <I>elemName
+<I>option</I></I></B></I> </DT>
+<DD>Returns the current value of the element configuration option given
+by <I>option</I>. <I>Option</I> may be any of the options described below for the element
+<B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>element closest <I>x y</I></B></I> <I>varName</I> ?<I>option value</I>?...
+?<I>elemName</I>?... </DT>
+<DD>Finds the data point closest to the window coordinates <I>x</I> and
+<I>y</I> in the element <I>elemName</I>. <I>ElemName</I> is the name of an element, that must
+not be hidden. If no elements are specified, then all visible elements
+are searched. It returns via the array variable <I>varName</I> the name of the
+closest element, the index of its closest point, and the graph coordinates
+of the point. Returns <I>0</I>, if no data point within the threshold distance
+can be found, otherwise <I>1</I> is returned. The following <I>option</I>-<I>value</I> pairs
+are available. <blockquote></DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a threshold distance where selected
+data points are ignored. <I>Pixels</I> is a valid screen distance, such as <I>2</I> or
+<I>1.2i</I>. If this option isn't specified, then it defaults to the value of the
+graph's <B>-halo</B> option. </DD>
+
+<DT><B>-interpolate <I>string</I></B> </DT>
+<DD>Indicates whether to consider projections
+that lie along the line segments connecting data points when searching
+for the closest point. The default value is <I>0</I>. The values for <I>string</I> are
+described below. <blockquote></DD>
+
+<DT><I>no</I> </DT>
+<DD>Search only for the closest data point. </DD>
+
+<DT><I>yes</I> </DT>
+<DD>Search includes
+projections that lie along the line segments connecting the data points.
+ </DD>
+
+<DT><I>x</I> </DT>
+<DD>Search includes vertical projections from the given X-coordinate. </DD>
+
+<DT><I>y</I>
+</DT>
+<DD>Search includes horizontal projections from the given Y-coordinate. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>element configure <I>elemName </I></B></I>?<I>elemName</I>... ?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options for elements. Several elements can be modified
+at the same time. If <I>option</I> isn't specified, a list describing all the current
+options for <I>elemName</I> is returned. If <I>option</I> is specified, but not <I>value</I>,
+then a list describing the option <I>option</I> is returned. If one or more <I>option</I>
+and <I>value</I> pairs are specified, then for each pair, the element option <I>option</I>
+is set to <I>value</I>. The following options are valid for elements. <blockquote></DD>
+
+<DT><B>-activepen
+<I>penName</I></B> </DT>
+<DD>Specifies pen to use to draw active element. If <I>penName</I> is <I>""</I>,
+no active elements will be drawn. The default is <I>activeLine</I>. </DD>
+
+<DT><B>-bindtags <I>tagList</I></B>
+</DT>
+<DD>Specifies the binding tags for the element. <I>TagList</I> is a list of binding
+tag names. The tags and their order will determine how events are handled
+for elements. Each tag in the list matching the current event sequence
+will have its Tcl command executed. Implicitly the name of the element
+is always the first tag in the list. The default value is <I>all</I>. </DD>
+
+<DT><B>-color <I>color</I></B>
+ </DT>
+<DD>Sets the color of the traces connecting the data points. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of element line. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the element
+line. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the lines
+will be solid. </DD>
+
+<DT><B>-data <I>coordList</I></B> </DT>
+<DD>Specifies the X-Y coordinates of the data.
+<I>CoordList</I> is a list of numeric expressions representing the X-Y coordinate
+pairs of each data point. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the interior color of symbols.
+ If <I>color</I> is <I>""</I>, then the interior of the symbol is transparent. If <I>color</I>
+is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option. The default
+is <I>defcolor</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the element is displayed. The
+default is <I>no</I>. </DD>
+
+<DT><B>-label <I>text</I></B> </DT>
+<DD>Sets the element's label in the legend. If <I>text</I>
+is <I>""</I>, the element will have no entry in the legend. The default label is
+the element's name. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the connecting lines
+between data points. If <I>pixels</I> is <I>0</I>, no connecting lines will be drawn
+between symbols. The default is <I>0</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Selects the X-axis to map the
+element's X-coordinates onto. <I>XAxis</I> must be the name of an axis. The default
+is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Selects the Y-axis to map the element's Y-coordinates onto.
+<I>YAxis</I> must be the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-offdash <I>color</I></B> </DT>
+<DD>Sets the
+color of the stripes when traces are dashed (see the <B>-dashes</B> option). If
+<I>color</I> is <I>""</I>, then the "off" pixels will represent gaps instead of stripes.
+ If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color or the outline around
+each symbol. If <I>color</I> is <I>""</I>, then no outline is drawn. If <I>color</I> is <I>defcolor</I>,
+then the color will be the same as the <B>-color</B> option. The default is <I>defcolor</I>.
+</DD>
+
+<DT><B>-outlinewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the outline bordering each symbol.
+ If <I>pixels</I> is <I>0</I>, no outline will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-pixels <I>pixels</I></B>
+</DT>
+<DD>Sets the size of symbols. If <I>pixels</I> is <I>0</I>, no symbols will be drawn. The
+default is <I>0.125i</I>. </DD>
+
+<DT><B>-scalesymbols <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, the size of
+the symbols drawn for <I>elemName</I> will change with scale of the X-axis and
+Y-axis. At the time this option is set, the current ranges of the axes are
+saved as the normalized scales (i.e scale factor is 1.0) and the element
+is drawn at its designated size (see the <B>-pixels</B> option). As the scale of
+the axes change, the symbol will be scaled according to the smaller of
+the X-axis and Y-axis scales. If <I>boolean</I> is false, the element's symbols are
+drawn at the designated size, regardless of axis scales. The default is
+<I>0</I>. </DD>
+
+<DT><B>-smooth <I>smooth</I></B> </DT>
+<DD>Specifies how connecting line segments are drawn between
+data points. <I>Smooth</I> can be either <I>linear</I>, <I>step</I>, <I>natural</I>, or <I>quadratic</I>. If
+<I>smooth</I> is <I>linear</I>, a single line segment is drawn, connecting both data
+points. When <I>smooth</I> is <I>step</I>, two line segments are drawn. The first is a
+horizontal line segment that steps the next X-coordinate. The second is
+a vertical line, moving to the next Y-coordinate. Both <I>natural</I> and <I>quadratic</I>
+generate multiple segments between data points. If <I>natural</I>, the segments
+are generated using a cubic spline. If <I>quadratic</I>, a quadratic spline is
+used. The default is <I>linear</I>. </DD>
+
+<DT><B>-styles <I>styleList</I></B> </DT>
+<DD>Specifies what pen to use
+based on the range of weights given. <I>StyleList</I> is a list of style specifications.
+Each style specification, in turn, is a list consisting of a pen name,
+and optionally a minimum and maximum range. Data points whose weight (see
+the <B>-weight</B> option) falls in this range, are drawn with this pen. If no
+range is specified it defaults to the index of the pen in the list. Note
+that this affects only symbol attributes. Line attributes, such as line
+width, dashes, etc. are ignored. </DD>
+
+<DT><B>-symbol <I>symbol</I></B> </DT>
+<DD>Specifies the symbol for
+data points. <I>Symbol</I> can be either <I>square</I>, <I>circle</I>, <I>diamond</I>, <I>plus</I>, <I>cross</I>,
+<I>splus</I>, <I>scross</I>, <I>triangle</I>, <I>""</I> (where no symbol is drawn), or a bitmap. Bitmaps
+are specified as "<I>source</I> ?<I>mask</I>?", where <I>source</I> is the name of the bitmap,
+and <I>mask</I> is the bitmap's optional mask. The default is <I>circle</I>. </DD>
+
+<DT><B>-trace <I>direction</I></B>
+ </DT>
+<DD>Indicates whether connecting lines between data points (whose X-coordinate
+values are either increasing or decreasing) are drawn. <I>Direction</I> must
+be <I>increasing</I>, <I>decreasing</I>, or <I>both</I>. For example, if <I>direction</I> is <I>increasing</I>,
+connecting lines will be drawn only between those data points where X-coordinate
+values are monotonically increasing. If <I>direction</I> is <I>both</I>, connecting lines
+will be draw between all data points. The default is <I>both</I>. </DD>
+
+<DT><B>-weights <I>wVec</I></B>
+ </DT>
+<DD>Specifies the weights of the individual data points. This, with the list
+pen styles (see the <B>-styles</B> option), controls how data points are drawn.
+ <I>WVec</I> is the name of a BLT vector or a list of numeric expressions representing
+the weights for each data point. </DD>
+
+<DT><B>-xdata <I>xVec</I></B> </DT>
+<DD>Specifies the X-coordinates
+of the data. <I>XVec</I> is the name of a BLT vector or a list of numeric expressions.
+</DD>
+
+<DT><B>-ydata <I>yVec</I></B> </DT>
+<DD>Specifies the Y-coordinates of the data. <I>YVec</I> is the name of
+a BLT vector or a list of numeric expressions. </DD>
+</DL>
+<P>
+Element configuration options
+may also be set by the <B>option</B> command. The resource class is <I>Element</I>. The
+resource name is the name of the element. <BR>
+<CODE>option add *Graph.Element.symbol line<BR>
+option add *Graph.e1.symbol line<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>element create <I>elemName</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a new element <I>elemName</I>.
+ It's an error is an element <I>elemName</I> already exists. If additional arguments
+are present, they specify options valid for the element <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>element deactivate <I>elemName</I></B></I> ?<I>elemName</I>?... </DT>
+<DD>Deactivates all the elements
+matching <I>pattern</I>. Elements whose names match any of the patterns given are
+redrawn using their normal colors. </DD>
+
+<DT><I>pathName <B>element delete</B></I> ?<I>elemName</I>?... </DT>
+<DD>Deletes
+all the named elements. The graph is automatically redrawn. </DD>
+
+<DT><I>pathName <B>element
+exists <I>elemName</I></B></I> </DT>
+<DD>Returns <I>1</I> if an element <I>elemName</I> currently exists and <I>0</I>
+otherwise. </DD>
+
+<DT><I>pathName <B>element names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns the elements matching
+one or more pattern. If no <I>pattern</I> is given, the names of all elements
+is returned. </DD>
+
+<DT><I>pathName <B>element show</B></I> ?<I>nameList</I>? </DT>
+<DD>Queries or modifies the
+element display list. The element display list designates the elements
+drawn and in what order. <I>NameList</I> is a list of elements to be displayed
+in the order they are named. If there is no <I>nameList</I> argument, the current
+display list is returned. </DD>
+
+<DT><I>pathName <B>element type</B></I> <I>elemName</I> </DT>
+<DD>Returns the type
+of <I>elemName</I>. If the element is a bar element, the commands returns the
+string <I>"bar"</I>, otherwise it returns <I>"line"</I>. </DD>
+</DL>
+
+<H3><A NAME="sect11" HREF="#toc11"></CODE><P>Grid Component</A></H3>
+Grid lines extend
+from the major and minor ticks of each axis horizontally or vertically
+across the plotting area. The following operations are available for grid
+lines.
+<DL>
+
+<DT><I>pathName <B>grid cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the grid line
+configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the grid <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>grid configure</B></I> ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options for grid lines. If
+<I>option</I> isn't specified, a list describing all the current grid options for
+<I>pathName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a list
+describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are
+specified, then for each pair, the grid line option <I>option</I> is set to <I>value</I>.
+ The following options are valid for grid lines. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color
+of the grid lines. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style
+of the grid lines. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the grid lines. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the grid will be solid lines.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid should be drawn. If <I>boolean</I> is true,
+grid lines are not shown. The default is <I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of grid lines. The default width is <I>1</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis to
+display grid lines. <I>XAxis</I> must be the name of an axis or <I>""</I> for no grid
+lines. The default is <I>""</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to display grid
+lines. <I>YAxis</I> must be the name of an axis or <I>""</I> for no grid lines. The default
+is <I>y</I>. </DD>
+
+<DT><B>-minor <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid lines should be drawn for
+minor ticks. If <I>boolean</I> is true, the lines will appear at minor tick intervals.
+ The default is <I>1</I>. </DD>
+</DL>
+<P>
+Grid configuration options may also be set by the <B>option</B>
+command. The resource name and class are <I>grid</I> and <I>Grid</I> respectively. <BR>
+<CODE>option add *Graph.grid.LineWidth 2<BR>
+option add *Graph.Grid.Color black<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>grid off</B></I> </DT>
+<DD>Turns off the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid on</B></I>
+</DT>
+<DD>Turns on the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid toggle</B></I> </DT>
+<DD>Toggles the display
+of the grid. </DD>
+</DL>
+
+<H3><A NAME="sect12" HREF="#toc12">Legend Component</A></H3>
+The legend displays a list of the data elements.
+ Each entry consists of the element's symbol and label. The legend can appear
+in any margin (the default location is in the right margin). It can also
+be positioned anywhere within the plotting area. <P>
+The following operations
+are valid for the legend.
+<DL>
+
+<DT><I>pathName <B>legend activate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend
+entries to be drawn using the active legend colors and relief. All entries
+whose element names match <I>pattern</I> are selected. To be selected, the element
+name must match only one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend bind <I>tagName</I></B></I> ?<I>sequence</I>?
+ ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the event
+sequence given by <I>sequence</I> occurs for a legend entry with this tag, <I>command</I>
+will be invoked. Implicitly the element names in the entry are tags. The
+syntax is similar to the <B>bind</B> command except that it operates on legend
+entries, rather than widgets. See the <B>bind</B> manual entry for complete details
+on <I>sequence</I> and the substitutions performed on <I>command</I> before invoking
+it. <P>
+If all arguments are specified then a new binding is created, replacing
+ any existing binding for the same <I>sequence</I> and <I>tagName</I>. If the first character
+of <I>command</I> is <I>+</I> then <I>command</I> augments an existing binding rather than
+replacing it. If no <I>command</I> argument is provided then the command currently
+associated with <I>tagName</I> and <I>sequence</I> (it's an error occurs if there's no
+such binding) is returned. If both <I>command</I> and <I>sequence</I> are missing then
+a list of all the event sequences for which bindings have been defined
+for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>legend cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of a
+legend configuration option. <I>Option</I> may be any option described below in
+the legend <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>legend configure </B></I>?<I>option value</I>?...
+</DT>
+<DD>Queries or modifies the configuration options for the legend. If <I>option</I>
+isn't specified, a list describing the current legend options for <I>pathName</I>
+is returned. If <I>option</I> is specified, but not <I>value</I>, then a list describing
+<I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are specified,
+then for each pair, the legend option <I>option</I> is set to <I>value</I>. The following
+options are valid for the legend. <blockquote></DD>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets the background
+color for active legend entries. All legend entries marked active (see
+the legend <B>activate</B> operation) are drawn using this background color. </DD>
+
+<DT><B>-activeborderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the outside edge of the active
+legend entries. The default is <I>2</I>. </DD>
+
+<DT><B>-activeforeground <I>color</I></B> </DT>
+<DD>Sets the foreground
+color for active legend entries. All legend entries marked as active (see
+the legend <B>activate</B> operation) are drawn using this foreground color. </DD>
+
+<DT><B>-activerelief
+<I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect desired for active legend entries. <I>Relief</I>
+denotes how the interior of the entry should appear relative to the legend;
+for example, <I>raised</I> means the entry should appear to protrude from the
+legend, relative to the surface of the legend. The default is <I>flat</I>. </DD>
+
+<DT><B>-anchor
+<I>anchor</I></B> </DT>
+<DD>Tells how to position the legend relative to the positioning point
+for the legend. This is dependent on the value of the <B>-position</B> option.
+The default is <I>center</I>. <blockquote></DD>
+
+<DT><I>left</I> or <I>right</I> </DT>
+<DD>The anchor describes how to position
+the legend vertically. </DD>
+
+<DT><I>top</I> or <I>bottom</I> </DT>
+<DD>The anchor describes how to position
+the legend horizontally. </DD>
+
+<DT><I>@x,y</I> </DT>
+<DD>The anchor specifies how to position the
+legend relative to the positioning point. For example, if <I>anchor</I> is <I>center</I>
+then the legend is centered on the point; if <I>anchor</I> is <I>n</I> then the legend
+will be drawn such that the top center point of the rectangular region
+occupied by the legend will be at the positioning point. </DD>
+
+<DT><I>plotarea</I> </DT>
+<DD>The anchor
+specifies how to position the legend relative to the plotting area. For
+example, if <I>anchor</I> is <I>center</I> then the legend is centered in the plotting
+area; if <I>anchor</I> is <I>ne</I> then the legend will be drawn such that occupies
+the upper right corner of the plotting area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background
+color of the legend. If <I>color</I> is <I>""</I>, the legend background with be transparent.
+</DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for legend entries. <I>TagList</I>
+is a list of binding tag names. The tags and their order will determine
+how events are handled for legend entries. Each tag in the list matching
+ the current event sequence will have its Tcl command executed. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the outside edge of the legend (if such border is being drawn; the <B>relief</B>
+option determines this). The default is <I>2</I> pixels. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD><I>FontName</I>
+specifies a font to use when drawing the labels of each element into the
+legend. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets
+the foreground color of the text drawn for the element's label. The default
+is <I>black</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the legend should be displayed.
+If <I>boolean</I> is true, the legend will not be draw. The default is <I>no</I>. </DD>
+
+<DT><B>-ipadx
+<I>pad</I></B> </DT>
+<DD>Sets the amount of internal padding to be added to the width of each
+legend entry. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I>
+has two elements, the left side of the legend entry is padded by the first
+distance and the right side by the second. If <I>pad</I> is just one distance,
+both the left and right sides are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-ipady
+<I>pad</I></B> </DT>
+<DD>Sets an amount of internal padding to be added to the height of each
+legend entry. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I>
+has two elements, the top of the entry is padded by the first distance
+and the bottom by the second. If <I>pad</I> is just one distance, both the top
+and bottom of the entry are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets
+the padding to the left and right exteriors of the legend. <I>Pad</I> can be a
+list of one or two screen distances. If <I>pad</I> has two elements, the left
+side of the legend is padded by the first distance and the right side by
+the second. If <I>pad</I> has just one distance, both the left and right sides
+are padded evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and
+below the legend. <I>Pad</I> can be a list of one or two screen distances. If
+<I>pad</I> has two elements, the area above the legend is padded by the first
+distance and the area below by the second. If <I>pad</I> is just one distance,
+both the top and bottom areas are padded evenly. The default is <I>0</I>. </DD>
+
+<DT><B>-position
+<I>pos</I></B> </DT>
+<DD>Specifies where the legend is drawn. The <B>-anchor</B> option also affects
+where the legend is positioned. If <I>pos</I> is <I>left</I>, <I>left</I>, <I>top</I>, or <I>bottom</I>, the
+legend is drawn in the specified margin. If <I>pos</I> is <I>plotarea</I>, then the legend
+is drawn inside the plotting area at a particular anchor. If <I>pos</I> is in
+the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window coordinates, the legend is
+drawn in the plotting area at the specified coordinates. The default is
+<I>right</I>. </DD>
+
+<DT><B>-raised <I>boolean</I></B> </DT>
+<DD>Indicates whether the legend is above or below the
+data elements. This matters only if the legend is in the plotting area.
+ If <I>boolean</I> is true, the legend will be drawn on top of any elements that
+may overlap it. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the border around the legend. <I>Relief</I> specifies how the interior of the
+legend should appear relative to the graph; for example, <I>raised</I> means the
+legend should appear to protrude from the graph, relative to the surface
+of the graph. The default is <I>sunken</I>. </DD>
+</DL>
+<P>
+Legend configuration options may also
+be set by the <B>option</B> command. The resource name and class are <I>legend</I> and
+<I>Legend</I> respectively. <BR>
+<CODE>option add *Graph.legend.Foreground blue<BR>
+option add *Graph.Legend.Relief raised<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>legend deactivate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend entries to be drawn using
+the normal legend colors and relief. All entries whose element names match
+<I>pattern</I> are selected. To be selected, the element name must match only
+one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend get <I>pos</I></B></I> </DT>
+<DD>Returns the name of the element whose
+entry is at the screen position <I>pos</I> in the legend. <I>Pos</I> must be in the form
+"<I>@x,y</I>", where <I>x</I> and <I>y</I> are window coordinates. If the given coordinates
+do not lie over a legend entry, <I>""</I> is returned. </DD>
+</DL>
+
+<H3><A NAME="sect13" HREF="#toc13">Pen Components</A></H3>
+Pens define
+attributes (both symbol and line style) for elements. Pens mirror the configuration
+options of data elements that pertain to how symbols and lines are drawn.
+ Data elements use pens to determine how they are drawn. A data element
+may use several pens at once. In this case, the pen used for a particular
+data point is determined from each element's weight vector (see the element's
+<B>-weight</B> and <B>-style</B> options). <P>
+One pen, called <I>activeLine</I>, is automatically
+created. It's used as the default active pen for elements. So you can change
+the active attributes for all elements by simply reconfiguring this pen.
+<BR>
+<CODE>.g pen configure "activeLine" -color green<BR>
+</CODE><P>You can create and use several pens. To create a pen, invoke the pen component
+and its create operation. <BR>
+<CODE>.g pen create myPen<BR>
+</CODE><P>You map pens to a data element using either the element's <B>-pen</B> or <B>-activepen</B>
+options. <BR>
+<CODE>.g element create "line1" -xdata $x -ydata $tempData \<BR>
+ -pen myPen<BR>
+</CODE><P>An element can use several pens at once. This is done by specifying the
+name of the pen in the element's style list (see the <B>-styles</B> option). <BR>
+<CODE>.g element configure "line1" -styles { myPen 2.0 3.0 }<BR>
+</CODE><P>This says that any data point with a weight between 2.0 and 3.0 is to be
+drawn using the pen <I>myPen</I>. All other points are drawn with the element's
+default attributes. <P>
+The following operations are available for pen components.
+<P>
+
+<DL>
+
+<DT><I>pathName <B>pen <B>cget <I>penName <I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option
+given by <I>option</I> for <I>penName</I>. <I>Option</I> may be any option described below for
+the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>configure <I>penName </I></B></B></I>?<I>penName</I>... ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options of <I>penName</I>. Several
+pens can be modified at once. If <I>option</I> isn't specified, a list describing
+the current options for <I>penName</I> is returned. If <I>option</I> is specified, but
+not <I>value</I>, then a list describing <I>option</I> is returned. If one or more <I>option</I>
+and <I>value</I> pairs are specified, then for each pair, the pen option <I>option</I>
+is set to <I>value</I>. The following options are valid for pens. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B>
+</DT>
+<DD>Sets the color of the traces connecting the data points. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of element line. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the element
+line. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the lines
+will be solid. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the interior color of symbols. If <I>color</I>
+is <I>""</I>, then the interior of the symbol is transparent. If <I>color</I> is <I>defcolor</I>,
+then the color will be the same as the <B>-color</B> option. The default is <I>defcolor</I>.
+</DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the connecting lines between data points.
+ If <I>pixels</I> is <I>0</I>, no connecting lines will be drawn between symbols. The
+default is <I>0</I>. </DD>
+
+<DT><B>-offdash <I>color</I></B> </DT>
+<DD>Sets the color of the stripes when traces are
+dashed (see the <B>-dashes</B> option). If <I>color</I> is <I>""</I>, then the "off" pixels will
+represent gaps instead of stripes. If <I>color</I> is <I>defcolor</I>, then the color
+will be the same as the <B>-color</B> option. The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outline <I>color</I></B>
+ </DT>
+<DD>Sets the color or the outline around each symbol. If <I>color</I> is <I>""</I>, then
+no outline is drawn. If <I>color</I> is <I>defcolor</I>, then the color will be the same
+as the <B>-color</B> option. The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outlinewidth <I>pixels</I></B> </DT>
+<DD>Sets
+the width of the outline bordering each symbol. If <I>pixels</I> is <I>0</I>, no outline
+will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-pixels <I>pixels</I></B> </DT>
+<DD>Sets the size of symbols.
+If <I>pixels</I> is <I>0</I>, no symbols will be drawn. The default is <I>0.125i</I>. </DD>
+
+<DT><B>-symbol <I>symbol</I></B>
+ </DT>
+<DD>Specifies the symbol for data points. <I>Symbol</I> can be either <I>square</I>, <I>circle</I>,
+<I>diamond</I>, <I>plus</I>, <I>cross</I>, <I>splus</I>, <I>scross</I>, <I>triangle</I>, <I>""</I> (where no symbol is drawn),
+or a bitmap. Bitmaps are specified as "<I>source</I> ?<I>mask</I>?", where <I>source</I> is
+the name of the bitmap, and <I>mask</I> is the bitmap's optional mask. The default
+is <I>circle</I>. </DD>
+
+<DT><B>-type <I>elemType</I></B> </DT>
+<DD>Specifies the type of element the pen is to be
+used with. This option should only be employed when creating the pen. This
+is for those that wish to mix different types of elements (bars and lines)
+on the same graph. The default type is "line". </DD>
+</DL>
+<P>
+Pen configuration options
+may be also be set by the <B>option</B> command. The resource class is <I>Pen</I>. The
+resource names are the names of the pens. <BR>
+<CODE>option add *Graph.Pen.Color blue<BR>
+option add *Graph.activeLine.color green<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>pen <B>create <I>penName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new pen by the name
+<I>penName</I>. No pen by the same name can already exist. <I>Option</I> and <I>value</I> are
+described in above in the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>delete
+</B></B></I>?<I>penName</I>?... </DT>
+<DD>Deletes the named pens. A pen is not really deleted until it is
+not longer in use, so it's safe to delete pens mapped to elements. </DD>
+
+<DT><I>pathName
+<B>pen names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of pens matching zero or more patterns.
+ If no <I>pattern</I> argument is give, the names of all pens are returned. </DD>
+</DL>
+
+<H3><A NAME="sect14" HREF="#toc14">PostScript
+Component</A></H3>
+The graph can generate encapsulated PostScript output. There are
+several configuration options you can specify to control how the plot will
+be generated. You can change the page dimensions and borders. The plot
+itself can be scaled, centered, or rotated to landscape. The PostScript
+output can be written directly to a file or returned through the interpreter.
+<P>
+The following postscript operations are available.
+<DL>
+
+<DT><I>pathName <B>postscript cget
+<I>option</I></B></I> </DT>
+<DD>Returns the current value of the postscript option given by <I>option</I>.
+ <I>Option</I> may be any option described below for the postscript <B>configure</B>
+operation. </DD>
+
+<DT><I>pathName <B>postscript configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options for PostScript generation. If <I>option</I> isn't specified,
+a list describing the current postscript options for <I>pathName</I> is returned.
+ If <I>option</I> is specified, but not <I>value</I>, then a list describing <I>option</I> is
+returned. If one or more <I>option</I> and <I>value</I> pairs are specified, then for
+each pair, the postscript option <I>option</I> is set to <I>value</I>. The following
+postscript options are available. <blockquote></DD>
+
+<DT><B>-center <I>boolean</I></B> </DT>
+<DD>Indicates whether the plot
+should be centered on the PostScript page. If <I>boolean</I> is false, the plot
+will be placed in the upper left corner of the page. The default is <I>1</I>. </DD>
+
+<DT><B>-colormap
+<I>varName</I></B> </DT>
+<DD><I>VarName</I> must be the name of a global array variable that specifies
+a color mapping from the X color name to PostScript. Each element of <I>varName</I>
+must consist of PostScript code to set a particular color value (e.g. ``<I>1.0
+1.0 0.0 setrgbcolor</I>''). When generating color information in PostScript, the
+array variable <I>varName</I> is checked if an element of the name as the color
+exists. If so, it uses its value as the PostScript command to set the color.
+ If this option hasn't been specified, or if there isn't an entry in <I>varName</I>
+for a given color, then it uses the red, green, and blue intensities from
+the X color. </DD>
+
+<DT><B>-colormode <I>mode</I></B> </DT>
+<DD>Specifies how to output color information. <I>Mode</I>
+must be either <I>color</I> (for full color output), <I>gray</I> (convert all colors
+to their gray-scale equivalents) or <I>mono</I> (convert foreground colors to black
+and background colors to white). The default mode is <I>color</I>. </DD>
+
+<DT><B>-fontmap <I>varName</I></B>
+</DT>
+<DD><I>VarName</I> must be the name of a global array variable that specifies a font
+mapping from the X font name to PostScript. Each element of <I>varName</I> must
+consist of a Tcl list with one or two elements; the name and point size
+of a PostScript font. When outputting PostScript commands for a particular
+font, the array variable <I>varName</I> is checked to see if an element by the
+ specified font exists. If there is such an element, then the font information
+contained in that element is used in the PostScript output. (If the point
+size is omitted from the list, the point size of the X font is used). Otherwise
+the X font is examined in an attempt to guess what PostScript font to use.
+ This works only for fonts whose foundry property is <I>Adobe</I> (such as Times,
+Helvetica, Courier, etc.). If all of this fails then the font defaults to
+<I>Helvetica-Bold</I>. </DD>
+
+<DT><B>-decorations <I>boolean</I></B> </DT>
+<DD>Indicates whether PostScript commands
+to generate color backgrounds and 3-D borders will be output. If <I>boolean</I>
+is false, the background will be white and no 3-D borders will be generated.
+The default is <I>1</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Sets the height of the plot. This lets you
+print the graph with a height different from the one drawn on the screen.
+ If <I>pixels</I> is 0, the height is the same as the widget's height. The default
+is <I>0</I>. </DD>
+
+<DT><B>-landscape <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, this specifies the printed area
+is to be rotated 90 degrees. In non-rotated output the X-axis of the printed
+area runs along the short dimension of the page (``portrait'' orientation);
+in rotated output the X-axis runs along the long dimension of the page (``landscape''
+orientation). Defaults to <I>0</I>. </DD>
+
+<DT><B>-maxpect <I>boolean</I></B> </DT>
+<DD>Indicates to scale the plot
+so that it fills the PostScript page. The aspect ratio of the graph is still
+retained. The default is <I>0</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the horizontal padding for the
+left and right page borders. The borders are exterior to the plot. <I>Pad</I>
+can be a list of one or two screen distances. If <I>pad</I> has two elements,
+the left border is padded by the first distance and the right border by
+the second. If <I>pad</I> has just one distance, both the left and right borders
+are padded evenly. The default is <I>1i</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the vertical padding
+for the top and bottom page borders. The borders are exterior to the plot.
+ <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has two elements,
+the top border is padded by the first distance and the bottom border by
+the second. If <I>pad</I> has just one distance, both the top and bottom borders
+are padded evenly. The default is <I>1i</I>. </DD>
+
+<DT><B>-paperheight <I>pixels</I></B> </DT>
+<DD>Sets the height
+of the postscript page. This can be used to select between different page
+sizes (letter, A4, etc). The default height is <I>11.0i</I>. </DD>
+
+<DT><B>-paperwidth <I>pixels</I></B> </DT>
+<DD>Sets
+the width of the postscript page. This can be used to select between different
+page sizes (letter, A4, etc). The default width is <I>8.5i</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Sets
+the width of the plot. This lets you generate a plot of a width different
+from that of the widget. If <I>pixels</I> is 0, the width is the same as the widget's
+width. The default is <I>0</I>. </DD>
+</DL>
+<P>
+Postscript configuration options may be also be
+set by the <B>option</B> command. The resource name and class are <I>postscript</I> and
+<I>Postscript</I> respectively. <BR>
+<CODE>option add *Graph.postscript.Decorations false<BR>
+option add *Graph.Postscript.Landscape true<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>postscript output </B></I>?<I>fileName</I>? ?<I>option value</I>?... </DT>
+<DD>Outputs a file of
+encapsulated PostScript. If a <I>fileName</I> argument isn't present, the command
+returns the PostScript. If any <I>option-value</I> pairs are present, they set configuration
+options controlling how the PostScript is generated. <I>Option</I> and <I>value</I> can
+be anything accepted by the postscript <B>configure</B> operation above. </DD>
+</DL>
+
+<H3><A NAME="sect15" HREF="#toc15">Marker
+Components</A></H3>
+Markers are simple drawing procedures used to annotate or highlight
+areas of the graph. Markers have various types: text strings, bitmaps,
+images, connected lines, windows, or polygons. They can be associated with
+a particular element, so that when the element is hidden or un-hidden, so
+is the marker. By default, markers are the last items drawn, so that data
+elements will appear in behind them. You can change this by configuring
+the <B>-under</B> option. <P>
+Markers, in contrast to elements, don't affect the scaling
+of the coordinate axes. They can also have <I>elastic</I> coordinates (specified
+by <I>-Inf</I> and <I>Inf</I> respectively) that translate into the minimum or maximum
+limit of the axis. For example, you can place a marker so it always remains
+in the lower left corner of the plotting area, by using the coordinates
+<I>-Inf</I>,<I>-Inf</I>. <P>
+The following operations are available for markers.
+<DL>
+
+<DT><I>pathName <B>marker
+after <I>markerId</I></B></I> ?<I>afterId</I>? </DT>
+<DD>Changes the order of the markers, drawing the
+first marker after the second. If no second <I>afterId</I> argument is specified,
+the marker is placed at the end of the display list. This command can be
+used to control how markers are displayed since markers are drawn in the
+order of this display list. </DD>
+
+<DT><I>pathName <B>marker before <I>markerId</I></B></I> ?<I>beforeId</I>? </DT>
+<DD>Changes
+the order of the markers, drawing the first marker before the second. If
+no second <I>beforeId</I> argument is specified, the marker is placed at the beginning
+of the display list. This command can be used to control how markers are
+displayed since markers are drawn in the order of this display list. </DD>
+
+<DT><I>pathName
+<B>marker bind <I>tagName</I></B></I> ?<I>sequence</I>? ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I>
+such that whenever the event sequence given by <I>sequence</I> occurs for a marker
+with this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B>
+command except that it operates on graph markers, rather than widgets.
+See the <B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>marker cget <I>option</I></B></I> </DT>
+<DD>Returns
+the current value of the marker configuration option given by <I>option</I>. <I>Option</I>
+may be any option described below in the <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>marker
+configure <I>markerId</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration
+options for markers. If <I>option</I> isn't specified, a list describing the current
+options for <I>markerId</I> is returned. If <I>option</I> is specified, but not <I>value</I>,
+then a list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I>
+pairs are specified, then for each pair, the marker option <I>option</I> is set
+to <I>value</I>. <P>
+The following options are valid for all markers. Each type of marker
+also has its own type-specific options. They are described in the sections
+below. <blockquote></DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for the marker. <I>TagList</I>
+is a list of binding tag names. The tags and their order will determine
+how events for markers are handled. Each tag in the list matching the
+current event sequence will have its Tcl command executed. Implicitly
+the name of the marker is always the first tag in the list. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-coords <I>coordList</I></B> </DT>
+<DD>Specifies the coordinates of the marker.
+<I>CoordList</I> is a list of graph coordinates. The number of coordinates required
+is dependent on the type of marker. Text, image, and window markers need
+only two coordinates (an X-Y coordinate). Bitmap markers can take either
+two or four coordinates (if four, they represent the corners of the bitmap).
+Line markers need at least four coordinates, polygons at least six. If <I>coordList</I>
+is <I>""</I>, the marker will not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-element <I>elemName</I></B>
+</DT>
+<DD>Links the marker with the element <I>elemName</I>. The marker is drawn only if
+the element is also currently displayed (see the element's <B>show</B> operation).
+ If <I>elemName</I> is <I>""</I>, the marker is always drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-hide
+<I>boolean</I></B> </DT>
+<DD>Indicates whether the marker is drawn. If <I>boolean</I> is true, the
+marker is not drawn. The default is <I>no</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis
+to map the marker's X-coordinates onto. <I>XAxis</I> must the name of an axis. The
+default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to map the marker's Y-coordinates
+onto. <I>YAxis</I> must the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-name <I>markerId</I></B> </DT>
+<DD>Changes
+the identifier for the marker. The identifier <I>markerId</I> can not already
+be used by another marker. If this option isn't specified, the marker's name
+is uniquely generated. </DD>
+
+<DT><B>-under <I>boolean</I></B> </DT>
+<DD>Indicates whether the marker is drawn
+below/above data elements. If <I>boolean</I> is true, the marker is be drawn underneath
+the data element symbols and lines. Otherwise, the marker is drawn on top
+of the element. The default is <I>0</I>. </DD>
+
+<DT><B>-xoffset <I>pixels</I></B> </DT>
+<DD>Specifies a screen distance
+to offset the marker horizontally. <I>Pixels</I> is a valid screen distance, such
+as <I>2</I> or <I>1.2i</I>. The default is <I>0</I>. </DD>
+
+<DT><B>-yoffset <I>pixels</I></B> </DT>
+<DD>Specifies a screen distance
+to offset the markers vertically. <I>Pixels</I> is a valid screen distance, such
+as <I>2</I> or <I>1.2i</I>. The default is <I>0</I>. </DD>
+</DL>
+<P>
+Marker configuration options may also be set
+by the <B>option</B> command. The resource class is either <I>BitmapMarker</I>, <I>ImageMarker</I>,
+ <I>LineMarker</I>, <I>PolygonMarker</I>, <I>TextMarker</I>, or <I>WindowMarker</I>, depending on the
+type of marker. The resource name is the name of the marker. <BR>
+<CODE>option add *Graph.TextMarker.Foreground white<BR>
+option add *Graph.BitmapMarker.Foreground white<BR>
+option add *Graph.m1.Background blue<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>marker create <I>type</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a marker of the selected
+type. <I>Type</I> may be either <I>text</I>, <I>line</I>, <I>bitmap</I>, <I>image</I>, <I>polygon</I>, or <I>window</I>.
+This command returns the marker identifier, used as the <I>markerId</I> argument
+in the other marker-related commands. If the <B>-name</B> option is used, this overrides
+the normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old. </DD>
+
+<DT><I>pathName <B>marker delete</B></I>
+?<I>name</I>?... </DT>
+<DD>Removes one of more markers. The graph will automatically be redrawn
+without the marker.. </DD>
+
+<DT><I>pathName <B>marker exists <I>markerId</I></B></I> </DT>
+<DD>Returns <I>1</I> if the
+marker <I>markerId</I> exists and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>marker names</B></I> ?<I>pattern</I>?
+ </DT>
+<DD>Returns the names of all the markers that currently exist. If <I>pattern</I>
+is supplied, only those markers whose names match it will be returned. </DD>
+
+<DT><I>pathName
+<B>marker type <I>markerId</I></B></I> </DT>
+<DD>Returns the type of the marker given by <I>markerId</I>,
+such as <I>line</I> or <I>text</I>. If <I>markerId</I> is not a valid a marker identifier, <I>""</I>
+is returned. </DD>
+</DL>
+
+<H3><A NAME="sect16" HREF="#toc16">Bitmap Markers</A></H3>
+A bitmap marker displays a bitmap. The size of
+the bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the bitmap.
+ The bitmap retains its normal width and height. If four coordinates, the
+first and second pairs of coordinates represent the corners of the bitmap.
+ The bitmap will be stretched or reduced as necessary to fit into the bounding
+rectangle. <P>
+Bitmap markers are created with the marker's <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create bitmap </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration options
+for the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's
+<B>configure</B> operation. <P>
+The following options are specific to bitmap markers:
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Same as the <B>-fill</B> option. </DD>
+
+<DT><B>-bitmap <I>bitmap</I></B> </DT>
+<DD>Specifies the bitmap
+to be displayed. If <I>bitmap</I> is <I>""</I>, the marker will not be displayed. The
+default is <I>""</I>. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background color of the bitmap. If <I>color</I>
+is the empty string, no background will be transparent. The default background
+color is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Same as the <B>-outline</B> option. </DD>
+
+<DT><B>-mask <I>mask</I></B> </DT>
+<DD>Specifies
+a mask for the bitmap to be displayed. This mask is a bitmap itself, denoting
+the pixels that are transparent. If <I>mask</I> is <I>""</I>, all pixels of the bitmap
+will be drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the foreground color
+of the bitmap. The default value is <I>black</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Sets the rotation
+of the bitmap. <I>Theta</I> is a real number representing the angle of rotation
+in degrees. The marker is first rotated and then placed according to its
+anchor position. The default rotation is <I>0.0</I>. </DD>
+</DL>
+
+<H3><A NAME="sect17" HREF="#toc17">Image Markers</A></H3>
+A image marker
+displays an image. Image markers are created with the marker's <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create image </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to image markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the image relative to the positioning point
+for the image. For example, if <I>anchor</I> is <I>center</I> then the image is centered
+on the point; if <I>anchor</I> is <I>n</I> then the image will be drawn such that the
+top center point of the rectangular region occupied by the image will be
+at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-image <I>image</I></B> </DT>
+<DD>Specifies
+the image to be drawn. If <I>image</I> is <I>""</I>, the marker will not be drawn. The
+default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Line Markers</A></H3>
+A line marker displays one or more connected
+line segments. Line markers are created with marker's <B>create</B> operation in
+the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create line </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to line markers:
+<DL>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the line. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the line.
+ Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the marker line
+will be solid. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background color of the line. This color
+is used with striped lines (see the <B>-fdashes</B> option). If <I>color</I> is the empty
+string, no background color is drawn (the line will be dashed, not striped).
+ The default background color is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the lines. The default width is <I>0</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the foreground color
+of the line. The default value is <I>black</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies a stipple
+pattern used to draw the line, rather than a solid line. <I>Bitmap</I> specifies
+a bitmap to use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then the line
+is drawn in a solid fashion. The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Polygon Markers</A></H3>
+A polygon
+marker displays a closed region described as two or more connected line
+segments. It is assumed the first and last points are connected. Polygon
+markers are created using the marker <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create polygon </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the <B>marker configure</B>
+command to change the marker's configuration. The following options are supported
+for polygon markers:
+<DL>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline
+of the polygon. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the outline. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the outline will be a solid
+line. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the fill color of the polygon. If <I>color</I> is <I>""</I>, then
+the interior of the polygon is transparent. The default is <I>white</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the outline of the polygon. If <I>pixels</I> is zero,
+ no outline is drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color of the
+outline of the polygon. If the polygon is stippled (see the <B>-stipple</B> option),
+then this represents the foreground color of the stipple. The default is
+<I>black</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies that the polygon should be drawn with a
+stippled pattern rather than a solid color. <I>Bitmap</I> specifies a bitmap to
+use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then the polygon is filled
+with a solid color (if the <B>-fill</B> option is set). The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Text
+Markers</A></H3>
+A text marker displays a string of characters on one or more lines
+of text. Embedded newlines cause line breaks. They may be used to annotate
+regions of the graph. Text markers are created with the <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create text </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option
+for the text marker. These same <I>option</I>-<I>value</I> pairs may be used with the
+ marker's <B>configure</B> operation. <P>
+The following options are specific to text
+markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B> </DT>
+<DD><I>Anchor</I> tells how to position the text relative to
+the positioning point for the text. For example, if <I>anchor</I> is <I>center</I> then
+the text is centered on the point; if <I>anchor</I> is <I>n</I> then the text will be
+drawn such that the top center point of the rectangular region occupied
+by the text will be at the positioning point. This default is <I>center</I>. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Same as the <B>-fill</B> option. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the font of the text.
+ The default is <I>*-Helvetica-Bold-R-Normal-*-120-*</I>. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the background
+color of the text. If <I>color</I> is the empty string, no background will be
+transparent. The default background color is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Same as
+the <B>-outline</B> option. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the text should be justified.
+ This matters only when the marker contains more than one line of text.
+<I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-outline <I>color</I></B>
+</DT>
+<DD>Sets the color of the text. The default value is <I>black</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the
+padding to the left and right exteriors of the text. <I>Pad</I> can be a list of
+one or two screen distances. If <I>pad</I> has two elements, the left side of
+the text is padded by the first distance and the right side by the second.
+ If <I>pad</I> has just one distance, both the left and right sides are padded
+evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and below the
+text. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has two
+elements, the area above the text is padded by the first distance and the
+area below by the second. If <I>pad</I> is just one distance, both the top and
+bottom areas are padded evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies
+the number of degrees to rotate the text. <I>Theta</I> is a real number representing
+the angle of rotation. The marker is first rotated along its center and
+is then drawn according to its anchor position. The default is <I>0.0</I>. </DD>
+
+<DT><B>-text <I>text</I></B>
+</DT>
+<DD>Specifies the text of the marker. The exact way the text is displayed may
+be affected by other options such as <B>-anchor</B> or <B>-rotate</B>. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Window Markers</A></H3>
+A window
+marker displays a widget at a given position. Window markers are created
+with the marker's <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create window </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+command. <P>
+The following options are specific to window markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the widget relative to the positioning point
+for the widget. For example, if <I>anchor</I> is <I>center</I> then the widget is centered
+on the point; if <I>anchor</I> is <I>n</I> then the widget will be displayed such that
+the top center point of the rectangular region occupied by the widget will
+be at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B>
+</DT>
+<DD>Specifies the height to assign to the marker's window. If this option isn't
+specified, or if it is specified as <I>""</I>, then the window is given whatever
+height the widget requests internally. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the width
+to assign to the marker's window. If this option isn't specified, or if it
+is specified as <I>""</I>, then the window is given whatever width the widget
+requests internally. </DD>
+
+<DT><B>-window <I>pathName</I></B> </DT>
+<DD>Specifies the widget to be managed
+by the graph. <I>PathName</I> must be a child of the <B>graph</B> widget. </DD>
+</DL>
+
+<H2><A NAME="sect22" HREF="#toc22">Graph Component
+Bindings</A></H2>
+Specific graph components, such as elements, markers and legend
+entries, can have a command trigger when event occurs in them, much like
+canvas items in Tk's canvas widget. Not all event sequences are valid. The
+only binding events that may be specified are those related to the mouse
+and keyboard (such as <B>Enter</B>, <B>Leave</B>, <B>ButtonPress</B>, <B>Motion</B>, and <B>KeyPress</B>).
+<P>
+Only one element or marker can be picked during an event. This means, that
+if the mouse is directly over both an element and a marker, only the uppermost
+component is selected. This isn't true for legend entries. Both a legend
+entry and an element (or marker) binding commands will be invoked if both
+items are picked. <P>
+It is possible for multiple bindings to match a particular
+event. This could occur, for example, if one binding is associated with
+the element name and another is associated with one of the element's tags
+(see the <B>-bindtags</B> option). When this occurs, all of the matching bindings
+are invoked. A binding associated with the element name is invoked first,
+followed by one binding for each of the element's bindtags. If there are
+multiple matching bindings for a single tag, then only the most specific
+binding is invoked. A continue command in a binding script terminates
+that script, and a break command terminates that script and skips any
+remaining scripts for the event, just as for the bind command. <P>
+The <B>-bindtags</B>
+option for these components controls addition tag names which can be matched.
+ Implicitly elements and markers always have tags matching their names.
+ Setting the value of the <B>-bindtags</B> option doesn't change this.
+<H2><A NAME="sect23" HREF="#toc23">C Language
+API</A></H2>
+You can manipulate data elements from the C language. There may be situations
+where it is too expensive to translate the data values from ASCII strings.
+ Or you might want to read data in a special file format. <P>
+Data can manipulated
+from the C language using BLT vectors. You specify the X-Y data coordinates
+of an element as vectors and manipulate the vector from C. The graph will
+be redrawn automatically after the vectors are updated. <P>
+From Tcl, create
+the vectors and configure the element to use them. <BR>
+<CODE>vector X Y<BR>
+.g element configure line1 -xdata X -ydata Y<BR>
+</CODE><P>To set data points from C, you pass the values as arrays of doubles using
+the <B>Blt_ResetVector</B> call. The vector is reset with the new data and at
+the next idle point (when Tk re-enters its event loop), the graph will be
+redrawn automatically. <BR>
+<CODE>#include &lt;tcl.h&gt;<BR>
+#include &lt;blt.h&gt;<BR>
+<P>
+register int i;<BR>
+Blt_Vector *xVec, *yVec;<BR>
+double x[50], y[50];<BR>
+<P>
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */<BR>
+if ((Blt_GetVector(interp, "X", &amp;xVec) != TCL_OK) ||<BR>
+ (Blt_GetVector(interp, "Y", &amp;yVec) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+<P>
+for (i = 0; i &lt; 50; i++) {<BR>
+ x[i] = i * 0.02;<BR>
+ y[i] = sin(x[i]);<BR>
+}<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<BR>
+<P>
+/* Put the data into BLT vectors */<BR>
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||<BR>
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+</CODE><P>See the <B>vector</B> manual page for more details.
+<H2><A NAME="sect24" HREF="#toc24">Speed Tips</A></H2>
+There may be cases
+where the graph needs to be drawn and updated as quickly as possible. If
+drawing speed becomes a big problem, here are a few tips to speed up displays.
+
+<UL>
+&#183;<LI>Try to minimize the number of data points. The more data points the looked
+at, the more work the graph must do. </LI>&#183;<LI>If your data is generated as floating
+point values, the time required to convert the data values to and from
+ASCII strings can be significant, especially when there any many data points.
+ You can avoid the redundant string-to-decimal conversions using the C API
+to BLT vectors. </LI>&#183;<LI>Data elements without symbols are drawn faster than with
+symbols. Set the data element's <B>-symbol</B> option to <I>none</I>. If you need to draw
+symbols, try using the simple symbols such as <I>splus</I> and <I>scross</I>. </LI>&#183;<LI>Don't stipple
+or dash the element. Solid lines are much faster. </LI>&#183;<LI>If you update data elements
+frequently, try turning off the widget's <B>-bufferelements</B> option. When the
+graph is first displayed, it draws data elements into an internal pixmap.
+ The pixmap acts as a cache, so that when the graph needs to be redrawn
+again, and the data elements or coordinate axes haven't changed, the pixmap
+is simply copied to the screen. This is especially useful when you are
+using markers to highlight points and regions on the graph. But if the
+graph is updated frequently, changing either the element data or coordinate
+axes, the buffering becomes redundant. </LI>
+</UL>
+
+<H2><A NAME="sect25" HREF="#toc25">Limitations</A></H2>
+Auto-scale routines do
+not use requested min/max limits as boundaries when the axis is logarithmically
+scaled. <P>
+The PostScript output generated for polygons with more than 1500
+points may exceed the limits of some printers (See PostScript Language
+Reference Manual, page 568). The work-around is to break the polygon into
+separate pieces.
+<H2><A NAME="sect26" HREF="#toc26">Keywords</A></H2>
+graph, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Syntax</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Example</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Graph Operations</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Graph Components</A></LI>
+<UL>
+<LI><A NAME="toc8" HREF="#sect8">Axis Components</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Crosshairs Component</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Element Components</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Grid Component</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Legend Component</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Pen Components</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">PostScript Component</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Marker Components</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Bitmap Markers</A></LI>
+<LI><A NAME="toc17" HREF="#sect17">Image Markers</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Line Markers</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Polygon Markers</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Text Markers</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Window Markers</A></LI>
+</UL>
+<LI><A NAME="toc22" HREF="#sect22">Graph Component Bindings</A></LI>
+<LI><A NAME="toc23" HREF="#sect23">C Language API</A></LI>
+<LI><A NAME="toc24" HREF="#sect24">Speed Tips</A></LI>
+<LI><A NAME="toc25" HREF="#sect25">Limitations</A></LI>
+<LI><A NAME="toc26" HREF="#sect26">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/hierbox.html b/blt/html/hierbox.html
new file mode 100644
index 00000000000..7f56d0e0a47
--- /dev/null
+++ b/blt/html/hierbox.html
@@ -0,0 +1,2331 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>treeview(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+treeview - Create and manipulate hierarchical
+table widgets
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>treeview</B> <I>pathName </I>?<I>options</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>treeview</B>
+widget displays a tree of data. It replaces both the <B>hiertable</B> and <B>hierbox</B>
+widgets. The <B>treeview</B> is 100% syntax compatible with the <B>hiertable</B> widget.
+ The <B>hiertable</B> command is retained for sake of script-level compatibility.
+ This widget obsoletes the <B>hierbox</B> widget. It does everything the old <B>hierbox</B>
+widget did, but also provides data sharing (via <I>tree data objects</I>) and
+the ability to tag nodes.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The <B>treeview</B> widget displays hierarchical
+data. Data is represented as nodes in a general-ordered tree. Each node
+may have sub-nodes and these nodes can in turn has their own children. <P>
+A
+node is displayed as a row entry in the widget. Each entry has a text label
+and icon. When a node has children, its entry is drawn with a small button
+to the left of the label. Clicking the mouse over this button opens or
+closes the node. When a node is <I>open</I>, its children are exposed. When it
+is <I>closed</I>, the children and their descedants are hidden. The button is
+normally a <I>+</I> or <I>-</I> symbol (ala Windows Explorer), but can be replaced with
+a pair of Tk images (open and closed images). <P>
+If the node has data associated
+with it, they can be displayed in columns running vertically on either
+side the tree. You can control the color, font, etc of each entry. Any
+entry label or data field can be edited in-place.
+<H2><A NAME="sect4" HREF="#toc4">Tree Data Object</A></H2>
+The tree
+is not stored inside the widget but in a tree data object (see the <B>tree</B>
+command for a further explanation). Tree data objects can be shared among
+different clients, such as a <B>treeview</B> widget or the <B>tree</B> command. You can
+walk the tree and manage its data with the <B>tree</B> command tree, while displaying
+it with the <B>treeview</B> widget. Whenever the tree is updated, the <B>treeview</B>
+widget is automatically redrawn. <P>
+By default, the <B>treeview</B> widget creates
+its own tree object. The tree initially contains just a root node. But you
+can also display trees created by the <B>tree</B> command using the <B>-tree</B> configuration
+option. <B>Treeview</B> widgets can share the same tree object, possibly displaying
+different views of the same data. <P>
+A tree object has both a Tcl and C API.
+ You can insert or delete nodes using <B>treeview</B> widget or <B>tree</B> command operations,
+but also from C code. For example, you can load the tree from your C code
+while still managing and displaying the tree from Tcl. The widget is automatically
+notified whenever the tree is modified via C or Tcl.
+<H2><A NAME="sect5" HREF="#toc5">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>treeview <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>treeview</B> command creates a new window <I>pathName</I> and makes it into a
+<B>treeview</B> widget. At the time this command is invoked, there must not exist
+a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options
+may be specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the <B>configure</B> operation
+below for the exact details about what <I>option</I> and <I>value</I> pairs are valid.
+<P>
+If successful, <B>treeview</B> returns the path name of the widget. It also creates
+a new Tcl command by the same name. You can use this command to invoke
+various operations that query or modify the widget. The general form is:
+<BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available are described in the <FONT SIZE=-1><B>TREEVIEW OPERATIONS</B></FONT>
+ section.
+
+<H2><A NAME="sect6" HREF="#toc6">IDs and Tags</A></H2>
+Nodes can be inserted into a tree using the <B>treeview</B> widget
+ <BR>
+<CODE>blt::treeview .t<BR>
+set node [.t insert end root "one"]<BR>
+</CODE><P>or <B>tree</B> command. <BR>
+<CODE>set tree [blt::tree create]<BR>
+set node [$tree insert root "one"]<BR>
+</CODE><P>In both cases, a number identifying the node is returned (the value of
+<I>$node</I>). This serial number or <I>id</I> uniquely identifies the node. Please note
+that you can't infer a location or position of a node from its id. The only
+exception is that the root node is always id <I>0</I>. Since nodes may have the
+same labels or be moved within the tree, ids provide an convenient way
+to identify nodes. If a tree is shared, the ids will be the same regardless
+if you are using by the <B>treeview</B> widget or the <B>tree</B> command. Ids are recycled
+when the node deleted. <P>
+A node may also have any number of <I>tags</I> associated
+with it. A tag is just a string of characters, and it may take any form
+except that of an integer. For example, "<I>x123</I>" is valid, but "<I>123</I>" isn't.
+ The same tag may be associated with many different nodes. This is typically
+done to associate a group of nodes. Many operations in the <B>treeview</B> widget
+take either node ids or tag names as arguments. Using a tag says to apply
+the operation to all nodes with that tag. <P>
+The tag <B>all</B> is implicitly associated
+with every node in the tree. It may be used to invoke operations on all
+the nodes in the tree. <P>
+Tags may be shared, just like trees, between clients.
+ For example, you can use the tags created by the <B>tree</B> command with <B>treeview</B>
+widgets.
+<H2><A NAME="sect7" HREF="#toc7">Special Node IDs</A></H2>
+There are also several special non-numeric ids.
+Special ids differ from tags in that they are always translated to their
+numeric equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to the
+<B>treeview</B> widget.
+<DL>
+
+<DT><B>active</B> </DT>
+<DD>The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon (see the <B>-activeicon</B>
+option). The <B>active</B> id is changed automatically by moving the mouse pointer
+over another node or by using the <B>entry activate</B> operation. Note that there
+can be only one active node at a time. </DD>
+
+<DT><B>anchor</B> </DT>
+<DD>The node representing the
+fixed end of the current selection. The anchor is set by the <B>selection
+anchor</B> operation. </DD>
+
+<DT><B>current</B> </DT>
+<DD>The node where the mouse pointer is currently
+located. But unlike <B>active</B>, this id changes while the selection is dragged.
+ It is used to determine the current node during button drags. </DD>
+
+<DT><B>down</B> </DT>
+<DD>The
+next open node from the current focus. The <B>down</B> of the last open node is
+the same. </DD>
+
+<DT><B>end</B> </DT>
+<DD>The last open node (in depth-first order) on the tree. </DD>
+
+<DT><B>focus</B>
+</DT>
+<DD>The node that currently has focus. When a node has focus, it receives key
+events. To indicate focus, the node is drawn with a dotted line around
+its label. You can change the focus using the <B>focus</B> operation. </DD>
+
+<DT><B>last</B> </DT>
+<DD>The
+last open node from the current focus. But unlike <B>up</B>, when the focus is
+at root, <B>last</B> wraps around to the last open node in the tree. </DD>
+
+<DT><B>mark</B> </DT>
+<DD>The node
+representing the non-fixed end of the current selection. The mark is set
+by the <B>selection mark</B> operation. </DD>
+
+<DT><B>next</B> </DT>
+<DD>The next open node from the current
+focus. But unlike <B>down</B>, when the focus is on last open node, <B>next</B> wraps
+around to the root node. </DD>
+
+<DT><B>nextsibling</B> </DT>
+<DD>The next sibling from the node with
+the current focus. If the node is already the last sibling then it is the
+<B>nextsibling<B>. </DD>
+
+<DT><B>parent</B></B></B> </DT>
+<DD>The parent of the node with the current focus. The <B>parent</B>
+of the root is also the root. </DD>
+
+<DT><B>prevsibling</B> </DT>
+<DD>The previous sibling from the
+node with the current focus. If the node is already the first sibling then
+it is the <B>prevsibling<B>. </DD>
+
+<DT><B>root</B></B></B> </DT>
+<DD>The root node. You can also use id <I>0</I> to indicate
+the root. </DD>
+
+<DT><B>up</B> </DT>
+<DD>The last open node (in depth-first order) from the current focus.
+The <B>up</B> of the root node (i.e. the root has focus) is also the root. </DD>
+
+<DT><B>view.top</B>
+</DT>
+<DD>First node that's current visible in the widget. </DD>
+
+<DT><B>view.bottom</B> </DT>
+<DD>Last node that's
+current visible in the widget. </DD>
+
+<DT><I>path</I> </DT>
+<DD>Absolute path of a node. Path names
+refer to the node name, not their entry labels. Paths don't have to start
+with a separator (see the <B>-separator</B> configuration option), but component
+names must be separated by the designated separator. </DD>
+
+<DT><B>@<I>x<B>,<I>y</I></B></I></B> </DT>
+<DD>Indicates the
+node that covers the point in the treeview window specified by <I>x</I> and <I>y</I>
+(in pixel coordinates). If no part of the entryd covers that point, then
+the closest node to that point is used. </DD>
+</DL>
+<P>
+A node may be specified as an id
+or tag. If the specifier is an integer then it is assumed to refer to the
+single node with that id. If the specifier is not an integer, it's checked
+to see if it's a special id (such as focus). Otherwise, it's assumed to be
+tag. Some operations only operate on a single node at a time; if a tag
+refers to more than one node, then an error is generated.
+<H2><A NAME="sect8" HREF="#toc8">Data Fields</A></H2>
+A node
+in the tree can have <I>data fields</I>. A data field is a name-value pair, used
+to represent arbitrary data in the node. Nodes can contain different fields
+(they aren't required to contain the same fields). You can optionally display
+these fields in the <B>treeview</B> widget in columns running on either side of
+the displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a specific
+field is left blank. Columns can be interactively resized, hidden, or,
+moved.
+<H2><A NAME="sect9" HREF="#toc9">Entry Bindings</A></H2>
+You can bind Tcl commands to be invoked when events
+occur on nodes (much like Tk canvas items). You can bind a node using its
+id or its <I>bindtags</I>. Bindtags are simply names that associate a binding
+with one or more nodes. There is a built-in tag <I>all</I> that all node entries
+automatically have.
+<H2><A NAME="sect10" HREF="#toc10">Treeview Operations</A></H2>
+The <B>treeview</B> operations are the invoked
+by specifying the widget's pathname, the operation, and any arguments that
+pertain to that operation. The general form is: <P>
+<BR>
+<CODE><I>pathName operation </I>?<I>arg arg ...</I>?<BR>
+<P>
+</CODE><P><I>Operation</I> and the <I>arg</I>s determine the exact behavior of the command. The
+following operation are available for <B>treeview</B> widgets:
+<DL>
+
+<DT><I>pathName <B>bbox</B></I> ?<B>-screen</B>?
+<I>tagOrId...</I> </DT>
+<DD>Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more <I>tagOrId</I> arguments.
+ If the <B>-screen</B> flag is given, then the x-y coordinates of the bounding
+box are returned as screen coordinates, not virtual coordinates. Virtual
+coordinates start from <I>0</I> from the root node. The returned list contains
+the following values. <blockquote></DD>
+
+<DT><I>x</I> </DT>
+<DD>X-coordinate of the upper-left corner of the bounding
+box. </DD>
+
+<DT><I>y</I> </DT>
+<DD>Y-coordinate of the upper-left corner of the bounding box. </DD>
+
+<DT><I>width</I> </DT>
+<DD>Width
+of the bounding box. </DD>
+
+<DT><I>height</I> </DT>
+<DD>Height of the bounding box. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for a node with this tag, <I>command</I>
+will be invoked. The syntax is similar to the <B>bind</B> command except that
+it operates on <B>treeview</B> entries, rather than widgets. See the <B>bind</B> manual
+entry for complete details on <I>sequence</I> and the substitutions performed
+on <I>command</I> before invoking it. <P>
+If all arguments are specified then a
+new binding is created, replacing any existing binding for the same <I>sequence</I>
+and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I> augments
+an existing binding rather than replacing it. If no <I>command</I> argument is
+provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button <I>operation</I></B></I> ?<I>args</I>?
+</DT>
+<DD>This command is used to control the button selectors within a <B>treeview</B>
+widget. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>button
+activate</B></I> <I>tagOrId</I> </DT>
+<DD>Designates the node given by <I>tagOrId</I> as active. When
+a node is active it's entry is drawn using its active icon (see the <B>-activeicon</B>
+option). Note that there can be only one active entry at a time. The special
+id <B>active</B> indicates the currently active node. </DD>
+
+<DT><I>pathName <B>button bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for an button of a node entry with
+this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B> command
+except that it operates on <B>treeview</B> buttons, rather than widgets. See the
+<B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>button configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the configuration options of the widget. If no <I>option</I> is specified, returns
+a list describing all of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B>
+for information on the format of this list). If <I>option</I> is specified with
+no <I>value</I>, then the command returns a list describing the one named option
+(this list will be identical to the corresponding sublist of the value
+returned if no <I>option</I> is specified). If one or more <I>option-value</I> pairs are
+specified, then the command modifies the given widget option(s) to have
+the given value(s); in this case the command returns an empty string. <I>Option</I>
+and <I>value</I> are described in the section <FONT SIZE=-1><B>BUTTON OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value of the configuration option given
+by <I>option</I>. <I>Option</I> may have any of the values accepted by the <B>configure</B> operation
+described below. </DD>
+
+<DT><I>pathName <B>close </B></I>?<B>-recurse</B>? <I>tagOrId...</I> </DT>
+<DD>Closes the node specified
+by <I>tagOrId</I>. In addition, if a Tcl script was specified by the <B>-closecommand</B>
+option, it is invoked. If the node is already closed, this command has
+no effect. If the <B>-recurse</B> flag is present, each child node is recursively
+closed. </DD>
+
+<DT><I>pathName <B>column <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are available
+for treeview columns. <blockquote></DD>
+
+<DT><I>pathName <B>column activate</B></I> <I>column</I> </DT>
+<DD>Sets the active column
+to <I>column</I>. <I>Column</I> is the name of a column in the widget. When a column is
+active, it's drawn using its <B>-activetitlebackground</B> and <B>-activetitleforeground</B>
+options. If <I>column</I> is the <I>""</I>, then no column will be active. If no column
+argument is provided, then the name of the currently active column is returned.
+</DD>
+
+<DT><I>pathName <B>column cget</B></I> <I>name</I> <I>option</I> </DT>
+<DD>Returns the current value of the column
+configuration option given by <I>option</I> for <I>name</I>. <I>Name</I> is the name of column
+that corresponds to a data field. <I>Option</I> may have any of the values accepted
+by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>column configure</B></I> <I>name</I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the column designated by <I>name</I>. <I>Name</I> is the name of the column corresponding
+to a data field. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>COLUMN OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>column delete</B></I> <I>field</I>
+?<I>field</I>...? </DT>
+<DD>Deletes one of more columns designated by <I>field</I>. Note that this
+does not delete the data fields themselves. </DD>
+
+<DT><I>pathName <B>column insert</B></I> <I>position</I>
+<I>field</I> ?<I>options</I>...? </DT>
+<DD>Inserts one of more columns designated by <I>field</I>. A column
+displays each node's data field by the same name. If the node doesn't have
+the given field, the cell is left blank. <I>Position</I> indicates where in the
+list of columns to add the new column. It may be either a number or <I>end</I>.
+ </DD>
+
+<DT><I>pathName <B>column invoke</B></I> <I>field</I> </DT>
+<DD>Invokes the Tcl command associated with the
+column <I>field</I>, if there is one (using the column's <B>-command</B> option). The
+command is ignored if the column's <B>-state</B> option set to <I>disabled</I>. </DD>
+
+<DT><I>pathName
+<B>column move <I>name</I></B></I> <I>dest</I> </DT>
+<DD>Moves the column <I>name</I> to the destination position.
+ <I>Dest</I> is the name of another column or a screen position in the form <I>@<I>x<I>,<I>y</I></I></I></I>.
+</DD>
+
+<DT><I>pathName <B>column names</B></I> </DT>
+<DD>Returns a list of the names of all columns in the
+widget. The list is ordered as the columns are drawn from left-to-right. </DD>
+
+<DT><I>pathName
+<B>column nearest</B></I> <I>x</I> ?<I>y</I>? </DT>
+<DD>Returns the name of the column closest to the given
+X-Y screen coordinate. If you provide a <I>y</I> argument (it's optional), a name
+is returned only when if the point is over a column's title. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>configure</B></I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the widget. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>TREEVIEW OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>curselection</B></I>
+</DT>
+<DD>Returns a list containing the ids of all of the entries that are currently
+selected. If there are no entries selected, then the empty string is returned.
+</DD>
+
+<DT><I>pathName <B>delete <I>tagOrId</I></B></I>... </DT>
+<DD>Deletes one or more entries given by <I>tagOrId</I> and
+its children. </DD>
+
+<DT><I>pathName <B>entry <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are
+available for treeview entries. <blockquote></DD>
+
+<DT><I>pathName <B>entry activate</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the
+active entry to the one specified by <I>tagOrId</I>. When an entry is active
+it is drawn using its active icon (see the <B>-activeicon</B> option). Note that
+there can be only one active node at a time. The special id of the currently
+active node is <B>active</B>. </DD>
+
+<DT><I>pathName <B>entry cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>entry
+children</B></I> <I>tagOrId</I> ?<I>first</I>? ?<I>last</I>? </DT>
+<DD>Returns a list of ids for the given range
+of children of <I>tagOrId</I>. <I>TagOrId</I> is the id or tag of the node to be examined.
+If only a <I>first</I> argument is present, then the id of the that child at
+that numeric position is returned. If both <I>first</I> and <I>last</I> arguments are
+given, then the ids of all the children in that range are returned. Otherwise
+the ids of all children are returned. </DD>
+
+<DT><I>pathName <B>entry configure</B></I> ?<I>option</I>?
+?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options of the
+widget. If no <I>option</I> is specified, returns a list describing all of the
+available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information on
+the format of this list). If <I>option</I> is specified with no <I>value</I>, then the
+command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described below: </DD>
+
+<DT><I>pathName <B>entry delete</B></I> <I>tagOrId</I> ?<I>first</I> ?<I>last</I>? </DT>
+<DD>Deletes the
+one or more children nodes of the parent <I>tagOrId</I>. If <I>first</I> and <I>last</I> arguments
+are present, they are positions designating a range of children nodes to
+be deleted. </DD>
+
+<DT><I>pathName <B>entry isbefore <I>tagOrId1</I></B></I> <I>tagOrId2</I> </DT>
+<DD>Returns 1 if <I>tagOrId1</I>
+is before <I>tagOrId2</I> and 0 otherwise. </DD>
+
+<DT><I>pathName <B>entry ishidden <I>tagOrId</I></B></I> </DT>
+<DD>Returns
+1 if the node is currently hidden and 0 otherwise. A node is also hidden
+if any of its ancestor nodes are closed or hidden. </DD>
+
+<DT><I>pathName <B>entry isopen
+<I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the node is currently open and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>entry size</B></I> <B>-recurse</B> <I>tagOrId</I> </DT>
+<DD>Returns the number of children for parent node
+<I>tagOrId</I>. If the <B>-recurse</B> flag is set, the number of all its descendants
+is returned. The node itself is not counted. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>find </B></I>?<I>flags</I>? <I>first</I>
+<I>last</I> </DT>
+<DD>Finds for all entries matching the criteria given by <I>flags</I>. A list
+of ids for all matching nodes is returned. <I>First</I> and <I>last</I> are ids designating
+the range of the search in depth-first order. If <I>last</I> is before <I>first</I>, then
+nodes are searched in reverse order. The valid flags are: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B>
+</DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern
+to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match
+against the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Patterns must match
+exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching
+is done in a fashion similar to that used by the C-shell. For the two
+strings to match, their contents must be identical except that the following
+ special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of
+ characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character
+in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a
+sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I>
+and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This
+provides a way of avoiding the special interpretation of the characters
+<I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern matching (i.e.
+the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Pick entries
+that don't match. </DD>
+
+<DT><B>-exec<I> string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked for
+each matching node. Percent substitutions are performed on <I>string</I> before
+ it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-count<I> number</I></B> </DT>
+<DD>Stop
+searching after <I>number</I> matches. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>focus
+</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the focus to the node given by <I>tagOrId</I>. When a node has focus,
+it can receive keyboard events. The special id <B>focus</B> designates the node
+that currently has focus. </DD>
+
+<DT><I>pathName <B>get </B></I>?<B>-full</B>? <I>tagOrId</I> <I>tagOrId</I>... </DT>
+<DD>Translates
+one or more ids to their node entry names. It returns a list of names
+for all the ids specified. If the <B>-full</B> flag is set, then the full pathnames
+are returned. </DD>
+
+<DT><I>pathName <B>hide </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Hides all nodes matching the
+criteria given by <I>flags</I>. The search is performed recursively for each node
+given by <I>tagOrId</I>. The valid flags are described below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies
+pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match
+against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match against
+the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Match patterns exactly. The
+is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching is done in a
+fashion similar to that used by the C-shell. For the two strings to match,
+their contents must be identical except that the following special sequences
+ may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of characters in string,
+including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I>
+</DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a sequence of the form
+<I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I> and <I>y</I>, inclusive, will
+match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This provides a way of avoiding
+ the special interpretation of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B>
+</DT>
+<DD>Use regular expression pattern matching (i.e. the same as implemented by
+the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Hide nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates
+the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>index </B></I>?<B>-at</B> <I>tagOrId</I>? <I>string</I> </DT>
+<DD>Returns the id of
+the node specified by <I>string</I>. <I>String</I> may be a tag or node id. Some special
+ids are normally relative to the node that has focus. The <B>-at</B> flag lets
+you select another node. </DD>
+
+<DT><I>pathName <B>insert </B></I>?<B>-at <I>tagOrId</I></B>? <I>position</I> <I>path</I> ?<I>options...</I>?
+?<I>path</I>? ?<I>options...</I>? </DT>
+<DD>Inserts one or more nodes at <I>position</I>. <I>Position</I> is the
+location (number or <I>end</I>) where the new nodes are added to the parent node.
+ <I>Path</I> is the pathname of the new node. Pathnames can be formated either
+as a Tcl list (each element is a path component) or as a string separated
+by a special character sequence (using the <B>-separator</B> option). Pathnames
+are normally absolute, but the <B>-at</B> switch lets you select a relative starting
+point. Its value is the id of the starting node. <P>
+All ancestors of the
+new node must already exist, unless the <B>-autocreate</B> option is set. It is
+also an error if a node already exists, unless the <B>-allowduplicates</B> option
+is set. <P>
+<I>Option</I> and <I>value</I> may have any of the values accepted by the <B>entry
+configure</B> operation described in the <FONT SIZE=-1><B>ENTRY OPERATIONS</B></FONT>
+ section below. This
+command returns a list of the ids of the new entries. </DD>
+
+<DT><I>pathName <B>move <I>tagOrId</I></B></I>
+<I>how</I> <I>destId</I> </DT>
+<DD>Moves the node given by <I>tagOrId</I> to the destination node. The
+node can not be an ancestor of the destination. <I>DestId</I> is the id of the
+destination node and can not be the root of the tree. In conjunction with
+<I>how</I>, it describes how the move is performed. <blockquote></DD>
+
+<DT><I>before</I> </DT>
+<DD>Moves the node before
+the destination node. </DD>
+
+<DT><I>after</I> </DT>
+<DD>Moves the node after the destination node. </DD>
+
+<DT><I>into</I>
+</DT>
+<DD>Moves the node to the end of the destination's list of children. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>nearest <I>x y</I></B></I> ?<I>varName</I>? </DT>
+<DD>Returns the id of the node entry closest to the given
+X-Y screen coordinate. The optional argument <I>varName</I> is the name of variable
+which is set to either <I>button</I> or <I>select</I> to indicate over what part of the
+node the coordinate lies. If the coordinate is not directly over any node,
+then <I>varName</I> will contain the empty string. </DD>
+
+<DT><I>pathName <B>open </B></I>?<B>-recurse</B>? <I>tagOrId...</I>
+</DT>
+<DD>Opens the one or more nodes specified by <I>tagOrId</I>. If a node is not already
+open, the Tcl script specified by the <B>-opencommand</B> option is invoked. If
+the <B>-recurse</B> flag is present, then each descendant is recursively opened.
+ </DD>
+
+<DT><I>pathName <B>range</B></I> ?<B>-open</B>? <I>first last</I> </DT>
+<DD>Returns the ids in depth-first order
+of the nodes between the <I>first</I> and <I>last</I> ids. If the <B>-open</B> flag is present,
+it indicates to consider only open nodes. If <I>last</I> is before <I>first</I>, then
+the ids are returned in reverse order. </DD>
+
+<DT><I>pathName <B>scan</B></I> <I>option args</I> </DT>
+<DD>This command
+implements scanning. It has two forms, depending on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>scan
+mark <I>x y</I></B></I> </DT>
+<DD>Records <I>x</I> and <I>y</I> and the current view in the treeview window;
+used in conjunction with later <B>scan dragto</B> commands. Typically this command
+is associated with a mouse button press in the widget. It returns an empty
+string. </DD>
+
+<DT><I>pathName <B>scan dragto <I>x y</I></B></I>. </DT>
+<DD>Computes the difference between its <I>x</I> and
+<I>y</I> arguments and the <I>x</I> and <I>y</I> arguments to the last <B>scan mark</B> command for
+the widget. It then adjusts the view by 10 times the difference in coordinates.
+ This command is typically associated with mouse motion events in the widget,
+to produce the effect of dragging the list at high speed through the window.
+ The return value is an empty string. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>see</B></I> ?<B>-anchor <I>anchor</I></B>? <I>tagOrId</I>
+</DT>
+<DD>Adjusts the view of entries so that the node given by <I>tagOrId</I> is visible
+in the widget window. It is an error if <B>tagOrId</B> is a tag that refers to
+more than one node. By default the node's entry is displayed in the middle
+of the window. This can changed using the <B>-anchor</B> flag. Its value is a Tk
+anchor position. </DD>
+
+<DT><I>pathName <B>selection <I>option arg</I></B></I> </DT>
+<DD>This command is used to adjust
+the selection within a <B>treeview</B> widget. It has several forms, depending
+on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>selection anchor <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection anchor
+to the node given by <I>tagOrId</I>. If <I>tagOrId</I> refers to a non-existent node, then
+the closest node is used. The selection anchor is the end of the selection
+that is fixed while dragging out a selection with the mouse. The special
+id <B>anchor</B> may be used to refer to the anchor node. </DD>
+
+<DT><I>pathName <B>selection cancel</B></I>
+</DT>
+<DD>Clears the temporary selection of entries back to the current anchor. Temporary
+selections are created by the <B>selection mark</B> operation. </DD>
+
+<DT><I>pathName <B>selection
+clear <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Removes the entries between <I>first</I> and <I>last</I> (inclusive)
+from the selection. Both <I>first</I> and <I>last</I> are ids representing a range of
+entries. If <I>last</I> isn't given, then only <I>first</I> is deselected. Entries outside
+the selection are not affected. </DD>
+
+<DT><I>pathName <B>selection clearall</B></I> </DT>
+<DD>Clears the entire
+selection. </DD>
+
+<DT><I>pathName <B>selection mark <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection mark to
+the node given by <I>tagOrId</I>. This causes the range of entries between the
+anchor and the mark to be temporarily added to the selection. The selection
+mark is the end of the selection that is fixed while dragging out a selection
+with the mouse. The special id <B>mark</B> may be used to refer to the current
+ mark node. If <I>tagOrId</I> refers to a non-existent node, then the mark is ignored.
+Resetting the mark will unselect the previous range. Setting the anchor
+finalizes the range. </DD>
+
+<DT><I>pathName <B>selection includes <I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the
+node given by <I>tagOrId</I> is currently selected, 0 if it isn't. </DD>
+
+<DT><I>pathName <B>selection
+present</B></I> </DT>
+<DD>Returns 1 if any nodes are currently selected and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>selection set <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects all of the nodes in the range between
+<I>first</I> and <I>last</I>, inclusive, without affecting the selection state of nodes
+outside that range. </DD>
+
+<DT><I>pathName <B>selection toggle <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects/deselects
+nodes in the range between <I>first</I> and <I>last</I>, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and visa versa.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>show </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Exposes all nodes matching the criteria given
+by <I>flags</I>. This is the inverse of the <B>hide</B> operation. The search is performed
+recursively for each node given by <I>tagOrId</I>. The valid flags are described
+below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I>
+pattern</I></B> </DT>
+<DD>Specifies pattern to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B>
+</DT>
+<DD>Specifies pattern to match against the entry's configuration option. </DD>
+
+<DT><B>-exact</B>
+</DT>
+<DD>Match patterns exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD><B>-glob</B> Use global pattern
+matching. Matching is done in a fashion similar to that used by the C-shell.
+ For the two strings to match, their contents must be identical except
+that the following special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches
+ any sequence of characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches
+any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set
+given by <I>chars</I>. If a sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any
+character between <I>x</I> and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single
+ character <I>x</I>. This provides a way of avoiding the special interpretation
+of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern
+matching (i.e. the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B>
+</DT>
+<DD>Expose nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort</B></I>
+?<I>operation</I>? <I>args...</I> </DT>
+<DD><blockquote></DD>
+
+<DT><I>pathName <B>sort auto</B></I> ?<I>boolean</I> </DT>
+<DD>Turns on/off automatic sorting
+of node entries. If <I>boolean</I> is true, entries will be automatically sorted
+as they are opened, closed, inserted, or deleted. If no <I>boolean</I> argument
+is provided, the current state is returned. </DD>
+
+<DT><I>pathName <B>sort cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>sort configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the sorting configuration options of the widget. If no <I>option</I> is specified,
+returns a list describing all of the available options for <I>pathName</I> (see
+<B>Tk_ConfigureInfo</B> for information on the format of this list). If <I>option</I>
+is specified with no <I>value</I>, then the command returns a list describing
+the one named option (this list will be identical to the corresponding
+sublist of the value returned if no <I>option</I> is specified). If one or more
+<I>option-value</I> pairs are specified, then the command modifies the given sorting
+option(s) to have the given value(s); in this case the command returns
+an empty string. <I>Option</I> and <I>value</I> are described below: <blockquote></DD>
+
+<DT><B>-column<I> string</I></B> </DT>
+<DD>Specifies
+the column to sort. Entries in the widget are rearranged according to this
+column. If <I>column</I> is <I>""</I> then no sort is performed. </DD>
+
+<DT><B>-command<I> string</I></B> </DT>
+<DD>Specifies
+a Tcl procedure to be called when sorting nodes. The procedure is called
+with three arguments: the pathname of the widget and the fields of two
+entries. The procedure returns 1 if the first node is greater than the
+second, -1 is the second is greater, and 0 if equal. </DD>
+
+<DT><B>-decreasing<I> boolean</I></B>
+</DT>
+<DD>Indicates to sort in ascending/descending order. If <I>boolean</I> is true, then
+the entries as in descending order. The default is <I>no</I>. </DD>
+
+<DT><B>-mode<I> string</I></B> </DT>
+<DD>Specifies
+how to compare entries when sorting. <I>String</I> may be one of the following:
+<blockquote></DD>
+
+<DT><I>ascii</I> </DT>
+<DD>Use string comparison based upon the ASCII collation order. </DD>
+
+<DT><I>dictionary</I>
+ </DT>
+<DD>Use dictionary-style comparison. This is the same as <I>ascii</I> except (a) case
+is ignored except as a tie-breaker and (b) if two strings contain embedded
+numbers, the numbers compare as integers, not characters. For example,
+"bigBoy" sorts between "bigbang" and "bigboy", and "x10y" sorts between
+"x9y" and "x11y". </DD>
+
+<DT><I>integer</I> </DT>
+<DD>Compares fields as integers. </DD>
+
+<DT><I>real</I> </DT>
+<DD>Compares fields
+as floating point numbers. </DD>
+
+<DT><I>command</I> </DT>
+<DD>Use the Tcl proc specified by the <B>-command</B>
+option to compare entries when sorting. If no command is specified, the
+sort reverts to <I>ascii</I> sorting. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort once</B></I> ?<I>flags</I>? <I>tagOrId...</I> </DT>
+<DD>Sorts
+the children for each entries specified by <I>tagOrId</I>. By default, entries
+are sorted by name, but you can specify a Tcl proc to do your own comparisons.
+<blockquote></DD>
+
+<DT><B>-recurse</B> </DT>
+<DD>Recursively sort the entire branch, not just the children. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>tag <I>operation args</I></B></I> </DT>
+<DD>Tags are a general means of selecting and marking nodes
+in the tree. A tag is just a string of characters, and it may take any form
+except that of an integer. The same tag may be associated with many different
+nodes. <P>
+Both <I>operation</I> and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below. <blockquote></DD>
+
+<DT><I>pathName</I>
+<B>tag add</B> <I>string</I> <I>id</I>... </DT>
+<DD>Adds the tag <I>string</I> to one of more entries. </DD>
+
+<DT><I>pathName</I> <B>tag
+delete</B> <I>string</I> <I>id</I>... </DT>
+<DD>Deletes the tag <I>string</I> from one or more entries. </DD>
+
+<DT><I>pathName</I>
+<B>tag forget</B> <I>string</I> </DT>
+<DD>Removes the tag <I>string</I> from all entries. It's not an error
+if no entries are tagged as <I>string</I>. </DD>
+
+<DT><I>pathName</I> <B>tag names</B> ?<I>id</I>? </DT>
+<DD>Returns a list
+of tags used. If an <I>id</I> argument is present, only those tags used by the
+node designated by <I>id</I> are returned. </DD>
+
+<DT><I>pathName</I> <B>tag nodes</B> <I>string</I> </DT>
+<DD>Returns a
+list of ids that have the tag <I>string</I>. If no node is tagged as <I>string</I>, then
+an empty string is returned. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>This operation
+is used to provide text editing for cells (data fields in a column) or
+entry labels. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>text
+apply</B></I> </DT>
+<DD>Applies the edited buffer, replacing the entry label or data field.
+The edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cancel</B></I> </DT>
+<DD>Cancels the editing operation,
+reverting the entry label or data value back to the previous value. The
+edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cget<I> value</I></B></I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>text
+configure</B></I> ?<I>option value</I>? </DT>
+<DD>Query or modify the configuration options of the
+edit window. If no <I>option</I> is specified, returns a list describing all of
+the available options (see <B>Tk_ConfigureInfo</B> for information on the format
+of this list). If <I>option</I> is specified with no <I>value</I>, then the command returns
+a list describing the one named option (this list will be identical to
+the corresponding sublist of the value returned if no <I>option</I> is specified).
+ If one or more <I>option-value</I> pairs are specified, then the command modifies
+the given widget option(s) to have the given value(s); in this case the
+command returns an empty string. <I>Option</I> and <I>value</I> are described in the section
+ <FONT SIZE=-1><B>TEXT EDITING OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text delete<I> first last</I></B></I> </DT>
+<DD>Deletes the
+characters in the edit buffer between the two given character positions.
+ </DD>
+
+<DT><I>pathName <B>text get</B></I> ?<I>-root</I>? <I>x y</I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text icursor<I> index</I></B></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text
+index<I> index</I></B></I> </DT>
+<DD>Returns the text index of given <I>index</I>. </DD>
+
+<DT><I>pathName <B>text insert<I>
+index string</I></B></I> </DT>
+<DD>Insert the text string <I>string</I> into the edit buffer at the
+index <I>index</I>. For example, the index 0 will prepend the buffer. </DD>
+
+<DT><I>pathName
+<B>text selection<I> args</I></B></I> </DT>
+<DD>This operation controls the selection of the editing
+window. Note that this differs from the selection of entries. It has the
+following forms: <blockquote></DD>
+
+<DT><I>pathName <B>text selection adjust<I> index</I></B></I> </DT>
+<DD>Adjusts either the
+first or last index of the selection. </DD>
+
+<DT><I>pathName <B>text selection clear</B></I> </DT>
+<DD>Clears
+the selection. </DD>
+
+<DT><I>pathName <B>text selection from<I> index</I></B></I> </DT>
+<DD>Sets the anchor of the
+selection. </DD>
+
+<DT><I>pathName <B>text selection present</B></I> </DT>
+<DD>Indicates if a selection is present.
+</DD>
+
+<DT><I>pathName <B>text selection range<I> start end</I></B></I> </DT>
+<DD>Sets both the anchor and mark of
+the selection. </DD>
+
+<DT><I>pathName <B>text selection to<I> index</I></B></I> </DT>
+<DD>Sets the unanchored end
+(mark) of the selection. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>toggle <I>tagOrId</I></B></I> </DT>
+<DD>Opens or closes the node
+given by <I>tagOrId</I>. If the corresponding <B>-opencommand</B> or <B>-closecommand</B> option
+is set, then that command is also invoked. </DD>
+
+<DT><I>pathName <B>xview <I>args</I></B></I> </DT>
+<DD>This command
+is used to query and change the horizontal position of the information
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>xview</B></I> </DT>
+<DD>Returns a list containing two elements. Each element is a real fraction
+between 0 and 1; together they describe the horizontal span that is visible
+in the window. For example, if the first element is .2 and the second element
+is .6, 20% of the <B>treeview</B> widget's text is off-screen to the left, the middle
+40% is visible in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the <B>-xscrollcommand</B> option.
+</DD>
+
+<DT><I>pathName <B>xview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the window so that the character
+position given by <I>tagOrId</I> is displayed at the left edge of the window. Character
+positions are defined by the width of the character <B>0</B>. </DD>
+
+<DT><I>pathName <B>xview moveto<I>
+fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so that <I>fraction</I> of the total width
+of the <B>treeview</B> widget's text is off-screen to the left. <I>fraction</I> must be
+a fraction between 0 and 1. </DD>
+
+<DT><I>pathName <B>xview scroll <I>number what</I></B></I> </DT>
+<DD>This command
+shifts the view in the window left or right according to <I>number</I> and <I>what</I>.
+<I>Number</I> must be an integer. <I>What</I> must be either <B>units</B> or <B>pages</B> or an abbreviation
+of one of these. If <I>what</I> is <B>units</B>, the view adjusts left or right by <I>number</I>
+character units (the width of the <B>0</B> character) on the display; if it is
+<B>pages</B> then the view adjusts by <I>number</I> screenfuls. If <I>number</I> is negative
+then characters farther to the left become visible; if it is positive
+then characters farther to the right become visible. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>yview <I>?args</I></B></I>?
+</DT>
+<DD>This command is used to query and change the vertical position of the text
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>yview</B></I> </DT>
+<DD>Returns a list containing two elements, both of which are real fractions
+between 0 and 1. The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means it is halfway
+through the treeview window, for example). The second element gives the
+position of the node just after the last one in the window, relative to
+the widget as a whole. These are the same values passed to scrollbars via
+the <B>-yscrollcommand</B> option. </DD>
+
+<DT><I>pathName <B>yview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the
+window so that the node given by <I>tagOrId</I> is displayed at the top of the
+window. </DD>
+
+<DT><I>pathName <B>yview moveto<I> fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so
+that the node given by <I>fraction</I> appears at the top of the window. <I>Fraction</I>
+is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates
+the node one-third the way through the <B>treeview</B> widget, and so on. </DD>
+
+<DT><I>pathName
+<B>yview scroll <I>number what</I></B></I> </DT>
+<DD>This command adjusts the view in the window up
+or down according to <I>number</I> and <I>what</I>. <I>Number</I> must be an integer. <I>What</I> must
+be either <B>units</B> or <B>pages</B>. If <I>what</I> is <B>units</B>, the view adjusts up or down
+by <I>number</I> lines; if it is <B>pages</B> then the view adjusts by <I>number</I> screenfuls.
+If <I>number</I> is negative then earlier nodes become visible; if it is positive
+then later nodes become visible. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect11" HREF="#toc11">Treeview Options</A></H2>
+In addition to the <B>configure</B>
+operation, widget configuration options may also be set by the Tk <B>option</B>
+command. The class resource name is <I>TreeView</I>. <BR>
+<CODE>option add *TreeView.Foreground white<BR>
+option add *TreeView.Background blue<BR>
+</CODE><P>The following widget options are available:
+<DL>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets
+the background color for active entries. A node is active when the mouse
+passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of the active node. A node is active when
+the mouse passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeicons
+<I>images</I></B> </DT>
+<DD>Specifies images to be displayed for an entry's icon when it is active.
+<I>Images</I> is a list of two Tk images: the first image is displayed when the
+node is open, the second when it is closed. </DD>
+
+<DT><B>-autocreate <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I>
+is true, automatically create missing ancestor nodes when inserting new
+nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-allowduplicates <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is true, allow nodes with duplicate pathnames when inserting
+new nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets
+the background color of the widget. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the 3-D border around the outside edge of the widget.
+The <B>-relief</B> option determines if the border is to be drawn. The default
+is <I>2</I>. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked when a node
+is closed. You can overrider this for individual entries using the entry's
+<B>-closecommand</B> option. The default is <I>""</I>. Percent substitutions are performed
+on <I>string</I> before it is executed. The following substitutions are valid:
+<blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-cursor
+<I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-dashes <I>number</I></B>
+</DT>
+<DD>Sets the dash style of the horizontal and vertical lines drawn connecting
+ entries. <I>Number</I> is the length in pixels of the dashes and gaps in the line.
+If <I>number</I> is <I>0</I>, solid lines will be drawn. The default is <I>1</I> (dotted). </DD>
+
+<DT><B>-exportselection
+<I>boolean</I></B> </DT>
+<DD>Indicates if the selection is exported. If the widget is exporting
+its selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type <B>STRING</B>; the value of the
+selection will be the label of the selected nodes, separated by newlines.
+ The default is <I>no</I>. </DD>
+
+<DT><B>-flat <I>boolean</I></B> </DT>
+<DD>Indicates whether to display the tree as
+a flattened list. If <I>boolean</I> is true, then the hierarchy will be a list
+of full paths for the nodes. This option also has affect on sorting. See
+the <FONT SIZE=-1><B>SORT OPERATIONS</B></FONT>
+ section for more information. The default is <I>no</I>. </DD>
+
+<DT><B>-focusdashes
+<I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline rectangle drawn around the
+entry label of the node that current has focus. <I>Number</I> is the length in
+pixels of the dashes and gaps in the line. If <I>number</I> is <I>0</I>, a solid line
+will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-focusforeground <I>color</I></B> </DT>
+<DD>Sets the color of
+the focus rectangle. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the
+font for entry labels. You can override this for individual entries with
+the entry's <B>-font</B> configuration option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of entry labels. You can override
+this for individual entries with the entry's <B>-foreground</B> configuration option.
+ The default is <I>black</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of
+widget. The default is <I>400</I>. </DD>
+
+<DT><B>-hideroot <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, it indicates
+that no entry for the root node should be displayed. The default is <I>no</I>.
+</DD>
+
+<DT><B>-highlightbackground <I>color</I></B> </DT>
+<DD>Specifies the normal color of the traversal
+highlight region when the widget does not have the input focus. </DD>
+
+<DT><B>-highlightcolor
+<I>color</I></B> </DT>
+<DD>Specifies the color of the traversal highlight rectangle when the
+widget has the input focus. The default is <I>black</I>. </DD>
+
+<DT><B>-highlightthickness <I>pixels</I></B>
+</DT>
+<DD>Specifies the width of the highlight rectangle indicating when the widget
+has input focus. The value may have any of the forms acceptable to <B>Tk_GetPixels</B>.
+ If the value is zero, no focus highlight will be displayed. The default
+is <I>2</I>. </DD>
+
+<DT><B>-icons <I>images</I></B> </DT>
+<DD>Specifies images for the entry's icon. <I>Images</I> is a list
+of two Tk images: the first image is displayed when the node is open,
+the second when it is closed. </DD>
+
+<DT><B>-linecolor <I>color</I></B> </DT>
+<DD>Sets the color of the connecting
+lines drawn between entries. The default is <I>black</I>. </DD>
+
+<DT><B>-linespacing <I>pixels</I></B> </DT>
+<DD>Sets
+the number of pixels spacing between entries. The default is <I>0</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Set the width of the lines drawn connecting entries. If <I>pixels</I> is
+<I>0</I>, no vertical or horizontal lines are drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-opencommand
+<I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked when a node is open. You can
+override this for individual entries with the entry's <B>-opencommand</B> configuration
+option. The default is <I>""</I>. Percent substitutions are performed on <I>string</I>
+before it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect for the widget. <I>Relief</I> specifies how the <B>treeview</B> widget
+should appear relative to widget it is packed into; for example, <I>raised</I>
+means the <B>treeview</B> widget should appear to protrude. The default is <I>sunken</I>.
+</DD>
+
+<DT><B>-scrollmode <I>mode</I></B> </DT>
+<DD>Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is <I>hierbox</I>. <blockquote></DD>
+
+<DT><I>listbox</I> </DT>
+<DD>Like the <B>listbox</B>
+widget, the last entry can always be scrolled to the top of the widget
+window. This allows the scrollbar thumb to shrink as the last entry is
+scrolled upward. </DD>
+
+<DT><I>hierbox</I> </DT>
+<DD>Like the <B>hierbox</B> widget, the last entry can only
+be viewed at the bottom of the widget window. The scrollbar stays a constant
+size. </DD>
+
+<DT><I>canvas</I> </DT>
+<DD>Like the <B>canvas</B> widget, the entries are bound within the
+scrolling area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-selectbackground <I>color</I></B> </DT>
+<DD>Sets the background color selected
+node entries. The default is <I>#ffffea</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the raised 3-D border drawn around the labels of selected entries. The
+default is <I>0</I>. <B>-selectcommand <I>string</I></B> Specifies a Tcl script to invoked when
+the set of selected nodes changes. The default is <I>""</I>. </DD>
+
+<DT><B>-selectforeground <I>color<B>
+</B></I></B></DT>
+<DD>Sets the color of the labels of selected node entries. The default is <I>black</I>.
+</DD>
+
+<DT><B>-selectmode <I>mode</I></B> </DT>
+<DD>Specifies the selection mode. If <I>mode</I> is <I>single</I>, only one
+node can be selected at a time. If <I>multiple</I> more than one node can be selected.
+The default is <I>single</I>. </DD>
+
+<DT><B>-separator <I>string</I></B> </DT>
+<DD>Specifies the character sequence
+to use when spliting the path components. The separator may be several
+characters wide (such as "::") Consecutive separators in a pathname are
+treated as one. If <I>string</I> is the empty string, the pathnames are Tcl lists.
+ Each element is a path component. The default is <I>""</I>. </DD>
+
+<DT><B>-showtitles <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is false, column titles are not be displayed. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-sortselection <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, nodes in the selection are
+ordered as they are currently displayed (depth-first or sorted), not in
+the order they were selected. The default is <I>no</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides
+information used when moving the focus from window to window via keyboard
+traversal (e.g., Tab and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window
+should be skipped entirely during keyboard traversal. <I>1</I> means that the
+this window should always receive the input focus. An empty value means
+that the traversal scripts make the decision whether to focus on the window.
+The default is <I>"1"</I>. </DD>
+
+<DT><B>-trim <I>string</I></B> </DT>
+<DD>Specifies a string leading characters to
+trim from entry pathnames before parsing. This only makes sense if the
+<B>-separator</B> is also set. The default is <I>""</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Sets the requested
+width of the widget. If <I>pixels</I> is 0, then the with is computed from the
+contents of the <B>treeview</B> widget. The default is <I>200</I>. </DD>
+
+<DT><B>-xscrollcommand <I>string</I></B>
+</DT>
+<DD>Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window changes,
+the widget will generate a Tcl command by concatenating the scroll command
+and two numbers. If this option is not specified, then no command will
+be executed. </DD>
+
+<DT><B>-xscrollincrement</B> <I>pixels</I> </DT>
+<DD>Sets the horizontal scrolling distance.
+The default is 20 pixels. </DD>
+
+<DT><B>-yscrollcommand <I>string</I></B> </DT>
+<DD>Specifies the prefix for
+a command used to communicate with vertical scrollbars. Whenever the vertical
+view in the widget's window changes, the widget will generate a Tcl command
+by concatenating the scroll command and two numbers. If this option is
+not specified, then no command will be executed. </DD>
+
+<DT><B>-yscrollincrement</B> <I>pixels</I>
+</DT>
+<DD>Sets the vertical scrolling distance. The default is 20 pixels. </DD>
+</DL>
+
+<H2><A NAME="sect12" HREF="#toc12">Entry Options</A></H2>
+Many
+widget configuration options have counterparts in entries. For example,
+there is a <B>-closecommand</B> configuration option for both widget itself and
+for individual entries. Options set at the widget level are global for
+all entries. If the entry configuration option is set, then it overrides
+the widget option. This is done to avoid wasting memory by replicated options.
+ Most entries will have redundant options. <P>
+There is no resource class or
+name for entries.
+<DL>
+
+<DT><B>-activeicons <I>images</I></B> </DT>
+<DD>Specifies images to be displayed as
+the entry's icon when it is active. This overrides the global <B>-activeicons</B>
+configuration option for the specific entry. <I>Images</I> is a list of two Tk
+images: the first image is displayed when the node is open, the second
+when it is closed. </DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for nodes.
+ <I>TagList</I> is a list of binding tag names. The tags and their order will
+determine how events are handled for nodes. Each tag in the list matching
+the current event sequence will have its Tcl command executed. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-button <I>string</I></B> </DT>
+<DD>Indicates whether a button should be displayed
+on the left side of the node entry. <I>String</I> can be <I>yes</I>, <I>no</I>, or <I>auto</I>. If
+<I>auto</I>, then a button is automatically displayed if the node has children.
+ This is the default. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked
+when the node is closed. This overrides the global <B>-closecommand</B> option
+for this entry. The default is <I>""</I>. Percent substitutions are performed on
+<I>string</I> before it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I>
+</DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-data
+<I>string</I></B> </DT>
+<DD>Sets data fields for the node. <I>String</I> is a list of name-value pairs
+to be set. The default is <I>""</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Sets the font for entry labels.
+ This overrides the widget's <B>-font</B> option for this node. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of the entry label. This overrides
+the widget's <B>-foreground</B> configuration option. The default is <I>""</I>. </DD>
+
+<DT><B>-icons <I>images</I></B>
+</DT>
+<DD>Specifies images to be displayed for the entry's icon. This overrides the
+global <B>-icons</B> configuration option. <I>Images</I> is a list of two Tk images: the
+first image is displayed when the node is open, the second when it is closed.
+</DD>
+
+<DT><B>-label <I>string</I></B> </DT>
+<DD>Sets the text for the entry's label. If not set, this defaults
+to the name of the node. The default is <I>""</I>. </DD>
+
+<DT><B>-opencommand <I>string</I></B> </DT>
+<DD>Specifies
+a Tcl script to be invoked when the entry is opened. This overrides the
+widget's <B>-opencommand</B> option for this node. The default is <I>""</I>. Percent substitutions
+are performed on <I>string</I> before it is executed. The following substitutions
+are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The
+full pathname of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single
+percent. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect13" HREF="#toc13">Button Options</A></H2>
+Button configuration options may also be set by the
+<B>option</B> command. The resource subclass is <I>Button</I>. The resource name is always
+<I>button</I>. <BR>
+<CODE>option add *TreeView.Button.Foreground white<BR>
+option add *TreeView.button.Background blue<BR>
+</CODE><P>The following are the configuration options available for buttons.
+<DL>
+
+<DT><B>-activebackground
+<I>color</I></B> </DT>
+<DD>Sets the background color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background of the button. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the button. The <B>-relief</B> option
+determines if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-closerelief <I>relief</I></B>
+</DT>
+<DD>Specifies the 3-D effect for the closed button. <I>Relief</I> indicates how the
+button should appear relative to the widget; for example, <I>raised</I> means
+the button should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B>
+</DT>
+<DD>Sets the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets
+the foreground color of buttons. The default is <I>black</I>. </DD>
+
+<DT><B>-images <I>images</I></B> </DT>
+<DD>Specifies
+images to be displayed for the button. <I>Images</I> is a list of two Tk images:
+ the first image is displayed when the button is open, the second when
+it is closed. If the <I>images</I> is the empty string, then a plus/minus gadget
+is drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-openrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of
+the open button. <I>Relief</I> indicates how the button should appear relative
+to the widget; for example, <I>raised</I> means the button should appear to protrude.
+ The default is <I>flat</I>. </DD>
+
+<DT><B>-size <I>pixels</I></B> </DT>
+<DD>Sets the requested size of the button.
+ The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect14" HREF="#toc14">Column Options</A></H2>
+Column configuration options may also
+be set by the <B>option</B> command. The resource subclass is <I>Column</I>. The resource
+name is the name of the column. <BR>
+<CODE>option add *TreeView.Column.Foreground white<BR>
+option add *TreeView.treeView.Background blue<BR>
+</CODE><P>The following configuration options are available for columns.
+<DL>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color of the column. This overrides the widget's
+<B>-background</B> option. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the 3-D border of the column. The <B>-relief</B> option determines if a border
+is to be drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-edit <I>boolean</I></B> </DT>
+<DD>Indicates if the column's
+data fields can be edited. If <I>boolean</I> is false, the data fields in the
+column may not be edited. The default is <I>yes</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Specifies
+the foreground color of the column. You can override this for individual
+entries with the entry's <B>-foreground</B> option. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B>
+ </DT>
+<DD>Sets the font for a column. You can override this for individual entries
+with the entry's <B>-font</B> option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, the column is not displayed. The default
+is <I>yes</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the column data fields title should
+be justified within the column. This matters only when the column is wider
+than the data field to be display. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>.
+ The default is <I>left</I>. </DD>
+
+<DT><B>-pad <I>pad</I></B> </DT>
+<DD>Specifies how much padding for the left and
+right sides of the column. <I>Pad</I> is a list of one or two screen distances.
+ If <I>pad</I> has two elements, the left side of the column is padded by the
+first distance and the right side by the second. If <I>pad</I> has just one distance,
+both the left and right sides are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-relief
+<I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the column. <I>Relief</I> specifies how the
+column should appear relative to the widget; for example, <I>raised</I> means
+the column should appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-state <I>state</I></B> </DT>
+<DD>Sets
+the state of the column. If <I>state</I> is <I>disable</I> then the column title can not
+be activated nor invoked. The default is <I>normal</I>. </DD>
+
+<DT><B>-text <I>string</I></B> </DT>
+<DD>Sets the title
+for the column. The default is <I>""</I>. </DD>
+
+<DT><B>-titleforeground <I>color</I></B> </DT>
+<DD>Sets the foreground
+color of the column title. The default is <I>black</I>. </DD>
+
+<DT><B>-titleshadow <I>color</I></B> </DT>
+<DD>Sets
+the color of the drop shadow of the column title. The default is <I>""</I>. </DD>
+
+<DT><B>-width
+<I>pixels</I></B> </DT>
+<DD>Sets the requested width of the column. This overrides the computed
+with of the column. If <I>pixels</I> is 0, the width is computed as from the contents
+of the column. The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect15" HREF="#toc15">Text Editing Options</A></H2>
+Text edit window configuration
+options may also be set by the <B>option</B> command. The resource class is <I>TreeViewEditor</I>.
+The resource name is always <I>edit</I>. <BR>
+<CODE>option add *TreeViewEditor.Foreground white<BR>
+option add *edit.Background blue<BR>
+</CODE><P>The following are the configuration options available for the text editing
+window.
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background of the text edit window. The
+default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the edit window. The <B>-relief</B> option determines if a border is to be drawn.
+ The default is <I>1</I>. </DD>
+
+<DT><B>-exportselection <I>boolean</I></B> </DT>
+<DD>Indicates if the text selection
+is exported. If the edit window is exporting its selection then it will
+observe the standard X11 protocols for handling the selection. Selections
+are available as type <B>STRING</B>. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect of the edit window. <I>Relief</I> indicates how the background should
+appear relative to the edit window; for example, <I>raised</I> means the background
+should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-selectbackground <I>color</I></B>
+</DT>
+<DD>Sets the background of the selected text in the edit window. The default
+is <I>white</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the selected text in the edit window. The <B>-selectrelief</B> option determines
+if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-selectforeground <I>color</I></B> </DT>
+<DD>Sets
+the foreground of the selected text in the edit window. The default is
+<I>white</I>. </DD>
+
+<DT><B>-selectrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the selected text
+in the edit window. <I>Relief</I> indicates how the text should appear relative
+to the edit window; for example, <I>raised</I> means the text should appear to
+protrude. The default is <I>flat</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect16" HREF="#toc16">Default Bindings</A></H2>
+Tk automatically creates
+class bindings for treeviews that give them Motif-like behavior. Much of
+the behavior of a <B>treeview</B> widget is determined by its <B>-selectmode</B> option,
+which selects one of two ways of dealing with the selection. <P>
+If the selection
+mode is <B>single</B>, only one node can be selected at a time. Clicking button
+1 on an node selects it and deselects any other selected item. <P>
+If the selection
+mode is <B>multiple</B>, any number of entries may be selected at once, including
+discontiguous ranges. Clicking Control-Button-1 on a node entry toggles its
+selection state without affecting any other entries. Pressing Shift-Button-1
+on a node entry selects it, extends the selection.
+<OL>
+<LI>In <B>extended</B> mode, the
+selected range can be adjusted by pressing button 1 with the Shift key
+down: this modifies the selection to consist of the entries between the
+anchor and the entry under the mouse, inclusive. The un-anchored end of this
+new selection can also be dragged with the button down. </LI><LI>In <B>extended</B> mode,
+pressing button 1 with the Control key down starts a toggle operation:
+the anchor is set to the entry under the mouse, and its selection state
+is reversed. The selection state of other entries isn't changed. If the mouse
+is dragged with button 1 down, then the selection state of all entries
+between the anchor and the entry under the mouse is set to match that of
+the anchor entry; the selection state of all other entries remains what
+it was before the toggle operation began. </LI><LI>If the mouse leaves the treeview
+window with button 1 down, the window scrolls away from the mouse, making
+information visible that used to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the button
+is released, or the end of the hierarchy is reached. </LI><LI>Mouse button 2 may
+be used for scanning. If it is pressed and dragged over the <B>treeview</B> widget,
+the contents of the hierarchy drag at high speed in the direction the mouse
+moves. </LI><LI>If the Up or Down key is pressed, the location cursor (active entry)
+moves up or down one entry. If the selection mode is <B>browse</B> or <B>extended</B>
+then the new active entry is also selected and all other entries are deselected.
+In <B>extended</B> mode the new active entry becomes the selection anchor. </LI><LI>In <B>extended</B>
+mode, Shift-Up and Shift-Down move the location cursor (active entry) up
+or down one entry and also extend the selection to that entry in a fashion
+similar to dragging with mouse button 1. </LI><LI>The Left and Right keys scroll
+the <B>treeview</B> widget view left and right by the width of the character <B>0</B>.
+Control-Left and Control-Right scroll the <B>treeview</B> widget view left and right
+by the width of the window. Control-Prior and Control-Next also scroll left
+and right by the width of the window. </LI><LI>The Prior and Next keys scroll the
+<B>treeview</B> widget view up and down by one page (the height of the window).
+</LI><LI>The Home and End keys scroll the <B>treeview</B> widget horizontally to the left
+and right edges, respectively. </LI><LI>Control-Home sets the location cursor to the
+the first entry, selects that entry, and deselects everything else in
+the widget. </LI><LI>Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else in the widget. </LI><LI>In <B>extended</B>
+mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End
+extends the selection to the last entry. </LI><LI>In <B>multiple</B> mode, Control-Shift-Home
+moves the location cursor to the first entry and Control-Shift-End moves
+the location cursor to the last entry. </LI><LI>The space and Select keys make a
+selection at the location cursor (active entry) just as if mouse button
+1 had been pressed over this entry. </LI><LI>In <B>extended</B> mode, Control-Shift-space
+and Shift-Select extend the selection to the active entry just as if button
+1 had been pressed with the Shift key down. </LI><LI>In <B>extended</B> mode, the Escape
+key cancels the most recent selection and restores all the entries in the
+selected range to their previous selection state. </LI><LI>Control-slash selects everything
+in the widget, except in <B>single</B> and <B>browse</B> modes, in which case it selects
+the active entry and deselects everything else. </LI><LI>Control-backslash deselects
+everything in the widget, except in <B>browse</B> mode where it has no effect.
+</LI><LI>The F16 key (labelled Copy on many Sun workstations) or Meta-w copies the
+selection in the widget to the clipboard, if there is a selection. </LI>
+</OL>
+<P>
+The behavior
+of <B>treeview</B> widgets can be changed by defining new bindings for individual
+widgets or by redefining the class bindings.
+<H3><A NAME="sect17" HREF="#toc17">Widget Bindings</A></H3>
+In addition
+to the above behavior, the following additional behavior is defined by
+the default widget class (TreeView) bindings.
+<DL>
+
+<DT><I>&lt;ButtonPress-2&gt;</I></DT>
+<DD>Starts scanning.
+ </DD>
+
+<DT><I>&lt;B2-Motion&gt;</I></DT>
+<DD>Adjusts the scan. </DD>
+
+<DT><I>&lt;ButtonRelease-2&gt;</I></DT>
+<DD>Stops scanning. </DD>
+
+<DT><I>&lt;B1-Leave&gt;</I></DT>
+<DD>Starts auto-scrolling.
+</DD>
+
+<DT><I>&lt;B1-Enter&gt;</I></DT>
+<DD>Starts auto-scrolling </DD>
+
+<DT><I>&lt;KeyPress-Up&gt;</I></DT>
+<DD>Moves the focus to the previous
+entry. </DD>
+
+<DT><I>&lt;KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the next entry. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Up&gt;</I></DT>
+<DD>Moves
+the focus to the previous sibling. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the
+next sibling. </DD>
+
+<DT><I>&lt;KeyPress-Prior&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or hidden
+entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Next&gt;</I></DT>
+<DD>Move the focus to the last entry. Closed
+or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Left&gt;</I></DT>
+<DD>Closes the entry. It is not an
+error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-Right&gt;</I></DT>
+<DD>Opens the entry, displaying
+its children. It is not an error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-space&gt;</I></DT>
+<DD>In
+"single" select mode this selects the entry. In "multiple" mode, it toggles
+the entry (if it was previous selected, it is not deselected). </DD>
+
+<DT><I>&lt;KeyRelease-space&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress-Return&gt;</I></DT>
+<DD>Sets the focus to the current entry. </DD>
+
+<DT><I>&lt;KeyRelease-Return&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress&gt;</I></DT>
+<DD>Moves to the next entry whose label starts with
+the letter typed. </DD>
+
+<DT><I>&lt;KeyPress-Home&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or
+hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-End&gt;</I></DT>
+<DD>Move the focus to the last entry.
+Closed or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-F1&gt;</I></DT>
+<DD>Opens all entries. </DD>
+
+<DT><I>&lt;KeyPress-F2&gt;</I></DT>
+<DD>Closes
+all entries (except root). </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Button Bindings</A></H3>
+Buttons have bindings. There are
+associated with the "all" bindtag (see the entry's -bindtag option). You
+can use the <B>bind</B> operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the button of
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the button back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the view so that the current entry is visible. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Entry Bindings</A></H3>
+Entries have
+default bindings. There are associated with the "all" bindtag (see the
+entry's -bindtag option). You can use the <B>bind</B> operation to modify them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the entry back to its normal state. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets
+the selection anchor the current entry. </DD>
+
+<DT><I>&lt;Double-ButtonPress-1&gt;</I></DT>
+<DD>Toggles the selection
+of the current entry. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>For "multiple" mode only. Saves the current
+location of the pointer for auto-scrolling. Resets the selection mark.
+</DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>For "multiple" mode only. Sets the selection anchor to the
+ current entry. </DD>
+
+<DT><I>&lt;Shift-ButtonPress-1&gt;</I></DT>
+<DD>For "multiple" mode only. Extends the selection.
+</DD>
+
+<DT><I>&lt;Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder.
+Does nothing. </DD>
+
+<DT><I>&lt;Shift-ButtonRelease-1&gt;</I></DT>
+<DD>Stop auto-scrolling. </DD>
+
+<DT><I>&lt;Control-ButtonPress-1&gt;</I></DT>
+<DD>For
+"multiple" mode only. Toggles and extends the selection. </DD>
+
+<DT><I>&lt;Control-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-ButtonRelease-1&gt;</I></DT>
+<DD>Stops
+auto-scrolling. </DD>
+
+<DT><I>&lt;Control-Shift-ButtonPress-1&gt;</I></DT>
+<DD>??? </DD>
+
+<DT><I>&lt;Control-Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Column
+Bindings</A></H3>
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the <B>column bind</B>
+operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the current column title. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns
+the column back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Invokes the command (see
+the column's -command option) if one if specified. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Column Rule Bindings</A></H3>
+
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current and activates the ruler. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the column back to its
+normal state. Deactivates the ruler. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets the resize anchor for
+the column. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>Sets the resize mark for the column. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the size of the column, based upon the resize anchor and mark positions.
+</DD>
+</DL>
+
+<H2><A NAME="sect22" HREF="#toc22">Example</A></H2>
+The <B>treeview</B> command creates a new widget. <BR>
+<CODE>treeview .h -bg white<BR>
+</CODE><P>A new Tcl command <I>.h</I> is also created. This command can be used to query
+and modify the <B>treeview</B> widget. For example, to change the background
+color of the table to "green", you use the new command and the widget's
+<B>configure</B> operation. <BR>
+<CODE># Change the background color.<BR>
+.h configure -background "green"<BR>
+</CODE><P>By default, the <B>treeview</B> widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the widget.
+ Above, the new tree object name is ".h". But you can use the <B>-tree</B> option
+to specify the name of another tree. <BR>
+<CODE># View the tree "myTree".<BR>
+.h configure -tree "myTree"<BR>
+</CODE><P>When a new tree is created, it contains only a root node. The node is automatically
+opened. The id of the root node is always <I>0</I> (you can use also use the special
+id <I>root</I>). The <B>insert</B> operation lets you insert one or more new entries into
+the tree. The last argument is the node's <I>pathname</I>. <BR>
+<CODE># Create a new entry named "myEntry"<BR>
+set id [.h insert end "myEntry"]<BR>
+</CODE><P>This appends a new node named "myEntry". It will positioned as the last
+child of the root of the tree (using the position "end"). You can supply
+another position to order the node within its siblings. <BR>
+<CODE># Prepend "fred".<BR>
+set id [.h insert 0 "fred"]<BR>
+</CODE><P>Entry names do not need to be unique. By default, the node's label is its
+name. To supply a different text label, add the <B>-label</B> option. <BR>
+<CODE># Create a new node named "fred"<BR>
+set id [.h insert end "fred" -label "Fred Flintstone"]<BR>
+</CODE><P>The <B>insert</B> operation returns the id of the new node. You can also use the
+<B>index</B> operation to get this information. <BR>
+<CODE># Get the id of "fred"<BR>
+.h index "fred"<BR>
+</CODE><P>To insert a node somewhere other than root, use the <B>-at</B> switch. It takes
+the id of the node where the new child will be added. <BR>
+<CODE># Create a new node "barney" in "fred".<BR>
+.h insert -at $id end "barney" <BR>
+</CODE><P>A pathname describes the path to an entry in the hierarchy. It's a list
+of entry names that compose the path in the tree. Therefore, you can also
+add "barney" to "fred" as follows. <BR>
+<CODE># Create a new sub-entry of "fred"<BR>
+.h insert end "fred barney" <BR>
+</CODE><P>Every name in the list is ancestor of the next. All ancestors must already
+exist. That means that an entry "fred" is an ancestor of "barney" and must
+already exist. But you can use the <B>-autocreate</B> configuration option to force
+the creation of ancestor nodes. <BR>
+<CODE># Force the creation of ancestors.<BR>
+.h configure -autocreate yes <BR>
+.h insert end "fred barney wilma betty" <BR>
+</CODE><P>Sometimes the pathname is already separated by a character sequence rather
+than formed as a list. A file name is a good example of this. You can use
+the <B>-separator</B> option to specify a separator string to split the path into
+its components. Each pathname inserted is automatically split using the
+separator string as a separator. Multiple separators are treated as one.
+<BR>
+<CODE>.h configure -separator /<BR>
+.h insert end "/usr/local/tcl/bin" <BR>
+</CODE><P>If the path is prefixed by extraneous characters, you can automatically
+trim it off using the <B>-trim</B> option. It removed the string from the path
+before it is parsed. <BR>
+<CODE>.h configure -trim C:/windows -separator /<BR>
+.h insert end "C:/window/system" <BR>
+</CODE><P>You can insert more than one entry at a time with the <B>insert</B> operation.
+ This can be much faster than looping over a list of names. <BR>
+<CODE># The slow way<BR>
+foreach f [glob $dir/*] {<BR>
+ .h insert end $f<BR>
+}<BR>
+# The fast way<BR>
+eval .h insert end [glob $dir/*]<BR>
+</CODE><P>In this case, the <B>insert</B> operation will return a list of ids of the new
+entries. <P>
+You can delete entries with the <B>delete</B> operation. It takes one
+or more tags of ids as its argument. It deletes the entry and all its children.
+<BR>
+<CODE>.h delete $id<BR>
+</CODE><P>Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the <B>-label</B> option that
+sets the entry's text label. The <B>entry configure</B> operation lets you set
+or modify an entry's configuration options. <BR>
+<CODE>.h entry configure $id -color red -font fixed<BR>
+</CODE><P>You can hide an entry and its children using the <B>-hide</B> option. <BR>
+<CODE>.h entry configure $id -hide yes<BR>
+</CODE><P>More that one entry can be configured at once. All entries specified are
+configured with the same options. <BR>
+<CODE>.h entry configure $i1 $i2 $i3 $i4 -color brown <BR>
+</CODE><P>An icon is displayed for each entry. It's a Tk image drawn to the left of
+the label. You can set the icon with the entry's <B>-icons</B> option. It takes
+a list of two image names: one to represent the open entry, another when
+it is closed. <BR>
+<CODE>set im1 [image create photo -file openfolder.gif]<BR>
+set im2 [image create photo -file closefolder.gif]<BR>
+.h entry configure $id -icons "$im1 $im2"<BR>
+</CODE><P>If <B>-icons</B> is set to the empty string, no icons are display. <P>
+If an entry has
+children, a button is displayed to the left of the icon. Clicking the mouse
+on this button opens or closes the sub-hierarchy. The button is normally
+a <I>+</I> or <I>-</I> symbol, but can be configured in a variety of ways using the <B>button
+configure</B> operation. For example, the <I>+</I> and <I>-</I> symbols can be replaced with
+Tk images. <BR>
+<CODE>set im1 [image create photo -file closefolder.gif]<BR>
+set im2 [image create photo -file downarrow.gif]<BR>
+.h button configure $id -images "$im1 $im2" \<BR>
+ -openrelief raised -closerelief raised<BR>
+</CODE><P>Entries can contain an arbitrary number of <I>data fields</I>. Data fields are
+name-value pairs. Both the value and name are strings. The entry's <B>-data</B> option
+lets you set data fields. <BR>
+<CODE>.h entry configure $id -data {mode 0666 group users}<BR>
+</CODE><P>The <B>-data</B> takes a list of name-value pairs. <P>
+You can display these data fields
+as <I>columns</I> in the <B>treeview</B> widget. You can create and configure columns
+with the <B>column</B> operation. For example, to add a new column to the widget,
+use the <B>column insert</B> operation. The last argument is the name of the data
+field that you want to display. <BR>
+<CODE>.h column insert end "mode"<BR>
+</CODE><P>The column title is displayed at the top of the column. By default, it's
+is the field name. You can override this using the column's <B>-text</B> option.
+<BR>
+<CODE>.h column insert end "mode" -text "File Permissions"<BR>
+</CODE><P>Columns have several configuration options. The <B>column configure</B> operation
+lets you query or modify column options. <BR>
+<CODE>.h column configure "mode" -justify left<BR>
+</CODE><P>The <B>-justify</B> option says how the data is justified within in the column.
+ The <B>-hide</B> option indicates whether the column is displayed. <BR>
+<CODE>.h column configure "mode" -hide yes<BR>
+</CODE><P>Entries can be selected by clicking on the mouse. Selected entries are
+drawn using the colors specified by the <B>-selectforeground</B> and <B>-selectbackground</B>
+configuration options. The selection itself is managed by the <B>selection</B>
+operation. <BR>
+<CODE># Clear all selections<BR>
+.h selection clear 0 end<BR>
+# Select the root node<BR>
+.h selection set 0 <BR>
+</CODE><P>The <B>curselection</B> operation returns a list of ids of all the selected entries.
+<BR>
+<CODE>set ids [.h curselection]<BR>
+</CODE><P>You can use the <B>get</B> operation to convert the ids to their pathnames. <BR>
+<CODE>set names [eval .h get -full $ids]<BR>
+</CODE><P>If a treeview is exporting its selection (using the <B>-exportselection</B> option),
+then it will observe the standard X11 protocols for handling the selection.
+ Treeview selections are available as type <B>STRING</B>; the value of the selection
+will be the pathnames of the selected entries, separated by newlines. <P>
+The
+<B>treeview</B> supports two modes of selection: <I>single</I> and <I>multiple</I>. In single
+select mode, only one entry can be selected at a time, while multiple select
+mode allows several entries to be selected. The mode is set by the widget's
+<B>-selectmode</B> option. <BR>
+<CODE>.h configure -selectmode "multiple"<BR>
+</CODE><P>You can be notified when the list of selected entries changes. The widget's
+<B>-selectcommand</B> specifies a Tcl procedure that is called whenever the selection
+changes. <BR>
+<CODE>proc SelectNotify { widget } {<BR>
+ set ids [$widget curselection]<BR>
+}<BR>
+.h configure -selectcommand "SelectNotify .h"<BR>
+</CODE><P>The widget supports the standard Tk scrolling and scanning operations. The
+<B>treeview</B> can be both horizontally and vertically. You can attach scrollbars
+to the <B>treeview</B> the same way as the listbox or canvas widgets. <BR>
+<CODE>scrollbar .xbar -orient horizontal -command ".h xview"<BR>
+scrollbar .ybar -orient vertical -command ".h yview"<BR>
+.h configure -xscrollcommand ".xbar set" \<BR>
+ -yscrollcommand ".ybar set"<BR>
+</CODE><P>There are three different modes of scrolling: <I>listbox</I>, <I>canvas</I>, and <I>hierbox</I>.
+ In <I>listbox</I> mode, the last entry can always be scrolled to the top of the
+widget. In <I>hierbox</I> mode, the last entry is always drawn at the bottom of
+the widget. The scroll mode is set by the widget's <B>-selectmode</B> option. <BR>
+<CODE>.h configure -scrollmode "listbox"<BR>
+</CODE><P>Entries can be programmatically opened or closed using the <B>open</B> and <B>close</B>
+operations respectively. <BR>
+<CODE>.h open $id<BR>
+.h close $id<BR>
+</CODE><P>When an entry is opened, a Tcl procedure can be automatically invoked. The
+<B>-opencommand</B> option specifies this procedure. This procedure can lazily
+insert entries as needed. <BR>
+<CODE>proc AddEntries { dir } {<BR>
+ eval .h insert end [glob -nocomplain $dir/*] <BR>
+}<BR>
+.h configure -opencommand "AddEntries %P"<BR>
+</CODE><P>Now when an entry is opened, the procedure <I>AddEntries</I> is called and adds
+children to the entry. Before the command is invoked, special "%" substitutions
+(like <B>bind</B>) are performed. Above, <I>%P</I> is translated to the pathname of the
+entry. <P>
+The same feature exists when an entry is closed. The <B>-closecommand</B>
+option specifies the procedure. <BR>
+<CODE>proc DeleteEntries { id } {<BR>
+ .h entry delete $id 0 end<BR>
+}<BR>
+.h configure -closecommand "DeleteEntries %#"<BR>
+</CODE><P>When an entry is closed, the procedure <I>DeleteEntries</I> is called and deletes
+the entry's children using the <B>entry delete</B> operation (<I>%#</I> is the id of entry).
+
+<H2><A NAME="sect23" HREF="#toc23">Keywords</A></H2>
+treeview, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Tree Data Object</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">IDs and Tags</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Special Node IDs</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Data Fields</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Entry Bindings</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Treeview Operations</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Treeview Options</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Entry Options</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Button Options</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Column Options</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Text Editing Options</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Default Bindings</A></LI>
+<UL>
+<LI><A NAME="toc17" HREF="#sect17">Widget Bindings</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Button Bindings</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Entry Bindings</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Column Bindings</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Column Rule Bindings</A></LI>
+</UL>
+<LI><A NAME="toc22" HREF="#sect22">Example</A></LI>
+<LI><A NAME="toc23" HREF="#sect23">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/hiertable.html b/blt/html/hiertable.html
new file mode 100644
index 00000000000..7f56d0e0a47
--- /dev/null
+++ b/blt/html/hiertable.html
@@ -0,0 +1,2331 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>treeview(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+treeview - Create and manipulate hierarchical
+table widgets
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>treeview</B> <I>pathName </I>?<I>options</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>treeview</B>
+widget displays a tree of data. It replaces both the <B>hiertable</B> and <B>hierbox</B>
+widgets. The <B>treeview</B> is 100% syntax compatible with the <B>hiertable</B> widget.
+ The <B>hiertable</B> command is retained for sake of script-level compatibility.
+ This widget obsoletes the <B>hierbox</B> widget. It does everything the old <B>hierbox</B>
+widget did, but also provides data sharing (via <I>tree data objects</I>) and
+the ability to tag nodes.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The <B>treeview</B> widget displays hierarchical
+data. Data is represented as nodes in a general-ordered tree. Each node
+may have sub-nodes and these nodes can in turn has their own children. <P>
+A
+node is displayed as a row entry in the widget. Each entry has a text label
+and icon. When a node has children, its entry is drawn with a small button
+to the left of the label. Clicking the mouse over this button opens or
+closes the node. When a node is <I>open</I>, its children are exposed. When it
+is <I>closed</I>, the children and their descedants are hidden. The button is
+normally a <I>+</I> or <I>-</I> symbol (ala Windows Explorer), but can be replaced with
+a pair of Tk images (open and closed images). <P>
+If the node has data associated
+with it, they can be displayed in columns running vertically on either
+side the tree. You can control the color, font, etc of each entry. Any
+entry label or data field can be edited in-place.
+<H2><A NAME="sect4" HREF="#toc4">Tree Data Object</A></H2>
+The tree
+is not stored inside the widget but in a tree data object (see the <B>tree</B>
+command for a further explanation). Tree data objects can be shared among
+different clients, such as a <B>treeview</B> widget or the <B>tree</B> command. You can
+walk the tree and manage its data with the <B>tree</B> command tree, while displaying
+it with the <B>treeview</B> widget. Whenever the tree is updated, the <B>treeview</B>
+widget is automatically redrawn. <P>
+By default, the <B>treeview</B> widget creates
+its own tree object. The tree initially contains just a root node. But you
+can also display trees created by the <B>tree</B> command using the <B>-tree</B> configuration
+option. <B>Treeview</B> widgets can share the same tree object, possibly displaying
+different views of the same data. <P>
+A tree object has both a Tcl and C API.
+ You can insert or delete nodes using <B>treeview</B> widget or <B>tree</B> command operations,
+but also from C code. For example, you can load the tree from your C code
+while still managing and displaying the tree from Tcl. The widget is automatically
+notified whenever the tree is modified via C or Tcl.
+<H2><A NAME="sect5" HREF="#toc5">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>treeview <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>treeview</B> command creates a new window <I>pathName</I> and makes it into a
+<B>treeview</B> widget. At the time this command is invoked, there must not exist
+a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options
+may be specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the <B>configure</B> operation
+below for the exact details about what <I>option</I> and <I>value</I> pairs are valid.
+<P>
+If successful, <B>treeview</B> returns the path name of the widget. It also creates
+a new Tcl command by the same name. You can use this command to invoke
+various operations that query or modify the widget. The general form is:
+<BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available are described in the <FONT SIZE=-1><B>TREEVIEW OPERATIONS</B></FONT>
+ section.
+
+<H2><A NAME="sect6" HREF="#toc6">IDs and Tags</A></H2>
+Nodes can be inserted into a tree using the <B>treeview</B> widget
+ <BR>
+<CODE>blt::treeview .t<BR>
+set node [.t insert end root "one"]<BR>
+</CODE><P>or <B>tree</B> command. <BR>
+<CODE>set tree [blt::tree create]<BR>
+set node [$tree insert root "one"]<BR>
+</CODE><P>In both cases, a number identifying the node is returned (the value of
+<I>$node</I>). This serial number or <I>id</I> uniquely identifies the node. Please note
+that you can't infer a location or position of a node from its id. The only
+exception is that the root node is always id <I>0</I>. Since nodes may have the
+same labels or be moved within the tree, ids provide an convenient way
+to identify nodes. If a tree is shared, the ids will be the same regardless
+if you are using by the <B>treeview</B> widget or the <B>tree</B> command. Ids are recycled
+when the node deleted. <P>
+A node may also have any number of <I>tags</I> associated
+with it. A tag is just a string of characters, and it may take any form
+except that of an integer. For example, "<I>x123</I>" is valid, but "<I>123</I>" isn't.
+ The same tag may be associated with many different nodes. This is typically
+done to associate a group of nodes. Many operations in the <B>treeview</B> widget
+take either node ids or tag names as arguments. Using a tag says to apply
+the operation to all nodes with that tag. <P>
+The tag <B>all</B> is implicitly associated
+with every node in the tree. It may be used to invoke operations on all
+the nodes in the tree. <P>
+Tags may be shared, just like trees, between clients.
+ For example, you can use the tags created by the <B>tree</B> command with <B>treeview</B>
+widgets.
+<H2><A NAME="sect7" HREF="#toc7">Special Node IDs</A></H2>
+There are also several special non-numeric ids.
+Special ids differ from tags in that they are always translated to their
+numeric equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to the
+<B>treeview</B> widget.
+<DL>
+
+<DT><B>active</B> </DT>
+<DD>The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon (see the <B>-activeicon</B>
+option). The <B>active</B> id is changed automatically by moving the mouse pointer
+over another node or by using the <B>entry activate</B> operation. Note that there
+can be only one active node at a time. </DD>
+
+<DT><B>anchor</B> </DT>
+<DD>The node representing the
+fixed end of the current selection. The anchor is set by the <B>selection
+anchor</B> operation. </DD>
+
+<DT><B>current</B> </DT>
+<DD>The node where the mouse pointer is currently
+located. But unlike <B>active</B>, this id changes while the selection is dragged.
+ It is used to determine the current node during button drags. </DD>
+
+<DT><B>down</B> </DT>
+<DD>The
+next open node from the current focus. The <B>down</B> of the last open node is
+the same. </DD>
+
+<DT><B>end</B> </DT>
+<DD>The last open node (in depth-first order) on the tree. </DD>
+
+<DT><B>focus</B>
+</DT>
+<DD>The node that currently has focus. When a node has focus, it receives key
+events. To indicate focus, the node is drawn with a dotted line around
+its label. You can change the focus using the <B>focus</B> operation. </DD>
+
+<DT><B>last</B> </DT>
+<DD>The
+last open node from the current focus. But unlike <B>up</B>, when the focus is
+at root, <B>last</B> wraps around to the last open node in the tree. </DD>
+
+<DT><B>mark</B> </DT>
+<DD>The node
+representing the non-fixed end of the current selection. The mark is set
+by the <B>selection mark</B> operation. </DD>
+
+<DT><B>next</B> </DT>
+<DD>The next open node from the current
+focus. But unlike <B>down</B>, when the focus is on last open node, <B>next</B> wraps
+around to the root node. </DD>
+
+<DT><B>nextsibling</B> </DT>
+<DD>The next sibling from the node with
+the current focus. If the node is already the last sibling then it is the
+<B>nextsibling<B>. </DD>
+
+<DT><B>parent</B></B></B> </DT>
+<DD>The parent of the node with the current focus. The <B>parent</B>
+of the root is also the root. </DD>
+
+<DT><B>prevsibling</B> </DT>
+<DD>The previous sibling from the
+node with the current focus. If the node is already the first sibling then
+it is the <B>prevsibling<B>. </DD>
+
+<DT><B>root</B></B></B> </DT>
+<DD>The root node. You can also use id <I>0</I> to indicate
+the root. </DD>
+
+<DT><B>up</B> </DT>
+<DD>The last open node (in depth-first order) from the current focus.
+The <B>up</B> of the root node (i.e. the root has focus) is also the root. </DD>
+
+<DT><B>view.top</B>
+</DT>
+<DD>First node that's current visible in the widget. </DD>
+
+<DT><B>view.bottom</B> </DT>
+<DD>Last node that's
+current visible in the widget. </DD>
+
+<DT><I>path</I> </DT>
+<DD>Absolute path of a node. Path names
+refer to the node name, not their entry labels. Paths don't have to start
+with a separator (see the <B>-separator</B> configuration option), but component
+names must be separated by the designated separator. </DD>
+
+<DT><B>@<I>x<B>,<I>y</I></B></I></B> </DT>
+<DD>Indicates the
+node that covers the point in the treeview window specified by <I>x</I> and <I>y</I>
+(in pixel coordinates). If no part of the entryd covers that point, then
+the closest node to that point is used. </DD>
+</DL>
+<P>
+A node may be specified as an id
+or tag. If the specifier is an integer then it is assumed to refer to the
+single node with that id. If the specifier is not an integer, it's checked
+to see if it's a special id (such as focus). Otherwise, it's assumed to be
+tag. Some operations only operate on a single node at a time; if a tag
+refers to more than one node, then an error is generated.
+<H2><A NAME="sect8" HREF="#toc8">Data Fields</A></H2>
+A node
+in the tree can have <I>data fields</I>. A data field is a name-value pair, used
+to represent arbitrary data in the node. Nodes can contain different fields
+(they aren't required to contain the same fields). You can optionally display
+these fields in the <B>treeview</B> widget in columns running on either side of
+the displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a specific
+field is left blank. Columns can be interactively resized, hidden, or,
+moved.
+<H2><A NAME="sect9" HREF="#toc9">Entry Bindings</A></H2>
+You can bind Tcl commands to be invoked when events
+occur on nodes (much like Tk canvas items). You can bind a node using its
+id or its <I>bindtags</I>. Bindtags are simply names that associate a binding
+with one or more nodes. There is a built-in tag <I>all</I> that all node entries
+automatically have.
+<H2><A NAME="sect10" HREF="#toc10">Treeview Operations</A></H2>
+The <B>treeview</B> operations are the invoked
+by specifying the widget's pathname, the operation, and any arguments that
+pertain to that operation. The general form is: <P>
+<BR>
+<CODE><I>pathName operation </I>?<I>arg arg ...</I>?<BR>
+<P>
+</CODE><P><I>Operation</I> and the <I>arg</I>s determine the exact behavior of the command. The
+following operation are available for <B>treeview</B> widgets:
+<DL>
+
+<DT><I>pathName <B>bbox</B></I> ?<B>-screen</B>?
+<I>tagOrId...</I> </DT>
+<DD>Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more <I>tagOrId</I> arguments.
+ If the <B>-screen</B> flag is given, then the x-y coordinates of the bounding
+box are returned as screen coordinates, not virtual coordinates. Virtual
+coordinates start from <I>0</I> from the root node. The returned list contains
+the following values. <blockquote></DD>
+
+<DT><I>x</I> </DT>
+<DD>X-coordinate of the upper-left corner of the bounding
+box. </DD>
+
+<DT><I>y</I> </DT>
+<DD>Y-coordinate of the upper-left corner of the bounding box. </DD>
+
+<DT><I>width</I> </DT>
+<DD>Width
+of the bounding box. </DD>
+
+<DT><I>height</I> </DT>
+<DD>Height of the bounding box. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for a node with this tag, <I>command</I>
+will be invoked. The syntax is similar to the <B>bind</B> command except that
+it operates on <B>treeview</B> entries, rather than widgets. See the <B>bind</B> manual
+entry for complete details on <I>sequence</I> and the substitutions performed
+on <I>command</I> before invoking it. <P>
+If all arguments are specified then a
+new binding is created, replacing any existing binding for the same <I>sequence</I>
+and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I> augments
+an existing binding rather than replacing it. If no <I>command</I> argument is
+provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button <I>operation</I></B></I> ?<I>args</I>?
+</DT>
+<DD>This command is used to control the button selectors within a <B>treeview</B>
+widget. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>button
+activate</B></I> <I>tagOrId</I> </DT>
+<DD>Designates the node given by <I>tagOrId</I> as active. When
+a node is active it's entry is drawn using its active icon (see the <B>-activeicon</B>
+option). Note that there can be only one active entry at a time. The special
+id <B>active</B> indicates the currently active node. </DD>
+
+<DT><I>pathName <B>button bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for an button of a node entry with
+this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B> command
+except that it operates on <B>treeview</B> buttons, rather than widgets. See the
+<B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>button configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the configuration options of the widget. If no <I>option</I> is specified, returns
+a list describing all of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B>
+for information on the format of this list). If <I>option</I> is specified with
+no <I>value</I>, then the command returns a list describing the one named option
+(this list will be identical to the corresponding sublist of the value
+returned if no <I>option</I> is specified). If one or more <I>option-value</I> pairs are
+specified, then the command modifies the given widget option(s) to have
+the given value(s); in this case the command returns an empty string. <I>Option</I>
+and <I>value</I> are described in the section <FONT SIZE=-1><B>BUTTON OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value of the configuration option given
+by <I>option</I>. <I>Option</I> may have any of the values accepted by the <B>configure</B> operation
+described below. </DD>
+
+<DT><I>pathName <B>close </B></I>?<B>-recurse</B>? <I>tagOrId...</I> </DT>
+<DD>Closes the node specified
+by <I>tagOrId</I>. In addition, if a Tcl script was specified by the <B>-closecommand</B>
+option, it is invoked. If the node is already closed, this command has
+no effect. If the <B>-recurse</B> flag is present, each child node is recursively
+closed. </DD>
+
+<DT><I>pathName <B>column <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are available
+for treeview columns. <blockquote></DD>
+
+<DT><I>pathName <B>column activate</B></I> <I>column</I> </DT>
+<DD>Sets the active column
+to <I>column</I>. <I>Column</I> is the name of a column in the widget. When a column is
+active, it's drawn using its <B>-activetitlebackground</B> and <B>-activetitleforeground</B>
+options. If <I>column</I> is the <I>""</I>, then no column will be active. If no column
+argument is provided, then the name of the currently active column is returned.
+</DD>
+
+<DT><I>pathName <B>column cget</B></I> <I>name</I> <I>option</I> </DT>
+<DD>Returns the current value of the column
+configuration option given by <I>option</I> for <I>name</I>. <I>Name</I> is the name of column
+that corresponds to a data field. <I>Option</I> may have any of the values accepted
+by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>column configure</B></I> <I>name</I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the column designated by <I>name</I>. <I>Name</I> is the name of the column corresponding
+to a data field. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>COLUMN OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>column delete</B></I> <I>field</I>
+?<I>field</I>...? </DT>
+<DD>Deletes one of more columns designated by <I>field</I>. Note that this
+does not delete the data fields themselves. </DD>
+
+<DT><I>pathName <B>column insert</B></I> <I>position</I>
+<I>field</I> ?<I>options</I>...? </DT>
+<DD>Inserts one of more columns designated by <I>field</I>. A column
+displays each node's data field by the same name. If the node doesn't have
+the given field, the cell is left blank. <I>Position</I> indicates where in the
+list of columns to add the new column. It may be either a number or <I>end</I>.
+ </DD>
+
+<DT><I>pathName <B>column invoke</B></I> <I>field</I> </DT>
+<DD>Invokes the Tcl command associated with the
+column <I>field</I>, if there is one (using the column's <B>-command</B> option). The
+command is ignored if the column's <B>-state</B> option set to <I>disabled</I>. </DD>
+
+<DT><I>pathName
+<B>column move <I>name</I></B></I> <I>dest</I> </DT>
+<DD>Moves the column <I>name</I> to the destination position.
+ <I>Dest</I> is the name of another column or a screen position in the form <I>@<I>x<I>,<I>y</I></I></I></I>.
+</DD>
+
+<DT><I>pathName <B>column names</B></I> </DT>
+<DD>Returns a list of the names of all columns in the
+widget. The list is ordered as the columns are drawn from left-to-right. </DD>
+
+<DT><I>pathName
+<B>column nearest</B></I> <I>x</I> ?<I>y</I>? </DT>
+<DD>Returns the name of the column closest to the given
+X-Y screen coordinate. If you provide a <I>y</I> argument (it's optional), a name
+is returned only when if the point is over a column's title. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>configure</B></I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the widget. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>TREEVIEW OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>curselection</B></I>
+</DT>
+<DD>Returns a list containing the ids of all of the entries that are currently
+selected. If there are no entries selected, then the empty string is returned.
+</DD>
+
+<DT><I>pathName <B>delete <I>tagOrId</I></B></I>... </DT>
+<DD>Deletes one or more entries given by <I>tagOrId</I> and
+its children. </DD>
+
+<DT><I>pathName <B>entry <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are
+available for treeview entries. <blockquote></DD>
+
+<DT><I>pathName <B>entry activate</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the
+active entry to the one specified by <I>tagOrId</I>. When an entry is active
+it is drawn using its active icon (see the <B>-activeicon</B> option). Note that
+there can be only one active node at a time. The special id of the currently
+active node is <B>active</B>. </DD>
+
+<DT><I>pathName <B>entry cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>entry
+children</B></I> <I>tagOrId</I> ?<I>first</I>? ?<I>last</I>? </DT>
+<DD>Returns a list of ids for the given range
+of children of <I>tagOrId</I>. <I>TagOrId</I> is the id or tag of the node to be examined.
+If only a <I>first</I> argument is present, then the id of the that child at
+that numeric position is returned. If both <I>first</I> and <I>last</I> arguments are
+given, then the ids of all the children in that range are returned. Otherwise
+the ids of all children are returned. </DD>
+
+<DT><I>pathName <B>entry configure</B></I> ?<I>option</I>?
+?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options of the
+widget. If no <I>option</I> is specified, returns a list describing all of the
+available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information on
+the format of this list). If <I>option</I> is specified with no <I>value</I>, then the
+command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described below: </DD>
+
+<DT><I>pathName <B>entry delete</B></I> <I>tagOrId</I> ?<I>first</I> ?<I>last</I>? </DT>
+<DD>Deletes the
+one or more children nodes of the parent <I>tagOrId</I>. If <I>first</I> and <I>last</I> arguments
+are present, they are positions designating a range of children nodes to
+be deleted. </DD>
+
+<DT><I>pathName <B>entry isbefore <I>tagOrId1</I></B></I> <I>tagOrId2</I> </DT>
+<DD>Returns 1 if <I>tagOrId1</I>
+is before <I>tagOrId2</I> and 0 otherwise. </DD>
+
+<DT><I>pathName <B>entry ishidden <I>tagOrId</I></B></I> </DT>
+<DD>Returns
+1 if the node is currently hidden and 0 otherwise. A node is also hidden
+if any of its ancestor nodes are closed or hidden. </DD>
+
+<DT><I>pathName <B>entry isopen
+<I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the node is currently open and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>entry size</B></I> <B>-recurse</B> <I>tagOrId</I> </DT>
+<DD>Returns the number of children for parent node
+<I>tagOrId</I>. If the <B>-recurse</B> flag is set, the number of all its descendants
+is returned. The node itself is not counted. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>find </B></I>?<I>flags</I>? <I>first</I>
+<I>last</I> </DT>
+<DD>Finds for all entries matching the criteria given by <I>flags</I>. A list
+of ids for all matching nodes is returned. <I>First</I> and <I>last</I> are ids designating
+the range of the search in depth-first order. If <I>last</I> is before <I>first</I>, then
+nodes are searched in reverse order. The valid flags are: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B>
+</DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern
+to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match
+against the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Patterns must match
+exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching
+is done in a fashion similar to that used by the C-shell. For the two
+strings to match, their contents must be identical except that the following
+ special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of
+ characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character
+in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a
+sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I>
+and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This
+provides a way of avoiding the special interpretation of the characters
+<I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern matching (i.e.
+the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Pick entries
+that don't match. </DD>
+
+<DT><B>-exec<I> string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked for
+each matching node. Percent substitutions are performed on <I>string</I> before
+ it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-count<I> number</I></B> </DT>
+<DD>Stop
+searching after <I>number</I> matches. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>focus
+</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the focus to the node given by <I>tagOrId</I>. When a node has focus,
+it can receive keyboard events. The special id <B>focus</B> designates the node
+that currently has focus. </DD>
+
+<DT><I>pathName <B>get </B></I>?<B>-full</B>? <I>tagOrId</I> <I>tagOrId</I>... </DT>
+<DD>Translates
+one or more ids to their node entry names. It returns a list of names
+for all the ids specified. If the <B>-full</B> flag is set, then the full pathnames
+are returned. </DD>
+
+<DT><I>pathName <B>hide </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Hides all nodes matching the
+criteria given by <I>flags</I>. The search is performed recursively for each node
+given by <I>tagOrId</I>. The valid flags are described below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies
+pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match
+against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match against
+the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Match patterns exactly. The
+is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching is done in a
+fashion similar to that used by the C-shell. For the two strings to match,
+their contents must be identical except that the following special sequences
+ may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of characters in string,
+including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I>
+</DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a sequence of the form
+<I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I> and <I>y</I>, inclusive, will
+match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This provides a way of avoiding
+ the special interpretation of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B>
+</DT>
+<DD>Use regular expression pattern matching (i.e. the same as implemented by
+the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Hide nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates
+the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>index </B></I>?<B>-at</B> <I>tagOrId</I>? <I>string</I> </DT>
+<DD>Returns the id of
+the node specified by <I>string</I>. <I>String</I> may be a tag or node id. Some special
+ids are normally relative to the node that has focus. The <B>-at</B> flag lets
+you select another node. </DD>
+
+<DT><I>pathName <B>insert </B></I>?<B>-at <I>tagOrId</I></B>? <I>position</I> <I>path</I> ?<I>options...</I>?
+?<I>path</I>? ?<I>options...</I>? </DT>
+<DD>Inserts one or more nodes at <I>position</I>. <I>Position</I> is the
+location (number or <I>end</I>) where the new nodes are added to the parent node.
+ <I>Path</I> is the pathname of the new node. Pathnames can be formated either
+as a Tcl list (each element is a path component) or as a string separated
+by a special character sequence (using the <B>-separator</B> option). Pathnames
+are normally absolute, but the <B>-at</B> switch lets you select a relative starting
+point. Its value is the id of the starting node. <P>
+All ancestors of the
+new node must already exist, unless the <B>-autocreate</B> option is set. It is
+also an error if a node already exists, unless the <B>-allowduplicates</B> option
+is set. <P>
+<I>Option</I> and <I>value</I> may have any of the values accepted by the <B>entry
+configure</B> operation described in the <FONT SIZE=-1><B>ENTRY OPERATIONS</B></FONT>
+ section below. This
+command returns a list of the ids of the new entries. </DD>
+
+<DT><I>pathName <B>move <I>tagOrId</I></B></I>
+<I>how</I> <I>destId</I> </DT>
+<DD>Moves the node given by <I>tagOrId</I> to the destination node. The
+node can not be an ancestor of the destination. <I>DestId</I> is the id of the
+destination node and can not be the root of the tree. In conjunction with
+<I>how</I>, it describes how the move is performed. <blockquote></DD>
+
+<DT><I>before</I> </DT>
+<DD>Moves the node before
+the destination node. </DD>
+
+<DT><I>after</I> </DT>
+<DD>Moves the node after the destination node. </DD>
+
+<DT><I>into</I>
+</DT>
+<DD>Moves the node to the end of the destination's list of children. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>nearest <I>x y</I></B></I> ?<I>varName</I>? </DT>
+<DD>Returns the id of the node entry closest to the given
+X-Y screen coordinate. The optional argument <I>varName</I> is the name of variable
+which is set to either <I>button</I> or <I>select</I> to indicate over what part of the
+node the coordinate lies. If the coordinate is not directly over any node,
+then <I>varName</I> will contain the empty string. </DD>
+
+<DT><I>pathName <B>open </B></I>?<B>-recurse</B>? <I>tagOrId...</I>
+</DT>
+<DD>Opens the one or more nodes specified by <I>tagOrId</I>. If a node is not already
+open, the Tcl script specified by the <B>-opencommand</B> option is invoked. If
+the <B>-recurse</B> flag is present, then each descendant is recursively opened.
+ </DD>
+
+<DT><I>pathName <B>range</B></I> ?<B>-open</B>? <I>first last</I> </DT>
+<DD>Returns the ids in depth-first order
+of the nodes between the <I>first</I> and <I>last</I> ids. If the <B>-open</B> flag is present,
+it indicates to consider only open nodes. If <I>last</I> is before <I>first</I>, then
+the ids are returned in reverse order. </DD>
+
+<DT><I>pathName <B>scan</B></I> <I>option args</I> </DT>
+<DD>This command
+implements scanning. It has two forms, depending on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>scan
+mark <I>x y</I></B></I> </DT>
+<DD>Records <I>x</I> and <I>y</I> and the current view in the treeview window;
+used in conjunction with later <B>scan dragto</B> commands. Typically this command
+is associated with a mouse button press in the widget. It returns an empty
+string. </DD>
+
+<DT><I>pathName <B>scan dragto <I>x y</I></B></I>. </DT>
+<DD>Computes the difference between its <I>x</I> and
+<I>y</I> arguments and the <I>x</I> and <I>y</I> arguments to the last <B>scan mark</B> command for
+the widget. It then adjusts the view by 10 times the difference in coordinates.
+ This command is typically associated with mouse motion events in the widget,
+to produce the effect of dragging the list at high speed through the window.
+ The return value is an empty string. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>see</B></I> ?<B>-anchor <I>anchor</I></B>? <I>tagOrId</I>
+</DT>
+<DD>Adjusts the view of entries so that the node given by <I>tagOrId</I> is visible
+in the widget window. It is an error if <B>tagOrId</B> is a tag that refers to
+more than one node. By default the node's entry is displayed in the middle
+of the window. This can changed using the <B>-anchor</B> flag. Its value is a Tk
+anchor position. </DD>
+
+<DT><I>pathName <B>selection <I>option arg</I></B></I> </DT>
+<DD>This command is used to adjust
+the selection within a <B>treeview</B> widget. It has several forms, depending
+on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>selection anchor <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection anchor
+to the node given by <I>tagOrId</I>. If <I>tagOrId</I> refers to a non-existent node, then
+the closest node is used. The selection anchor is the end of the selection
+that is fixed while dragging out a selection with the mouse. The special
+id <B>anchor</B> may be used to refer to the anchor node. </DD>
+
+<DT><I>pathName <B>selection cancel</B></I>
+</DT>
+<DD>Clears the temporary selection of entries back to the current anchor. Temporary
+selections are created by the <B>selection mark</B> operation. </DD>
+
+<DT><I>pathName <B>selection
+clear <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Removes the entries between <I>first</I> and <I>last</I> (inclusive)
+from the selection. Both <I>first</I> and <I>last</I> are ids representing a range of
+entries. If <I>last</I> isn't given, then only <I>first</I> is deselected. Entries outside
+the selection are not affected. </DD>
+
+<DT><I>pathName <B>selection clearall</B></I> </DT>
+<DD>Clears the entire
+selection. </DD>
+
+<DT><I>pathName <B>selection mark <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection mark to
+the node given by <I>tagOrId</I>. This causes the range of entries between the
+anchor and the mark to be temporarily added to the selection. The selection
+mark is the end of the selection that is fixed while dragging out a selection
+with the mouse. The special id <B>mark</B> may be used to refer to the current
+ mark node. If <I>tagOrId</I> refers to a non-existent node, then the mark is ignored.
+Resetting the mark will unselect the previous range. Setting the anchor
+finalizes the range. </DD>
+
+<DT><I>pathName <B>selection includes <I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the
+node given by <I>tagOrId</I> is currently selected, 0 if it isn't. </DD>
+
+<DT><I>pathName <B>selection
+present</B></I> </DT>
+<DD>Returns 1 if any nodes are currently selected and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>selection set <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects all of the nodes in the range between
+<I>first</I> and <I>last</I>, inclusive, without affecting the selection state of nodes
+outside that range. </DD>
+
+<DT><I>pathName <B>selection toggle <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects/deselects
+nodes in the range between <I>first</I> and <I>last</I>, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and visa versa.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>show </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Exposes all nodes matching the criteria given
+by <I>flags</I>. This is the inverse of the <B>hide</B> operation. The search is performed
+recursively for each node given by <I>tagOrId</I>. The valid flags are described
+below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I>
+pattern</I></B> </DT>
+<DD>Specifies pattern to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B>
+</DT>
+<DD>Specifies pattern to match against the entry's configuration option. </DD>
+
+<DT><B>-exact</B>
+</DT>
+<DD>Match patterns exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD><B>-glob</B> Use global pattern
+matching. Matching is done in a fashion similar to that used by the C-shell.
+ For the two strings to match, their contents must be identical except
+that the following special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches
+ any sequence of characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches
+any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set
+given by <I>chars</I>. If a sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any
+character between <I>x</I> and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single
+ character <I>x</I>. This provides a way of avoiding the special interpretation
+of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern
+matching (i.e. the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B>
+</DT>
+<DD>Expose nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort</B></I>
+?<I>operation</I>? <I>args...</I> </DT>
+<DD><blockquote></DD>
+
+<DT><I>pathName <B>sort auto</B></I> ?<I>boolean</I> </DT>
+<DD>Turns on/off automatic sorting
+of node entries. If <I>boolean</I> is true, entries will be automatically sorted
+as they are opened, closed, inserted, or deleted. If no <I>boolean</I> argument
+is provided, the current state is returned. </DD>
+
+<DT><I>pathName <B>sort cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>sort configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the sorting configuration options of the widget. If no <I>option</I> is specified,
+returns a list describing all of the available options for <I>pathName</I> (see
+<B>Tk_ConfigureInfo</B> for information on the format of this list). If <I>option</I>
+is specified with no <I>value</I>, then the command returns a list describing
+the one named option (this list will be identical to the corresponding
+sublist of the value returned if no <I>option</I> is specified). If one or more
+<I>option-value</I> pairs are specified, then the command modifies the given sorting
+option(s) to have the given value(s); in this case the command returns
+an empty string. <I>Option</I> and <I>value</I> are described below: <blockquote></DD>
+
+<DT><B>-column<I> string</I></B> </DT>
+<DD>Specifies
+the column to sort. Entries in the widget are rearranged according to this
+column. If <I>column</I> is <I>""</I> then no sort is performed. </DD>
+
+<DT><B>-command<I> string</I></B> </DT>
+<DD>Specifies
+a Tcl procedure to be called when sorting nodes. The procedure is called
+with three arguments: the pathname of the widget and the fields of two
+entries. The procedure returns 1 if the first node is greater than the
+second, -1 is the second is greater, and 0 if equal. </DD>
+
+<DT><B>-decreasing<I> boolean</I></B>
+</DT>
+<DD>Indicates to sort in ascending/descending order. If <I>boolean</I> is true, then
+the entries as in descending order. The default is <I>no</I>. </DD>
+
+<DT><B>-mode<I> string</I></B> </DT>
+<DD>Specifies
+how to compare entries when sorting. <I>String</I> may be one of the following:
+<blockquote></DD>
+
+<DT><I>ascii</I> </DT>
+<DD>Use string comparison based upon the ASCII collation order. </DD>
+
+<DT><I>dictionary</I>
+ </DT>
+<DD>Use dictionary-style comparison. This is the same as <I>ascii</I> except (a) case
+is ignored except as a tie-breaker and (b) if two strings contain embedded
+numbers, the numbers compare as integers, not characters. For example,
+"bigBoy" sorts between "bigbang" and "bigboy", and "x10y" sorts between
+"x9y" and "x11y". </DD>
+
+<DT><I>integer</I> </DT>
+<DD>Compares fields as integers. </DD>
+
+<DT><I>real</I> </DT>
+<DD>Compares fields
+as floating point numbers. </DD>
+
+<DT><I>command</I> </DT>
+<DD>Use the Tcl proc specified by the <B>-command</B>
+option to compare entries when sorting. If no command is specified, the
+sort reverts to <I>ascii</I> sorting. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort once</B></I> ?<I>flags</I>? <I>tagOrId...</I> </DT>
+<DD>Sorts
+the children for each entries specified by <I>tagOrId</I>. By default, entries
+are sorted by name, but you can specify a Tcl proc to do your own comparisons.
+<blockquote></DD>
+
+<DT><B>-recurse</B> </DT>
+<DD>Recursively sort the entire branch, not just the children. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>tag <I>operation args</I></B></I> </DT>
+<DD>Tags are a general means of selecting and marking nodes
+in the tree. A tag is just a string of characters, and it may take any form
+except that of an integer. The same tag may be associated with many different
+nodes. <P>
+Both <I>operation</I> and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below. <blockquote></DD>
+
+<DT><I>pathName</I>
+<B>tag add</B> <I>string</I> <I>id</I>... </DT>
+<DD>Adds the tag <I>string</I> to one of more entries. </DD>
+
+<DT><I>pathName</I> <B>tag
+delete</B> <I>string</I> <I>id</I>... </DT>
+<DD>Deletes the tag <I>string</I> from one or more entries. </DD>
+
+<DT><I>pathName</I>
+<B>tag forget</B> <I>string</I> </DT>
+<DD>Removes the tag <I>string</I> from all entries. It's not an error
+if no entries are tagged as <I>string</I>. </DD>
+
+<DT><I>pathName</I> <B>tag names</B> ?<I>id</I>? </DT>
+<DD>Returns a list
+of tags used. If an <I>id</I> argument is present, only those tags used by the
+node designated by <I>id</I> are returned. </DD>
+
+<DT><I>pathName</I> <B>tag nodes</B> <I>string</I> </DT>
+<DD>Returns a
+list of ids that have the tag <I>string</I>. If no node is tagged as <I>string</I>, then
+an empty string is returned. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>This operation
+is used to provide text editing for cells (data fields in a column) or
+entry labels. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>text
+apply</B></I> </DT>
+<DD>Applies the edited buffer, replacing the entry label or data field.
+The edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cancel</B></I> </DT>
+<DD>Cancels the editing operation,
+reverting the entry label or data value back to the previous value. The
+edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cget<I> value</I></B></I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>text
+configure</B></I> ?<I>option value</I>? </DT>
+<DD>Query or modify the configuration options of the
+edit window. If no <I>option</I> is specified, returns a list describing all of
+the available options (see <B>Tk_ConfigureInfo</B> for information on the format
+of this list). If <I>option</I> is specified with no <I>value</I>, then the command returns
+a list describing the one named option (this list will be identical to
+the corresponding sublist of the value returned if no <I>option</I> is specified).
+ If one or more <I>option-value</I> pairs are specified, then the command modifies
+the given widget option(s) to have the given value(s); in this case the
+command returns an empty string. <I>Option</I> and <I>value</I> are described in the section
+ <FONT SIZE=-1><B>TEXT EDITING OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text delete<I> first last</I></B></I> </DT>
+<DD>Deletes the
+characters in the edit buffer between the two given character positions.
+ </DD>
+
+<DT><I>pathName <B>text get</B></I> ?<I>-root</I>? <I>x y</I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text icursor<I> index</I></B></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text
+index<I> index</I></B></I> </DT>
+<DD>Returns the text index of given <I>index</I>. </DD>
+
+<DT><I>pathName <B>text insert<I>
+index string</I></B></I> </DT>
+<DD>Insert the text string <I>string</I> into the edit buffer at the
+index <I>index</I>. For example, the index 0 will prepend the buffer. </DD>
+
+<DT><I>pathName
+<B>text selection<I> args</I></B></I> </DT>
+<DD>This operation controls the selection of the editing
+window. Note that this differs from the selection of entries. It has the
+following forms: <blockquote></DD>
+
+<DT><I>pathName <B>text selection adjust<I> index</I></B></I> </DT>
+<DD>Adjusts either the
+first or last index of the selection. </DD>
+
+<DT><I>pathName <B>text selection clear</B></I> </DT>
+<DD>Clears
+the selection. </DD>
+
+<DT><I>pathName <B>text selection from<I> index</I></B></I> </DT>
+<DD>Sets the anchor of the
+selection. </DD>
+
+<DT><I>pathName <B>text selection present</B></I> </DT>
+<DD>Indicates if a selection is present.
+</DD>
+
+<DT><I>pathName <B>text selection range<I> start end</I></B></I> </DT>
+<DD>Sets both the anchor and mark of
+the selection. </DD>
+
+<DT><I>pathName <B>text selection to<I> index</I></B></I> </DT>
+<DD>Sets the unanchored end
+(mark) of the selection. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>toggle <I>tagOrId</I></B></I> </DT>
+<DD>Opens or closes the node
+given by <I>tagOrId</I>. If the corresponding <B>-opencommand</B> or <B>-closecommand</B> option
+is set, then that command is also invoked. </DD>
+
+<DT><I>pathName <B>xview <I>args</I></B></I> </DT>
+<DD>This command
+is used to query and change the horizontal position of the information
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>xview</B></I> </DT>
+<DD>Returns a list containing two elements. Each element is a real fraction
+between 0 and 1; together they describe the horizontal span that is visible
+in the window. For example, if the first element is .2 and the second element
+is .6, 20% of the <B>treeview</B> widget's text is off-screen to the left, the middle
+40% is visible in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the <B>-xscrollcommand</B> option.
+</DD>
+
+<DT><I>pathName <B>xview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the window so that the character
+position given by <I>tagOrId</I> is displayed at the left edge of the window. Character
+positions are defined by the width of the character <B>0</B>. </DD>
+
+<DT><I>pathName <B>xview moveto<I>
+fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so that <I>fraction</I> of the total width
+of the <B>treeview</B> widget's text is off-screen to the left. <I>fraction</I> must be
+a fraction between 0 and 1. </DD>
+
+<DT><I>pathName <B>xview scroll <I>number what</I></B></I> </DT>
+<DD>This command
+shifts the view in the window left or right according to <I>number</I> and <I>what</I>.
+<I>Number</I> must be an integer. <I>What</I> must be either <B>units</B> or <B>pages</B> or an abbreviation
+of one of these. If <I>what</I> is <B>units</B>, the view adjusts left or right by <I>number</I>
+character units (the width of the <B>0</B> character) on the display; if it is
+<B>pages</B> then the view adjusts by <I>number</I> screenfuls. If <I>number</I> is negative
+then characters farther to the left become visible; if it is positive
+then characters farther to the right become visible. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>yview <I>?args</I></B></I>?
+</DT>
+<DD>This command is used to query and change the vertical position of the text
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>yview</B></I> </DT>
+<DD>Returns a list containing two elements, both of which are real fractions
+between 0 and 1. The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means it is halfway
+through the treeview window, for example). The second element gives the
+position of the node just after the last one in the window, relative to
+the widget as a whole. These are the same values passed to scrollbars via
+the <B>-yscrollcommand</B> option. </DD>
+
+<DT><I>pathName <B>yview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the
+window so that the node given by <I>tagOrId</I> is displayed at the top of the
+window. </DD>
+
+<DT><I>pathName <B>yview moveto<I> fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so
+that the node given by <I>fraction</I> appears at the top of the window. <I>Fraction</I>
+is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates
+the node one-third the way through the <B>treeview</B> widget, and so on. </DD>
+
+<DT><I>pathName
+<B>yview scroll <I>number what</I></B></I> </DT>
+<DD>This command adjusts the view in the window up
+or down according to <I>number</I> and <I>what</I>. <I>Number</I> must be an integer. <I>What</I> must
+be either <B>units</B> or <B>pages</B>. If <I>what</I> is <B>units</B>, the view adjusts up or down
+by <I>number</I> lines; if it is <B>pages</B> then the view adjusts by <I>number</I> screenfuls.
+If <I>number</I> is negative then earlier nodes become visible; if it is positive
+then later nodes become visible. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect11" HREF="#toc11">Treeview Options</A></H2>
+In addition to the <B>configure</B>
+operation, widget configuration options may also be set by the Tk <B>option</B>
+command. The class resource name is <I>TreeView</I>. <BR>
+<CODE>option add *TreeView.Foreground white<BR>
+option add *TreeView.Background blue<BR>
+</CODE><P>The following widget options are available:
+<DL>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets
+the background color for active entries. A node is active when the mouse
+passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of the active node. A node is active when
+the mouse passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeicons
+<I>images</I></B> </DT>
+<DD>Specifies images to be displayed for an entry's icon when it is active.
+<I>Images</I> is a list of two Tk images: the first image is displayed when the
+node is open, the second when it is closed. </DD>
+
+<DT><B>-autocreate <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I>
+is true, automatically create missing ancestor nodes when inserting new
+nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-allowduplicates <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is true, allow nodes with duplicate pathnames when inserting
+new nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets
+the background color of the widget. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the 3-D border around the outside edge of the widget.
+The <B>-relief</B> option determines if the border is to be drawn. The default
+is <I>2</I>. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked when a node
+is closed. You can overrider this for individual entries using the entry's
+<B>-closecommand</B> option. The default is <I>""</I>. Percent substitutions are performed
+on <I>string</I> before it is executed. The following substitutions are valid:
+<blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-cursor
+<I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-dashes <I>number</I></B>
+</DT>
+<DD>Sets the dash style of the horizontal and vertical lines drawn connecting
+ entries. <I>Number</I> is the length in pixels of the dashes and gaps in the line.
+If <I>number</I> is <I>0</I>, solid lines will be drawn. The default is <I>1</I> (dotted). </DD>
+
+<DT><B>-exportselection
+<I>boolean</I></B> </DT>
+<DD>Indicates if the selection is exported. If the widget is exporting
+its selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type <B>STRING</B>; the value of the
+selection will be the label of the selected nodes, separated by newlines.
+ The default is <I>no</I>. </DD>
+
+<DT><B>-flat <I>boolean</I></B> </DT>
+<DD>Indicates whether to display the tree as
+a flattened list. If <I>boolean</I> is true, then the hierarchy will be a list
+of full paths for the nodes. This option also has affect on sorting. See
+the <FONT SIZE=-1><B>SORT OPERATIONS</B></FONT>
+ section for more information. The default is <I>no</I>. </DD>
+
+<DT><B>-focusdashes
+<I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline rectangle drawn around the
+entry label of the node that current has focus. <I>Number</I> is the length in
+pixels of the dashes and gaps in the line. If <I>number</I> is <I>0</I>, a solid line
+will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-focusforeground <I>color</I></B> </DT>
+<DD>Sets the color of
+the focus rectangle. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the
+font for entry labels. You can override this for individual entries with
+the entry's <B>-font</B> configuration option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of entry labels. You can override
+this for individual entries with the entry's <B>-foreground</B> configuration option.
+ The default is <I>black</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of
+widget. The default is <I>400</I>. </DD>
+
+<DT><B>-hideroot <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, it indicates
+that no entry for the root node should be displayed. The default is <I>no</I>.
+</DD>
+
+<DT><B>-highlightbackground <I>color</I></B> </DT>
+<DD>Specifies the normal color of the traversal
+highlight region when the widget does not have the input focus. </DD>
+
+<DT><B>-highlightcolor
+<I>color</I></B> </DT>
+<DD>Specifies the color of the traversal highlight rectangle when the
+widget has the input focus. The default is <I>black</I>. </DD>
+
+<DT><B>-highlightthickness <I>pixels</I></B>
+</DT>
+<DD>Specifies the width of the highlight rectangle indicating when the widget
+has input focus. The value may have any of the forms acceptable to <B>Tk_GetPixels</B>.
+ If the value is zero, no focus highlight will be displayed. The default
+is <I>2</I>. </DD>
+
+<DT><B>-icons <I>images</I></B> </DT>
+<DD>Specifies images for the entry's icon. <I>Images</I> is a list
+of two Tk images: the first image is displayed when the node is open,
+the second when it is closed. </DD>
+
+<DT><B>-linecolor <I>color</I></B> </DT>
+<DD>Sets the color of the connecting
+lines drawn between entries. The default is <I>black</I>. </DD>
+
+<DT><B>-linespacing <I>pixels</I></B> </DT>
+<DD>Sets
+the number of pixels spacing between entries. The default is <I>0</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Set the width of the lines drawn connecting entries. If <I>pixels</I> is
+<I>0</I>, no vertical or horizontal lines are drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-opencommand
+<I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked when a node is open. You can
+override this for individual entries with the entry's <B>-opencommand</B> configuration
+option. The default is <I>""</I>. Percent substitutions are performed on <I>string</I>
+before it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect for the widget. <I>Relief</I> specifies how the <B>treeview</B> widget
+should appear relative to widget it is packed into; for example, <I>raised</I>
+means the <B>treeview</B> widget should appear to protrude. The default is <I>sunken</I>.
+</DD>
+
+<DT><B>-scrollmode <I>mode</I></B> </DT>
+<DD>Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is <I>hierbox</I>. <blockquote></DD>
+
+<DT><I>listbox</I> </DT>
+<DD>Like the <B>listbox</B>
+widget, the last entry can always be scrolled to the top of the widget
+window. This allows the scrollbar thumb to shrink as the last entry is
+scrolled upward. </DD>
+
+<DT><I>hierbox</I> </DT>
+<DD>Like the <B>hierbox</B> widget, the last entry can only
+be viewed at the bottom of the widget window. The scrollbar stays a constant
+size. </DD>
+
+<DT><I>canvas</I> </DT>
+<DD>Like the <B>canvas</B> widget, the entries are bound within the
+scrolling area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-selectbackground <I>color</I></B> </DT>
+<DD>Sets the background color selected
+node entries. The default is <I>#ffffea</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the raised 3-D border drawn around the labels of selected entries. The
+default is <I>0</I>. <B>-selectcommand <I>string</I></B> Specifies a Tcl script to invoked when
+the set of selected nodes changes. The default is <I>""</I>. </DD>
+
+<DT><B>-selectforeground <I>color<B>
+</B></I></B></DT>
+<DD>Sets the color of the labels of selected node entries. The default is <I>black</I>.
+</DD>
+
+<DT><B>-selectmode <I>mode</I></B> </DT>
+<DD>Specifies the selection mode. If <I>mode</I> is <I>single</I>, only one
+node can be selected at a time. If <I>multiple</I> more than one node can be selected.
+The default is <I>single</I>. </DD>
+
+<DT><B>-separator <I>string</I></B> </DT>
+<DD>Specifies the character sequence
+to use when spliting the path components. The separator may be several
+characters wide (such as "::") Consecutive separators in a pathname are
+treated as one. If <I>string</I> is the empty string, the pathnames are Tcl lists.
+ Each element is a path component. The default is <I>""</I>. </DD>
+
+<DT><B>-showtitles <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is false, column titles are not be displayed. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-sortselection <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, nodes in the selection are
+ordered as they are currently displayed (depth-first or sorted), not in
+the order they were selected. The default is <I>no</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides
+information used when moving the focus from window to window via keyboard
+traversal (e.g., Tab and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window
+should be skipped entirely during keyboard traversal. <I>1</I> means that the
+this window should always receive the input focus. An empty value means
+that the traversal scripts make the decision whether to focus on the window.
+The default is <I>"1"</I>. </DD>
+
+<DT><B>-trim <I>string</I></B> </DT>
+<DD>Specifies a string leading characters to
+trim from entry pathnames before parsing. This only makes sense if the
+<B>-separator</B> is also set. The default is <I>""</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Sets the requested
+width of the widget. If <I>pixels</I> is 0, then the with is computed from the
+contents of the <B>treeview</B> widget. The default is <I>200</I>. </DD>
+
+<DT><B>-xscrollcommand <I>string</I></B>
+</DT>
+<DD>Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window changes,
+the widget will generate a Tcl command by concatenating the scroll command
+and two numbers. If this option is not specified, then no command will
+be executed. </DD>
+
+<DT><B>-xscrollincrement</B> <I>pixels</I> </DT>
+<DD>Sets the horizontal scrolling distance.
+The default is 20 pixels. </DD>
+
+<DT><B>-yscrollcommand <I>string</I></B> </DT>
+<DD>Specifies the prefix for
+a command used to communicate with vertical scrollbars. Whenever the vertical
+view in the widget's window changes, the widget will generate a Tcl command
+by concatenating the scroll command and two numbers. If this option is
+not specified, then no command will be executed. </DD>
+
+<DT><B>-yscrollincrement</B> <I>pixels</I>
+</DT>
+<DD>Sets the vertical scrolling distance. The default is 20 pixels. </DD>
+</DL>
+
+<H2><A NAME="sect12" HREF="#toc12">Entry Options</A></H2>
+Many
+widget configuration options have counterparts in entries. For example,
+there is a <B>-closecommand</B> configuration option for both widget itself and
+for individual entries. Options set at the widget level are global for
+all entries. If the entry configuration option is set, then it overrides
+the widget option. This is done to avoid wasting memory by replicated options.
+ Most entries will have redundant options. <P>
+There is no resource class or
+name for entries.
+<DL>
+
+<DT><B>-activeicons <I>images</I></B> </DT>
+<DD>Specifies images to be displayed as
+the entry's icon when it is active. This overrides the global <B>-activeicons</B>
+configuration option for the specific entry. <I>Images</I> is a list of two Tk
+images: the first image is displayed when the node is open, the second
+when it is closed. </DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for nodes.
+ <I>TagList</I> is a list of binding tag names. The tags and their order will
+determine how events are handled for nodes. Each tag in the list matching
+the current event sequence will have its Tcl command executed. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-button <I>string</I></B> </DT>
+<DD>Indicates whether a button should be displayed
+on the left side of the node entry. <I>String</I> can be <I>yes</I>, <I>no</I>, or <I>auto</I>. If
+<I>auto</I>, then a button is automatically displayed if the node has children.
+ This is the default. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked
+when the node is closed. This overrides the global <B>-closecommand</B> option
+for this entry. The default is <I>""</I>. Percent substitutions are performed on
+<I>string</I> before it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I>
+</DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-data
+<I>string</I></B> </DT>
+<DD>Sets data fields for the node. <I>String</I> is a list of name-value pairs
+to be set. The default is <I>""</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Sets the font for entry labels.
+ This overrides the widget's <B>-font</B> option for this node. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of the entry label. This overrides
+the widget's <B>-foreground</B> configuration option. The default is <I>""</I>. </DD>
+
+<DT><B>-icons <I>images</I></B>
+</DT>
+<DD>Specifies images to be displayed for the entry's icon. This overrides the
+global <B>-icons</B> configuration option. <I>Images</I> is a list of two Tk images: the
+first image is displayed when the node is open, the second when it is closed.
+</DD>
+
+<DT><B>-label <I>string</I></B> </DT>
+<DD>Sets the text for the entry's label. If not set, this defaults
+to the name of the node. The default is <I>""</I>. </DD>
+
+<DT><B>-opencommand <I>string</I></B> </DT>
+<DD>Specifies
+a Tcl script to be invoked when the entry is opened. This overrides the
+widget's <B>-opencommand</B> option for this node. The default is <I>""</I>. Percent substitutions
+are performed on <I>string</I> before it is executed. The following substitutions
+are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The
+full pathname of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single
+percent. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect13" HREF="#toc13">Button Options</A></H2>
+Button configuration options may also be set by the
+<B>option</B> command. The resource subclass is <I>Button</I>. The resource name is always
+<I>button</I>. <BR>
+<CODE>option add *TreeView.Button.Foreground white<BR>
+option add *TreeView.button.Background blue<BR>
+</CODE><P>The following are the configuration options available for buttons.
+<DL>
+
+<DT><B>-activebackground
+<I>color</I></B> </DT>
+<DD>Sets the background color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background of the button. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the button. The <B>-relief</B> option
+determines if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-closerelief <I>relief</I></B>
+</DT>
+<DD>Specifies the 3-D effect for the closed button. <I>Relief</I> indicates how the
+button should appear relative to the widget; for example, <I>raised</I> means
+the button should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B>
+</DT>
+<DD>Sets the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets
+the foreground color of buttons. The default is <I>black</I>. </DD>
+
+<DT><B>-images <I>images</I></B> </DT>
+<DD>Specifies
+images to be displayed for the button. <I>Images</I> is a list of two Tk images:
+ the first image is displayed when the button is open, the second when
+it is closed. If the <I>images</I> is the empty string, then a plus/minus gadget
+is drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-openrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of
+the open button. <I>Relief</I> indicates how the button should appear relative
+to the widget; for example, <I>raised</I> means the button should appear to protrude.
+ The default is <I>flat</I>. </DD>
+
+<DT><B>-size <I>pixels</I></B> </DT>
+<DD>Sets the requested size of the button.
+ The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect14" HREF="#toc14">Column Options</A></H2>
+Column configuration options may also
+be set by the <B>option</B> command. The resource subclass is <I>Column</I>. The resource
+name is the name of the column. <BR>
+<CODE>option add *TreeView.Column.Foreground white<BR>
+option add *TreeView.treeView.Background blue<BR>
+</CODE><P>The following configuration options are available for columns.
+<DL>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color of the column. This overrides the widget's
+<B>-background</B> option. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the 3-D border of the column. The <B>-relief</B> option determines if a border
+is to be drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-edit <I>boolean</I></B> </DT>
+<DD>Indicates if the column's
+data fields can be edited. If <I>boolean</I> is false, the data fields in the
+column may not be edited. The default is <I>yes</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Specifies
+the foreground color of the column. You can override this for individual
+entries with the entry's <B>-foreground</B> option. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B>
+ </DT>
+<DD>Sets the font for a column. You can override this for individual entries
+with the entry's <B>-font</B> option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, the column is not displayed. The default
+is <I>yes</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the column data fields title should
+be justified within the column. This matters only when the column is wider
+than the data field to be display. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>.
+ The default is <I>left</I>. </DD>
+
+<DT><B>-pad <I>pad</I></B> </DT>
+<DD>Specifies how much padding for the left and
+right sides of the column. <I>Pad</I> is a list of one or two screen distances.
+ If <I>pad</I> has two elements, the left side of the column is padded by the
+first distance and the right side by the second. If <I>pad</I> has just one distance,
+both the left and right sides are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-relief
+<I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the column. <I>Relief</I> specifies how the
+column should appear relative to the widget; for example, <I>raised</I> means
+the column should appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-state <I>state</I></B> </DT>
+<DD>Sets
+the state of the column. If <I>state</I> is <I>disable</I> then the column title can not
+be activated nor invoked. The default is <I>normal</I>. </DD>
+
+<DT><B>-text <I>string</I></B> </DT>
+<DD>Sets the title
+for the column. The default is <I>""</I>. </DD>
+
+<DT><B>-titleforeground <I>color</I></B> </DT>
+<DD>Sets the foreground
+color of the column title. The default is <I>black</I>. </DD>
+
+<DT><B>-titleshadow <I>color</I></B> </DT>
+<DD>Sets
+the color of the drop shadow of the column title. The default is <I>""</I>. </DD>
+
+<DT><B>-width
+<I>pixels</I></B> </DT>
+<DD>Sets the requested width of the column. This overrides the computed
+with of the column. If <I>pixels</I> is 0, the width is computed as from the contents
+of the column. The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect15" HREF="#toc15">Text Editing Options</A></H2>
+Text edit window configuration
+options may also be set by the <B>option</B> command. The resource class is <I>TreeViewEditor</I>.
+The resource name is always <I>edit</I>. <BR>
+<CODE>option add *TreeViewEditor.Foreground white<BR>
+option add *edit.Background blue<BR>
+</CODE><P>The following are the configuration options available for the text editing
+window.
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background of the text edit window. The
+default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the edit window. The <B>-relief</B> option determines if a border is to be drawn.
+ The default is <I>1</I>. </DD>
+
+<DT><B>-exportselection <I>boolean</I></B> </DT>
+<DD>Indicates if the text selection
+is exported. If the edit window is exporting its selection then it will
+observe the standard X11 protocols for handling the selection. Selections
+are available as type <B>STRING</B>. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect of the edit window. <I>Relief</I> indicates how the background should
+appear relative to the edit window; for example, <I>raised</I> means the background
+should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-selectbackground <I>color</I></B>
+</DT>
+<DD>Sets the background of the selected text in the edit window. The default
+is <I>white</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the selected text in the edit window. The <B>-selectrelief</B> option determines
+if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-selectforeground <I>color</I></B> </DT>
+<DD>Sets
+the foreground of the selected text in the edit window. The default is
+<I>white</I>. </DD>
+
+<DT><B>-selectrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the selected text
+in the edit window. <I>Relief</I> indicates how the text should appear relative
+to the edit window; for example, <I>raised</I> means the text should appear to
+protrude. The default is <I>flat</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect16" HREF="#toc16">Default Bindings</A></H2>
+Tk automatically creates
+class bindings for treeviews that give them Motif-like behavior. Much of
+the behavior of a <B>treeview</B> widget is determined by its <B>-selectmode</B> option,
+which selects one of two ways of dealing with the selection. <P>
+If the selection
+mode is <B>single</B>, only one node can be selected at a time. Clicking button
+1 on an node selects it and deselects any other selected item. <P>
+If the selection
+mode is <B>multiple</B>, any number of entries may be selected at once, including
+discontiguous ranges. Clicking Control-Button-1 on a node entry toggles its
+selection state without affecting any other entries. Pressing Shift-Button-1
+on a node entry selects it, extends the selection.
+<OL>
+<LI>In <B>extended</B> mode, the
+selected range can be adjusted by pressing button 1 with the Shift key
+down: this modifies the selection to consist of the entries between the
+anchor and the entry under the mouse, inclusive. The un-anchored end of this
+new selection can also be dragged with the button down. </LI><LI>In <B>extended</B> mode,
+pressing button 1 with the Control key down starts a toggle operation:
+the anchor is set to the entry under the mouse, and its selection state
+is reversed. The selection state of other entries isn't changed. If the mouse
+is dragged with button 1 down, then the selection state of all entries
+between the anchor and the entry under the mouse is set to match that of
+the anchor entry; the selection state of all other entries remains what
+it was before the toggle operation began. </LI><LI>If the mouse leaves the treeview
+window with button 1 down, the window scrolls away from the mouse, making
+information visible that used to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the button
+is released, or the end of the hierarchy is reached. </LI><LI>Mouse button 2 may
+be used for scanning. If it is pressed and dragged over the <B>treeview</B> widget,
+the contents of the hierarchy drag at high speed in the direction the mouse
+moves. </LI><LI>If the Up or Down key is pressed, the location cursor (active entry)
+moves up or down one entry. If the selection mode is <B>browse</B> or <B>extended</B>
+then the new active entry is also selected and all other entries are deselected.
+In <B>extended</B> mode the new active entry becomes the selection anchor. </LI><LI>In <B>extended</B>
+mode, Shift-Up and Shift-Down move the location cursor (active entry) up
+or down one entry and also extend the selection to that entry in a fashion
+similar to dragging with mouse button 1. </LI><LI>The Left and Right keys scroll
+the <B>treeview</B> widget view left and right by the width of the character <B>0</B>.
+Control-Left and Control-Right scroll the <B>treeview</B> widget view left and right
+by the width of the window. Control-Prior and Control-Next also scroll left
+and right by the width of the window. </LI><LI>The Prior and Next keys scroll the
+<B>treeview</B> widget view up and down by one page (the height of the window).
+</LI><LI>The Home and End keys scroll the <B>treeview</B> widget horizontally to the left
+and right edges, respectively. </LI><LI>Control-Home sets the location cursor to the
+the first entry, selects that entry, and deselects everything else in
+the widget. </LI><LI>Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else in the widget. </LI><LI>In <B>extended</B>
+mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End
+extends the selection to the last entry. </LI><LI>In <B>multiple</B> mode, Control-Shift-Home
+moves the location cursor to the first entry and Control-Shift-End moves
+the location cursor to the last entry. </LI><LI>The space and Select keys make a
+selection at the location cursor (active entry) just as if mouse button
+1 had been pressed over this entry. </LI><LI>In <B>extended</B> mode, Control-Shift-space
+and Shift-Select extend the selection to the active entry just as if button
+1 had been pressed with the Shift key down. </LI><LI>In <B>extended</B> mode, the Escape
+key cancels the most recent selection and restores all the entries in the
+selected range to their previous selection state. </LI><LI>Control-slash selects everything
+in the widget, except in <B>single</B> and <B>browse</B> modes, in which case it selects
+the active entry and deselects everything else. </LI><LI>Control-backslash deselects
+everything in the widget, except in <B>browse</B> mode where it has no effect.
+</LI><LI>The F16 key (labelled Copy on many Sun workstations) or Meta-w copies the
+selection in the widget to the clipboard, if there is a selection. </LI>
+</OL>
+<P>
+The behavior
+of <B>treeview</B> widgets can be changed by defining new bindings for individual
+widgets or by redefining the class bindings.
+<H3><A NAME="sect17" HREF="#toc17">Widget Bindings</A></H3>
+In addition
+to the above behavior, the following additional behavior is defined by
+the default widget class (TreeView) bindings.
+<DL>
+
+<DT><I>&lt;ButtonPress-2&gt;</I></DT>
+<DD>Starts scanning.
+ </DD>
+
+<DT><I>&lt;B2-Motion&gt;</I></DT>
+<DD>Adjusts the scan. </DD>
+
+<DT><I>&lt;ButtonRelease-2&gt;</I></DT>
+<DD>Stops scanning. </DD>
+
+<DT><I>&lt;B1-Leave&gt;</I></DT>
+<DD>Starts auto-scrolling.
+</DD>
+
+<DT><I>&lt;B1-Enter&gt;</I></DT>
+<DD>Starts auto-scrolling </DD>
+
+<DT><I>&lt;KeyPress-Up&gt;</I></DT>
+<DD>Moves the focus to the previous
+entry. </DD>
+
+<DT><I>&lt;KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the next entry. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Up&gt;</I></DT>
+<DD>Moves
+the focus to the previous sibling. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the
+next sibling. </DD>
+
+<DT><I>&lt;KeyPress-Prior&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or hidden
+entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Next&gt;</I></DT>
+<DD>Move the focus to the last entry. Closed
+or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Left&gt;</I></DT>
+<DD>Closes the entry. It is not an
+error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-Right&gt;</I></DT>
+<DD>Opens the entry, displaying
+its children. It is not an error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-space&gt;</I></DT>
+<DD>In
+"single" select mode this selects the entry. In "multiple" mode, it toggles
+the entry (if it was previous selected, it is not deselected). </DD>
+
+<DT><I>&lt;KeyRelease-space&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress-Return&gt;</I></DT>
+<DD>Sets the focus to the current entry. </DD>
+
+<DT><I>&lt;KeyRelease-Return&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress&gt;</I></DT>
+<DD>Moves to the next entry whose label starts with
+the letter typed. </DD>
+
+<DT><I>&lt;KeyPress-Home&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or
+hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-End&gt;</I></DT>
+<DD>Move the focus to the last entry.
+Closed or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-F1&gt;</I></DT>
+<DD>Opens all entries. </DD>
+
+<DT><I>&lt;KeyPress-F2&gt;</I></DT>
+<DD>Closes
+all entries (except root). </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Button Bindings</A></H3>
+Buttons have bindings. There are
+associated with the "all" bindtag (see the entry's -bindtag option). You
+can use the <B>bind</B> operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the button of
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the button back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the view so that the current entry is visible. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Entry Bindings</A></H3>
+Entries have
+default bindings. There are associated with the "all" bindtag (see the
+entry's -bindtag option). You can use the <B>bind</B> operation to modify them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the entry back to its normal state. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets
+the selection anchor the current entry. </DD>
+
+<DT><I>&lt;Double-ButtonPress-1&gt;</I></DT>
+<DD>Toggles the selection
+of the current entry. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>For "multiple" mode only. Saves the current
+location of the pointer for auto-scrolling. Resets the selection mark.
+</DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>For "multiple" mode only. Sets the selection anchor to the
+ current entry. </DD>
+
+<DT><I>&lt;Shift-ButtonPress-1&gt;</I></DT>
+<DD>For "multiple" mode only. Extends the selection.
+</DD>
+
+<DT><I>&lt;Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder.
+Does nothing. </DD>
+
+<DT><I>&lt;Shift-ButtonRelease-1&gt;</I></DT>
+<DD>Stop auto-scrolling. </DD>
+
+<DT><I>&lt;Control-ButtonPress-1&gt;</I></DT>
+<DD>For
+"multiple" mode only. Toggles and extends the selection. </DD>
+
+<DT><I>&lt;Control-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-ButtonRelease-1&gt;</I></DT>
+<DD>Stops
+auto-scrolling. </DD>
+
+<DT><I>&lt;Control-Shift-ButtonPress-1&gt;</I></DT>
+<DD>??? </DD>
+
+<DT><I>&lt;Control-Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Column
+Bindings</A></H3>
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the <B>column bind</B>
+operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the current column title. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns
+the column back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Invokes the command (see
+the column's -command option) if one if specified. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Column Rule Bindings</A></H3>
+
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current and activates the ruler. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the column back to its
+normal state. Deactivates the ruler. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets the resize anchor for
+the column. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>Sets the resize mark for the column. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the size of the column, based upon the resize anchor and mark positions.
+</DD>
+</DL>
+
+<H2><A NAME="sect22" HREF="#toc22">Example</A></H2>
+The <B>treeview</B> command creates a new widget. <BR>
+<CODE>treeview .h -bg white<BR>
+</CODE><P>A new Tcl command <I>.h</I> is also created. This command can be used to query
+and modify the <B>treeview</B> widget. For example, to change the background
+color of the table to "green", you use the new command and the widget's
+<B>configure</B> operation. <BR>
+<CODE># Change the background color.<BR>
+.h configure -background "green"<BR>
+</CODE><P>By default, the <B>treeview</B> widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the widget.
+ Above, the new tree object name is ".h". But you can use the <B>-tree</B> option
+to specify the name of another tree. <BR>
+<CODE># View the tree "myTree".<BR>
+.h configure -tree "myTree"<BR>
+</CODE><P>When a new tree is created, it contains only a root node. The node is automatically
+opened. The id of the root node is always <I>0</I> (you can use also use the special
+id <I>root</I>). The <B>insert</B> operation lets you insert one or more new entries into
+the tree. The last argument is the node's <I>pathname</I>. <BR>
+<CODE># Create a new entry named "myEntry"<BR>
+set id [.h insert end "myEntry"]<BR>
+</CODE><P>This appends a new node named "myEntry". It will positioned as the last
+child of the root of the tree (using the position "end"). You can supply
+another position to order the node within its siblings. <BR>
+<CODE># Prepend "fred".<BR>
+set id [.h insert 0 "fred"]<BR>
+</CODE><P>Entry names do not need to be unique. By default, the node's label is its
+name. To supply a different text label, add the <B>-label</B> option. <BR>
+<CODE># Create a new node named "fred"<BR>
+set id [.h insert end "fred" -label "Fred Flintstone"]<BR>
+</CODE><P>The <B>insert</B> operation returns the id of the new node. You can also use the
+<B>index</B> operation to get this information. <BR>
+<CODE># Get the id of "fred"<BR>
+.h index "fred"<BR>
+</CODE><P>To insert a node somewhere other than root, use the <B>-at</B> switch. It takes
+the id of the node where the new child will be added. <BR>
+<CODE># Create a new node "barney" in "fred".<BR>
+.h insert -at $id end "barney" <BR>
+</CODE><P>A pathname describes the path to an entry in the hierarchy. It's a list
+of entry names that compose the path in the tree. Therefore, you can also
+add "barney" to "fred" as follows. <BR>
+<CODE># Create a new sub-entry of "fred"<BR>
+.h insert end "fred barney" <BR>
+</CODE><P>Every name in the list is ancestor of the next. All ancestors must already
+exist. That means that an entry "fred" is an ancestor of "barney" and must
+already exist. But you can use the <B>-autocreate</B> configuration option to force
+the creation of ancestor nodes. <BR>
+<CODE># Force the creation of ancestors.<BR>
+.h configure -autocreate yes <BR>
+.h insert end "fred barney wilma betty" <BR>
+</CODE><P>Sometimes the pathname is already separated by a character sequence rather
+than formed as a list. A file name is a good example of this. You can use
+the <B>-separator</B> option to specify a separator string to split the path into
+its components. Each pathname inserted is automatically split using the
+separator string as a separator. Multiple separators are treated as one.
+<BR>
+<CODE>.h configure -separator /<BR>
+.h insert end "/usr/local/tcl/bin" <BR>
+</CODE><P>If the path is prefixed by extraneous characters, you can automatically
+trim it off using the <B>-trim</B> option. It removed the string from the path
+before it is parsed. <BR>
+<CODE>.h configure -trim C:/windows -separator /<BR>
+.h insert end "C:/window/system" <BR>
+</CODE><P>You can insert more than one entry at a time with the <B>insert</B> operation.
+ This can be much faster than looping over a list of names. <BR>
+<CODE># The slow way<BR>
+foreach f [glob $dir/*] {<BR>
+ .h insert end $f<BR>
+}<BR>
+# The fast way<BR>
+eval .h insert end [glob $dir/*]<BR>
+</CODE><P>In this case, the <B>insert</B> operation will return a list of ids of the new
+entries. <P>
+You can delete entries with the <B>delete</B> operation. It takes one
+or more tags of ids as its argument. It deletes the entry and all its children.
+<BR>
+<CODE>.h delete $id<BR>
+</CODE><P>Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the <B>-label</B> option that
+sets the entry's text label. The <B>entry configure</B> operation lets you set
+or modify an entry's configuration options. <BR>
+<CODE>.h entry configure $id -color red -font fixed<BR>
+</CODE><P>You can hide an entry and its children using the <B>-hide</B> option. <BR>
+<CODE>.h entry configure $id -hide yes<BR>
+</CODE><P>More that one entry can be configured at once. All entries specified are
+configured with the same options. <BR>
+<CODE>.h entry configure $i1 $i2 $i3 $i4 -color brown <BR>
+</CODE><P>An icon is displayed for each entry. It's a Tk image drawn to the left of
+the label. You can set the icon with the entry's <B>-icons</B> option. It takes
+a list of two image names: one to represent the open entry, another when
+it is closed. <BR>
+<CODE>set im1 [image create photo -file openfolder.gif]<BR>
+set im2 [image create photo -file closefolder.gif]<BR>
+.h entry configure $id -icons "$im1 $im2"<BR>
+</CODE><P>If <B>-icons</B> is set to the empty string, no icons are display. <P>
+If an entry has
+children, a button is displayed to the left of the icon. Clicking the mouse
+on this button opens or closes the sub-hierarchy. The button is normally
+a <I>+</I> or <I>-</I> symbol, but can be configured in a variety of ways using the <B>button
+configure</B> operation. For example, the <I>+</I> and <I>-</I> symbols can be replaced with
+Tk images. <BR>
+<CODE>set im1 [image create photo -file closefolder.gif]<BR>
+set im2 [image create photo -file downarrow.gif]<BR>
+.h button configure $id -images "$im1 $im2" \<BR>
+ -openrelief raised -closerelief raised<BR>
+</CODE><P>Entries can contain an arbitrary number of <I>data fields</I>. Data fields are
+name-value pairs. Both the value and name are strings. The entry's <B>-data</B> option
+lets you set data fields. <BR>
+<CODE>.h entry configure $id -data {mode 0666 group users}<BR>
+</CODE><P>The <B>-data</B> takes a list of name-value pairs. <P>
+You can display these data fields
+as <I>columns</I> in the <B>treeview</B> widget. You can create and configure columns
+with the <B>column</B> operation. For example, to add a new column to the widget,
+use the <B>column insert</B> operation. The last argument is the name of the data
+field that you want to display. <BR>
+<CODE>.h column insert end "mode"<BR>
+</CODE><P>The column title is displayed at the top of the column. By default, it's
+is the field name. You can override this using the column's <B>-text</B> option.
+<BR>
+<CODE>.h column insert end "mode" -text "File Permissions"<BR>
+</CODE><P>Columns have several configuration options. The <B>column configure</B> operation
+lets you query or modify column options. <BR>
+<CODE>.h column configure "mode" -justify left<BR>
+</CODE><P>The <B>-justify</B> option says how the data is justified within in the column.
+ The <B>-hide</B> option indicates whether the column is displayed. <BR>
+<CODE>.h column configure "mode" -hide yes<BR>
+</CODE><P>Entries can be selected by clicking on the mouse. Selected entries are
+drawn using the colors specified by the <B>-selectforeground</B> and <B>-selectbackground</B>
+configuration options. The selection itself is managed by the <B>selection</B>
+operation. <BR>
+<CODE># Clear all selections<BR>
+.h selection clear 0 end<BR>
+# Select the root node<BR>
+.h selection set 0 <BR>
+</CODE><P>The <B>curselection</B> operation returns a list of ids of all the selected entries.
+<BR>
+<CODE>set ids [.h curselection]<BR>
+</CODE><P>You can use the <B>get</B> operation to convert the ids to their pathnames. <BR>
+<CODE>set names [eval .h get -full $ids]<BR>
+</CODE><P>If a treeview is exporting its selection (using the <B>-exportselection</B> option),
+then it will observe the standard X11 protocols for handling the selection.
+ Treeview selections are available as type <B>STRING</B>; the value of the selection
+will be the pathnames of the selected entries, separated by newlines. <P>
+The
+<B>treeview</B> supports two modes of selection: <I>single</I> and <I>multiple</I>. In single
+select mode, only one entry can be selected at a time, while multiple select
+mode allows several entries to be selected. The mode is set by the widget's
+<B>-selectmode</B> option. <BR>
+<CODE>.h configure -selectmode "multiple"<BR>
+</CODE><P>You can be notified when the list of selected entries changes. The widget's
+<B>-selectcommand</B> specifies a Tcl procedure that is called whenever the selection
+changes. <BR>
+<CODE>proc SelectNotify { widget } {<BR>
+ set ids [$widget curselection]<BR>
+}<BR>
+.h configure -selectcommand "SelectNotify .h"<BR>
+</CODE><P>The widget supports the standard Tk scrolling and scanning operations. The
+<B>treeview</B> can be both horizontally and vertically. You can attach scrollbars
+to the <B>treeview</B> the same way as the listbox or canvas widgets. <BR>
+<CODE>scrollbar .xbar -orient horizontal -command ".h xview"<BR>
+scrollbar .ybar -orient vertical -command ".h yview"<BR>
+.h configure -xscrollcommand ".xbar set" \<BR>
+ -yscrollcommand ".ybar set"<BR>
+</CODE><P>There are three different modes of scrolling: <I>listbox</I>, <I>canvas</I>, and <I>hierbox</I>.
+ In <I>listbox</I> mode, the last entry can always be scrolled to the top of the
+widget. In <I>hierbox</I> mode, the last entry is always drawn at the bottom of
+the widget. The scroll mode is set by the widget's <B>-selectmode</B> option. <BR>
+<CODE>.h configure -scrollmode "listbox"<BR>
+</CODE><P>Entries can be programmatically opened or closed using the <B>open</B> and <B>close</B>
+operations respectively. <BR>
+<CODE>.h open $id<BR>
+.h close $id<BR>
+</CODE><P>When an entry is opened, a Tcl procedure can be automatically invoked. The
+<B>-opencommand</B> option specifies this procedure. This procedure can lazily
+insert entries as needed. <BR>
+<CODE>proc AddEntries { dir } {<BR>
+ eval .h insert end [glob -nocomplain $dir/*] <BR>
+}<BR>
+.h configure -opencommand "AddEntries %P"<BR>
+</CODE><P>Now when an entry is opened, the procedure <I>AddEntries</I> is called and adds
+children to the entry. Before the command is invoked, special "%" substitutions
+(like <B>bind</B>) are performed. Above, <I>%P</I> is translated to the pathname of the
+entry. <P>
+The same feature exists when an entry is closed. The <B>-closecommand</B>
+option specifies the procedure. <BR>
+<CODE>proc DeleteEntries { id } {<BR>
+ .h entry delete $id 0 end<BR>
+}<BR>
+.h configure -closecommand "DeleteEntries %#"<BR>
+</CODE><P>When an entry is closed, the procedure <I>DeleteEntries</I> is called and deletes
+the entry's children using the <B>entry delete</B> operation (<I>%#</I> is the id of entry).
+
+<H2><A NAME="sect23" HREF="#toc23">Keywords</A></H2>
+treeview, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Tree Data Object</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">IDs and Tags</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Special Node IDs</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Data Fields</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Entry Bindings</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Treeview Operations</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Treeview Options</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Entry Options</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Button Options</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Column Options</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Text Editing Options</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Default Bindings</A></LI>
+<UL>
+<LI><A NAME="toc17" HREF="#sect17">Widget Bindings</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Button Bindings</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Entry Bindings</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Column Bindings</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Column Rule Bindings</A></LI>
+</UL>
+<LI><A NAME="toc22" HREF="#sect22">Example</A></LI>
+<LI><A NAME="toc23" HREF="#sect23">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/htext.html b/blt/html/htext.html
new file mode 100644
index 00000000000..67cc43905f4
--- /dev/null
+++ b/blt/html/htext.html
@@ -0,0 +1,397 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>htext(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+htext - Create and manipulate hypertext widgets
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>htext</B> <I>pathName </I>?<I>option value</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+<P>
+The <B>htext</B> command creates
+a new window (given by the <I>pathName</I> argument) and makes it into a <B>htext</B>
+widget. Additional options, described above, may be specified on the command
+line or in the option database to configure aspects of the widget such
+as its color and font. At the time this command is invoked, there must
+not exist a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. The
+<B>htext</B> command returns its <I>pathName</I>. <P>
+The <B>htext</B> widget is hybrid of a non-editable
+text widget and a geometry manager (e.g. the packer). It displays text (optionally
+read from file) in a window. Text can be scrolled either horizontally or
+vertically using the <B>htext</B> window as a viewport. In addition, Tcl commands
+can be embedded into the text which are evaluated as the text is parsed.
+ Text between special double characters (percent signs "%%") is immediately
+passed to the Tcl interpreter for evaluation. <P>
+Furthermore, any widget
+or widget hierarchy can be packed in-line and made to appear on the current
+line of the text. Widgets are packed using the <B>htext append</B> command. All
+widgets must be children of the <B>htext</B> window and must already exist before
+packing. Once a widget has been packed it cannot be moved to a different
+position within the text. Widgets can be resized but they will remain
+at the same position within the text. <P>
+Before a file or text string is parsed
+by the <B>htext</B> widget, all the widget's current children are destroyed. You
+can reload files or text without worrying about unmapping or destroying
+each child window beforehand. <P>
+Setting the either the <B>-filename</B> or <B>-text</B> configuration
+option will adjust the value of the other. If both options are set, the
+file takes precedence. When a new file is read using the <B>-filename</B> option,
+the value of the <B>-text</B> option is reset to the empty string. Likewise, when
+the <B>-text</B> option is set, the string representing the <B>-filename</B> option is
+cleared.
+<H2><A NAME="sect3" HREF="#toc3">File Format</A></H2>
+The format of <B>htext</B> text file is typically ASCII text.
+ Text enclosed by special double characters (by default, percent signs
+'%%') is interpreted and executed as Tcl commands. The special character
+ may be specified by the <B>-specialchar</B> option. In the following example of
+a <B>htext</B> file, a button widget is appended to the text between the words
+"<I>a</I>" and "<I>which</I>". The <I>pathName</I> of the <B>htext</B> widget is "<I>.ht</I>". <BR>
+<CODE><I>This will be displayed as normal text. <BR>
+</I>But this will become a %% <BR>
+ button .ht.button -text "button" -fg red<BR>
+ .ht append .ht.button <BR>
+%% which can invoke a Tcl command.<BR>
+<P>
+
+<H2><A NAME="sect4" HREF="#toc4"></CODE><P>Indices</A></H2>
+<P>
+Some of the widget operations (<B>selection</B>, gotoline, <B>search</B>, etc.)
+take one or more indices as arguments. An index is a string used to indicate
+a particular place within the text, such as the first and last characters
+in a range to be selected. <P>
+An index must have one of the following forms:
+
+<DL>
+
+<DT><I>line<B>.<I>char</I></B></I> </DT>
+<DD>Indicates <I>char</I>'th character on line <I>line</I>. Both lines and characters
+are number from 0, so "0.0" is the first beginning of the text. <I>Char</I> may
+be undesignated. In this case a character position of 0 is assumed. </DD>
+
+<DT><B>@<I>x<B>,<I>y</I></B></I></B>
+</DT>
+<DD>Indicates the character that covers the pixel whose x and y coordinates
+within the text's window are <I>x</I> and <I>y</I>. </DD>
+
+<DT><B>end</B> </DT>
+<DD>Indicates the end of the text. </DD>
+
+<DT><B>anchor</B>
+</DT>
+<DD>Indicates the anchor point for the selection, which is set with the <B>selection</B>
+operation. </DD>
+
+<DT><B>sel.first</B> </DT>
+<DD>Indicates the first character in the selection. It is
+an error to use this form if the selection isn't in the entry window. </DD>
+
+<DT><B>sel.last</B>
+</DT>
+<DD>Indicates the character just after the last one in the selection. It is
+an error to use this form if the selection isn't in the entry window. </DD>
+</DL>
+
+<H2><A NAME="sect5" HREF="#toc5">Variables</A></H2>
+<P>
+The
+following global Tcl variables are maintained when an <B>htext</B> file is parsed.
+
+<DL>
+
+<DT><B>htext(widget)</B> </DT>
+<DD>is the pathname of the <B>htext</B> widget. </DD>
+
+<DT><B>htext(file)</B> </DT>
+<DD>is the
+name of the file the <B>htext</B> widget is currently parsing. It is the empty
+string when the <B>-text</B> option is used. </DD>
+
+<DT><B><A HREF="htext.l.html">htext(line)</B></A>
+ </DT>
+<DD>is the current line number
+in the text. </DD>
+</DL>
+<P>
+This information might be used to construct hyper links
+between different files and/or lines. <P>
+
+<H2><A NAME="sect6" HREF="#toc6">Syntax</A></H2>
+The <B>htext</B> command creates a
+new Tcl command whose name is <I>pathName</I>. This command may be used to invoke
+various operations on the widget. It has the following general form: <BR>
+<P>
+<CODE><I>pathName oper </I>?<I>args</I>?<BR>
+</CODE><P><I>Oper</I> and <I>args</I> determine the exact behavior of the command. <P>
+
+<H2><A NAME="sect7" HREF="#toc7">Operations</A></H2>
+The
+following operations are available for <B>htext</B> widgets:
+<DL>
+
+<DT><I>pathName <B>append <I>window
+</I></B></I>?<I>option value</I>?... </DT>
+<DD>Embeds the widget <I>window</I> into the htext widget. <I>Window</I>
+is the pathname of the widget to be embedded which must be a child of <I>pathName</I>.
+ <I>Window</I> will be positioned in the htext widget at the current location
+of the text. If <I>option</I> and <I>value</I> pairs are present, they configure various
+aspects how <I>window</I> appears in <I>pathName</I>. The following options are available.
+<blockquote></DD>
+
+<DT><B>-anchor <I>anchorPos</I></B> </DT>
+<DD>Specifies how <I>window</I> will be arranged if there is any
+extra space in the cavity surrounding the window. For example, if <I>anchorPos</I>
+is <B>center</B> then the window is centered in the cavity; if <I>anchorPos</I> is <B>w</B>
+then the window will be drawn such it touches the leftmost edge of the
+cavity. The default is <I>center</I>. </DD>
+
+<DT><B>-fill <I>style</I></B> </DT>
+<DD>Specifies how the <I>window</I> should
+be stretched to occupy the extra space in the cavity surrounding it (if
+any exists). <I>Style</I> is <I>none</I>, <I>x</I>, <I>y</I>, <I>both</I>. If <I>style</I> is <I>x</I>, the width of <I>window</I>
+is expanded to fill the cavity. If <I>style</I> is <B>y</B>, the height is expanded.
+The default is <I>none</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Sets the height of the cavity surrounding
+<I>window</I>. If <I>pixels</I> is zero, the height of the cavity will be the same as
+the requested height of <I>window</I>. If <I>pixels</I> is less than the requested height
+of <I>window</I>, <I>window</I> will be reduced to fit the cavity. The default is <I>0</I>. </DD>
+
+<DT><B>-ipadx
+<I>pad</I></B> </DT>
+<DD>Sets the amount of internal padding to be added to the width <I>window</I>.
+ <I>Pad</I> can be a list of one or two numbers. If <I>pad</I> has two elements, the
+left side of <I>window</I> is extended by the first value and the right side by
+the second value. If <I>pad</I> is just one value, both the left and right sides
+are padded by evenly by the value. The default is <I>0</I>. </DD>
+
+<DT><B>-ipady <I>pad</I></B> </DT>
+<DD>Sets an amount
+of internal padding to be added to the height of <I>window</I>. <I>Pad</I> can be a list
+of one or two numbers. If <I>pad</I> has two elements, the top of <I>window</I> is padded
+by the first value and the bottom by the second value. If <I>pad</I> is just one
+number, both the top and bottom are padded evenly by the value. The default
+is <I>0</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Justifies <I>window</I> vertically within the cavity containing
+it in relation to the line of text. <I>Justify</I> is <B>top</B>, <B>bottom</B>, or <B>center</B>.
+ If <I>justify</I> is <I>center</I> the widget is centered along the baseline of the
+line of text. The default is <I>center</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the padding on the left
+and right sides of <I>window</I>. <I>Pad</I> can be a list of one or two numbers. If <I>pad</I>
+has two elements, the left side of <I>window</I> is padded by the first value
+and the right side by the second value. If <I>pad</I> has just one value, both
+the left and right sides are padded evenly by the value. The default is
+<I>0</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and below <I>window</I>. <I>Pad</I> can be a list of
+one or two numbers. If <I>pad</I> has two elements, the area above <I>window</I> is padded
+by the first value and the area below by the second value. If <I>pad</I> is just
+one number, both the top and bottom are padded by the value. The default
+is <I>0</I>. </DD>
+
+<DT><B>-relheight <I>value</I></B> </DT>
+<DD>Specifies the height of the cavity containing <I>window</I>
+relative to the height of <I>pathName</I>. <I>Value</I> is real number indicating the
+ratio of the height of the cavity to the height of <I>pathName</I>. As the height
+of <I>pathName</I> changes, so will the height of <I>window</I>. If <I>value</I> is 0.0 or less,
+the height of the cavity is the requested height <I>window</I>. The default is
+<I>0.0</I>. </DD>
+
+<DT><B>-relwidth <I>value</I></B> </DT>
+<DD>Specifies the width of the cavity containing <I>window</I> relative
+to the width of <I>pathName</I>. <I>Value</I> is real number indicating the ratio of
+the width of the cavity to the width of IpathName. As the height of <I>pathName</I>
+changes, so will the height of <I>window</I>. If <I>value</I> is 0.0 or less, the width
+of the cavity is the requested width of <I>window</I>. The default is <I>0.0</I>. </DD>
+
+<DT><B>-width
+<I>value</I></B> </DT>
+<DD>Species the width of the cavity containing the child window. <I>Value</I>
+must be in a form accepted by <B>Tk_GetPixels</B>. If <I>value</I> is greater than zero,
+the cavity is resized to that width. If the requested window width is
+greater than the cavity's width, the window will be reduced to fit the cavity.
+By default, the cavity is requested width of the child window. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>configure</B></I> ?<I>window</I>? ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Queries or modifies the
+configuration options of the text widget or one of its embedded widgets.
+ If no <I>window</I> argument is present, the htext widget itself is configured.
+ Otherwise <I>window</I> is the pathname of a widget already embedded into the
+htext widget. Then this command configure the options for the embedded widget.
+</DD>
+</DL>
+<P>
+If <I>option</I> isn't specified, a list describing all of the current options
+for <I>pathName</I> or <I>window</I> is returned. If <I>option</I> is specified, but not <I>value</I>,
+then a list describing the option <I>option</I> is returned. If one or more <I>option</I>
+and <I>value</I> pairs are specified, then for each pair, the htext or embedded
+ window option <I>option</I> is set to <I>value</I>. <P>
+The following options are valid for
+the htext widget. <blockquote>
+<DL>
+
+<DT><B>-background</B> <I>color<I> </I></I></DT>
+<DD>Sets the background of the htext widget
+to <I>color</I>. This default is <I>white</I>. </DD>
+
+<DT><B>-cursor</B> <I>cursor</I> </DT>
+<DD>Specifies the cursor for
+the htext widget. The default cursor is <I>pencil</I>. </DD>
+
+<DT><B>-filename</B> <I>fileName</I> </DT>
+<DD>Specifies
+a <B>htext</B> file to be displayed in the window. If the value is the empty string,
+the <B>-text</B> option is used instead. See the section <FONT SIZE=-1><B>FILE</B></FONT>
+ for a description
+of the <B>htext</B> file format. </DD>
+
+<DT><B>-font</B> <I>fontName</I> </DT>
+<DD>Sets the font of the text in the
+htext widget to <I>fontName</I>. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>. </DD>
+
+<DT><B>-foreground</B>
+<I>color</I> </DT>
+<DD>Sets the foreground of the htext widget to <I>color</I>. This is the color
+of the text. This default is <I>black</I>. </DD>
+
+<DT><B>-height</B> <I>pixels</I> </DT>
+<DD>Specifies the height of
+the htext widget window. </DD>
+
+<DT><B>-linespacing</B> <I>pixels</I> </DT>
+<DD>Specifies the spacing between
+each line of text. The value must be in a form accepted by <B>Tk_GetPixels</B>.
+The default value is 1 pixel. </DD>
+
+<DT><B>-specialchar</B> <I>number</I> </DT>
+<DD>Specifies the ASCII value
+of the special double character delimiters. In <B>htext</B> files, the text between
+these special characters is evaluated as a block of Tcl commands. The default
+special character is the <I>0x25</I> (percent sign). </DD>
+
+<DT><B>-text</B> <I>text</I> </DT>
+<DD>Specifies the
+text to be displayed in the htext widget. <I>Text</I> can be any valid string
+of characters. See <FONT SIZE=-1><B>FILE FORMAT</B></FONT>
+ for a description. </DD>
+
+<DT><B>-xscrollcommand</B> <I>string</I>
+ </DT>
+<DD>Specifies the prefix for a command used to communicate with horizontal
+scrollbars. When the view in the htext widget's window changes (or whenever
+anything else occurs that could change the display in a scrollbar, such
+as a change in the total size of the widget's contents), the widget invoke
+<I>string</I> concatenated by two numbers. Each of the numbers is a fraction between
+0 and 1, which indicates a position in the document. If this option is
+not specified, then no command will be executed. </DD>
+
+<DT><B>-yscrollcommand</B> <I>string</I> </DT>
+<DD>Specifies
+the prefix for a command used to communicate with vertical scrollbars.
+When the view in the htext widget's window changes (or whenever anything
+else occurs that could change the display in a scrollbar, such as a change
+in the total size of the widget's contents), the widget invoke <I>string</I> concatenated
+by two numbers. Each of the numbers is a fraction between 0 and 1, which
+indicates a position in the document. If this option is not specified,
+then no command will be executed. </DD>
+
+<DT><B>-width</B> <I>pixels</I> </DT>
+<DD>Specifies the desired width
+of the viewport window. If the <I>pixels</I> is less than one, the window will
+grow to accommodate the widest line of text. </DD>
+
+<DT><B>-xscrollunits</B> <I>pixels</I> </DT>
+<DD>Specifies
+the horizontal scrolling distance. The default is 10 pixels. </DD>
+
+<DT><B>-yscrollunits</B>
+<I>pixels</I> </DT>
+<DD>Specifies the vertical scrolling distance. The default is 10 pixels.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>gotoline </B></I>?<I>index</I>? </DT>
+<DD>Sets the top line of the text to <I>index</I>. <I>Index</I>
+must be a valid text index (the character offset is ignored). If an <I>index</I>
+isn't provided, the current line number is returned. </DD>
+
+<DT><I>pathName <B>scan mark
+<I>position</I></B></I> </DT>
+<DD>Records <I>position</I> and the current view in the text window; used
+in conjunction with later <B>scan dragto</B> commands. <I>Position</I> must be in the
+form "<I>@x,y</I>, where <I>x</I> and <I>y</I> are window coordinates. Typically this command
+is associated with a mouse button press in the widget. It returns an empty
+string. </DD>
+
+<DT><I>pathName <B>scan dragto <I>position</I></B></I> </DT>
+<DD>Computes the difference between <I>position</I>
+and the position registered in the last <B>scan mark</B> command for the widget.
+ The view is then adjusted up or down by 10 times the difference in coordinates.
+ This command is can be associated with mouse motion events to produce
+the effect of dragging the text at high speed through the window. <I>Position</I>
+must be in the form "<I>@x,y</I>, where <I>x</I> and <I>y</I> are window coordinates. The command
+returns an empty string. </DD>
+
+<DT><I>pathName <B>search <I>pattern</I></B></I> ?<I>from</I>? ?<I>to</I>? </DT>
+<DD>Returns the
+number of the next line matching <I>pattern</I>. <I>Pattern</I> is a string which obeys
+the matching rules of <B>Tcl_StringMatch</B>. <I>From</I> and <I>to</I> are text line numbers
+(inclusive) which bound the search. If no match for <I>pattern</I> can be found,
+<B>-1</B> is returned. </DD>
+
+<DT><I>pathName <B>xview </B></I>?<I>position</I>? </DT>
+<DD>Moves the viewport horizontally
+to the new text x-coordinate position. <I>Position</I> is the offset from the
+left side of the text to the current position and must be in a form accepted
+by <B>Tk_GetPixels</B>. If <I>position</I> is not present, the current text position is
+returned. </DD>
+
+<DT><I>pathName <B>yview </B></I>?<I>position</I>? </DT>
+<DD>Moves the viewport vertically to the
+new text y-coordinate position. <I>Position</I> is the offset from the top of
+the text to the current position and must be in a form accepted by <B>Tk_GetPixels</B>.
+If <I>position</I> is not present, the current text position is returned. </DD>
+</DL>
+
+<H2><A NAME="sect8" HREF="#toc8">Bugs</A></H2>
+Text
+with embedded tabs can be obscured by child windows when scrolled horizontally.
+
+<H2><A NAME="sect9" HREF="#toc9">Keywords</A></H2>
+hypertext, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">File Format</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Indices</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Variables</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Syntax</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Operations</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Bugs</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/spline.html b/blt/html/spline.html
new file mode 100644
index 00000000000..368746cc2f1
--- /dev/null
+++ b/blt/html/spline.html
@@ -0,0 +1,160 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>spline(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+spline - Fit curves with spline interpolation
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<P>
+<B>spline natural <I>x y sx sy</I></B> <P>
+<B>spline quadratic <I>x y sx sy</I></B>
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The
+<B>spline</B> command computes a spline fitting a set of data points (x and y
+vectors) and produces a vector of the interpolated images (y-coordinates)
+at a given set of x-coordinates.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Curve fitting has many applications.
+ In graphs, curve fitting can be useful for displaying curves which are
+aesthetically pleasing to the eye. Another advantage is that you can quickly
+generate arbitrary points on the curve from a small set of data points.
+<P>
+A spline is a device used in drafting to produce smoothed curves. The points
+of the curve, known as <I>knots</I>, are fixed and the <I>spline</I>, typically a thin
+strip of wood or metal, is bent around the knots to create the smoothed
+curve. Spline interpolation is the mathematical equivalent. The curves
+between adjacent knots are piecewise functions such that the resulting
+spline runs exactly through all the knots. The order and coefficients of
+the polynominal determine the "looseness" or "tightness" of the curve fit
+from the line segments formed by the knots. <P>
+The <B>spline</B> command performs
+spline interpolation using cubic ("natural") or quadratic polynomial functions.
+ It computes the spline based upon the knots, which are given as x and
+y vectors. The interpolated new points are determined by another vector
+which represents the abscissas (x-coordinates) or the new points. The ordinates
+(y-coordinates) are interpolated using the spline and written to another
+vector.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+Before we can use the <B>spline</B> command, we need to create two
+BLT vectors which will represent the knots (x and y coordinates) of the
+data that we're going to fit. Obviously, both vectors must be the same length.
+<BR>
+<CODE># Create sample data of ten points. <BR>
+vector x(10) y(10)<BR>
+<P>
+for {set i 10} {$i &gt; 0} {incr i -1} {<BR>
+ set x($i-1) [expr $i*$i]<BR>
+ set y($i-1) [expr sin($i*$i*$i)]<BR>
+}<BR>
+</CODE><P>We now have two vectors <I>x</I> and <I>y</I> representing the ten data points we're trying
+to fit. The order of the values of <I>x</I> must be monotonically increasing.
+We can use the vector's <B>sort</B> operation to sort the vectors. <BR>
+<CODE>x sort y<BR>
+</CODE><P>The components of <I>x</I> are sorted in increasing order. The components of <I>y</I>
+are rearranged so that the original x,y coordinate pairings are retained.
+<P>
+A third vector is needed to indicate the abscissas (x-coordinates) of the
+new points to be interpolated by the spline. Like the x vector, the vector
+of abscissas must be monotonically increasing. All the abscissas must lie
+between the first and last knots (x vector) forming the spline. <P>
+How the
+abscissas are picked is arbitrary. But if we are going to plot the spline,
+we will want to include the knots too. Since both the quadratic and natural
+splines preserve the knots (an abscissa from the x vector will always produce
+the corresponding ordinate from the y vector), we can simply make the new
+vector a superset of <I>x</I>. It will contain the same coordinates as <I>x</I>, but also
+the abscissas of the new points we want interpolated. A simple way is to
+use the vector's <B>populate</B> operation. <BR>
+<CODE>x populate sx 10<BR>
+</CODE><P>This creates a new vector <I>sx</I>. It contains the abscissas of <I>x</I>, but in addition
+<I>sx</I> will have ten evenly distributed values between each abscissa. You can
+interpolate any points you wish, simply by setting the vector values. <P>
+Finally,
+we generate the ordinates (the images of the spline) using the <B>spline</B> command.
+ The ordinates are stored in a fourth vector. <BR>
+<CODE>spline natural x y sx sy<BR>
+</CODE><P>This creates a new vector <I>sy</I>. It will have the same length as <I>sx</I>. The vectors
+<I>sx</I> and <I>sy</I> represent the smoothed curve which we can now plot. <BR>
+<CODE>graph .graph<BR>
+.graph element create original -x x -y x -color blue<BR>
+.graph element create spline -x sx -y sy -color red<BR>
+table . .graph<BR>
+</CODE><P>The <B>natural</B> operation employs a cubic interpolant when forming the spline.
+ In terms of the draftmen's spline, a <I>natural spline</I> requires the least
+amount of energy to bend the spline (strip of wood), while still passing
+through each knot. In mathematical terms, the second derivatives of the
+first and last points are zero. <P>
+Alternatively, you can generate a spline
+using the <B>quadratic</B> operation. Quadratic interpolation produces a spline
+which follows the line segments of the data points much more closely.
+ <BR>
+<CODE>spline quadratic x y sx sy <BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Operations</A></H2>
+
+<DL>
+
+<DT><B>spline natural <I>x y sx sy</I></B> </DT>
+<DD>Computes a cubic spline from the data
+points represented by the vectors <I>x</I> and <I>y</I> and interpolates new points using
+vector <I>sx</I> as the x-coordinates. The resulting y-coordinates are written to
+a new vector <I>sy</I>. The vectors <I>x</I> and <I>y</I> must be the same length and contain
+at least three components. The order of the components of <I>x</I> must be monotonically
+increasing. <I>Sx</I> is the vector containing the x-coordinates of the points to
+be interpolated. No component of <I>sx</I> can be less than first component of
+<I>x</I> or greater than the last component. The order of the components of <I>sx</I>
+must be monotonically increasing. <I>Sy</I> is the name of the vector where the
+calculated y-coordinates will be stored. If <I>sy</I> does not already exist, a
+new vector will be created. </DD>
+
+<DT><B>spline quadratic <I>x y sx sy</I></B> </DT>
+<DD>Computes a quadratic
+spline from the data points represented by the vectors <I>x</I> and <I>y</I> and interpolates
+new points using vector <I>sx</I> as the x-coordinates. The resulting y-coordinates
+are written to a new vector <I>sy</I>. The vectors <I>x</I> and <I>y</I> must be the same length
+and contain at least three components. The order of the components of <I>x</I>
+must be monotonically increasing. <I>Sx</I> is the vector containing the x-coordinates
+of the points to be interpolated. No component of <I>sx</I> can be less than first
+component of <I>x</I> or greater than the last component. The order of the components
+of <I>sx</I> must be monotonically increasing. <I>Sy</I> is the name of the vector where
+the calculated y-coordinates are stored. If <I>sy</I> does not already exist, a
+new vector will be created. </DD>
+</DL>
+
+<H2><A NAME="sect6" HREF="#toc6">References</A></H2>
+<BR>
+<PRE>
+Numerical Analysis
+by R. Burden, J. Faires and A. Reynolds.<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;
+Prindle, Weber &amp; Schmidt, 1981, pp. 112
+
+Shape Preserving Quadratic Splines
+by D.F.Mcallister &amp; J.A.Roulier
+Coded by S.L.Dodd &amp; M.Roulier N.C.State University.
+
+</PRE>The original code for the quadratric spline can be found in TOMS #574.
+<H2><A NAME="sect7" HREF="#toc7">Keywords</A></H2>
+spline,
+vector, graph <P>
+ <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Operations</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">References</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/stripchart.html b/blt/html/stripchart.html
new file mode 100644
index 00000000000..9a9fe823b00
--- /dev/null
+++ b/blt/html/stripchart.html
@@ -0,0 +1,2179 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>stripchart(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+stripchart - 2D strip chart for plotting x and
+y coordinate data.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>stripchart<I> <I>pathName </I></I></B>?<I>option value</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The
+<B>stripchart</B> command creates a strip chart for plotting two-dimensional data
+(x,y coordinates). It has many configurable components: coordinate axes,
+elements, legend, grid lines, cross hairs, etc. They allow you to customize
+the look and feel of the strip chart. <P>
+The <B>stripchart</B> is essentially the
+same as the <B>graph</B> widget. It works almost exactly the very same way. <P>
+The
+use of a strip chart differs in that the X-axis typically refers to time
+points. Data values are added at intervals. The strip chart lets you automatically
+maintain a view of the most recent time points. The axis options <B>-shiftby</B>
+and <B>-autorange</B> control this. You can specify different line styles for data
+points (see the <B>-styles</B> option).
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The <B>stripchart</B> command creates
+a new window for plotting two-dimensional data (x,y coordinates). Data points
+are plotted in a box displayed in the center of the new window. This is
+the <I>plotting area</I>. The coordinate axes are displayed in the margins around
+the plotting area. By default, the legend is displayed in the right margin.
+ The title is displayed in top margin. <P>
+A strip chart is composed of several
+components: coordinate axes, data elements, legend, grid, cross hairs,
+pens, postscript, and annotation markers.
+<DL>
+
+<DT><I>axis</I> </DT>
+<DD>The stripchart widget can
+display up to four coordinate axes (two X-coordinate and two Y-coordinate
+axes), but you can create and use any number of axes. Axes control what
+region of data is displayed and how the data is scaled. Each axis consists
+of the axis line, title, major and minor ticks, and tick labels. Tick labels
+display the value of each major tick. </DD>
+
+<DT><I>crosshairs</I> </DT>
+<DD>Cross hairs are used to
+finely position the mouse pointer in relation to the coordinate axes. Two
+perpendicular lines are drawn across the plotting area, intersecting at
+the current location of the mouse pointer. </DD>
+
+<DT><I>element</I> </DT>
+<DD>An element represents
+a set of data points. Elements can be plotted with a symbol at each data
+point and lines connecting the points. The appearance of the element, such
+as its symbol, line width, and color is configurable. </DD>
+
+<DT><I>grid</I> </DT>
+<DD>Extends the
+major and minor ticks of the X-axis and/or Y-axis across the plotting area.
+ </DD>
+
+<DT><I>legend</I> </DT>
+<DD>The legend displays the name and symbol of each data element.
+The legend can be drawn in any margin or in the plotting area. </DD>
+
+<DT><I>marker</I> </DT>
+<DD>Markers
+are used annotate or highlight areas of the graph. For example, you could
+use a polygon marker to fill an area under a curve, or a text marker to
+label a particular data point. Markers come in various forms: text strings,
+bitmaps, connected line segments, images, polygons, or embedded widgets.
+</DD>
+
+<DT><I>pen</I> </DT>
+<DD>Pens define attributes (both symbol and line style) for elements.
+Data elements use pens to specify how they should be drawn. A data element
+may use many pens at once. Here, the particular pen used for a data point
+is determined from each element's weight vector (see the element's <B>-weight</B>
+and <B>-style</B> options). </DD>
+
+<DT><I>postscript</I> </DT>
+<DD>The widget can generate encapsulated PostScript
+output. This component has several options to configure how the PostScript
+is generated. </DD>
+</DL>
+
+<H2><A NAME="sect4" HREF="#toc4">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>stripchart <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>stripchart</B> command creates a new window <I>pathName</I> and makes it into
+a <B>stripchart</B> widget. At the time this command is invoked, there must not
+exist a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional
+options may may be specified on the command line or in the option database
+to configure aspects of the strip chart such as its colors and font. See
+the <B>configure</B> operation below for the exact details as to what <I>option</I> and
+<I>value</I> pairs are valid. <P>
+If successful, <B>stripchart</B> returns the path name of
+the widget. It also creates a new Tcl command by the same name. You can
+use this command to perform various operations that query or modify the
+graph. The general form is: <BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for the strip chart are described in the <FONT SIZE=-1><B>STRIPCHART
+OPERATIONS</B></FONT>
+ section. <P>
+The command can also be used to access components of
+the strip chart. <BR>
+<P>
+<CODE><I>pathName component operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>The operation, now located after the name of the component, is the function
+to be performed on that component. Each component has its own set of operations
+that manipulate that component. They will be described below in their own
+sections.
+<H2><A NAME="sect5" HREF="#toc5">Example</A></H2>
+The <B>stripchart</B> command creates a new strip chart. <BR>
+<CODE># Create a new strip chart. Plotting area is black.<BR>
+stripchart .s -plotbackground black<BR>
+</CODE><P>A new Tcl command <I>.s</I> is also created. This command can be used to query
+and modify the strip chart. For example, to change the title of the strip
+chart to "My Plot", you use the new command and the widget's <B>configure</B> operation.
+<BR>
+<CODE># Change the title.<BR>
+.s configure -title "My Plot"<BR>
+</CODE><P>A strip chart has several components. To access a particular component you
+use the component's name. For example, to add data elements, you use the
+new command and the <B>element</B> component. <BR>
+<CODE># Create a new element named "line1"<BR>
+.s element create line1 \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-xdata { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;155.85 166.60 175.38 }<BR>
+</CODE><P>The element's X and Y coordinates are specified using lists of numbers.
+Alternately, BLT vectors could be used to hold the X-Y coordinates. <BR>
+<CODE># Create two vectors and add them to the strip chart.<BR>
+vector xVec yVec<BR>
+xVec set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }<BR>
+yVec set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85 <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;166.60 175.38 }<BR>
+.s element create line1 -xdata xVec -ydata yVec<BR>
+</CODE><P>The advantage of using vectors is that when you modify one, the graph is
+automatically redrawn to display the new values. <BR>
+<CODE># Change the X-Y coordinates of the first point.<BR>
+set xVec(0) 0.18<BR>
+set yVec(0) 25.18<BR>
+</CODE><P>An element named <I>line1</I> is now created in <I>.s</I>. By default, the element's label
+in the legend will be also <I>line1</I>. You can change the label, or specify no
+legend entry, again using the element's <B>configure</B> operation. <BR>
+<CODE># Don't display "line1" in the legend.<BR>
+.s element configure line1 -label ""<BR>
+</CODE><P>You can configure more than just the element's label. An element has many
+attributes such as symbol type and size, dashed or solid lines, colors,
+line width, etc. <BR>
+<CODE>.s element configure line1 -symbol square -color red \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-dashes { 2 4 2 } -linewidth 2 -pixels 2c<BR>
+</CODE><P>Four coordinate axes are automatically created: <I>x</I>, <I>x2</I>, <I>y</I>, and <I>y2</I>. And by
+default, elements are mapped onto the axes <I>x</I> and <I>y</I>. This can be changed
+with the <B>-mapx</B> and <B>-mapy</B> options. <BR>
+<CODE># Map "line1" on the alternate Y-axis "y2".<BR>
+.s element configure line1 -mapy y2<BR>
+</CODE><P>Axes can be configured in many ways too. For example, you change the scale
+of the Y-axis from linear to log using the <B>axis</B> operation. <BR>
+<CODE># Y-axis is log scale.<BR>
+.s axis configure y -logscale yes<BR>
+</CODE><P>Axis limits are reset by simply specifying new axis limits using the <B>-min</B>
+and <B>-max</B> configuration options. <BR>
+<CODE>.s axis configure x -min 1.0 -max 1.5<BR>
+.s axis configure y -min 12.0 -max 55.15<BR>
+</CODE><P>By default, the limits of the axis are determined from data values. To reset
+back to the default limits, set the <B>-min</B> and <B>-max</B> options to the empty value.
+<BR>
+<CODE># Reset the axes to autoscale again.<BR>
+.s axis configure x -min {} -max {}<BR>
+.s axis configure y -min {} -max {}<BR>
+</CODE><P>It's common with strip charts to automatically maintain a view of the most
+recent time points. You can do this my setting the <B>-autorange</B> option. <BR>
+<CODE>.s axis configure x -autorange 20.0<BR>
+</CODE><P>If the time points are added in X-coordinates 1.0 unit, only the last twenty
+time points will be displayed. As more data is added, the view will march
+along. <P>
+Sometimes the rate of data is so high that changing the axis limits
+with each additional time point is prohibitive. You can use the <B>-shiftby</B>
+option to define an increment to shift the view when needed. <BR>
+<CODE>.s axis configure x -shiftby 15.0<BR>
+</CODE><P>When the view is shifted, it will allow a range of 15 new time points to
+be added until the axis limits are recomputed. <P>
+By default, the legend is
+displayed in the right margin. You can change this or any other legend
+configuration options using the <B>legend</B> component. <BR>
+<CODE># Configure the legend font, color, and relief<BR>
+.s legend configure -position left -relief raised \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-font fixed -fg blue<BR>
+</CODE><P>To prevent the legend from being displayed, turn on the <B>-hide</B> option. <BR>
+<CODE># Don't display the legend.<BR>
+.s legend configure -hide yes<BR>
+</CODE><P>The <B>stripchart</B> widget has simple drawing procedures called markers. They
+can be used to highlight or annotate data in the strip chart. The types
+of markers available are bitmaps, images, polygons, lines, or windows. Markers
+can be used, for example, to mark or brush points. Here is a text marker
+which labels the data first point. Markers are created using the <B>marker</B>
+operation. <BR>
+<CODE># Create a label for the first data point of "line1".<BR>
+.s marker create text -name first_marker -coords { 0.2 26.18 } \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-text "start" -anchor se -xoffset -10 -yoffset -10<BR>
+</CODE><P>This creates a text marker named <I>first_marker</I>. It will display the text
+"start" near the coordinates of the first data point. The <B>-anchor</B>, <B>-xoffset</B>,
+and <B>-yoffset</B> options are used to display the marker above and to the left
+of the data point, so that the actual data point isn't covered by the marker.
+ By default, markers are drawn last, on top of data. You can change this
+with the <B>-under</B> option. <BR>
+<CODE># Draw the label before elements are drawn.<BR>
+.s marker configure first_marker -under yes<BR>
+</CODE><P>You can add cross hairs or grid lines using the <B>crosshairs</B> and <B>grid</B> operations.
+<BR>
+<CODE># Display both cross hairs and grid lines.<BR>
+.s crosshairs configure -hide no -color red<BR>
+.s grid configure -hide no -dashes { 2 2 }<BR>
+</CODE><P>Finally, to get hardcopy of the strip chart, use the <B>postscript</B> operation.
+<BR>
+<CODE># Print the strip chart into file "file.ps"<BR>
+.s postscript output file.ps -maxpect yes -decorations no<BR>
+</CODE><P>This generates a file <I>file.ps</I> containing the encapsulated PostScript of
+the strip chart. The option <B>-maxpect</B> says to scale the plot to the size
+of the page. Turning off the <B>-decorations</B> option indicates that no borders
+or color backgrounds should be displayed (i.e. the background of the margins,
+legend, and plotting area will be white).
+<H2><A NAME="sect6" HREF="#toc6">Stripchart Operations</A></H2>
+
+<DL>
+
+<DT><I>pathName
+<B>axis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>bar <I>elemName
+</I></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new barchart element <I>elemName</I>. It's an error if
+an element <I>elemName</I> already exists. See the manual for <B>barchart</B> for details
+about what <I>option</I> and <I>value</I> pairs are valid. </DD>
+
+<DT><I>pathName <B>cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the stripchart configuration option given by <I>option</I>.
+ <I>Option</I> may be any option described below for the <B>configure</B> operation. </DD>
+
+<DT><I>pathName
+<B>configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration options
+of the strip chart. If <I>option</I> isn't specified, a list describing all of
+the current options for <I>pathName</I> is returned. If <I>option</I> is specified, but
+not <I>value</I>, then a list describing <I>option</I> is returned. If one or more <I>option</I>
+and <I>value</I> pairs are specified, then for each pair, the stripchart option
+<I>option</I> is set to <I>value</I>. The following options are valid for the stripchart.
+<blockquote></DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background color. This includes the margins and
+legend, but not the plotting area. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the 3-D border around the outside edge of the widget. The <B>-relief</B> option
+determines if the border is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-bottommargin
+<I>pixels</I></B> </DT>
+<DD>Specifies the size of the margin below the X-coordinate axis. If
+<I>pixels</I> is <I>0</I>, the size of the margin is selected automatically. The default
+is <I>0</I>. </DD>
+
+<DT><B>-bufferelements <I>boolean</I></B> </DT>
+<DD>Indicates whether to draw elements into a pixmap
+before displaying them on the screen. The advantage of buffering elements
+is when markers are used heavily. Markers can be moved and redrawn without
+requiring every element to be redrawn again. The disadvantage is that it
+takes slightly longer to draw the graph. If <I>boolean</I> is true, data elements
+are drawn to an internal pixmap. The option should be turned off if the
+plot is updated frequently. See the <FONT SIZE=-1><B>SPEED TIPS</B></FONT>
+ section. The default is <I>1</I>.
+</DD>
+
+<DT><B>-buffergraph <I>boolean</I></B> </DT>
+<DD>Indicates whether to draw the graph into a pixmap first.
+If <I>boolean</I> is true, the entire graph is drawn into a pixmap and then copied
+onto the screen. This reduces flashing. If false, the graph is drawn directly
+into the window. Especially under Windows, turning off the option can
+be helpful when the stripchart is updated frequently. Turning off this
+option also turns <B>-bufferelements</B> off. See the <FONT SIZE=-1><B>SPEED TIPS</B></FONT>
+ section. The default
+is <I>1</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is
+<I>crosshair</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the title font. The default is <I>*-Helvetica-Bold-R-Normal-*-18-180-*</I>.
+</DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a maximum distance to consider when searching for
+the closest data point (see the element's <B>closest</B> operation below). Data
+points further than <I>pixels</I> away are ignored. The default is <I>0.5i</I>. </DD>
+
+<DT><B>-height
+<I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget. The default is <I>4i</I>. </DD>
+
+<DT><B>-invertxy
+<I>boolean</I></B> </DT>
+<DD>Indicates whether the placement X-axis and Y-axis should be inverted.
+ If <I>boolean</I> is true, the X and Y axes are swapped. The default is <I>0</I>. </DD>
+
+<DT><B>-justify
+<I>justify</I></B> </DT>
+<DD>Specifies how the title should be justified. This matters only
+when the title contains more than one line of text. <I>Justify</I> must be <I>left</I>,
+<I>right</I>, or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-leftmargin <I>pixels</I></B> </DT>
+<DD>Sets the size
+of the margin from the left edge of the window to the Y-coordinate axis.
+ If <I>pixels</I> is <I>0</I>, the size is calculated automatically. The default is <I>0</I>.
+</DD>
+
+<DT><B>-plotbackground <I>color</I></B> </DT>
+<DD>Specifies the background color of the plotting area.
+ The default is <I>white</I>. </DD>
+
+<DT><B>-plotborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border
+around the plotting area. The <B>-plotrelief</B> option determines if a border
+is drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-plotpadx <I>pad</I></B> </DT>
+<DD>Sets the amount of padding to be
+added to the left and right sides of the plotting area. <I>Pad</I> can be a list
+of one or two screen distances. If <I>pad</I> has two elements, the left side
+of the plotting area entry is padded by the first distance and the right
+side by the second. If <I>pad</I> is just one distance, both the left and right
+sides are padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotpady <I>pad</I></B> </DT>
+<DD>Sets the amount
+of padding to be added to the top and bottom of the plotting area. <I>Pad</I>
+can be a list of one or two screen distances. If <I>pad</I> has two elements,
+the top of the plotting area is padded by the first distance and the bottom
+by the second. If <I>pad</I> is just one distance, both the top and bottom are
+padded evenly. The default is <I>8</I>. </DD>
+
+<DT><B>-plotrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the plotting area. <I>Relief</I> indicates how the interior of the plotting
+area should appear relative to rest of the strip chart; for example, <I>raised</I>
+means the plot should appear to protrude from the strip chart, relative
+to the surface of the strip chart. The default is <I>sunken</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B>
+</DT>
+<DD>Specifies the 3-D effect for the widget. <I>Relief</I> indicates how the strip
+chart should appear relative to widget it is packed into; for example,
+<I>raised</I> means the strip chart should appear to protrude. The default is
+<I>flat</I>. </DD>
+
+<DT><B>-rightmargin <I>pixels</I></B> </DT>
+<DD>Sets the size of margin from the plotting area
+to the right edge of the window. By default, the legend is displayed in
+this margin. If <I>pixels</I> is than 1, the margin size is selected automatically.
+</DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides information used when moving the focus from window
+to window via keyboard traversal (e.g., Tab and Shift-Tab). If <I>focus</I> is <I>0</I>,
+this means that this window should be skipped entirely during keyboard
+traversal. <I>1</I> means that the this window should always receive the input
+focus. An empty value means that the traversal scripts make the decision
+whether to focus on the window. The default is <I>""</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies
+a tiled background. If <I>image</I> isn't <I>""</I>, the background is tiled using <I>image</I>.
+Otherwise, the normal background color is drawn (see the <B>-background</B> option).
+ <I>Image</I> must be an image created using the Tk <B>image</B> command. The default
+is <I>""</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title to <I>text</I>. If <I>text</I> is <I>""</I>, no title will be
+displayed. </DD>
+
+<DT><B>-topmargin <I>pixels</I></B> </DT>
+<DD>Specifies the size of the margin above the x2
+axis. If <I>pixels</I> is <I>0</I>, the margin size is calculated automatically. </DD>
+
+<DT><B>-width
+<I>pixels</I></B> </DT>
+<DD>Specifies the requested width of the widget. The default is <I>5i</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>crosshairs <I>operation </I></B></I>?<I>arg</I>? </DT>
+<DD>See the <FONT SIZE=-1><B>CROSSHAIRS COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName
+<B>element <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>ELEMENT COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>extents
+<I>item</I></B></I> </DT>
+<DD>Returns the size of a particular item in the strip chart. <I>Item</I> must
+be either <I>leftmargin</I>, <I>rightmargin</I>, <I>topmargin</I>, <I>bottommargin</I>, <I>plotwidth</I>,
+or <I>plotheight</I>. </DD>
+
+<DT><I>pathName <B>grid <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>GRID COMPONENT</B></FONT>
+ section.
+</DD>
+
+<DT><I>pathName <B>invtransform <I>winX winY</I></B></I> </DT>
+<DD>Performs an inverse coordinate transformation,
+mapping window coordinates back to graph coordinates, using the standard
+X-axis and Y-axis. Returns a list of containing the graph coordinates. </DD>
+
+<DT><I>pathName
+<B>legend <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>LEGEND COMPONENT</B></FONT>
+ section. </DD>
+
+<DT><I>pathName <B>line
+<I>elemName</I></B></I> ?<I>option value</I>?... </DT>
+<DD>The operation is the same as <B>element</B>. </DD>
+
+<DT><I>pathName <B>marker
+<I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>MARKER COMPONENTS</B></FONT>
+ section. </DD>
+
+<DT><I>pathName</I> <B>metafile</B> ?<I>fileName</I>?
+</DT>
+<DD><I>This operation is for Window platforms only</I>. Creates a Windows enhanced
+metafile of the stripchart. If present, <I>fileName</I> is the file name of the
+new metafile. Otherwise, the metafile is automatically added to the clipboard.
+</DD>
+
+<DT><I>pathName <B>postscript <I>operation </I></B></I>?<I>arg</I>?... </DT>
+<DD>See the <FONT SIZE=-1><B>POSTSCRIPT COMPONENT</B></FONT>
+ section.
+</DD>
+
+<DT><I>pathName <B>snap <I>photoName</I></B></I> </DT>
+<DD>Takes a snapshot of the strip chart and stores
+the contents in the photo image <I>photoName</I>. <I>PhotoName</I> is the name of a Tk
+photo image that must already exist. </DD>
+
+<DT><I>pathName <B>transform <I>x y</I></B></I> </DT>
+<DD>Performs a
+coordinate transformation, mapping graph coordinates to window coordinates,
+using the standard X-axis and Y-axis. Returns a list containing the X-Y screen
+coordinates. </DD>
+
+<DT><I>pathName <B>xaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>x2axis <I>operation</I></B></I> ?<I>arg</I>?...
+ </DT>
+<DD></DD>
+
+<DT><I>pathName <B>yaxis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <B>y2axis <I>operation</I></B></I> ?<I>arg</I>?... </DT>
+<DD>See the
+ <FONT SIZE=-1><B>AXIS COMPONENTS</B></FONT>
+ section. </DD>
+</DL>
+
+<H2><A NAME="sect7" HREF="#toc7">Stripchart Components</A></H2>
+A strip chart is composed
+of several components: coordinate axes, data elements, legend, grid, cross
+hairs, postscript, and annotation markers. Instead of one big set of configuration
+options and operations, the strip chart is partitioned, where each component
+has its own configuration options and operations that specifically control
+that aspect or part of the strip chart.
+<H3><A NAME="sect8" HREF="#toc8">Axis Components</A></H3>
+Four coordinate
+axes are automatically created: two X-coordinate axes (<I>x</I> and <I>x2</I>) and two
+Y-coordinate axes (<I>y</I>, and <I>y2</I>). By default, the axis <I>x</I> is located in the
+bottom margin, <I>y</I> in the left margin, <I>x2</I> in the top margin, and <I>y2</I> in the
+right margin. <P>
+An axis consists of the axis line, title, major and minor
+ticks, and tick labels. Major ticks are drawn at uniform intervals along
+the axis. Each tick is labeled with its coordinate value. Minor ticks are
+drawn at uniform intervals within major ticks. <P>
+The range of the axis controls
+what region of data is plotted. Data points outside the minimum and maximum
+limits of the axis are not plotted. By default, the minimum and maximum
+limits are determined from the data, but you can reset either limit. <P>
+You
+can create and use several axes. To create an axis, invoke the axis component
+and its create operation. <BR>
+<CODE># Create a new axis called "temperature"<BR>
+.s axis create temperature<BR>
+</CODE><P>You map data elements to an axis using the element's -mapy and -mapx configuration
+options. They specify the coordinate axes an element is mapped onto. <BR>
+<CODE># Now map the temperature data to this axis.<BR>
+.s element create "temp" -xdata $x -ydata $tempData \<BR>
+ -mapy temperature<BR>
+</CODE><P>While you can have many axes, only four axes can be displayed simultaneously.
+ They are drawn in each of the margins surrounding the plotting area. The
+axes x and y are drawn in the bottom and left margins. The axes x2 and y2
+are drawn in top and right margins. Only x and y are shown by default. Note
+that the axes can have different scales. <P>
+To display a different axis, you
+invoke one of the following components: <B>xaxis</B>, <B>yaxis</B>, <B>x2axis</B>, and <B>y2axis</B>.
+The <B>use</B> operation designates the axis to be drawn in the corresponding
+margin: <B>xaxis</B> in the bottom, <B>yaxis</B> in the left, <B>x2axis</B> in the top, and
+<B>y2axis</B> in the right. <BR>
+<CODE># Display the axis temperature in the left margin.<BR>
+.s yaxis use temperature<BR>
+<P>
+</CODE><P>You can configure axes in many ways. The axis scale can be linear or logarithmic.
+ The values along the axis can either monotonically increase or decrease.
+ If you need custom tick labels, you can specify a Tcl procedure to format
+the label as you wish. You can control how ticks are drawn, by changing
+the major tick interval or the number of minor ticks. You can define non-uniform
+tick intervals, such as for time-series plots. <P>
+
+<DL>
+
+<DT><I>pathName <B>axis <B>cget <I>axisName
+<I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option given by <I>option</I> for <I>axisName</I>.
+ <I>Option</I> may be any option described below for the axis <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>axis <B>configure <I>axisName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies the
+configuration options of <I>axisName</I>. If <I>option</I> isn't specified, a list describing
+all the current options for <I>axisName</I> is returned. If <I>option</I> is specified,
+but not <I>value</I>, then a list describing <I>option</I> is returned. If one or more
+<I>option</I> and <I>value</I> pairs are specified, then for each pair, the axis option
+<I>option</I> is set to <I>value</I>. The following options are valid for axes. <blockquote></DD>
+
+<DT><B>-autorange
+<I>range</I></B> </DT>
+<DD>Sets the range of values for the axis to <I>range</I>. The axis limits
+are automatically reset to display the most recent data points in this
+range. If <I>range</I> is 0.0, the range is determined from the limits of the
+data. If <B>-min</B> or <B>-max</B> are specified, they override this option. The default
+is <I>0.0</I>. </DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the axis and tick labels. The default
+is <I>black</I>. </DD>
+
+<DT><B>-command <I>prefix</I></B> </DT>
+<DD>Specifies a Tcl command to be invoked when formatting
+the axis tick labels. <I>Prefix</I> is a string containing the name of a Tcl proc
+and any extra arguments for the procedure. This command is invoked for
+each major tick on the axis. Two additional arguments are passed to the
+procedure: the pathname of the widget and the current the numeric value
+of the tick. The procedure returns the formatted tick label. If <I>""</I> is returned,
+no label will appear next to the tick. You can get the standard tick labels
+again by setting <I>prefix</I> to <I>""</I>. The default is <I>""</I>. <P>
+Please note that this
+procedure is invoked while the strip chart is redrawn. You may query the
+configuration options. But do not reset them, because this can have unexpected
+results. </DD>
+
+<DT><B>-descending <I>boolean</I></B> </DT>
+<DD>Indicates whether the values along the axis
+are monotonically increasing or decreasing. If <I>boolean</I> is true, the axis
+values will be decreasing. The default is <I>0</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether
+the axis is displayed. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the axis title should
+be justified. This matters only when the axis title contains more than
+one line of text. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>. The default is
+<I>center</I>. </DD>
+
+<DT><B>-limits <I>formatStr</I></B> </DT>
+<DD>Specifies a printf-like description to format the
+minimum and maximum limits of the axis. The limits are displayed at the
+top/bottom or left/right sides of the plotting area. <I>FormatStr</I> is a list
+of one or two format descriptions. If one description is supplied, both
+the minimum and maximum limits are formatted in the same way. If two, the
+first designates the format for the minimum limit, the second for the maximum.
+ If <I>""</I> is given as either description, then the that limit will not be
+displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the axis
+and tick lines. The default is <I>1</I> pixel. </DD>
+
+<DT><B>-logscale <I>boolean</I></B> </DT>
+<DD>Indicates whether
+the scale of the axis is logarithmic or linear. If <I>boolean</I> is true, the
+axis is logarithmic. The default scale is linear. </DD>
+
+<DT><B>-loose <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the limits of the axis should fit the data points tightly, at the
+outermost data points, or loosely, at the outer tick intervals. This is
+relevant only when the axis limit is automatically calculated. If <I>boolean</I>
+is true, the axis range is "loose". The default is <I>0</I>. </DD>
+
+<DT><B>-majorticks <I>majorList</I></B>
+</DT>
+<DD>Specifies where to display major axis ticks. You can use this option to
+display ticks at non-uniform intervals. <I>MajorList</I> is a list of axis coordinates
+designating the location of major ticks. No minor ticks are drawn. If <I>majorList</I>
+is <I>""</I>, major ticks will be automatically computed. The default is <I>""</I>. </DD>
+
+<DT><B>-max
+<I>value</I></B> </DT>
+<DD>Sets the maximum limit of <I>axisName</I>. Any data point greater than
+<I>value</I> is not displayed. If <I>value</I> is <I>""</I>, the maximum limit is calculated
+using the largest data value. The default is <I>""</I>. </DD>
+
+<DT><B>-min <I>value</I></B> </DT>
+<DD>Sets the minimum
+limit of <I>axisName</I>. Any data point less than <I>value</I> is not displayed. If
+<I>value</I> is <I>""</I>, the minimum limit is calculated using the smallest data value.
+The default is <I>""</I>. </DD>
+
+<DT><B>-minorticks <I>minorList</I></B> </DT>
+<DD>Specifies where to display minor
+axis ticks. You can use this option to display minor ticks at non-uniform
+intervals. <I>MinorList</I> is a list of real values, ranging from 0.0 to 1.0, designating
+the placement of a minor tick. No minor ticks are drawn if the <B>-majortick</B>
+option is also set. If <I>minorList</I> is <I>""</I>, minor ticks will be automatically
+computed. The default is <I>""</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the how many degrees
+to rotate the axis tick labels. <I>Theta</I> is a real value representing the number
+of degrees to rotate the tick labels. The default is <I>0.0</I> degrees. </DD>
+
+<DT><B>-shiftby
+<I>value</I></B> </DT>
+<DD>Specifies how much to automatically shift the range of the axis. When
+the new data exceeds the current axis maximum, the maximum is increased
+in increments of <I>value</I>. You can use this option to prevent the axis limits
+from being recomputed at each new time point. If <I>value</I> is 0.0, then no automatic
+shifting is done. The default is <I>0.0</I>. </DD>
+
+<DT><B>-showticks <I>boolean</I></B> </DT>
+<DD>Indicates whether
+axis ticks should be drawn. If <I>boolean</I> is true, ticks are drawn. If false,
+only the axis line is drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-stepsize <I>value</I></B> </DT>
+<DD>Specifies the
+interval between major axis ticks. If <I>value</I> isn't a valid interval (must
+be less than the axis range), the request is ignored and the step size
+is automatically calculated. </DD>
+
+<DT><B>-subdivisions <I>number</I></B> </DT>
+<DD>Indicates how many minor
+axis ticks are to be drawn. For example, if <I>number</I> is two, only one minor
+tick is drawn. If <I>number</I> is one, no minor ticks are displayed. The default
+is <I>2</I>. </DD>
+
+<DT><B>-tickfont <I>fontName</I></B> </DT>
+<DD>Specifies the font for axis tick labels. The default
+is <I>*-Courier-Bold-R-Normal-*-100-*</I>. </DD>
+
+<DT><B>-ticklength <I>pixels</I></B> </DT>
+<DD>Sets the length of major
+and minor ticks (minor ticks are half the length of major ticks). If <I>pixels</I>
+is less than zero, the axis will be inverted with ticks drawn pointing
+towards the plot. The default is <I>0.1i</I>. </DD>
+
+<DT><B>-title <I>text</I></B> </DT>
+<DD>Sets the title of the axis.
+If <I>text</I> is <I>""</I>, no axis title will be displayed. </DD>
+
+<DT><B>-titlecolor <I>color</I></B> </DT>
+<DD>Sets
+the color of the axis title. The default is <I>black</I>. </DD>
+
+<DT><B>-titlefont <I>fontName</I></B> </DT>
+<DD>Specifies
+the font for axis title. The default is <I>*-Helvetica-Bold-R-Normal-*-14-140-*</I>. </DD>
+</DL>
+<P>
+Axis
+configuration options may be also be set by the <B>option</B> command. The resource
+class is <I>Axis</I>. The resource names are the names of the axes (such as <I>x</I>
+or <I>x2</I>). <BR>
+<CODE>option add *Stripchart.Axis.Color blue<BR>
+option add *Stripchart.x.LogScale true<BR>
+option add *Stripchart.x2.LogScale false<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>axis <B>create <I>axisName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new axis by the
+name <I>axisName</I>. No axis by the same name can already exist. <I>Option</I> and <I>value</I>
+are described in above in the axis <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>axis <B>delete
+</B></B></I>?<I>axisName</I>?... </DT>
+<DD>Deletes the named axes. An axis is not really deleted until it
+is not longer in use, so it's safe to delete axes mapped to elements. </DD>
+
+<DT><I>pathName
+<B>axis invtransform <I>axisName value</I></B></I> </DT>
+<DD>Performs the inverse transformation, changing
+the screen coordinate <I>value</I> to a graph coordinate, mapping the value mapped
+to <I>axisName</I>. Returns the graph coordinate. </DD>
+
+<DT><I>pathName <B>axis limits <I>axisName</I></B></I>
+</DT>
+<DD>Returns a list of the minimum and maximum limits for <I>axisName</I>. The order
+of the list is <I>min max</I>. </DD>
+
+<DT><I>pathName <B>axis names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of
+axes matching zero or more patterns. If no <I>pattern</I> argument is give, the
+names of all axes are returned. </DD>
+
+<DT><I>pathName <B>axis transform <I>axisName value</I></B></I> </DT>
+<DD>Transforms
+the coordinate <I>value</I> to a screen coordinate by mapping the it to <I>axisName</I>.
+ Returns the transformed screen coordinate. </DD>
+</DL>
+<P>
+Only four axes can be displayed
+simultaneously. By default, they are <I>x</I>, <I>y</I>, <I>x2</I>, and <I>y2</I>. You can swap in
+a different axis with <B>use</B> operation of the special axis components: <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B>. <BR>
+<CODE>.g create axis temp<BR>
+.g create axis time<BR>
+...<BR>
+.g xaxis use temp<BR>
+.g yaxis use time<BR>
+</CODE><P>Only the axes specified for use are displayed on the screen. <P>
+The <B>xaxis</B>,
+<B>x2axis</B>, <B>yaxis</B>, and <B>y2axis</B> components operate on an axis location rather
+than a specific axis like the more general <B>axis</B> component does. The <B>xaxis</B>
+component manages the X-axis located in the bottom margin (whatever axis
+that happens to be). Likewise, <B>yaxis</B> uses the Y-axis in the left margin,
+<B>x2axis</B> the top X-axis, and <B>y2axis</B> the right Y-axis. <P>
+They implicitly control
+the axis that is currently using to that location. By default, <B>xaxis</B> uses
+the <I>x</I> axis, <B>yaxis</B> uses <I>y</I>, <B>x2axis</B> uses <I>x2</I>, and <B>y2axis</B> uses <I>y2</I>. These components
+can be more convenient to use than always determining what axes are current
+being displayed by the graph. <P>
+The following operations are available for
+axes. They mirror exactly the operations of the <B>axis</B> component. The <I>axis</I>
+argument must be <B>xaxis</B>, <B>x2axis</B>, <B>yaxis</B>, or <B>y2axis</B>.
+<DL>
+
+<DT><I>pathName <I>axis <B>cget <I>option</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>configure </B></I></I>?<I>option value</I>?... </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> invtransform <I>value</I></B></I></I>
+</DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis <B>limits</B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> transform <I>value</I></B></I></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <I>axis<B> use </B></I></I>?<I>axisName</I>?
+ </DT>
+<DD>Designates the axis <I>axisName</I> is to be displayed at this location. <I>AxisName</I>
+can not be already in use at another location. This command returns the
+name of the axis currently using this location. </DD>
+</DL>
+
+<H3><A NAME="sect9" HREF="#toc9">Crosshairs Component</A></H3>
+Cross
+hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position the
+mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives. This
+means that they can be quickly drawn and erased without redrawing the entire
+strip chart. <P>
+The following operations are available for cross hairs:
+<DL>
+
+<DT><I>pathName
+<B>crosshairs cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the cross hairs configuration
+option given by <I>option</I>. <I>Option</I> may be any option described below for the
+cross hairs <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>crosshairs configure </B></I>?<I>option value</I>?...
+ </DT>
+<DD>Queries or modifies the configuration options of the cross hairs. If
+<I>option</I> isn't specified, a list describing all the current options for the
+cross hairs is returned. If <I>option</I> is specified, but not <I>value</I>, then a
+list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs
+are specified, then for each pair, the cross hairs option <I>option</I> is set
+to <I>value</I>. The following options are available for cross hairs. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B>
+ </DT>
+<DD>Sets the color of the cross hairs. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B>
+</DT>
+<DD>Sets the dash style of the cross hairs. <I>DashList</I> is a list of up to 11 numbers
+that alternately represent the lengths of the dashes and gaps on the cross
+hair lines. Each number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the
+cross hairs will be solid lines. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether cross hairs
+are drawn. If <I>boolean</I> is true, cross hairs are not drawn. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Set the width of the cross hair lines. The default
+is <I>1</I>. </DD>
+
+<DT><B>-position <I>pos</I></B> </DT>
+<DD>Specifies the screen position where the cross hairs
+intersect. <I>Pos</I> must be in the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window
+coordinates of the intersection. </DD>
+</DL>
+<P>
+Cross hairs configuration options may be
+also be set by the <B>option</B> command. The resource name and class are <I>crosshairs</I>
+and <I>Crosshairs</I> respectively. <BR>
+<CODE>option add *Stripchart.Crosshairs.LineWidth 2<BR>
+option add *Stripchart.Crosshairs.Color red<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>crosshairs off</B></I> </DT>
+<DD>Turns of the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs on</B></I>
+</DT>
+<DD>Turns on the display of the cross hairs. </DD>
+
+<DT><I>pathName <B>crosshairs toggle</B></I> </DT>
+<DD>Toggles
+the current state of the cross hairs, alternately mapping and unmapping
+the cross hairs. </DD>
+</DL>
+
+<H3><A NAME="sect10" HREF="#toc10">Element Components</A></H3>
+A data element represents a set of data.
+ It contains x and y vectors containing the coordinates of the data points.
+ Elements can be displayed with a symbol at each data point and lines connecting
+the points. Elements also control the appearance of the data, such as the
+symbol type, line width, color etc. <P>
+When new data elements are created,
+they are automatically added to a list of displayed elements. The display
+list controls what elements are drawn and in what order. <P>
+The following
+operations are available for elements.
+<DL>
+
+<DT><I>pathName <B>element activate <I>elemName
+</I></B></I>?<I>index</I>?... </DT>
+<DD>Specifies the data points of element <I>elemName</I> to be drawn using
+active foreground and background colors. <I>ElemName</I> is the name of the element
+and <I>index</I> is a number representing the index of the data point. If no indices
+are present then all data points become active. </DD>
+
+<DT><I>pathName <B>element cget <I>elemName
+<I>option</I></I></B></I> </DT>
+<DD>Returns the current value of the element configuration option given
+by <I>option</I>. <I>Option</I> may be any option described below for the element <B>configure</B>
+operation. </DD>
+
+<DT><I>pathName <B>element closest <I>x y</I></B></I> <I>varName</I> ?<I>option value</I>?... ?<I>elemName</I>?...
+</DT>
+<DD>Finds the data point closest to the window coordinates <I>x</I> and <I>y</I> in the element
+<I>elemName</I>. <I>ElemName</I> is the name of an element, that must not be hidden.
+If no elements are specified, then all visible elements are searched. It
+returns via the array variable <I>varName</I> the name of the closest element,
+the index of its closest point, and the graph coordinates of the point.
+Returns <I>0</I>, if no data point within the threshold distance can be found,
+otherwise <I>1</I> is returned. The following <I>option</I>-<I>value</I> pairs are available.
+<blockquote></DD>
+
+<DT><B>-halo <I>pixels</I></B> </DT>
+<DD>Specifies a threshold distance where selected data points are
+ignored. <I>Pixels</I> is a valid screen distance, such as <I>2</I> or <I>1.2i</I>. If this option
+isn't specified, then it defaults to the value of the stripchart's <B>-halo</B> option.
+</DD>
+
+<DT><B>-interpolate <I>boolean</I></B> </DT>
+<DD>Indicates that both the data points and interpolated
+points along the line segment formed should be considered. If <I>boolean</I>
+is true, the closest line segment will be selected instead of the closest
+point. If this option isn't specified, <I>boolean</I> defaults to <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>element
+configure <I>elemName </I></B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration
+options for elements. If <I>option</I> isn't specified, a list describing all the
+current options for <I>elemName</I> is returned. If <I>option</I> is specified, but not
+<I>value</I>, then a list describing the option <I>option</I> is returned. If one or
+more <I>option</I> and <I>value</I> pairs are specified, then for each pair, the element
+option <I>option</I> is set to <I>value</I>. The following options are valid for elements.
+<blockquote></DD>
+
+<DT><B>-activepen <I>penName</I></B> </DT>
+<DD>Specifies pen to use to draw active element. If <I>penName</I>
+is <I>""</I>, no active elements will be drawn. The default is <I>activeLine</I>. </DD>
+
+<DT><B>-color
+<I>color</I></B> </DT>
+<DD>Sets the color of the traces connecting the data points. </DD>
+
+<DT><B>-dashes
+<I>dashList</I></B> </DT>
+<DD>Sets the dash style of element line. <I>DashList</I> is a list of up to
+11 numbers that alternately represent the lengths of the dashes and gaps
+on the element line. Each number must be between 1 and 255. If <I>dashList</I>
+is <I>""</I>, the lines will be solid. </DD>
+
+<DT><B>-data <I>coordList</I></B> </DT>
+<DD>Specifies the X-Y coordinates
+of the data. <I>CoordList</I> is a list of numeric expressions representing the
+X-Y coordinate pairs of each data point. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the interior color
+of symbols. If <I>color</I> is <I>""</I>, then the interior of the symbol is transparent.
+ If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the element is
+displayed. The default is <I>no</I>. </DD>
+
+<DT><B>-label <I>text</I></B> </DT>
+<DD>Sets the element's label in the
+legend. If <I>text</I> is <I>""</I>, the element will have no entry in the legend. The
+default label is the element's name. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of
+the connecting lines between data points. If <I>pixels</I> is <I>0</I>, no connecting
+lines will be drawn between symbols. The default is <I>0</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Selects
+the X-axis to map the element's X-coordinates onto. <I>XAxis</I> must be the name
+of an axis. The default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Selects the Y-axis to map the element's
+Y-coordinates onto. <I>YAxis</I> must be the name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-offdash
+<I>color</I></B> </DT>
+<DD>Sets the color of the stripes when traces are dashed (see the <B>-dashes</B>
+option). If <I>color</I> is <I>""</I>, then the "off" pixels will represent gaps instead
+of stripes. If <I>color</I> is <I>defcolor</I>, then the color will be the same as the
+<B>-color</B> option. The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color or
+the outline around each symbol. If <I>color</I> is <I>""</I>, then no outline is drawn.
+If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outlinewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the outline
+bordering each symbol. If <I>pixels</I> is <I>0</I>, no outline will be drawn. The default
+is <I>1</I>. </DD>
+
+<DT><B>-pixels <I>pixels</I></B> </DT>
+<DD>Sets the size of symbols. If <I>pixels</I> is <I>0</I>, no symbols
+will be drawn. The default is <I>0.125i</I>. </DD>
+
+<DT><B>-scalesymbols <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is
+true, the size of the symbols drawn for <I>elemName</I> will change with scale
+of the X-axis and Y-axis. At the time this option is set, the current ranges
+of the axes are saved as the normalized scales (i.e scale factor is 1.0)
+and the element is drawn at its designated size (see the <B>-pixels</B> option).
+ As the scale of the axes change, the symbol will be scaled according to
+the smaller of the X-axis and Y-axis scales. If <I>boolean</I> is false, the element's
+symbols are drawn at the designated size, regardless of axis scales. The
+default is <I>0</I>. </DD>
+
+<DT><B>-smooth <I>smooth</I></B> </DT>
+<DD>Specifies how connecting line segments are
+drawn between data points. <I>Smooth</I> can be either <I>linear</I>, <I>step</I>, <I>natural</I>, or
+<I>quadratic</I>. If <I>smooth</I> is <I>linear</I>, a single line segment is drawn, connecting
+both data points. When <I>smooth</I> is <I>step</I>, two line segments are drawn. The first
+is a horizontal line segment which steps the next x-coordinate. The second
+is a vertical line, moving to the next y-coordinate. Both <I>natural</I> and <I>quadratic</I>
+generate multiple segments between data points. If <I>natural</I>, the segments
+are generated using a cubic spline. If <I>quadratic</I>, a quadratic spline is
+used. The default is <I>linear</I>. </DD>
+
+<DT><B>-styles <I>styleList</I></B> </DT>
+<DD>Specifies what pen to use
+based upon the range of weights given. <I>StyleList</I> is a list of style specifications.
+Each style specification, in turn, is a list consisting of a pen name,
+and optionally a minimum and maximum range. Data points whose weight (see
+the <B>-weight</B> option) falls in this range, are drawn with this pen. If no
+range is specified it defaults to the number of the pen in the list. </DD>
+
+<DT><B>-symbol
+<I>symbol</I></B> </DT>
+<DD>Specifies the symbol for data points. <I>Symbol</I> can be either <I>square</I>,
+<I>circle</I>, <I>diamond</I>, <I>plus</I>, <I>cross</I>, <I>splus</I>, <I>scross</I>, <I>triangle</I>, <I>""</I> (where no symbol
+is drawn), or a bitmap. Bitmaps are specified as "<I>source</I> ?<I>mask</I>?", where
+<I>source</I> is the name of the bitmap, and <I>mask</I> is the bitmap's optional mask.
+ The default is <I>circle</I>. </DD>
+
+<DT><B>-weights <I>wVec</I></B> </DT>
+<DD>Specifies the weights of the individual
+data points. This, in conjunction with the list pen styles (see the <B>-styles</B>
+option) controls how data points are drawn. <I>WVec</I> is the name of a BLT vector
+or a list of numeric expressions representing the weights for each data
+point. </DD>
+
+<DT><B>-xdata <I>xVec</I></B> </DT>
+<DD>Specifies the x-coordinates of the data. <I>XVec</I> is the name
+of a BLT vector or a list of numeric expressions. </DD>
+
+<DT><B>-ydata <I>yVec</I></B> </DT>
+<DD>Specifies
+the y-coordinates of the data. <I>YVec</I> is the name of a BLT vector or a list
+of numeric expressions. </DD>
+</DL>
+<P>
+Element configuration options may also be set by
+the <B>option</B> command. The resource class is <I>Element</I>. The resource name is
+the name of the element. <BR>
+<CODE>option add *Stripchart.Element.symbol line<BR>
+option add *Stripchart.e1.symbol line<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>element create <I>elemName</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a new element <I>elemName</I>.
+ It's an error is an element <I>elemName</I> already exists. If additional arguments
+are present, they specify options valid for element <B>configure</B> operation.
+</DD>
+
+<DT><I>pathName <B>element deactivate <I>elemName</I></B></I> ?<I>elemName</I>?... </DT>
+<DD>Deactivates all the elements
+matching <I>pattern</I>. Elements whose names match any of the patterns given are
+redrawn using their normal colors. </DD>
+
+<DT><I>pathName <B>element delete</B></I> ?<I>elemName</I>?... </DT>
+<DD>Deletes
+all the named elements. The graph is automatically redrawn. </DD>
+
+<DT><I>pathName <B>element
+exists <I>elemName</I></B></I> </DT>
+<DD>Returns <I>1</I> if an element <I>elemName</I> currently exists and <I>0</I>
+otherwise. </DD>
+
+<DT><I>pathName <B>element names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns the elements matching
+one or more pattern. If no <I>pattern</I> is given, the names of all elements
+is returned. </DD>
+
+<DT><I>pathName <B>element show</B></I> ?<I>nameList</I>? </DT>
+<DD>Queries or modifies the
+element display list. The element display list designates the elements
+drawn and in what order. <I>NameList</I> is a list of elements to be displayed
+in the order they are named. If there is no <I>nameList</I> argument, the current
+display list is returned. </DD>
+
+<DT><I>pathName <B>element type</B></I> <I>elemName</I> </DT>
+<DD>Returns the type
+of <I>elemName</I>. If the element is a bar element, the commands returns the
+string <I>"bar"</I>, otherwise it returns <I>"line"</I>. </DD>
+</DL>
+
+<H3><A NAME="sect11" HREF="#toc11"></CODE><P>Grid Component</A></H3>
+Grid lines extend
+from the major and minor ticks of each axis horizontally or vertically
+across the plotting area. The following operations are available for grid
+lines.
+<DL>
+
+<DT><I>pathName <B>grid cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the grid line
+configuration option given by <I>option</I>. <I>Option</I> may be any option described
+below for the grid <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>grid configure</B></I> ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options for grid lines. If
+<I>option</I> isn't specified, a list describing all the current grid options for
+<I>pathName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a list
+describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs are
+specified, then for each pair, the grid line option <I>option</I> is set to <I>value</I>.
+ The following options are valid for grid lines. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color
+of the grid lines. The default is <I>black</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style
+of the grid lines. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the grid lines. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the grid will be solid lines.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid should be drawn. If <I>boolean</I> is true,
+grid lines are not shown. The default is <I>yes</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of grid lines. The default width is <I>1</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B> </DT>
+<DD>Specifies the X-axis to
+display grid lines. <I>XAxis</I> must be the name of an axis. The default is <I>x</I>.
+</DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to display grid lines. <I>YAxis</I> must be the
+name of an axis. The default is <I>y</I>. </DD>
+
+<DT><B>-minor <I>boolean</I></B> </DT>
+<DD>Indicates whether the grid
+lines should be drawn for minor ticks. If <I>boolean</I> is true, the lines will
+appear at minor tick intervals. The default is <I>1</I>. </DD>
+</DL>
+<P>
+Grid configuration options
+may also be set by the <B>option</B> command. The resource name and class are
+<I>grid</I> and <I>Grid</I> respectively. <BR>
+<CODE>option add *Stripchart.grid.LineWidth 2<BR>
+option add *Stripchart.Grid.Color black<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>grid off</B></I> </DT>
+<DD>Turns off the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid on</B></I>
+</DT>
+<DD>Turns on the display the grid lines. </DD>
+
+<DT><I>pathName <B>grid toggle</B></I> </DT>
+<DD>Toggles the display
+of the grid. </DD>
+</DL>
+
+<H3><A NAME="sect12" HREF="#toc12">Legend Component</A></H3>
+The legend displays a list of the data elements.
+ Each entry consists of the element's symbol and label. The legend can appear
+in any margin (the default location is in the right margin). It can also
+be positioned anywhere within the plotting area. <P>
+The following operations
+are valid for the legend.
+<DL>
+
+<DT><I>pathName <B>legend activate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend
+entries to be drawn using the active legend colors and relief. All entries
+whose element names match <I>pattern</I> are selected. To be selected, the element
+name must match only one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend cget <I>option</I></B></I> </DT>
+<DD>Returns the
+current value of a legend configuration option. <I>Option</I> may be any option
+described below in the legend <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>legend configure
+</B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies the configuration options for the legend.
+ If <I>option</I> isn't specified, a list describing the current legend options
+for <I>pathName</I> is returned. If <I>option</I> is specified, but not <I>value</I>, then a
+list describing <I>option</I> is returned. If one or more <I>option</I> and <I>value</I> pairs
+are specified, then for each pair, the legend option <I>option</I> is set to <I>value</I>.
+ The following options are valid for the legend. <blockquote></DD>
+
+<DT><B>-activebackground <I>color</I></B>
+</DT>
+<DD>Sets the background color for active legend entries. All legend entries
+marked active (see the legend <B>activate</B> operation) are drawn using this
+background color. </DD>
+
+<DT><B>-activeborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border
+around the outside edge of the active legend entries. The default is <I>2</I>.
+</DD>
+
+<DT><B>-activeforeground <I>color</I></B> </DT>
+<DD>Sets the foreground color for active legend entries.
+ All legend entries marked as active (see the legend <B>activate</B> operation)
+are drawn using this foreground color. </DD>
+
+<DT><B>-activerelief <I>relief</I></B> </DT>
+<DD>Specifies the
+3-D effect desired for active legend entries. <I>Relief</I> denotes how the interior
+of the entry should appear relative to the legend; for example, <I>raised</I>
+means the entry should appear to protrude from the legend, relative to
+the surface of the legend. The default is <I>flat</I>. </DD>
+
+<DT><B>-anchor <I>anchor</I></B> </DT>
+<DD>Tells how
+to position the legend relative to the positioning point for the legend.
+ This is dependent on the value of the <B>-position</B> option. The default is
+<I>center</I>. <blockquote></DD>
+
+<DT><I>left</I> or <I>right</I> </DT>
+<DD>The anchor describes how to position the legend vertically.
+ </DD>
+
+<DT><I>top</I> or <I>bottom</I> </DT>
+<DD>The anchor describes how to position the legend horizontally.
+ </DD>
+
+<DT><I>@x,y</I> </DT>
+<DD>The anchor specifies how to position the legend relative to the
+positioning point. For example, if <I>anchor</I> is <I>center</I> then the legend is centered
+on the point; if <I>anchor</I> is <I>n</I> then the legend will be drawn such that the
+top center point of the rectangular region occupied by the legend will
+be at the positioning point. </DD>
+
+<DT><I>plotarea</I> </DT>
+<DD>The anchor specifies how to position
+the legend relative to the plotting area. For example, if <I>anchor</I> is <I>center</I>
+then the legend is centered in the plotting area; if <I>anchor</I> is <I>ne</I> then
+the legend will be drawn such that occupies the upper right corner of the
+plotting area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background color of the legend.
+If <I>color</I> is <I>""</I>, the legend background with be transparent. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the 3-D border around the outside edge of the legend (if
+such border is being drawn; the <B>relief</B> option determines this). The default
+is <I>2</I> pixels. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD><I>FontName</I> specifies a font to use when drawing
+the labels of each element into the legend. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the foreground color of the text drawn for the element's
+label. The default is <I>black</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the legend should
+be displayed. If <I>boolean</I> is true, the legend will not be draw. The default
+is <I>no</I>. </DD>
+
+<DT><B>-ipadx <I>pad</I></B> </DT>
+<DD>Sets the amount of internal padding to be added to the
+width of each legend entry. <I>Pad</I> can be a list of one or two screen distances.
+ If <I>pad</I> has two elements, the left side of the legend entry is padded by
+the first distance and the right side by the second. If <I>pad</I> is just one
+distance, both the left and right sides are padded evenly. The default
+is <I>2</I>. </DD>
+
+<DT><B>-ipady <I>pad</I></B> </DT>
+<DD>Sets an amount of internal padding to be added to the height
+of each legend entry. <I>Pad</I> can be a list of one or two screen distances.
+ If <I>pad</I> has two elements, the top of the entry is padded by the first distance
+and the bottom by the second. If <I>pad</I> is just one distance, both the top
+and bottom of the entry are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets
+the padding to the left and right exteriors of the legend. <I>Pad</I> can be a
+list of one or two screen distances. If <I>pad</I> has two elements, the left
+side of the legend is padded by the first distance and the right side by
+the second. If <I>pad</I> has just one distance, both the left and right sides
+are padded evenly. The default is <I>4</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and
+below the legend. <I>Pad</I> can be a list of one or two screen distances. If
+<I>pad</I> has two elements, the area above the legend is padded by the first
+distance and the area below by the second. If <I>pad</I> is just one distance,
+both the top and bottom areas are padded evenly. The default is <I>0</I>. </DD>
+
+<DT><B>-position
+<I>pos</I></B> </DT>
+<DD>Specifies where the legend is drawn. The <B>-anchor</B> option also affects
+where the legend is positioned. If <I>pos</I> is <I>left</I>, <I>left</I>, <I>top</I>, or <I>bottom</I>, the
+legend is drawn in the specified margin. If <I>pos</I> is <I>plotarea</I>, then the legend
+is drawn inside the plotting area at a particular anchor. If <I>pos</I> is in
+the form "<I>@x,y</I>", where <I>x</I> and <I>y</I> are the window coordinates, the legend is
+drawn in the plotting area at the specified coordinates. The default is
+<I>right</I>. </DD>
+
+<DT><B>-raised <I>boolean</I></B> </DT>
+<DD>Indicates whether the legend is above or below the
+data elements. This matters only if the legend is in the plotting area.
+ If <I>boolean</I> is true, the legend will be drawn on top of any elements that
+may overlap it. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect
+for the border around the legend. <I>Relief</I> specifies how the interior of the
+legend should appear relative to the strip chart; for example, <I>raised</I> means
+the legend should appear to protrude from the strip chart, relative to
+the surface of the strip chart. The default is <I>sunken</I>. </DD>
+</DL>
+<P>
+Legend configuration
+options may also be set by the <B>option</B> command. The resource name and class
+are <I>legend</I> and <I>Legend</I> respectively. <BR>
+<CODE>option add *Stripchart.legend.Foreground blue<BR>
+option add *Stripchart.Legend.Relief raised<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>legend deactivate <I>pattern</I></B></I>... </DT>
+<DD>Selects legend entries to be drawn using
+the normal legend colors and relief. All entries whose element names match
+<I>pattern</I> are selected. To be selected, the element name must match only
+one <I>pattern</I>. </DD>
+
+<DT><I>pathName <B>legend get <I>pos</I></B></I> </DT>
+<DD>Returns the name of the element whose
+entry is at the screen position <I>pos</I> in the legend. <I>Pos</I> must be in the form
+"<I>@x,y</I>", where <I>x</I> and <I>y</I> are window coordinates. If the given coordinates
+do not lie over a legend entry, <I>""</I> is returned. </DD>
+</DL>
+
+<H3><A NAME="sect13" HREF="#toc13">Pen Components</A></H3>
+Pens define
+attributes (both symbol and line style) for elements. Pens mirror the configuration
+options of data elements that pertain to how symbols and lines are drawn.
+ Data elements use pens to determine how they are drawn. A data element
+may use several pens at once. In this case, the pen used for a particular
+data point is determined from each element's weight vector (see the element's
+<B>-weight</B> and <B>-style</B> options). <P>
+One pen, called <I>activeLine</I>, is automatically
+created. It's used as the default active pen for elements. So you can change
+the active attributes for all elements by simply reconfiguring this pen.
+<BR>
+<CODE>.s pen configure "activeLine" -color green<BR>
+</CODE><P>You can create and use any number of pens. To create a pen, invoke the pen
+component and its create operation. <BR>
+<CODE>.s pen create myPen<BR>
+</CODE><P>You map pens to a data element using either the element's <B>-pen</B> or <B>-activepen</B>
+options. <BR>
+<CODE>.s element create "line1" -xdata $x -ydata $tempData \<BR>
+ -pen myPen<BR>
+</CODE><P>An element can use several pens at once. This is done by specifying the
+name of the pen in the element's style list (see the <B>-styles</B> option). <BR>
+<CODE>.s element configure "line1" -styles { myPen 2.0 3.0 }<BR>
+</CODE><P>This says that any data point with a weight between 2.0 and 3.0 is to be
+drawn using the pen <I>myPen</I>. All other points are drawn with the element's
+default attributes. <P>
+The following operations are available for pen components.
+<P>
+
+<DL>
+
+<DT><I>pathName <B>pen <B>cget <I>penName <I>option</I></I></B></B></I> </DT>
+<DD>Returns the current value of the option
+given by <I>option</I> for <I>penName</I>. <I>Option</I> may be any option described below for
+the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>configure <I>penName </I></B></B></I>?<I>option value</I>?...
+</DT>
+<DD>Queries or modifies the configuration options of <I>penName</I>. If <I>option</I> isn't
+specified, a list describing the current options for <I>penName</I> is returned.
+ If <I>option</I> is specified, but not <I>value</I>, then a list describing <I>option</I> is
+returned. If one or more <I>option</I> and <I>value</I> pairs are specified, then for
+each pair, the pen option <I>option</I> is set to <I>value</I>. The following options
+are valid for pens. <blockquote></DD>
+
+<DT><B>-color <I>color</I></B> </DT>
+<DD>Sets the color of the traces connecting
+the data points. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of element line. <I>DashList</I>
+is a list of up to 11 numbers that alternately represent the lengths of
+the dashes and gaps on the element line. Each number must be between 1
+and 255. If <I>dashList</I> is <I>""</I>, the lines will be solid. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the
+interior color of symbols. If <I>color</I> is <I>""</I>, then the interior of the symbol
+is transparent. If <I>color</I> is <I>defcolor</I>, then the color will be the same as
+the <B>-color</B> option. The default is <I>defcolor</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the connecting lines between data points. If <I>pixels</I> is <I>0</I>, no connecting
+lines will be drawn between symbols. The default is <I>0</I>. </DD>
+
+<DT><B>-offdash <I>color</I></B> </DT>
+<DD>Sets
+the color of the stripes when traces are dashed (see the <B>-dashes</B> option).
+ If <I>color</I> is <I>""</I>, then the "off" pixels will represent gaps instead of stripes.
+ If <I>color</I> is <I>defcolor</I>, then the color will be the same as the <B>-color</B> option.
+ The default is <I>defcolor</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color or the outline around
+each symbol. If <I>color</I> is <I>""</I>, then no outline is drawn. If <I>color</I> is <I>defcolor</I>,
+then the color will be the same as the <B>-color</B> option. The default is <I>defcolor</I>.
+</DD>
+
+<DT><B>-outlinewidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the outline bordering each symbol.
+ If <I>pixels</I> is <I>0</I>, no outline will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-pixels <I>pixels</I></B>
+</DT>
+<DD>Sets the size of symbols. If <I>pixels</I> is <I>0</I>, no symbols will be drawn. The
+default is <I>0.125i</I>. </DD>
+
+<DT><B>-symbol <I>symbol</I></B> </DT>
+<DD>Specifies the symbol for data points. <I>Symbol</I>
+can be either <I>square</I>, <I>circle</I>, <I>diamond</I>, <I>plus</I>, <I>cross</I>, <I>splus</I>, <I>scross</I>, <I>triangle</I>,
+<I>""</I> (where no symbol is drawn), or a bitmap. Bitmaps are specified as "<I>source</I>
+?<I>mask</I>?", where <I>source</I> is the name of the bitmap, and <I>mask</I> is the bitmap's
+optional mask. The default is <I>circle</I>. </DD>
+
+<DT><B>-type <I>elemType</I></B> </DT>
+<DD>Specifies the type
+of element the pen is to be used with. This option should only be employed
+when creating the pen. This is for those that wish to mix different types
+of elements (bars and lines) on the same graph. The default type is "line".
+</DD>
+</DL>
+<P>
+Pen configuration options may be also be set by the <B>option</B> command. The
+resource class is <I>Pen</I>. The resource names are the names of the pens. <BR>
+<CODE>option add *Stripchart.Pen.Color blue<BR>
+option add *Stripchart.activeLine.color green<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>pen <B>create <I>penName </I></B></B></I>?<I>option value</I>?... </DT>
+<DD>Creates a new pen by the name
+<I>penName</I>. No pen by the same name can already exist. <I>Option</I> and <I>value</I> are
+described in above in the pen <B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>pen <B>delete
+</B></B></I>?<I>penName</I>?... </DT>
+<DD>Deletes the named pens. A pen is not really deleted until it is
+not longer in use, so it's safe to delete pens mapped to elements. </DD>
+
+<DT><I>pathName
+<B>pen names </B></I>?<I>pattern</I>?... </DT>
+<DD>Returns a list of pens matching zero or more patterns.
+ If no <I>pattern</I> argument is give, the names of all pens are returned. </DD>
+</DL>
+
+<H3><A NAME="sect14" HREF="#toc14">PostScript
+Component</A></H3>
+The strip chart can generate encapsulated PostScript output. There
+are several configuration options you can specify to control how the plot
+is generated. You can change the page dimensions and borders. The plot
+itself can be scaled, centered, or rotated to landscape. The PostScript
+output can be written directly to a file or returned through the interpreter.
+<P>
+The following postscript operations are available.
+<DL>
+
+<DT><I>pathName <B>postscript cget
+<I>option</I></B></I> </DT>
+<DD>Returns the current value of the postscript option given by <I>option</I>.
+ <I>Option</I> may be any option described below for the postscript <B>configure</B>
+operation. </DD>
+
+<DT><I>pathName <B>postscript configure </B></I>?<I>option value</I>?... </DT>
+<DD>Queries or modifies
+the configuration options for PostScript generation. If <I>option</I> isn't specified,
+a list describing the current postscript options for <I>pathName</I> is returned.
+ If <I>option</I> is specified, but not <I>value</I>, then a list describing <I>option</I> is
+returned. If one or more <I>option</I> and <I>value</I> pairs are specified, then for
+each pair, the postscript option <I>option</I> is set to <I>value</I>. The following
+postscript options are available. <blockquote></DD>
+
+<DT><B>-center <I>boolean</I></B> </DT>
+<DD>Indicates whether the plot
+should be centered on the PostScript page. If <I>boolean</I> is false, the plot
+will be placed in the upper left corner of the page. The default is <I>1</I>. </DD>
+
+<DT><B>-colormap
+<I>varName</I></B> </DT>
+<DD><I>VarName</I> must be the name of a global array variable that specifies
+a color mapping from the X color name to PostScript. Each element of <I>varName</I>
+must consist of PostScript code to set a particular color value (e.g. ``<I>1.0
+1.0 0.0 setrgbcolor</I>''). When outputting color information in PostScript, the
+array variable <I>varName</I> is checked to see if an element of the name of
+the color exists. If so, it uses the value of the element as the PostScript
+command to set the color. If this option hasn't been specified, or if there
+isn't an entry in <I>varName</I> for a given color, then it uses the red, green,
+and blue intensities from the X color. </DD>
+
+<DT><B>-colormode <I>mode</I></B> </DT>
+<DD>Specifies how to output
+color information. <I>Mode</I> must be either <I>color</I> (for full color output), <I>gray</I>
+(convert all colors to their gray-scale equivalents) or <I>mono</I> (convert foreground
+colors to black and background colors to white). The default mode is <I>color</I>.
+ </DD>
+
+<DT><B>-fontmap <I>varName</I></B> </DT>
+<DD><I>VarName</I> must be the name of a global array variable that
+specifies a font mapping from the X font name to PostScript. Each element
+of <I>varName</I> must consist of a Tcl list with one or two elements, which are
+the name and point size of a PostScript font. When outputting PostScript
+commands for a particular font, the array variable <I>varName</I> is checked
+to see an element of the specified font exists. If there is such an element,
+then the font information contained in that element is used in the PostScript
+output. (If the point size is omitted from the list, the point size of
+the X font is used). Otherwise the X font is examined in an attempt to
+guess what PostScript font to use. This works only for fonts whose foundry
+property is <I>Adobe</I> (such as Times, Helvetica, Courier, etc.). If all of this
+fails then the font defaults to <I>Helvetica-Bold</I>. </DD>
+
+<DT><B>-decorations <I>boolean</I></B> </DT>
+<DD>Indicates
+if PostScript commands to generate color backgrounds and 3-D borders should
+be output. If <I>boolean</I> is false, the background will be white and no 3-D
+borders will be generated. The default is <I>1</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Sets the height
+of the plot. This lets you plot the stripchart with a height different
+from the one displayed on the screen. If <I>pixels</I> is 0, the height is the
+same as the displayed height. The default is <I>0</I>. </DD>
+
+<DT><B>-landscape <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I>
+is true, this specifies the printed area is to be rotated 90 degrees. In
+non-rotated output the X-axis of the printed area runs along the short dimension
+of the page (``portrait'' orientation); in rotated output the X-axis runs along
+the long dimension of the page (``landscape'' orientation). Defaults to <I>0</I>. </DD>
+
+<DT><B>-maxpect
+<I>boolean</I></B> </DT>
+<DD>Indicates to scale the the plot so that it fills the PostScript
+page. The aspect ratio of the strip chart is still retained. The default
+is <I>0</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the horizontal padding for the left and right page borders.
+ The borders are exterior to the plot. <I>Pad</I> can be a list of one or two
+screen distances. If <I>pad</I> has two elements, the left border is padded by
+the first distance and the right border by the second. If <I>pad</I> has just
+one distance, both the left and right borders are padded evenly. The default
+is <I>1i</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the vertical padding for the top and bottom page borders.
+The borders are exterior to the plot. <I>Pad</I> can be a list of one or two screen
+distances. If <I>pad</I> has two elements, the top border is padded by the first
+distance and the bottom border by the second. If <I>pad</I> has just one distance,
+both the top and bottom borders are padded evenly. The default is <I>1i</I>. </DD>
+
+<DT><B>-paperheight
+<I>pixels</I></B> </DT>
+<DD>Sets the height of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default height is <I>11.0i</I>.
+</DD>
+
+<DT><B>-paperwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the postscript page. This can be used
+to select between different page sizes (letter, A4, etc). The default width
+is <I>8.5i</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Sets the width of the plot. This lets you plot the
+strip chart with a width different from the one drawn on the screen. If
+<I>pixels</I> is 0, the width is the same as the widget's width. The default is
+<I>0</I>. </DD>
+</DL>
+<P>
+Postscript configuration options may be also be set by the <B>option</B> command.
+ The resource name and class are <I>postscript</I> and <I>Postscript</I> respectively.
+<BR>
+<CODE>option add *Stripchart.postscript.Decorations false<BR>
+option add *Stripchart.Postscript.Landscape true<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>postscript output </B></I>?<I>fileName</I>? ?<I>option value</I>?... </DT>
+<DD>Outputs a file of
+encapsulated PostScript. If a <I>fileName</I> argument isn't present, the command
+returns the PostScript. If any <I>option-value</I> pairs are present, they set configuration
+options controlling how the PostScript is generated. <I>Option</I> and <I>value</I> can
+be anything accepted by the postscript <B>configure</B> operation above. </DD>
+</DL>
+
+<H3><A NAME="sect15" HREF="#toc15">Marker
+Components</A></H3>
+Markers are simple drawing procedures used to annotate or highlight
+areas of the strip chart. Markers have various types: text strings, bitmaps,
+images, connected lines, windows, or polygons. They can be associated with
+a particular element, so that when the element is hidden or un-hidden, so
+is the marker. By default, markers are the last items drawn, so that data
+elements will appear in behind them. You can change this by configuring
+the <B>-under</B> option. <P>
+Markers, in contrast to elements, don't affect the scaling
+of the coordinate axes. They can also have <I>elastic</I> coordinates (specified
+by <I>-Inf</I> and <I>Inf</I> respectively) that translate into the minimum or maximum
+limit of the axis. For example, you can place a marker so it always remains
+in the lower left corner of the plotting area, by using the coordinates
+<I>-Inf</I>,<I>-Inf</I>. <P>
+The following operations are available for markers.
+<DL>
+
+<DT><I>pathName <B>marker
+after <I>markerId</I></B></I> ?<I>afterId</I>? </DT>
+<DD>Changes the order of the markers, drawing the
+first marker after the second. If no second <I>afterId</I> argument is specified,
+the marker is placed at the end of the display list. This command can be
+used to control how markers are displayed since markers are drawn in the
+order of this display list. </DD>
+
+<DT><I>pathName <B>marker before <I>markerId</I></B></I> ?<I>beforeId</I>? </DT>
+<DD>Changes
+the order of the markers, drawing the first marker before the second. If
+no second <I>beforeId</I> argument is specified, the marker is placed at the beginning
+of the display list. This command can be used to control how markers are
+displayed since markers are drawn in the order of this display list. </DD>
+
+<DT><I>pathName
+<B>marker cget <I>option</I></B></I> </DT>
+<DD>Returns the current value of the marker configuration
+option given by <I>option</I>. <I>Option</I> may be any option described below in the
+<B>configure</B> operation. </DD>
+
+<DT><I>pathName <B>marker configure <I>markerId</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Queries
+or modifies the configuration options for markers. If <I>option</I> isn't specified,
+a list describing the current options for <I>markerId</I> is returned. If <I>option</I>
+is specified, but not <I>value</I>, then a list describing <I>option</I> is returned.
+If one or more <I>option</I> and <I>value</I> pairs are specified, then for each pair,
+the marker option <I>option</I> is set to <I>value</I>. <P>
+The following options are valid
+for all markers. Each type of marker also has its own type-specific options.
+ They are described in the sections below. <blockquote></DD>
+
+<DT><B>-coords <I>coordList</I></B> </DT>
+<DD>Specifies the
+coordinates of the marker. <I>CoordList</I> is a list of graph coordinates. The
+number of coordinates required is dependent on the type of marker. Text,
+image, and window markers need only two coordinates (an X-Y coordinate).
+ Bitmap markers can take either two or four coordinates (if four, they
+represent the corners of the bitmap). Line markers need at least four coordinates,
+polygons at least six. If <I>coordList</I> is <I>""</I>, the marker will not be displayed.
+The default is <I>""</I>. </DD>
+
+<DT><B>-element <I>elemName</I></B> </DT>
+<DD>Links the marker with the element <I>elemName</I>.
+ The marker is drawn only if the element is also currently displayed (see
+the element's <B>show</B> operation). If <I>elemName</I> is <I>""</I>, the marker is always drawn.
+ The default is <I>""</I>. </DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>Indicates whether the marker is drawn.
+If <I>boolean</I> is true, the marker is not drawn. The default is <I>no</I>. </DD>
+
+<DT><B>-mapx <I>xAxis</I></B>
+ </DT>
+<DD>Specifies the X-axis to map the marker's X-coordinates onto. <I>XAxis</I> must the
+name of an axis. The default is <I>x</I>. </DD>
+
+<DT><B>-mapy <I>yAxis</I></B> </DT>
+<DD>Specifies the Y-axis to map
+the marker's Y-coordinates onto. <I>YAxis</I> must the name of an axis. The default
+is <I>y</I>. </DD>
+
+<DT><B>-name <I>markerId</I></B> </DT>
+<DD>Changes the identifier for the marker. The identifier
+<I>markerId</I> can not already be used by another marker. If this option isn't
+specified, the marker's name is uniquely generated. </DD>
+
+<DT><B>-under <I>boolean</I></B> </DT>
+<DD>Indicates
+whether the marker is drawn below/above data elements. If <I>boolean</I> is true,
+the marker is be drawn underneath the data element symbols and lines. Otherwise,
+the marker is drawn on top of the element. The default is <I>0</I>. </DD>
+
+<DT><B>-xoffset <I>pixels</I></B>
+</DT>
+<DD>Specifies a screen distance to offset the marker horizontally. <I>Pixels</I> is
+a valid screen distance, such as <I>2</I> or <I>1.2i</I>. The default is <I>0</I>. </DD>
+
+<DT><B>-yoffset <I>pixels</I></B>
+</DT>
+<DD>Specifies a screen distance to offset the markers vertically. <I>Pixels</I> is
+a valid screen distance, such as <I>2</I> or <I>1.2i</I>. The default is <I>0</I>. </DD>
+</DL>
+<P>
+Marker configuration
+options may also be set by the <B>option</B> command. The resource class is either
+<I>BitmapMarker</I>, <I>ImageMarker</I>, <I>LineMarker</I>, <I>PolygonMarker</I>, <I>TextMarker</I>, or
+<I>WindowMarker</I>, depending on the type of marker. The resource name is the
+name of the marker. <BR>
+<CODE>option add *Stripchart.TextMarker.Foreground white<BR>
+option add *Stripchart.BitmapMarker.Foreground white<BR>
+option add *Stripchart.m1.Background blue<BR>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P><I>pathName <B>marker create <I>type</I></B></I> ?<I>option value</I>?... </DT>
+<DD>Creates a marker of the selected
+type. <I>Type</I> may be either <I>text</I>, <I>line</I>, <I>bitmap</I>, <I>image</I>, <I>polygon</I>, or <I>window</I>.
+This command returns the marker identifier, used as the <I>markerId</I> argument
+in the other marker-related commands. If the <B>-name</B> option is used, this overrides
+the normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old. </DD>
+
+<DT><I>pathName <B>marker delete</B></I>
+?<I>name</I>?... </DT>
+<DD>Removes one of more markers. The graph will automatically be redrawn
+without the marker.. </DD>
+
+<DT><I>pathName <B>marker exists <I>markerId</I></B></I> </DT>
+<DD>Returns <I>1</I> if the
+marker <I>markerId</I> exists and <I>0</I> otherwise. </DD>
+
+<DT><I>pathName <B>marker names</B></I> ?<I>pattern</I>?
+ </DT>
+<DD>Returns the names of all the markers that currently exist. If <I>pattern</I>
+is supplied, only those markers whose names match it will be returned. </DD>
+
+<DT><I>pathName
+<B>marker type <I>markerId</I></B></I> </DT>
+<DD>Returns the type of the marker given by <I>markerId</I>,
+such as <I>line</I> or <I>text</I>. If <I>markerId</I> is not a valid a marker identifier, <I>""</I>
+is returned. </DD>
+</DL>
+
+<H3><A NAME="sect16" HREF="#toc16">Bitmap Markers</A></H3>
+A bitmap marker displays a bitmap. The size of
+the bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the bitmap.
+ The bitmap retains its normal width and height. If four coordinates, the
+first and second pairs of coordinates represent the corners of the bitmap.
+ The bitmap will be stretched or reduced as necessary to fit into the bounding
+rectangle. <P>
+Bitmap markers are created with the marker's <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create bitmap </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration options
+for the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's
+<B>configure</B> operation. <P>
+The following options are specific to bitmap markers:
+
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background color of the bitmap. If <I>color</I> is <I>""</I>,
+the background color will be transparent. The default background color
+is <I>white</I>. </DD>
+
+<DT><B>-bitmap <I>bitmap</I></B> </DT>
+<DD>Specifies the bitmap to be displayed. If <I>bitmap</I>
+is <I>""</I>, the marker will not be displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B>
+ </DT>
+<DD>Sets the foreground color of the bitmap. The default foreground color
+is <I>black</I>. </DD>
+
+<DT><B>-mask <I>mask</I></B> </DT>
+<DD>Specifies a mask for the bitmap to be displayed. This
+mask is a bitmap itself, denoting the pixels that are transparent. If <I>mask</I>
+is <I>""</I>, all pixels of the bitmap will be drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-rotate
+<I>theta</I></B> </DT>
+<DD>Sets the rotation of the bitmap. <I>Theta</I> is a real number representing
+the angle of rotation in degrees. The marker is first rotated and then
+placed according to its anchor position. The default rotation is <I>0.0</I>. </DD>
+</DL>
+
+<H3><A NAME="sect17" HREF="#toc17">Image
+Markers</A></H3>
+A image marker displays an image. Image markers are created with
+the marker's <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create image </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to image markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the image relative to the positioning point
+for the image. For example, if <I>anchor</I> is <I>center</I> then the image is centered
+on the point; if <I>anchor</I> is <I>n</I> then the image will be drawn such that the
+top center point of the rectangular region occupied by the image will be
+at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-image <I>image</I></B> </DT>
+<DD>Specifies
+the image to be drawn. If <I>image</I> is <I>""</I>, the marker will not be drawn. The
+default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Line Markers</A></H3>
+A line marker displays one or more connected
+line segments. Line markers are created with marker's <B>create</B> operation in
+the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create line </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+operation. <P>
+The following options are specific to line markers:
+<DL>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color of the line. The option is affects the line
+color only when the <B>-stipple</B> option is set. If this option isn't specified
+then it defaults to <I>white</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of the line.
+<I>DashList</I> is a list of up to 11 numbers that alternately represent the lengths
+of the dashes and gaps on the line. Each number must be between 1 and 255.
+ If <I>dashList</I> is <I>""</I>, the marker line will be solid. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets
+the foreground color. The default foreground color is <I>black</I>. </DD>
+
+<DT><B>-linewidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the lines. The default width is <I>0</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies
+a stipple pattern used to draw the line, rather than a solid line. <I>Bitmap</I>
+specifies a bitmap to use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then
+the line is drawn in a solid fashion. The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Polygon Markers</A></H3>
+A
+polygon marker displays a closed region described as two or more connected
+line segments. It is assumed the first and last points are connected. Polygon
+markers are created using the marker <B>create</B> operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create polygon </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the <B>marker configure</B>
+command to change the marker's configuration. The following options are supported
+for polygon markers:
+<DL>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline
+of the polygon. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the outline. Each number
+must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the outline will be a solid
+line. </DD>
+
+<DT><B>-fill <I>color</I></B> </DT>
+<DD>Sets the fill color of the polygon. If <I>color</I> is <I>""</I>, then
+the interior of the polygon is transparent. The default is <I>white</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the outline of the polygon. If <I>pixels</I> is zero,
+ no outline is drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-outline <I>color</I></B> </DT>
+<DD>Sets the color of the
+outline of the polygon. If the polygon is stippled (see the <B>-stipple</B> option),
+then this represents the foreground color of the stipple. The default is
+<I>black</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies that the polygon should be drawn with a
+stippled pattern rather than a solid color. <I>Bitmap</I> specifies a bitmap to
+use as the stipple pattern. If <I>bitmap</I> is <I>""</I>, then the polygon is filled
+with a solid color (if the <B>-fill</B> option is set). The default is <I>""</I>. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Text
+Markers</A></H3>
+A text marker displays a string of characters on one or more lines
+of text. Embedded newlines cause line breaks. They may be used to annotate
+regions of the strip chart. Text markers are created with the <B>create</B> operation
+in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create text </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option
+for the text marker. These same <I>option</I>-<I>value</I> pairs may be used with the
+ marker's <B>configure</B> operation. <P>
+The following options are specific to text
+markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B> </DT>
+<DD><I>Anchor</I> tells how to position the text relative to
+the positioning point for the text. For example, if <I>anchor</I> is <I>center</I> then
+the text is centered on the point; if <I>anchor</I> is <I>n</I> then the text will be
+drawn such that the top center point of the rectangular region occupied
+by the text will be at the positioning point. This default is <I>center</I>. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color of the text string. If <I>color</I> is <I>""</I>, the
+background will be transparent. The default is <I>white</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies
+the font of the text. The default is <I>*-Helvetica-Bold-R-Normal-*-120-*</I>. </DD>
+
+<DT><B>-foreground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of the text. The default is <I>black</I>. </DD>
+
+<DT><B>-justify
+<I>justify</I></B> </DT>
+<DD>Specifies how the text should be justified. This matters only when
+the marker contains more than one line of text. <I>Justify</I> must be <I>left</I>, <I>right</I>,
+or <I>center</I>. The default is <I>center</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the padding to the left
+and right exteriors of the text. <I>Pad</I> can be a list of one or two screen
+distances. If <I>pad</I> has two elements, the left side of the text is padded
+by the first distance and the right side by the second. If <I>pad</I> has just
+one distance, both the left and right sides are padded evenly. The default
+is <I>4</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding above and below the text. <I>Pad</I> can be a list
+of one or two screen distances. If <I>pad</I> has two elements, the area above
+the text is padded by the first distance and the area below by the second.
+If <I>pad</I> is just one distance, both the top and bottom areas are padded evenly.
+ The default is <I>4</I>. </DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the number of degrees to rotate
+the text. <I>Theta</I> is a real number representing the angle of rotation. The
+marker is first rotated along its center and is then drawn according to
+its anchor position. The default is <I>0.0</I>. </DD>
+
+<DT><B>-text <I>text</I></B> </DT>
+<DD>Specifies the text of the
+marker. The exact way the text is displayed may be affected by other options
+such as <B>-anchor</B> or <B>-rotate</B>. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Window Markers</A></H3>
+A window marker displays a widget
+at a given position. Window markers are created with the marker's <B>create</B>
+operation in the form: <BR>
+<P>
+<CODE><I>pathName <B>marker create window </B></I>?<I>option value</I>?...<BR>
+</CODE><P>There may be many <I>option</I>-<I>value</I> pairs, each sets a configuration option for
+the marker. These same <I>option</I>-<I>value</I> pairs may be used with the marker's <B>configure</B>
+command. <P>
+The following options are specific to window markers:
+<DL>
+
+<DT><B>-anchor <I>anchor</I></B>
+</DT>
+<DD><I>Anchor</I> tells how to position the widget relative to the positioning point
+for the widget. For example, if <I>anchor</I> is <I>center</I> then the widget is centered
+on the point; if <I>anchor</I> is <I>n</I> then the widget will be displayed such that
+the top center point of the rectangular region occupied by the widget will
+be at the positioning point. This option defaults to <I>center</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B>
+</DT>
+<DD>Specifies the height to assign to the marker's window. If this option isn't
+specified, or if it is specified as <I>""</I>, then the window is given whatever
+height the widget requests internally. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the width
+to assign to the marker's window. If this option isn't specified, or if it
+is specified as <I>""</I>, then the window is given whatever width the widget
+requests internally. </DD>
+
+<DT><B>-window <I>pathName</I></B> </DT>
+<DD>Specifies the widget to be managed.
+ <I>PathName</I> must be a child of the <B>stripchart</B> widget. </DD>
+</DL>
+
+<H2><A NAME="sect22" HREF="#toc22">Graph Component Bindings</A></H2>
+Specific
+stripchart components, such as elements, markers and legend entries, can
+have a command trigger when event occurs in them, much like canvas items
+in Tk's canvas widget. Not all event sequences are valid. The only binding
+events that may be specified are those related to the mouse and keyboard
+(such as <B>Enter</B>, <B>Leave</B>, <B>ButtonPress</B>, <B>Motion</B>, and <B>KeyPress</B>). <P>
+Only one element
+or marker can be picked during an event. This means, that if the mouse
+is directly over both an element and a marker, only the uppermost component
+is selected. This isn't true for legend entries. Both a legend entry and
+an element (or marker) binding commands will be invoked if both items
+are picked. <P>
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the element
+name and another is associated with one of the element's tags (see the <B>-bindtags</B>
+option). When this occurs, all of the matching bindings are invoked. A
+binding associated with the element name is invoked first, followed by
+one binding for each of the element's bindtags. If there are multiple matching
+bindings for a single tag, then only the most specific binding is invoked.
+ A continue command in a binding script terminates that script, and a
+break command terminates that script and skips any remaining scripts for
+the event, just as for the bind command. <P>
+The <B>-bindtagsR option for these
+components controls addition tag names which can be matched. Implicitly
+elements and markers always have tags matching their names. Setting the
+value of the <B>-bindtags</B></B> option doesn't change this.
+<H2><A NAME="sect23" HREF="#toc23">C Language API</A></H2>
+You can manipulate
+data elements from the C language. There may be situations where it is
+too expensive to translate the data values from ASCII strings. Or you might
+want to read data in a special file format. <P>
+Data can manipulated from the
+C language using BLT vectors. You specify the x and y data coordinates of
+an element as vectors and manipulate the vector from C. The strip chart
+will be redrawn automatically after the vectors are updated. <P>
+From Tcl, create
+the vectors and configure the element to use them. <BR>
+<CODE>vector X Y<BR>
+.s element configure line1 -xdata X -ydata Y<BR>
+</CODE><P>To set data points from C, you pass the values as arrays of doubles using
+the <B>Blt_ResetVector</B> call. The vector is reset with the new data and at
+the next idle point (when Tk re-enters its event loop), the strip chart
+will be redrawn automatically. <BR>
+<CODE>#include &lt;tcl.h&gt;<BR>
+#include &lt;blt.h&gt;<BR>
+<P>
+register int i;<BR>
+Blt_Vector *xVec, *yVec;<BR>
+double x[50], y[50];<BR>
+<P>
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */<BR>
+if ((Blt_GetVector(interp, "X", 50, &amp;xVec) != TCL_OK) ||<BR>
+ (Blt_GetVector(interp, "Y", 50, &amp;yVec) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+<P>
+for (i = 0; i &lt; 50; i++) {<BR>
+ x[i] = i * 0.02;<BR>
+ y[i] = sin(x[i]);<BR>
+}<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<BR>
+<P>
+/* Put the data into BLT vectors */<BR>
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||<BR>
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+</CODE><P>See the <B>vector</B> manual page for more details.
+<H2><A NAME="sect24" HREF="#toc24">Speed Tips</A></H2>
+There may be cases
+where the strip chart needs to be drawn and updated as quickly as possible.
+ If drawing speed becomes a big problem, here are a few tips to speed up
+displays.
+<UL>
+&#183;<LI>Try to minimize the number of data points. The more data points
+the looked at, the more work the strip chart must do. </LI>&#183;<LI>If your data is generated
+as floating point values, the time required to convert the data values
+to and from ASCII strings can be significant, especially when there any
+many data points. You can avoid the redundant string-to-decimal conversions
+using the C API to BLT vectors. </LI>&#183;<LI>Data elements without symbols are drawn
+faster than with symbols. Set the data element's <B>-symbol</B> option to <I>none</I>. If
+you need to draw symbols, try using the simple symbols such as <I>splus</I> and
+<I>scross</I>. </LI>&#183;<LI>Don't stipple or dash the element. Solid lines are much faster. </LI>&#183;<LI>If
+you update data elements frequently, try turning off the widget's <B>-bufferelements</B>
+option. When the strip chart is first displayed, it draws data elements
+into an internal pixmap. The pixmap acts as a cache, so that when the strip
+chart needs to be redrawn again, and the data elements or coordinate axes
+haven't changed, the pixmap is simply copied to the screen. This is especially
+useful when you are using markers to highlight points and regions on the
+strip chart. But if the strip chart is updated frequently, changing either
+the element data or coordinate axes, the buffering becomes redundant. </LI>
+</UL>
+
+<H2><A NAME="sect25" HREF="#toc25">Limitations</A></H2>
+Auto-scale
+routines do not use requested min/max limits as boundaries when the axis
+is logarithmically scaled. <P>
+The PostScript output generated for polygons
+with more than 1500 points may exceed the limits of some printers (See
+PostScript Language Reference Manual, page 568). The work-around is to break
+the polygon into separate pieces.
+<H2><A NAME="sect26" HREF="#toc26">Future Incompatibility</A></H2>
+The <B>-mapped</B> options
+are obsoleted and will be removed. You can achieve the same results using
+the <B>-hide</B> option instead. <BR>
+<CODE># Works for now.<BR>
+.s legend configure -mapped no<BR>
+<P>
+# Instead use this.<BR>
+.s legend configure -hide yes <BR>
+
+<H2><A NAME="sect27" HREF="#toc27"></CODE><P>Keywords</A></H2>
+stripchart, graph, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Syntax</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Example</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Stripchart Operations</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Stripchart Components</A></LI>
+<UL>
+<LI><A NAME="toc8" HREF="#sect8">Axis Components</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Crosshairs Component</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Element Components</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Grid Component</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Legend Component</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Pen Components</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">PostScript Component</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Marker Components</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Bitmap Markers</A></LI>
+<LI><A NAME="toc17" HREF="#sect17">Image Markers</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Line Markers</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Polygon Markers</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Text Markers</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Window Markers</A></LI>
+</UL>
+<LI><A NAME="toc22" HREF="#sect22">Graph Component Bindings</A></LI>
+<LI><A NAME="toc23" HREF="#sect23">C Language API</A></LI>
+<LI><A NAME="toc24" HREF="#sect24">Speed Tips</A></LI>
+<LI><A NAME="toc25" HREF="#sect25">Limitations</A></LI>
+<LI><A NAME="toc26" HREF="#sect26">Future Incompatibility</A></LI>
+<LI><A NAME="toc27" HREF="#sect27">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/table.html b/blt/html/table.html
new file mode 100644
index 00000000000..178075b874b
--- /dev/null
+++ b/blt/html/table.html
@@ -0,0 +1,721 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>table(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+table - Arranges widgets in a table
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>table
+<I>container</I></B> ?<I>widget index option value</I>?... <P>
+<B>table arrange</B> <I>container</I> <P>
+<B>table cget
+<I>container</I></B> ?<I>item</I>? <I>option</I> <P>
+<B>table configure <I>container</I></B> ?<I>item</I>?... ?<I>option value</I>?...
+<P>
+<B>table extents <I>container</I></B> <I>item</I> <P>
+<B>table forget <I>widget</I></B> ?<I>widget</I>?... <P>
+<B>table info <I>container</I></B>
+<I>item</I> <P>
+<B>table locate <I>container</I></B> <I>x y</I> <P>
+<B>table containers </B>?<I>switch</I>? ?<I>arg</I>? <P>
+<B>table save
+<I>container</I></B> <P>
+<B>table search <I>container</I></B> ?<I>switch arg</I>?...
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>table</B> command
+arranges widgets in a table. The alignment of widgets is detemined by their
+row and column positions and the number of rows or columns that they span.
+
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Probably the most painstaking aspect of building a graphical
+application is getting the placement and size of the widgets just right.
+It usually takes many iterations to align widgets and adjust their spacing.
+That's because managing the geometry of widgets is simply not a packing
+problem, but also graphical design problem. Attributes such as alignment,
+symmetry, and balance are more important than minimizing the amount of
+space used for packing. <P>
+The <B>table</B> geometry manager arranges widgets in a
+table. It's easy to align widgets (horizontally and vertically) or to create
+empty space to balance the arrangement of the widgets. Widgets (called
+<I>slaves</I> in the Tk parlance) are arranged inside a containing widget (called
+the <I>master</I>). Widgets are positioned at row,column locations and may span
+any number of rows or columns. More than one widget can occupy a single
+location. <P>
+The placement of widget windows determines both the size and arrangement
+of the table. The table queries the requested size of each widget. The
+<I>requested size</I> of a widget is the natural size of the widget (before the
+widget is shrunk or expanded). The height of each row and the width of
+each column is the largest widget spanning that row or column. The size
+of the table is in turn the sum of the row and column sizes. This is the
+table's <I>normal size</I>. <P>
+The total number of rows and columns in a table is determined
+from the indices specified. The table grows dynamically as windows are
+added at larger indices.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+The table geometry manager is created by
+invoking the <B>table</B> command. <BR>
+<CODE># Create a table in the root window<BR>
+table .<BR>
+</CODE><P>The window <I>.</I> is now the <I>container</I> of the table. Widgets are packed into
+the table and displayed within the confines of the container. <P>
+You add widgets
+to the table by row and column location. Row and column indices start from
+zero. <BR>
+<CODE>label .title -text "This is a title"<BR>
+<P>
+# Add a label to the table<BR>
+table . .title 0,0 <BR>
+</CODE><P>The label <I>.title</I> is added to the table. We can add more widgets in the same
+way. <BR>
+<CODE>button .ok -text "Ok"<BR>
+button .cancel -text "Cancel"<BR>
+<P>
+# Add two buttons<BR>
+table . .ok 1,0<BR>
+table . .cancel 1,1<BR>
+</CODE><P>Two buttons <I>.ok</I> and <I>.cancel</I> are now packed into the second row of the table.
+ They each occupy one cell of the table. By default, widgets span only
+a single row and column. <P>
+The first column contains two widgets, <I>.title</I> and
+<I>.ok</I>. By default, the widest of the two widgets will define the width of
+the column. However, we want <I>.title</I> to be centered horizontally along the
+top of the table. We can make <I>.title</I> span two columns using the <B>configure</B>
+operation. <BR>
+<CODE># Make the label span both columns<BR>
+table configure . .title -cspan 2<BR>
+</CODE><P>The label <I>.title</I> will now be centered along the top row of the table. <P>
+In
+the above example, we've create and arranged the layout for the table invoking
+the <B>table</B> command several times. Alternately, we could have used a single
+<B>table</B> command. <BR>
+<CODE>label .title -text "This is a title"<BR>
+button .ok -text "Ok"<BR>
+button .cancel -text "Cancel"<BR>
+<P>
+# Create and pack the table<BR>
+table . \<BR>
+ .title 0,0 -cspan 2 \<BR>
+ .ok 1,0 \<BR>
+ .cancel 1,1<BR>
+</CODE><P>The table will override the requested width and height of the container
+so that the window fits the table exactly. This also means that any change
+to the size of table will be propagated up through the Tk window hierarchy.
+ This feature can be turned off using the <B>configure</B> operation again. <BR>
+<CODE>table configure . -propagate no<BR>
+</CODE><P>You can also set the width of height of the table to a specific value. This
+supersedes the calculated table size. <BR>
+<CODE># Make the container 4 inches wide, 3 inches high<BR>
+table configure . -reqwidth 4i -reqheight 3i<BR>
+</CODE><P>If a widget is smaller than the cell(s) it occupies, the widget will float
+within the extra space. By default, the widget will be centered within
+the space, but you can anchor the widget to any side of cell using the
+<B>-anchor</B> configuration option. <BR>
+<CODE>table configure . .ok -anchor w<BR>
+</CODE><P>The <B>-fill</B> option expands the widget to fill the extra space either vertically
+or horizontally (or both). <BR>
+<CODE># Make the title label fill the entire top row<BR>
+table configure . .title -cspan 2 -fill x <BR>
+<P>
+# Each button will be as height of the 2nd row.<BR>
+table configure . .ok .cancel -fill y<BR>
+</CODE><P>The width of <I>.title</I> will be the combined widths of both columns. Both <I>.ok</I>
+and <I>.cancel</I> will become as tall as the second row. <P>
+The <B>-padx</B> and <B>-pady</B> options
+control the amount of padding around the widget. Both options take a list
+of one or two values. <BR>
+<CODE># Pad the title by two pixels above and below.<BR>
+table configure . .title -pady 2<BR>
+<P>
+# Pad each button 2 pixels on the left, and 4 on the right.<BR>
+table configure . .ok .cancel -padx { 2 4 }<BR>
+</CODE><P>If the list has only one value, then both exterior sides (top and bottom
+or left and right) of the widget are padded by that amount. If the list
+has two elements, the first specifies padding for the top or left side
+and the second for the bottom or right side. <P>
+Like the container, you can
+also override the requested widths and heights of widgets using the <B>-reqwidth</B>
+and <B>-reqheight</B> options. This is especially useful with character-based widgets
+(such as buttons, labels, text, listbox, etc) that let you specify their
+size only in units of characters and lines, instead of pixels. <BR>
+<CODE># Make all buttons one inch wide<BR>
+table configure . .ok .cancel -reqwidth 1i<BR>
+<P>
+</CODE><P>Each row and column of the table can be configured, again using the <B>configure</B>
+operation. Rows are and columns are designated by <I>R<I>i</I></I> and <I>C<I>i</I></I> respectively,
+where <I>i</I> is the index of the row or column. <P>
+For example, you can set the
+size of a row or column. <BR>
+<CODE># Make the 1st column 2 inches wide<BR>
+table configure . c0 -width 2.0i<BR>
+<P>
+# Make the 2nd row 1/2 inch high.<BR>
+table configure . r1 -height 0.5i<BR>
+</CODE><P>The new size for the row or column overrides its calculated size. If no
+widgets span the row or column, its height or width is zero. So you can
+use the <B>-width</B> and <B>-height</B> options to create empty spaces in the table. <BR>
+<CODE># Create an empty row and column<BR>
+table configure . r2 c2 -width 1i<BR>
+</CODE><P>The <B>-pady</B> option lets you add padding to the top and bottom sides of rows.
+ The <B>-padx</B> option adds padding to the left and right sides of columns. Both
+options take a list of one or two values. <BR>
+<CODE># Pad above the title by two pixels <BR>
+table configure . r0 -pady { 2 0 }<BR>
+<P>
+# Pad each column 4 pixels on the left, and 2 on the right.<BR>
+table configure . c* -padx { 2 4 }<BR>
+<P>
+</CODE><P>Notice that you can configure all the rows and columns using either <I>R*</I>
+or <I>C*</I>. <P>
+When the container is resized, the rows and columns of the table
+are also resized. Only the rows or columns that contain widgets (a widget
+spans the row or column) grow or shrink. The <B>-resize</B> option indicates whether
+the row or column can be shrunk or stretched. If the value is <I>shrink</I>, the
+row or column can only be resized smaller. If <I>expand</I>, it can only be resized
+larger. If <I>none</I>, the row or column is frozen at its requested size. <BR>
+<CODE># Let the 1st column get smaller, but not bigger<BR>
+table configure . c0 -resize shrink<BR>
+<P>
+# Let the 2nd column get bigger, not smaller<BR>
+table configure . c1 -resize expand<BR>
+<P>
+# Don't resize the first row <BR>
+table configure . r0 -resize none<BR>
+</CODE><P>The following example packs a canvas, two scrollbars, and a title. The rows
+and columns containing the scrollbars are frozen at their requested size,
+so that even if the frame is resized, the scrollbars will remain the same
+width. <BR>
+<CODE>table . \<BR>
+ .title 0,0 -cspan 3 \<BR>
+ .canvas 1,1 -fill both \<BR>
+ .vscroll 1,2 -fill y \<BR>
+ .hscroll 2,1 -fill x<BR>
+<P>
+# Don't let the scrollbars resize<BR>
+table configure . c2 r2 -resize none<BR>
+<P>
+# Create an empty space to balance the scrollbar<BR>
+table configure . c0 -width .vscroll<BR>
+</CODE><P>Note that the value of the <B>-width</B> option is the name of a widget window.
+ This indicates that the width of the column should be the same as the
+requested width of <I>.vscroll</I>. <P>
+Finally, the <B>forget</B> operation removes widgets
+from the table. <BR>
+<CODE># Remove the windows from the table<BR>
+table forget .quit .frame<BR>
+</CODE><P>It's not necessary to specify the container. The <B>table</B> command determines
+the container from the widget name.
+<H2><A NAME="sect5" HREF="#toc5">Operations</A></H2>
+The following operations are
+available for the <B>table</B>:
+<DL>
+
+<DT><B>table <I>container</I></B> ?<I>widget index option value</I>?...
+</DT>
+<DD>Adds the widget <I>widget</I> to the table at <I>index</I>. <I>Index</I> is a row,column position
+in the table. It must be in the form <I>row</I>,<I>column</I> where <I>row</I> and <I>column</I> are
+the respective row and column numbers, starting from zero (0,0 is the upper
+leftmost position). <I>Row</I> and <I>column</I> may also be numeric expressions that
+are recursively evaluated. If a table doesn't exist for <I>container</I>, one is
+created. <I>Widget</I> is the path name of the window, that must already exist,
+to be arranged inside of <I>container</I>. <I>Option</I> and <I>value</I> are described in the
+ <FONT SIZE=-1><B>WIDGET</B></FONT>
+ section. </DD>
+
+<DT><B>table arrange</B> <I>container</I> </DT>
+<DD>Forces the table to compute its
+layout immediately. Normally, the table geometry manager will wait until
+the next idle point, before calculating the size of its rows and columns.
+ This is useful for collecting the <I>normal</I> sizes of rows and columns, that
+are based upon the requested widget sizes. </DD>
+
+<DT><B>table cget</B> <I>container </I>?<I>item</I>?<I> option</I>
+</DT>
+<DD>Returns the current value of the configuration option specific to <I>item</I>
+given by <I>option</I>. <I>Item</I> is either a row or column index, or the path name
+of a widget. <I>Item</I> can be in any form describe in the <B>configure</B> operation
+below. If no <I>item</I> argument is provided, then the configuration option is
+for the table itself. <I>Option</I> may be any one of the options described in
+the appropiate section for <I>item</I>. </DD>
+
+<DT><B>table configure</B> <I>container item</I>... ?<I>option
+value</I>?... </DT>
+<DD>Queries or modifies the configuration options specific to <I>item</I>.
+If no <I>option</I> is specified, this command returns a list describing all of
+the available options for <I>item</I> If the argument <I>item</I> is omitted, then the
+specified configuration options are for the table itself. Otherwise <I>item</I>
+must be either a row or column specification, or the path name of a widget.
+ The following <I>item</I> types are available. <blockquote></DD>
+
+<DT><I>C<I>i</I></I> </DT>
+<DD>Specifies the column of <I>container</I>
+to be configured. <I>Item</I> must be in the form <I>C<I>n</I></I>, where <I>i</I> is the index of
+ the column. See the <FONT SIZE=-1><B>COLUMN</B></FONT>
+ section. </DD>
+
+<DT><I>R<I>i</I></I> </DT>
+<DD>Specifies the row of <I>container</I>
+to be configured. <I>Item</I> must be in the form <I>R<I>i</I></I>, where <I>i</I> is the index of the
+row. See the <FONT SIZE=-1><B>ROW</B></FONT>
+ section. </DD>
+
+<DT><I>widget</I> </DT>
+<DD>Specifies a widget of <I>container</I> to be
+queried. <I>Widget</I> is the path name of a widget packed in <I>container</I>. See the
+<FONT SIZE=-1><B>WIDGET</B></FONT>
+ section. </DD>
+
+<DT>No argument </DT>
+<DD>Specifies that the table itself is to be queried.
+ See the <FONT SIZE=-1><B>TABLE</B></FONT>
+ section for a description of the option-value pairs for
+the table. </DD>
+</DL>
+</blockquote>
+<blockquote><P>
+The <I>option<I> and <I>value</I></I></I> pairs are specific to <I>item</I>. If <I>option</I> is
+specified with no <I>value</I>, then the command returns a list describing the
+one named option (this list will be identical to the corresponding sublist
+of the value returned if no <I>option</I> is specified). If one or more <I>option-value</I>
+pairs are specified, then the command modifies the given option(s) to have
+the given value(s); in this case the command returns the empty string. </blockquote>
+
+<DL>
+
+<DT><B>table
+extents <I>container</I></B> <I>index</I> </DT>
+<DD>Queries the location and dimensions of row and
+columns in the table. <I>Index</I> can be either a row or column index or a table
+index. Returns a list of the x,y coordinates (upperleft corner) and dimensions
+(width and height) of the cell, row, or column. </DD>
+
+<DT><B>table forget <I>widget</I></B> ?<I>widget</I>?...
+</DT>
+<DD>Requests that <I>widget</I> no longer have its geometry managed. <I>Widget</I> is the
+pathname of the window currently managed by some table. The window will
+be unmapped so that it no longer appears on the screen. If <I>widget</I> is not
+currently managed by any table, an error message is returned, otherwise
+the empty string. </DD>
+
+<DT><B>table info <I>container</I></B> <I>item</I> </DT>
+<DD>Returns a list of the current
+configuration options for <I>item</I>. The list returned is exactly in the form
+that might be specified to the <B>table</B> command. It can be used to save and
+reset table configurations. <I>Item</I> must be one of the following. <blockquote></DD>
+
+<DT><I>C<I>i</I></I> </DT>
+<DD>Specifies
+the column of <I>container</I> to be queried. <I>Item</I> must be in the form <I>C<I>n</I></I>, where
+<I>n</I> is the index of the column. </DD>
+
+<DT><I>R<I>i</I></I> </DT>
+<DD>Specifies the row of <I>container</I> to be
+queried. <I>Item</I> must be in the form <I>R<I>i</I></I>, where <I>i</I> is the index of the row.
+</DD>
+
+<DT><I>widget</I> </DT>
+<DD>Specifies a widget of <I>container</I> to be queried. <I>Widget</I> is the path
+name of a widget packed in <I>container</I>. </DD>
+
+<DT>No argument </DT>
+<DD>Specifies that the table
+itself is to be queried. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>table locate <I>container</I></B> <I>x y</I> </DT>
+<DD>Returns the table index
+(row,column) of the cell containing the given screen coordinates. The <I>x</I>
+and <I>y</I> arguments represent the x and y coordinates of the sample point to
+be tested. </DD>
+
+<DT><B>table containers </B>?<I>switch arg</I>? </DT>
+<DD>Returns a list of all container
+windows matching a given criteria (using <I>switch</I> and <I>arg</I>). If no <I>switch</I>
+and <I>arg</I> arguments are given, the names of all container windows (only those
+using the <B>table</B> command) are returned. The following are valid switches:
+<blockquote></DD>
+
+<DT><B>-pattern</B> <I>pattern</I> </DT>
+<DD>Returns a list of pathnames of all container windows matching
+<I>pattern</I>. </DD>
+
+<DT><B>-slave</B> <I>window</I> </DT>
+<DD>Returns the name of the container window of table
+managing <I>window</I>. <I>Window</I> must be the path name of widget. If <I>window</I> is not
+managed by any table, the empty string is returned. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>table search <I>container</I></B>
+?<I>switch arg</I>?... </DT>
+<DD>Returns the names of all the widgets in <I>container</I> matching
+the criteria given by <I>switch</I> and <I>arg</I>. <I>Container</I> is name of the container
+window associated with the table to be searched. The name of the widget
+is returned if any one <I>switch</I>-<I>arg</I> criteria matches. If no <I>switch</I>-<I>arg</I> arguments
+are given, the names of all widgets managed by <I>container</I> are returned.
+The following are switches are available: <blockquote></DD>
+
+<DT><B>-pattern</B> <I>pattern</I> </DT>
+<DD>Returns the names
+of any names of the widgets matching <I>pattern</I>. </DD>
+
+<DT><B>-span</B> <I>index</I> </DT>
+<DD>Returns the
+names of widgets that span <I>index</I>. A widget does not need to start at <I>index</I>
+to be included. <I>Index</I> must be in the form <I>row</I>,<I>column</I>, where <I>row</I> and <I>column</I>
+are valid row and column numbers. </DD>
+
+<DT><B>-start</B> <I>index</I> </DT>
+<DD>Returns the names of widgets
+that start at <I>index</I>. <I>Index</I> must be in the form <I>row</I>,<I>column</I>, where <I>row</I> and
+<I>column</I> are valid row and column numbers. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect6" HREF="#toc6">Table Options</A></H2>
+To configure the table
+itself, you omit the <I>item</I> argument when invoking the <B>configure</B> operation.
+<BR>
+<CODE><B>table configure</B> <I>container</I> ?<I>option value</I>?...<BR>
+</CODE><P>The following options are available for the table: <blockquote>
+<DL>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets how much
+padding to add to the left and right exteriors of the table. <I>Pad</I> can be
+a list of one or two numbers. If <I>pad</I> has two elements, the left side of
+the table is padded by the first value and the right side by the second
+value. If <I>pad</I> has just one value, both the left and right sides are padded
+evenly by the value. The default is <I>0</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets how much padding to
+add to the top and bottom exteriors of the table. <I>Pad</I> can be a list of one
+or two numbers. If <I>pad</I> has two elements, the area above the table is padded
+by the first value and the area below by the second value. If <I>pad</I> is just
+one number, both the top and bottom areas are padded by the value. The
+default is <I>0</I>. </DD>
+
+<DT><B>-propagate <I>boolean</I></B> </DT>
+<DD>Indicates if the table should override
+the requested width and height of the <I>container</I> window. If <I>boolean</I> is false,
+<I>container</I> will not be resized. <I>Container</I> will be its requested size. The
+default is <I>1</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect7" HREF="#toc7">Widget Options</A></H2>
+widgets are configured by specifying the name
+of the widget when invoking the <B>configure</B> operation. <BR>
+<P>
+<CODE><B>table configure</B> <I>container <I>widget</I></I> ?<I>option value</I>?...<BR>
+</CODE><P><I>Widget</I> must be the path name of a window already packed in the table associated
+with <I>container</I>. The following options are available for widgets: <blockquote>
+<DL>
+
+<DT><B>-anchor
+<I>anchor</I></B> </DT>
+<DD>Anchors <I>widget</I> to a particular edge of the cell(s) it resides. This
+option has effect only if the space of the spans surrounding <I>widget</I> is
+larger than <I>widget</I>. <I>Anchor</I> specifies how <I>widget</I> will be positioned in the
+space. For example, if <I>anchor</I> is <I>center</I> then the window is centered in
+the rows and columns it spans; if <I>anchor</I> is <I>w</I> then the window will be aligned
+with the leftmost edge of the span. The default is <I>center</I>. </DD>
+
+<DT><B>-columnspan <I>number</I></B>
+</DT>
+<DD>Sets the number of columns <I>widget</I> will span. The default is <I>1</I>. </DD>
+
+<DT><B>-columncontrol
+<I>control</I></B> </DT>
+<DD>Specifies how the width of <I>widget</I> should control the width of the
+columns it spans. <I>Control</I> is either <I>normal</I>, <I>none</I>, or <I>full</I>. The default
+is <I>normal</I>. <blockquote></DD>
+
+<DT><I>none</I> </DT>
+<DD>The width of <I>widget</I> is not considered. </DD>
+
+<DT><I>full</I> </DT>
+<DD>Only the width
+of <I>widget</I> will be considered when computing the widths of the columns.
+</DD>
+
+<DT><I>normal</I> </DT>
+<DD>Indicates that the widest widget spanning the column will determine
+ the width of the span. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-fill <I>fill</I></B> </DT>
+<DD>Specifies if <I>widget</I> should be stretched
+to fill any free space in the span surrounding <I>widget</I>. <I>Fill</I> is either <I>none</I>,
+<I>x</I>, <I>y</I>, <I>both</I>. The default is <I>none</I>. <blockquote></DD>
+
+<DT><I>x</I> </DT>
+<DD>The widget can grow horizontally. </DD>
+
+<DT><I>y</I>
+</DT>
+<DD>The widget can grow vertically. </DD>
+
+<DT><I>both</I> </DT>
+<DD>The widget can grow both vertically
+and horizontally. </DD>
+
+<DT><I>none</I> </DT>
+<DD>The widget does not grow along with the span.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-ipadx <I>pixels</I></B> </DT>
+<DD>Sets how much horizontal padding to add internally on the
+left and right sides of <I>widget</I>. <I>Pixels</I> must be a valid screen distance
+like <I>2</I> or <I>0.3i</I>. The default is <I>0</I>. </DD>
+
+<DT><B>-ipady <I>pixels</I></B> </DT>
+<DD>Sets how much vertical padding
+to add internally on the top and bottom of <I>widget</I>. <I>Pixels</I> must be a valid
+screen distance like <I>2</I> or <I>0.3i</I>. The default is <I>0</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets how much
+padding to add to the left and right exteriors of <I>widget</I>. <I>Pad</I> can be a list
+of one or two numbers. If <I>pad</I> has two elements, the left side of <I>widget</I>
+is padded by the first value and the right side by the second value. If
+<I>pad</I> has just one value, both the left and right sides are padded evenly
+by the value. The default is <I>0</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets how much padding to add to
+the top and bottom exteriors of <I>widget</I>. <I>Pad</I> can be a list of one or two
+numbers. If <I>pad</I> has two elements, the area above <I>widget</I> is padded by the
+first value and the area below by the second value. If <I>pad</I> is just one
+number, both the top and bottom areas are padded by the value. The default
+is <I>0</I>. </DD>
+
+<DT><B>-reqheight <I>height</I></B> </DT>
+<DD>Specifies the limits of the requested height for
+<I>widget</I>. <I>Height</I> is a list of bounding values. See the <FONT SIZE=-1><B>BOUNDING</B></FONT>
+ section
+for a description of this list. By default, the height of <I>widget</I> is its
+requested height with its internal padding (see the <B>-ipady</B> option). The
+bounds specified by <I>height</I> either override the height completely, or bound
+the height between two sizes. The default is <I>""</I>. </DD>
+
+<DT><B>-reqwidth <I>width</I></B> </DT>
+<DD>Specifies
+the limits of the requested width for <I>widget</I>. <I>Width</I> is a list of bounding
+values. See the <FONT SIZE=-1><B>BOUNDING</B></FONT>
+ section for a description of this list. By default,
+the width of <I>widget</I> is its requested width with its internal padding (set
+the <B>-ipadx</B> option). The bounds specified by <I>width</I> either override the width
+completely, or bound the height between two sizes. The default is <I>""</I>. </DD>
+
+<DT><B>-rowspan
+<I>number</I></B> </DT>
+<DD>Sets the number of rows <I>widget</I> will span. The default is <I>1</I>. </DD>
+
+<DT><B>-rowcontrol
+<I>control</I></B> </DT>
+<DD>Specifies how the height of <I>widget</I> should control the height of
+the rows it spans. <I>Control</I> is either <I>normal</I>, <I>none</I>, or <I>full</I>. The default
+is <I>normal</I>. <blockquote></DD>
+
+<DT><I>none</I> </DT>
+<DD>The height of <I>widget</I> is not considered. </DD>
+
+<DT><I>full</I> </DT>
+<DD>Only the
+height of <I>widget</I> will be considered when computing the heights of the rows.
+ </DD>
+
+<DT><I>normal</I> </DT>
+<DD>Indicates that the tallest widget spanning the row will determine
+ the height of the span. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<H2><A NAME="sect8" HREF="#toc8">Column Options</A></H2>
+To configure a column in the table,
+specify the column index as <I>C<I>i</I></I>, where <I>i</I> is the index of the column to be
+configured. <BR>
+<P>
+<CODE><B>table configure</B> <I>container <I>C<I>i</I></I></I> ?<I>option value</I>?...<BR>
+</CODE><P>If the index is specified as <I>C*</I>, then all columns of the table will be
+configured. The following options are available for table columns. <blockquote>
+<DL>
+
+<DT><B>-padx
+<I>pad</I></B> </DT>
+<DD>Sets the padding to the left and right of the column. <I>Pad</I> can be a list
+of one or two numbers. If <I>pad</I> has two elements, the left side of the column
+is padded by the first value and the right side by the second value. If
+<I>pad</I> has just one value, both the left and right sides are padded evenly
+by the value. The default is <I>0</I>. </DD>
+
+<DT><B>-resize <I>mode</I></B> </DT>
+<DD>Indicates that the column can
+expand or shrink from its requested width when the table is resized. <I>Mode</I>
+must be one of the following: <I>none</I>, <I>expand</I>, <I>shrink</I>, or <I>both</I>. If <I>mode</I> is
+ <I>expand</I> the width of the column is expanded if there is extra space in
+the container window. If <I>mode</I> is <I>shrink</I> its width may be reduced beyond
+its requested width if there is not enough space in the container. The default
+is <I>none</I>. </DD>
+
+<DT><B>-width <I>width</I></B> </DT>
+<DD>Specifies the limits within that the width of the column
+may expand or shrink. <I>Width</I> is a list of bounding values. See the section
+<FONT SIZE=-1><B>BOUNDING</B></FONT>
+ for a description of this list. By default there are no constraints.
+</DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect9" HREF="#toc9">Row Options</A></H2>
+To configure a row in the table, specify the row index as <I>R<I>i</I></I>,
+where <I>i</I> is the index of the row to be configured. <BR>
+<P>
+<CODE><B>table configure</B> <I>container <I>R<I>i</I></I></I> ?<I>option value</I>?...<BR>
+</CODE><P>If the index is specified as <I>R*</I>, then all rows of the table will be configured.
+ The following options are available for table rows. <blockquote>
+<DL>
+
+<DT><B>-height <I>height</I></B> </DT>
+<DD>Specifies
+the limits of the height that the row may expand or shrink to. <I>Height</I> is
+a list of bounding values. See the section <FONT SIZE=-1><B>BOUNDING</B></FONT>
+ for a description
+of this list. By default there are no constraints. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding
+above and below the row. <I>Pad</I> can be a list of one or two numbers. If <I>pad</I>
+has two elements, the area above the row is padded by the first value and
+the area below by the second value. If <I>pad</I> is just one number, both the
+top and bottom areas are padded by the value. The default is <I>0</I>. </DD>
+
+<DT><B>-resize <I>mode</I></B>
+</DT>
+<DD>Indicates that the row can expand or shrink from its requested height
+when the table is resized. <I>Mode</I> must be one of the following: <I>none</I>, <I>expand</I>,
+<I>shrink</I>, or <I>both</I>. If <I>mode</I> is <I>expand</I> the height of the row is expanded if
+there is extra space in the container. If <I>mode</I> is <I>shrink</I> its height may
+be reduced beyond its requested height if there is not enough space in
+ the container. The default is <I>none</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect10" HREF="#toc10">Bounding Sizes</A></H2>
+Sometimes it's more useful
+to limit resizes to an acceptable range, than to fix the size to a particular
+value or disallow resizing altogether. Similar to the way the <B>wm</B> command
+lets you specify a <B>minsize</B> and <B>maxsize</B> for a toplevel window, you can bound
+the sizes the container, a widget, row, or column may take. The <B>-width</B>, <B>-height</B>,
+<B>-reqwidth</B>, and <B>-reqheight</B> options, take a list of one, two, or three values.
+We can take a previous example and instead preventing resizing, bound the
+size of the scrollbars between two values. <BR>
+<CODE>table . \<BR>
+ .title 0,0 -cspan 3 \<BR>
+ .canvas 1,1 -fill both \<BR>
+ .vscroll 1,2 -fill y \<BR>
+ .hscroll 2,1 -fill x<BR>
+<P>
+# Bound the scrollbars between 1/8 and 1/2 inch<BR>
+table configure . c2 -width { 0.125 0.5 }<BR>
+table configure . r2 -height { 0.125 0.5 }<BR>
+table configure . vscroll .hscroll -fill both<BR>
+</CODE><P>The scrollbars will get no smaller than 1/8 of an inch, or bigger than
+1/2 inch. The initial size will be their requested size, so long as it
+is within the specified bounds. <P>
+How the elements of the list are interpreted
+is dependent upon the number of elements in the list. <blockquote>
+<DL>
+
+<DT>{<I></I>} </DT>
+<DD>Empty list. No
+bounds are set. The default sizing is performed. </DD>
+
+<DT>{<I> x </I>} </DT>
+<DD>Fixes the size to
+<I>x</I>. The window or partition cannot grow or shrink. </DD>
+
+<DT>{<I> min max </I>} </DT>
+<DD>Sets up minimum
+and maximum limits for the size of the window or partition. The window
+or partition can be reduced less than <I>min</I>, nor can it be stretched beyond
+<I>max</I>. </DD>
+
+<DT>{<I> min max nom </I>} </DT>
+<DD>Specifies minimum and maximum size limits, but also
+specifies a nominal size <I>nom</I>. This overrides the calculated size of the
+window or partition. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect11" HREF="#toc11">Miscellaneous</A></H2>
+Another feature is that you can put two
+widgets in the same cell of the table. This is useful when you want to
+add decorations around a widget. <BR>
+<CODE>frame .frame -bd 1 -relief sunken<BR>
+button .quit -text "Quit"<BR>
+<P>
+# Put both the frame and the button in the same cell.<BR>
+table . \<BR>
+ .quit 1,0 -padx 2 -pady 2 \<BR>
+ .frame 1,0 -fill both<BR>
+
+<H2><A NAME="sect12" HREF="#toc12"></CODE><P>Limitations</A></H2>
+A long standing bug in Tk (circa 1993), there is no way to detect
+if a window is already a container of a different geometry manager. This
+is usually done by accident, such as the following where all three widgets
+are arranged in the same container ".", but using different geometry managers.
+<BR>
+<CODE> table .f1<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;...<BR>
+ pack .f2<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;...<BR>
+ grid .f3<BR>
+</CODE><P>This leads to bizarre window resizing, as each geometry manager applies
+its own brand of layout policies. When the container is a top level window
+(such as "."), your window manager may become locked as it responds to the
+never-ending stream of resize requests.
+<H2><A NAME="sect13" HREF="#toc13">Keywords</A></H2>
+frame, geometry manager,
+location, table, size <P>
+ <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Operations</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Table Options</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Widget Options</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Column Options</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Row Options</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Bounding Sizes</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Miscellaneous</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Limitations</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/tabset.html b/blt/html/tabset.html
new file mode 100644
index 00000000000..265ebb30df6
--- /dev/null
+++ b/blt/html/tabset.html
@@ -0,0 +1,936 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>tabset(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+tabset - Create and manipulate tabset widgets
+
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>tabset</B> <I>pathName </I>?<I>options</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>tabset</B> widget displays
+a series of overlapping folders. Only the contents of one folder at a time
+is displayed. By clicking on the tab's of a folder, you can view other folders.
+ Each folder may contain any Tk widget that can be automatically positioned
+and resized in the folder. <P>
+There's no limit to the number of folders. Tabs
+can be tiered or scrolled. Pages (i.e. embedded widgets) can be torn off
+and displayed in another toplevel widget, and also restored. A tabset can
+also be used as just a set of tabs, without a displaying any pages. You
+can bind events to individual tabs, so it's easy to add features like "balloon
+help".
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Notebooks are a popular graphical paradigm. They allow
+you to organize many windows in a single widget. For example, you might
+have an application the displays several X-Y graphs at the same time. Typically,
+you can't pack the graphs into the same <B>frame</B> because they are too large.
+ The other alternative is to pack the graphs into several <B>toplevel</B> widgets,
+allowing them to overlap on the screen. The problem is that all the different
+toplevel windows clutter the screen and are difficult to manage. <P>
+The <B>tabset</B>
+widget lets organize your application by displaying each graph as a page
+in a folder of a notebook. Only one page is visible at a time. When you
+click on a tab, the folder (graph) corresponding to the tab is displayed
+in the <B>tabset</B> widget. The tabset also lets you temporarily tear pages out
+of the notebook into a separate toplevel widget, and put them back in the
+tabset later. For example, you could compare two graphs side-by-side by tearing
+them out, and then replace them when you are finished. <P>
+A tabset may contain
+an unlimited number of folders. If there are too many tabs to view, you
+can arrange them as multiple tiers or scroll the tabs. The tabset uses the
+conventional Tk scrollbar syntax, so you can attach a scrollbar too.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+You
+create a tabset widget with the <B>tabset</B> command. <BR>
+<CODE># Create a new tabset<BR>
+tabset .ts -relief sunken -borderwidth 2 <BR>
+</CODE><P>A new Tcl command <I>.ts</I> is also created. This command can be used to query
+and modify the tabset. For example, to change the default font used by
+all the tab labels, you use the new command and the tabset's <B>configure</B> operation.
+<BR>
+<CODE># Change the default font.<BR>
+.ts configure -font "fixed"<BR>
+</CODE><P>You can then add folders using the <B>insert</B> operation. <BR>
+<CODE># Create a new folder "f1"<BR>
+.ts insert 0 "f1"<BR>
+</CODE><P>This inserts the new tab named "f1" into the tabset. The index <I>0</I> indicates
+location to insert the new tab. You can also use the index <I>end</I> to append
+a tab to the end of the tabset. By default, the text of the tab is the
+name of the tab. You can change this by configuring the <B>-text</B> option. <BR>
+<CODE># Change the label of "f1"<BR>
+.ts tab configure "f1" -label "Tab #1" <BR>
+</CODE><P>The <B>insert</B> operation lets you add one or more folders at a time. <BR>
+<CODE>.ts insert end "f2" -label "Tab #2" "f3" "f4" <BR>
+</CODE><P>The tab on each folder contains a label. A label may display both an image
+and a text string. You can reconfigure the tab's attributes (foreground/background
+colors, font, rotation, etc) using the <B>tab configure</B> operation. <BR>
+<CODE># Add an image to the label of "f1"<BR>
+set image [image create photo -file stopsign.gif]<BR>
+.ts tab configure "f1" -image $image<BR>
+.ts tab configure "f2" -rotate 90<BR>
+</CODE><P>Each folder may contain an embedded widget to represent its contents. The
+widget to be embedded must be a child of the tabset widget. Using the <B>-window</B>
+option, you specify the name of widget to be embedded. But don't pack the
+widget, the tabset takes care of placing and arranging the widget for you.
+<BR>
+<CODE>graph .ts.graph<BR>
+.ts tab configure "f1" -window ".ts.graph" \<BR>
+ -fill both -padx 0.25i -pady 0.25i<BR>
+</CODE><P>The size of the folder is determined the sizes of the Tk widgets embedded
+inside each folder. The folder will be as wide as the widest widget in
+any folder. The tallest determines the height. You can use the tab's <B>-pagewidth</B>
+and <B>-pageheight</B> options override this. <P>
+Other options control how the widget
+appears in the folder. The <B>-fill</B> option says that you wish to have the widget
+stretch to fill the available space in the folder. <BR>
+<CODE>.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i<BR>
+<P>
+</CODE><P>Now when you click the left mouse button on "f1", the graph will be displayed
+in the folder. It will be automatically hidden when another folder is selected.
+ If you click on the right mouse button, the embedded widget will be moved
+into a toplevel widget of its own. Clicking again on the right mouse button
+puts it back into the folder. <P>
+If you want to share a page between two different
+folders, the <B>-command</B> option lets you specify a Tcl command to be invoked
+whenever the folder is selected. You can reset the <B>-window</B> option for the
+tab whenever it's clicked. <BR>
+<CODE>.ts tab configure "f2" -command { <BR>
+ .ts tab configure "f2" -window ".ts.graph"<BR>
+}<BR>
+.ts tab configure "f1" -command { <BR>
+ .ts tab configure "f1" -window ".ts.graph"<BR>
+}<BR>
+</CODE><P>If you have many folders, you may wish to stack tabs in multiple tiers.
+ The tabset's <B>-tiers</B> option requests a maximum number of tiers. The default
+is one tier. <BR>
+<CODE>.ts configure -tiers 2<BR>
+</CODE><P>If the tabs can fit in less tiers, the widget will use that many. Whenever
+there are more tabs than can be displayed in the maximum number of tiers,
+the tabset will automatically let you scroll the tabs. You can even attach
+a scrollbar to the tabset. <BR>
+<CODE>.ts configure -scrollcommand { .sbar set } -scrollincrement 20<BR>
+.sbar configure -orient horizontal -command { .ts view }<BR>
+</CODE><P>By default tabs are along the top of the tabset from left to right. But
+tabs can be placed on any side of the tabset using the <B>-side</B> option. <BR>
+<CODE># Arrange tabs along the right side of the tabset. <BR>
+.ts configure -side right -rotate 270<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Syntax</A></H2>
+The <B>tabset</B> command creates a new window using the <I>pathName</I> argument
+and makes it into a tabset widget. <BR>
+<P>
+<CODE><B>tabset <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>Additional options may be specified on the command line or in the option
+database to configure aspects of the tabset such as its colors, font, text,
+and relief. The <B>tabset</B> command returns its <I>pathName</I> argument. At the time
+this command is invoked, there must not exist a window named <I>pathName</I>,
+but <I>pathName</I>'s parent must exist. <P>
+When first created, a new tabset contains
+no tabs. Tabs are added or deleted using widget operations described below.
+It is not necessary for all the tabs to be displayed in the tabset window
+at once; commands described below may be used to change the view in the
+window. Tabsets allow scrolling of tabs using the <B>-scrollcommand</B> option.
+They also support scanning (see the <B>scan</B> operation). Tabs may be arranged
+along any side of the tabset window using the <B>-side</B> option. <P>
+The size of the
+tabset window is determined the number of tiers of tabs and the sizes of
+the Tk widgets embedded inside each folder. The widest widget determines
+the width of the folder. The tallest determines the height. If no folders
+contain an embedded widget, the size is detemined solely by the size of
+the tabs. <P>
+You can override either dimension with the tabset's <B>-width</B> and
+<B>-height</B> options.
+<H2><A NAME="sect6" HREF="#toc6">Tabset Indices</A></H2>
+Indices refer to individual tabs/folders in
+the tabset. Many of the operations for tabset widgets take one or more
+indices as arguments. An index may take several forms:
+<DL>
+
+<DT><I>number</I> </DT>
+<DD>Unique node
+id of the tab. </DD>
+
+<DT><B>@<I>x<B>,<I>y</I></B></I></B> </DT>
+<DD>Tab that covers the point in the tabset window specified
+by <I>x</I> and <I>y</I> (in screen coordinates). If no tab covers that point, then the
+index is ignored. </DD>
+
+<DT><B>select</B> </DT>
+<DD>The currently selected tab. The <B>select</B> index is
+ typically changed by either clicking on the tab with the left mouse button
+or using the widget's <B>invoke</B> operation. </DD>
+
+<DT><B>active</B> </DT>
+<DD>The tab where the mouse pointer
+is currently located. The label is drawn using its active colors (see
+the <B>-activebackground</B> and <B>-activeforeground</B> options). The <B>active</B> index is
+typically changed by moving the mouse pointer over a tab or using the widget's
+<B>activate</B> operation. There can be only one active tab at a time. If there
+is no tab located under the mouse pointer, the index is ignored. </DD>
+
+<DT><B>focus</B>
+</DT>
+<DD>Tab that currently has the widget's focus. This tab is displayed with a dashed
+line around its label. You can change this using the <B>focus</B> operation. If
+no tab has focus, then the index is ignored. </DD>
+
+<DT><B>down</B> </DT>
+<DD>Tab immediately below
+the tab that currently has focus, if there is one. If there is no tab below,
+the current tab is returned. </DD>
+
+<DT><B>left</B> </DT>
+<DD>Tab immediately to the left the tab that
+currently has focus, if there is one. If there is no tab to the left, the
+current tab is returned. </DD>
+
+<DT><B>right</B> </DT>
+<DD>Tab immediately to the right the tab that
+currently has focus, if there is one. If there is no tab to the right, the
+current tab is returned. </DD>
+
+<DT><B>up</B> </DT>
+<DD>Tab immediately above, if there is one, to
+the tab that currently has focus. If there is no tab above, the current
+tab is returned. </DD>
+
+<DT><B>end</B> </DT>
+<DD>Last tab in the tabset. If there are no tabs in the
+tabset then the index is ignored. </DD>
+</DL>
+<P>
+Some indices may not always be available.
+ For example, if the mouse is not over any tab, "active" does not have
+an index. For most tabset operations this is harmless and ignored.
+<H2><A NAME="sect7" HREF="#toc7">Tabset
+Operations</A></H2>
+All <B>tabset</B> operations are invoked by specifying the widget's pathname,
+the operation, and any arguments that pertain to that operation. The general
+form is: <P>
+<BR>
+<P>
+<CODE><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<I>pathName operation </I>?<I>arg arg ...</I>?<BR>
+<P>
+</CODE><P><I>Operation</I> and the <I>arg</I>s determine the exact behavior of the command. The
+following operations are available for tabset widgets:
+<DL>
+
+<DT><I>pathName <B>activate</B></I>
+<I>index</I> </DT>
+<DD>Sets the active tab to the one indicated by <I>index</I>. The active tab
+is drawn with its <I>active</I> colors (see the <B>-activebackground</B> and <B>-activeforeground</B>
+options) and may be retrieved with the index <B>active</B>. Only one tab may be
+active at a time. If <I>index</I> is the empty string, then all tabs will be drawn
+with their normal foreground and background colors. </DD>
+
+<DT><I>pathName <B>bind</B></I> <I>tagName</I>
+?<I>sequence</I>? ?<I>command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever
+the event sequence given by <I>sequence</I> occurs for a tab with this tag, <I>command</I>
+will be invoked. The syntax is similar to the <B>bind</B> command except that
+it operates on tabs, rather than widgets. See the <B>bind</B> manual entry for
+complete details on <I>sequence</I> and the substitutions performed on <I>command</I>.
+ <P>
+If all arguments are specified then a new binding is created, replacing
+ any existing binding for the same <I>sequence</I> and <I>tagName</I>. If the first character
+of <I>command</I> is <I>+</I> then <I>command</I> augments an existing binding rather than
+replacing it. If no <I>command</I> argument is provided then the command currently
+associated with <I>tagName</I> and <I>sequence</I> (it's an error occurs if there's no
+such binding) is returned. If both <I>command</I> and <I>sequence</I> are missing then
+a list of all the event sequences for which bindings have been defined
+for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value of the configuration
+option given by <I>option</I>. <I>Option</I> may have any of the values accepted by the
+<B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>configure</B></I> ?<I>option</I>? ?<I>value
+option value ...</I>? </DT>
+<DD>Query or modify the configuration options of the widget.
+If no <I>option</I> is specified, returns a list describing all the available
+options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information on the format
+of this list). If <I>option</I> is specified with no <I>value</I>, then the command returns
+a list describing the one named option (this list will be identical to
+the corresponding sublist of the value returned if no <I>option</I> is specified).
+ If one or more <I>option-value</I> pairs are specified, then the command modifies
+the given widget option(s) to have the given value(s); in this case the
+command returns an empty string. <I>Option</I> and <I>value</I> are described below: <blockquote></DD>
+
+<DT><B>-activebackground
+<I>color</I></B> </DT>
+<DD>Sets the default active background color for tabs. A tab is active
+when the mouse is positioned over it or set by the <B>activate</B> operation.
+Individual tabs may override this option by setting the tab's <B>-activebackground</B>
+option. </DD>
+
+<DT><B>-activeforeground <I>color</I></B> </DT>
+<DD>Sets the default active foreground color
+for tabs. A tab is active when the mouse is positioned over it or set by
+the <B>activate</B> operation. Individual tabs may override this option by setting
+the tab's <B>-activeforeground</B> option. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background color
+of the tabset. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the outside edge of the widget. The <B>-relief</B> option determines how the border
+is to be drawn. The default is <I>2</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor.
+ The default cursor is <I>""</I>. </DD>
+
+<DT><B>-dashes <I>dashList</I></B> </DT>
+<DD>Sets the dash style of the focus
+outline. When a tab has the widget's focus, it is drawn with a dashed outline
+around its label. <I>DashList</I> is a list of up to 11 numbers that alternately
+represent the lengths of the dashes and gaps on the cross hair lines. Each
+number must be between 1 and 255. If <I>dashList</I> is <I>""</I>, the outline will be
+a solid line. The default value is <I>5 2</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Sets the default
+font for the text in tab labels. Individual tabs may override this by setting
+the tab's <B>-font</B> option. The default value is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the default color of tab labels. Individual tabs
+may override this option by setting the tab's <B>-foreground</B> option. The default
+value is <I>black</I>. </DD>
+
+<DT><B>-gap <I>size</I></B> </DT>
+<DD>Sets the gap (in pixels) between tabs. The default
+value is <I>2</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of widget. If <I>pixels</I>
+is 0, then the height of the widget will be calculated based on the size
+the tabs and their pages. The default is <I>0</I>. </DD>
+
+<DT><B>-highlightbackground <I>color</I></B> </DT>
+<DD>Sets
+the color to display in the traversal highlight region when the tabset
+does not have the input focus. </DD>
+
+<DT><B>-highlightcolor <I>color</I></B> </DT>
+<DD>Sets the color to
+use for the traversal highlight rectangle that is drawn around the widget
+when it has the input focus. The default is <I>black</I>. </DD>
+
+<DT><B>-highlightthickness <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the highlight rectangle to draw around the outside of
+ the widget when it has the input focus. <I>Pixels</I> is a non-negative value
+and may have any of the forms acceptable to <B>Tk_GetPixels</B>. If the value is
+zero, no focus highlight is drawn around the widget. The default is <I>2</I>. </DD>
+
+<DT><B>-pageheight
+<I>pixels</I></B> </DT>
+<DD>Sets the requested height of the page. The page is the area under
+the tab used to display the page contents. If <I>pixels</I> is <I>0</I>, the maximum
+height of all embedded tab windows is used. The default is <I>0</I>. </DD>
+
+<DT><B>-pagewidth
+<I>pixels</I></B> </DT>
+<DD>Sets the requested width of the page. The page is the area under
+the tab used to display the page contents. If <I>pixels</I> is <I>0</I>, the maximum
+width of all embedded tab windows is used. The default is <I>0</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B>
+</DT>
+<DD>Specifies the 3-D effect for the tabset widget. <I>Relief</I> specifies how the
+tabset should appear relative to widget that it is packed into; for example,
+<I>raised</I> means the tabset should appear to protrude. The default is <I>sunken</I>.
+</DD>
+
+<DT><B>-rotate <I>theta</I></B> </DT>
+<DD>Specifies the degrees to rotate text in tab labels. <I>Theta</I> is
+a real value representing the number of degrees to rotate the tick labels.
+ The default is <I>0.0</I> degrees. </DD>
+
+<DT><B>-samewidth <I>boolean</I></B> </DT>
+<DD>Indicates if each tab should
+be the same width. If true, each tab will be as wide as the widest tab.
+ The default is <I>no</I>. </DD>
+
+<DT><B>-scrollcommand <I>string</I></B> </DT>
+<DD>Specifies the prefix for a command
+for communicating with scrollbars. Whenever the view in the widget's window
+ changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed. </DD>
+
+<DT><B>-scrollincrement <I>pixels</I></B> </DT>
+<DD>Sets the smallest number
+of pixels to scroll the tabs. If <I>pixels</I> is greater than 0, this sets the
+units for scrolling (e.g., when you the change the view by clicking on
+the left and right arrows of a scrollbar). </DD>
+
+<DT><B>-selectbackground <I>color</I></B> </DT>
+<DD>Sets the
+color to use when displaying background of the selected tab. Individual
+tabs can override this option by setting the tab's <B>-selectbackground</B> option.
+</DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the raised 3-D border to draw
+around the label of the selected tab. <I>Pixels</I> must be a non-negative value.
+The default value is <I>1</I>. </DD>
+
+<DT><B>-selectcommand <I>string</I></B> </DT>
+<DD>Specifies a default Tcl script
+to be associated with tabs. This command is typically invoked when left
+mouse button is released over the tab. Individual tabs may override this
+with the tab's <B>-command</B> option. The default value is <I>""</I>. </DD>
+
+<DT><B>-selectforeground <I>color<B>
+</B></I></B></DT>
+<DD>Sets the default color of the selected tab's text label. Individual tabs
+can override this option by setting the tab's <B>-selectforeground</B> option. The
+default value is <I>black</I>. </DD>
+
+<DT><B>-selectpad <I>pixels<B> </B></I></B></DT>
+<DD>Specifies extra padding to be displayed
+around the selected tab. The default value is <I>3</I>. </DD>
+
+<DT><B>-side <I>side<B> </B></I></B></DT>
+<DD>Specifies the
+side of the widget to place tabs. The following values are valid for <I>side</I>.
+The default value is <I>top</I>. <blockquote></DD>
+
+<DT><I>top</I> </DT>
+<DD>Tabs are drawn along the top. </DD>
+
+<DT><I>left</I> </DT>
+<DD>Tabs are
+drawn along the left side. </DD>
+
+<DT><I>right</I> </DT>
+<DD>Tabs are drawn along the right side. </DD>
+
+<DT><I>both</I>
+</DT>
+<DD>Tabs are drawn along the bottom side. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-slant <I>slant</I></B> </DT>
+<DD>Specifies if the tabs
+should be slanted 45 degrees on the left and/or right sides. The following
+values are valid for <I>slant</I>. The default is <I>none</I>. <blockquote></DD>
+
+<DT><I>none</I> </DT>
+<DD>Tabs are drawn as
+a rectangle. </DD>
+
+<DT><I>left</I> </DT>
+<DD>The left side of the tab is slanted. </DD>
+
+<DT><I>right</I> </DT>
+<DD>The right
+side of the tab is slanted. </DD>
+
+<DT><I>both</I> </DT>
+<DD>Boths sides of the tab are slanted. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-tabbackground
+<I>color</I></B> </DT>
+<DD>Sets the default background color of tabs. Individual tabs can override
+this option by setting the tab's <B>-background</B> option. </DD>
+
+<DT><B>-tabborderwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the 3-D border around the outside edge of the tab. The
+<B>-tabrelief</B> option determines how the border is to be drawn. The default
+is <I>2</I>. </DD>
+
+<DT><B>-tabforeground <I>color</I></B> </DT>
+<DD>Specifies the color to use when displaying a tab's
+label. Individual tabs can override this option by setting the tab's <B>-foreground</B>
+option. </DD>
+
+<DT><B>-tabrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect for both tabs and folders.
+ <I>Relief</I> specifies how the tabs should appear relative to background of
+the widget; for example, <I>raised</I> means the tab should appear to protrude.
+ The default is <I>raised</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides information used when
+moving the focus from window to window via keyboard traversal (e.g., Tab
+and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window should be skipped
+entirely during keyboard traversal. <I>1</I> means that the this window should
+always receive the input focus. An empty value means that the traversal
+scripts decide whether to focus on the window. The default is <I>1</I>. </DD>
+
+<DT><B>-textside
+<I>side<B> </B></I></B></DT>
+<DD>If both images and text are specified for a tab, this option determines
+on which side of the tab the text is to be displayed. The valid sides are
+<I>left</I>, <I>right</I>, <I>top</I>, and <I>bottom</I>. The default value is <I>left</I>. </DD>
+
+<DT><B>-tiers <I>number<B> </B></I></B></DT>
+<DD>Specifies
+the maximum number of tiers to use to display the tabs. The default value
+is <I>1</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies a tiled background for the widget. If <I>image</I>
+isn't <I>""</I>, the background is tiled using <I>image</I>. Otherwise, the normal background
+color is drawn (see the <B>-background</B> option). <I>Image</I> must be an image created
+using the Tk <B>image</B> command. The default is <I>""</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Specifies the
+requested width of the widget. If <I>pixels</I> is 0, then the width of the widget
+will be calculated based on the size the tabs and their pages. The default
+is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>delete <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Deletes one or more tabs from the tabset.
+ <I>First</I> and <I>last</I> are the first and last indices, defining a range of tabs
+to be deleted. If <I>last</I> isn't specified, then only the tab at <I>first</I> is
+deleted. </DD>
+
+<DT><I>pathName <B>focus <I>index</I></B></I> </DT>
+<DD>Designates a tab to get the widget's focus.
+ This tab is displayed with a dashed line around its label. </DD>
+
+<DT><I>pathName <B>get</B></I>
+<I>index</I> </DT>
+<DD>Returns the name of the tab. The value of <I>index</I> may be in any form
+described in the section <FONT SIZE=-1><B>TABSET INDICES</B></FONT>
+ </DD>
+
+<DT><I>pathName <B>index</B></I> ?<I>flag</I>? <I>string</I>
+</DT>
+<DD>Returns the node id of the tab specified by <I>string</I>. If <I>flag</I> is <B>-name</B>, then
+<I>string</I> is the name of a tab. If <I>flag</I> is <B>-index</B>, <I>string</I> is an index such as
+"active" or "focus". If <I>flag</I> isn't specified, it defaults to <B>-index</B>. </DD>
+
+<DT><I>pathName
+<B>insert</B></I> <I>position <I>name</I></I> ?<I>option value</I>?... </DT>
+<DD>Inserts new tabs into the tabset. Tabs
+are inserted just before the tab given by <I>position</I>. <I>Position</I> may be either
+a number, indicating where in the list the new tab should be added, or
+<B>end</B>, indicating that the new tab is to be added the end of the list. <I>Name</I>
+is the symbolic name of the tab. <I>Be careful not to use a number. Otherwise
+the tabset will confuse it with tab indices</I>. Returns a list of indices
+for all the new tabs. </DD>
+
+<DT><I>pathName <B>invoke <I>index</I></B></I> </DT>
+<DD>Selects the tab given by <I>index</I>,
+maps the tab's embedded widget, and invokes the Tcl command associated
+with the tab, if there is one. The return value is the return value
+from the Tcl command, or an empty string if there is no command associated
+with the tab. This command is ignored if the tab's state (see the <B>-state</B>
+option) is disabled. </DD>
+
+<DT><I>pathName <B>move</B></I> <I>index</I> <B>before</B>|<B>after</B> <I>index</I> </DT>
+<DD>Moves the tab
+<I>index</I> to a new position in the tabset. </DD>
+
+<DT><I>pathName <B>nearest</B></I> <I>x</I> <I>y</I> </DT>
+<DD>Returns the
+name of the tab nearest to given X-Y screen coordinate. </DD>
+
+<DT><I>pathName <B>scan</B></I> <I>option
+args</I> </DT>
+<DD>This command implements scanning on tabsets. It has two forms, depending
+on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>scan mark <I>x y</I></B></I> </DT>
+<DD>Records <I>x</I> and <I>y</I> and the current view
+in the tabset window; used with later <B>scan dragto</B> commands. Typically this
+command is associated with a mouse button press in the widget. It returns
+an empty string. </DD>
+
+<DT><I>pathName <B>scan dragto <I>x y</I></B></I>. </DT>
+<DD>This command computes the difference
+between its <I>x</I> and <I>y</I> arguments and the <I>x</I> and <I>y</I> arguments to the last <B>scan
+mark</B> command for the widget. It then adjusts the view by 10 times the difference
+in coordinates. This command is typically associated with mouse motion
+events in the widget, to produce the effect of dragging the list at high
+speed through the window. The return value is an empty string. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>see <I>index</I></B></I> </DT>
+<DD>Scrolls the tabset so that the tab <I>index</I> is visible in the widget's
+window. </DD>
+
+<DT><I>pathName <B>size</B></I> </DT>
+<DD>Returns the number of tabs in the tabset. </DD>
+
+<DT><I>pathName
+<B>tab <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>See the <FONT SIZE=-1><B>TAB OPERATIONS</B></FONT>
+ section below. </DD>
+
+<DT><I>pathName <B>view
+<I>args</I></B></I> </DT>
+<DD>This command queries or changes the position of the tabset in the
+widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName <B>view</B></I>
+</DT>
+<DD>Returns a list of two numbers between 0.0 and 1.0 that describe the amount
+and position of the tabset that is visible in the window. For example,
+if <I>view</I> is "0.2 0.6", 20% of the tabset's text is off-screen to the left, 40%
+is visible in the window, and 40% of the tabset is off-screen to the right.
+ These are the same values passed to scrollbars via the <B>-scrollcommand</B> option.
+</DD>
+
+<DT><I>pathName <B>view moveto<I> fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so that <I>fraction</I>
+of the total width of the tabset text is off-screen to the left. <I>fraction</I>
+must be a number between 0.0 and 1.0. </DD>
+
+<DT><I>pathName <B>view scroll <I>number what</I></B></I> </DT>
+<DD>This
+command shifts the view in the window (left/top or right/bottom) according
+to <I>number</I> and <I>what</I>. <I>Number</I> must be an integer. <I>What</I> must be either <B>units</B>
+or <B>pages</B> or an abbreviation of these. If <I>what</I> is <B>units</B>, the view adjusts
+left or right by <I>number</I> scroll units (see the <B>-scrollincrement</B> option).
+; if it is <B>pages</B> then the view adjusts by <I>number</I> widget windows. If <I>number</I>
+is negative then tabs farther to the left become visible; if it is positive
+then tabs farther to the right become visible. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect8" HREF="#toc8">Tab Operations</A></H2>
+
+<DL>
+
+<DT><I>pathName <B>tab
+cget</B></I> <I>nameOrIndex</I> <I>option</I> </DT>
+<DD>Returns the current value of the configuration
+option given by <I>option</I>. <I>Option</I> may have any of the values accepted by the
+<B>tab configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>tab configure</B></I> <I>nameOrIndex</I>
+?<I>nameOrIndex</I>...? <I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration
+options of one or more tabs. If no <I>option</I> is specified, this operation returns
+a list describing all the available options for <I>nameOrIndex</I>. <I>NameOrIndex</I>
+can be either the name of a tab or its index. Names of tabs take precedence
+over their indices. That means a tab named <I>focus</I> is picked over the "focus"
+tab. </DD>
+</DL>
+<P>
+If <I>option</I> is specified, but not <I>value</I>, then a list describing the one
+named option is returned. If one or more <I>option-value</I> pairs are specified,
+then each named tab (specified by <I>nameOrIndex</I>) will have its configurations
+option(s) set the given value(s). In this last case, the empty string is
+returned. <I>Option</I> and <I>value</I> are described below: <blockquote>
+<DL>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets
+the active background color for <I>nameOrIndex</I>. A tab is active when the mouse
+is positioned over it or set by the <B>activate</B> operation. This overrides
+the widget's <B>-activebackground</B> option. </DD>
+
+<DT><B>-activeforeground <I>color</I></B> </DT>
+<DD>Sets the default
+active foreground color <I>nameOrIndex</I>. A tab is "active" when the mouse is
+positioned over it or set by the <B>activate</B> operation. Individual tabs may
+override this option by setting the tab's <B>-activeforeground</B> option. </DD>
+
+<DT><B>-anchor
+<I>anchor</I></B> </DT>
+<DD>Anchors the tab's embedded widget to a particular edge of the folder.
+This option has effect only if the space in the folder surrounding the
+ embedded widget is larger than the widget itself. <I>Anchor</I> specifies how
+the widget will be positioned in the extra space. For example, if <I>anchor</I>
+is <I>center</I> then the window is centered in the folder ; if <I>anchor</I> is <I>w</I> then
+the window will be aligned with the leftmost edge of the folder. The default
+value is <I>center</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background color for <I>nameOrIndex</I>.
+ Setting this option overides the widget's <B>-tabbackground</B> option. </DD>
+
+<DT><B>-bindtags
+<I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for this tab. <I>TagList</I> is a list of binding
+tag names. The tags and their order will determine how commands for events
+in tabs are invoked. Each tag in the list matching the event sequence
+will have its Tcl command executed. Implicitly the name of the tab is
+always the first tag in the list. The default value is <I>all</I>. </DD>
+
+<DT><B>-command <I>string</I></B>
+</DT>
+<DD>Specifies a Tcl script to be associated with <I>nameOrIndex</I>. This command
+is typically invoked when left mouse button is released over the tab.
+Setting this option overrides the widget's <B>-selectcommand</B> option. </DD>
+
+<DT><B>-data <I>string</I></B>
+</DT>
+<DD>Specifies a string to be associated with <I>nameOrIndex</I>. This value isn't used
+in the widget code. It may be used in Tcl bindings to associate extra
+data (other than the image or text) with the tab. The default value is <I>""</I>.
+</DD>
+
+<DT><B>-fill <I>fill</I></B> </DT>
+<DD>If the space in the folder surrounding the tab's embedded widget
+is larger than the widget, then <I>fill</I> indicates if the embedded widget
+ should be stretched to occupy the extra space. <I>Fill</I> is either <I>none</I>, <I>x</I>,
+<I>y</I>, <I>both</I>. For example, if <I>fill</I> is <I>x</I>, then the widget is stretched horizontally.
+ If <I>fill</I> is <I>y</I>, the widget is stretched vertically. The default is <I>none</I>.
+</DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Sets the font for the text in tab labels. If <I>fontName</I> is
+not the empty string, this overrides the tabset's <B>-font</B> option. The default
+value is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the color of the label for <I>nameOrIndex</I>.
+ If <I>color</I> is not the empty string, this overrides the widget's <B>-tabforeground</B>
+ option. The default value is <I>""</I>. </DD>
+
+<DT><B>-image <I>imageName</I></B> </DT>
+<DD>Specifies the image to
+be drawn in label for <I>nameOrIndex</I>. If <I>image</I> is <I>""</I>, no image will be drawn.
+ Both text and images may be displayed at the same time in tab labels. The
+default value is <I>""</I>. </DD>
+
+<DT><B>-ipadx <I>pad</I></B> </DT>
+<DD>Sets the padding to the left and right of
+the label. <I>Pad</I> can be a list of one or two screen distances. If <I>pad</I> has
+two elements, the left side of the label is padded by the first distance
+and the right side by the second. If <I>pad</I> has just one distance, both the
+left and right sides are padded evenly. The default value is <I>0</I>. </DD>
+
+<DT><B>-ipady <I>pad</I></B>
+</DT>
+<DD>Sets the padding to the top and bottom of the label. <I>Pad</I> can be a list of
+one or two screen distances. If <I>pad</I> has two elements, the top of the label
+is padded by the first distance and the bottom by the second. If <I>pad</I> has
+just one distance, both the top and bottom sides are padded evenly. The
+default value is <I>0</I>. </DD>
+
+<DT><B>-padx <I>pad</I></B> </DT>
+<DD>Sets the padding around the left and right
+of the embedded widget, if one exists. <I>Pad</I> can be a list of one or two
+screen distances. If <I>pad</I> has two elements, the left side of the widget
+is padded by the first distance and the right side by the second. If <I>pad</I>
+has just one distance, both the left and right sides are padded evenly.
+ The default value is <I>0</I>. </DD>
+
+<DT><B>-pady <I>pad</I></B> </DT>
+<DD>Sets the padding around the top and bottom
+of the embedded widget, if one exists. <I>Pad</I> can be a list of one or two screen
+distances. If <I>pad</I> has two elements, the top of the widget is padded by
+the first distance and the bottom by the second. If <I>pad</I> has just one distance,
+both the top and bottom sides are padded evenly. The default value is <I>0</I>.
+</DD>
+
+<DT><B>-selectbackground <I>color</I></B> </DT>
+<DD>Sets the color to use when displaying background
+of the selected tab. If <I>color</I> is not the empty string, this overrides the
+widget's <B>-selectbackground</B> option. The default value is <I>""</I>. </DD>
+
+<DT><B>-shadow <I>color</I></B> </DT>
+<DD>Sets
+the shadow color for the text in the tab's label. Drop shadows are useful
+when both the foreground and background of the tab have similar color intensities.
+If <I>color</I> is the empty string, no shadow is drawn. The default value is <I>""</I>.
+</DD>
+
+<DT><B>-state <I>state</I></B> </DT>
+<DD>Sets the state of the tab. If <I>state</I> is <I>disable</I> the text of the
+tab is drawn as engraved and operations on the tab (such as <B>invoke</B> and
+<B>tab tearoff</B>) are ignored. The default is <I>normal</I>. </DD>
+
+<DT><B>-stipple <I>bitmap</I></B> </DT>
+<DD>Specifies
+a stipple pattern to use for the background of the folder when the window
+is torn off. <I>Bitmap</I> specifies a bitmap to use as the stipple pattern. The
+default is <I>BLT</I>. </DD>
+
+<DT><B>-text <I>text</I></B> </DT>
+<DD>Specifies the text of the tab's label. The exact
+way the text is drawn may be affected by other options such as <B>-state</B> or
+<B>-rotate</B>. </DD>
+
+<DT><B>-window <I>pathName</I></B> </DT>
+<DD>Specifies the widget to be embedded into the tab.
+ <I>PathName</I> must be a child of the <B>tabset</B> widget. The tabset will "pack"
+and manage the size and placement of <I>pathName</I>. The default value is <I>""</I>.
+</DD>
+
+<DT><B>-windowheight <I>pixels</I></B> </DT>
+<DD>Sets the requested height of the page. The page is
+the area under the tab used to display the page contents. If <I>pixels</I> is
+<I>0</I>, the maximum height of all embedded tab windows is used. The default
+is <I>0</I>. </DD>
+
+<DT><B>-windowwidth <I>pixels</I></B> </DT>
+<DD>Sets the requested width of the page. The page
+is the area under the tab used to display the page contents. If <I>pixels</I>
+is <I>0</I>, the maximum width of all embedded tab windows is used. The default
+is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>tab names</B></I> ?<I>pattern</I>? </DT>
+<DD>Returns the names of all the tabs matching
+the given pattern. If no <I>pattern</I> argument is provided, then all tab names
+are returned. </DD>
+
+<DT><I>pathName <B>tab tearoff <I>index</I></B></I> ?<I>newName</I>? </DT>
+<DD>Reparents the widget
+embedded into <I>index</I>, placing it inside of <I>newName</I>. <I>NewName</I> is either the
+name of an new widget that will contain the embedded widget or the name
+of the <B>tabset</B> widget. It the last case, the embedded widget is put back
+into the folder. <P>
+If no <I>newName</I> argument is provided, then the name of the
+current parent of the embedded widget is returned. </DD>
+</DL>
+
+<H2><A NAME="sect9" HREF="#toc9">Default Bindings</A></H2>
+<P>
+BLT automatically
+generates class bindings that supply tabsets their default behaviors. The
+following event sequences are set by default for tabsets (via the class
+bind tag <I>Tabset</I>):
+<DL>
+
+<DT><B>&lt;ButtonPress-2&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;B2-Motion&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;ButtonRelease-2&gt;</B></DT>
+<DD>Mouse button 2 may
+be used for scanning. If it is pressed and dragged over the tabset, the
+contents of the tabset drag at high speed in the direction the mouse moves.
+</DD>
+
+<DT><B>&lt;KeyPress-Up&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;KeyPress-Down&gt;</B></DT>
+<DD>The up and down arrow keys move the focus to the
+tab immediately above or below the current focus tab. The tab with focus
+is drawn with the a dashed outline around the tab label. </DD>
+
+<DT><B>&lt;KeyPress-Left&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;KeyPress-Right&gt;</B></DT>
+<DD>The
+left and right arrow keys move the focus to the tab immediately to the
+left or right of the current focus tab. The tab with focus is drawn with
+the a dashed outline around the tab label. </DD>
+
+<DT><B>&lt;KeyPress-space&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;KeyPress-Return&gt;</B></DT>
+<DD>The
+space and return keys select the current tab given focus. When a folder
+is selected, it's command is invoked and the embedded widget is mapped.
+</DD>
+</DL>
+<P>
+Each tab, by default, also has a set of bindings (via the tag <I>all</I>). These
+bindings may be reset using the tabset's <B>bind</B> operation.
+<DL>
+
+<DT><B>&lt;Enter&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;Leave&gt;</B></DT>
+<DD>When
+the mouse pointer enters a tab, it is activated (i.e. drawn in its active
+colors) and when the pointer leaves, it is redrawn in its normal colors.
+</DD>
+
+<DT><B>&lt;ButtonRelease-1&gt;</B></DT>
+<DD>Clicking with the left mouse button on a tab causes the tab
+to be selected and its Tcl script (see the <B>-command</B> or <B>-selectcommand</B> options)
+to be invoked. The folder and any embedded widget (if one is specified)
+is automatically mapped. </DD>
+
+<DT><B>&lt;ButtonRelease-3&gt;</B></DT>
+<DD></DD>
+
+<DT><B>&lt;Control-ButtonRelease-1&gt;</B></DT>
+<DD>Clicking on
+the right mouse button (or the left mouse button with the Control key held
+down) tears off the current page into its own toplevel widget. The embedded
+widget is re-packed into a new toplevel and an outline of the widget is
+drawn in the folder. Clicking again (toggling) will reverse this operation
+and replace the page back in the folder. </DD>
+</DL>
+
+<H2><A NAME="sect10" HREF="#toc10">Bind Tags</A></H2>
+You can bind commands
+to tabs that are triggered when a particular event sequence occurs in them,
+much like canvas items in Tk's canvas widget. Not all event sequences are
+valid. The only binding events that may be specified are those related
+to the mouse and keyboard (such as <B>Enter</B>, <B>Leave</B>, <B>ButtonPress</B>, <B>Motion</B>,
+and <B>KeyPress</B>). <P>
+It is possible for multiple bindings to match a particular
+event. This could occur, for example, if one binding is associated with
+the tab name and another is associated with the tab's tags (see the <B>-bindtags</B>
+option). When this occurs, all the matching bindings are invoked. A binding
+associated with the tab name is invoked first, followed by one binding
+for each of the tab's bindtags. If there are multiple matching bindings
+for a single tag, then only the most specific binding is invoked. A continue
+command in a binding script terminates that script, and a break command
+ terminates that script and skips any remaining scripts for the event,
+ just as for the bind command. <P>
+The <B>-bindtags</B> option for tabs controls addition
+tag names that can be matched. Implicitly the first tag for each tab is
+its name. Setting the value of the <B>-bindtags</B> option doesn't change this.
+<H2><A NAME="sect11" HREF="#toc11">Keywords</A></H2>
+tabset,
+widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Tabset Indices</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Tabset Operations</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Tab Operations</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Default Bindings</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Bind Tags</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/tile.html b/blt/html/tile.html
new file mode 100644
index 00000000000..48f79e71156
--- /dev/null
+++ b/blt/html/tile.html
@@ -0,0 +1,100 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>tile(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+tile - Tiling versions of Tk widgets
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<P>
+<B>tile::button
+<I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::checkbutton <I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::frame
+<I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::label <I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::radiobutton
+<I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::scrollbar <I>pathName</I></B> <I>option value</I>... <P>
+<B>tile::toplevel
+<I>pathName</I></B> <I>option value</I>... <P>
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The tile widgets let you create textured
+backgrounds. The texture is a Tk image which is tiled over the entire background
+of the widget.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+With the advent of Tk 4.0, images are now easy
+to create and use in applications. Images add interest to applications
+and they convey more information. But one area where Tk hasn't taken advantage
+of images is using images as textures for widgets. Since tiling is a standard
+feature of windowing systems, it's very easy to use images as textures.
+<P>
+The tile widgets take the standard Tk 4.0 widgets and add tiling configuration
+options to them. Textures are specified by the name of the image you wish
+to be tiled across the background of the widget.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+To add tiling
+to a widget, you simply create an image using Tk's <B>image</B> command and use
+the image name as the value for the <B>-tile</B> configuration option of the widget.
+<BR>
+<CODE>image create photo my_texture -file tan_paper.gif<BR>
+blt::tile::frame .f -tile my_texture<BR>
+</CODE><P>The image <I>my_texture</I> is added to the frame. If <I>my_texture</I> is updated,
+so will the widget background. <BR>
+<CODE>image create photo my_texture -file rain.gif<BR>
+</CODE><P>The tile widget commands reside in the "blt::tile" namespace, so as not
+to collide with the normal Tk widgets. An easy way to add tiling to existing
+programs is to import the tile widget commands into the global namespace.
+<BR>
+<CODE>image create photo my_texture -file tan_paper.gif<BR>
+namespace import -force blt::tile::*<BR>
+frame .f -tile my_texture<BR>
+</CODE><P>To use one image for all texturing, you can use the "Tile" option class
+name to specify the same image for all tile widgets. <BR>
+<CODE>image create photo my_texture -file tan_paper.gif<BR>
+option add *Tile my_texture<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Options</A></H2>
+The following configurations options are added to the widgets. If
+a <B>-tile<B> or <B>-activetile</B></B></B> option is specified, it overrides the background color
+of the widget.
+<DL>
+
+<DT><B>-activetile <I>image</I></B> </DT>
+<DD>Specifies a textured background to display
+when the widget is active. This option is available for the <B>tilebutton</B>,
+<B>tilecheckbutton</B>, <B>tileradiobutton</B>, and <B>tilescrollbar</B> widgets. <I>Image</I> is the
+name an image created using Tk's <B>image</B> command. The background of the widget
+is tiled with <I>image</I>. If <I>image</I> is <I>""</I>, then the active background color is
+displayed. The default is <I>""</I>. </DD>
+
+<DT><B>-tile <I>image</I></B> </DT>
+<DD>Specifies a textured background
+to display for the widget. <I>Image</I> is the name an image created using Tk's
+<B>image</B> command. The background of the widget is tiled with <I>image</I>. If <I>image</I>
+is <I>""</I>, then the normal background color is displayed. The default is <I>""</I>.
+</DD>
+</DL>
+
+<H2><A NAME="sect6" HREF="#toc6">Keywords</A></H2>
+tile, texture, button, label, radiobutton, checkbutton, scrollbar,
+frame, toplevel <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Options</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/tree.html b/blt/html/tree.html
new file mode 100644
index 00000000000..0d4e020ab2a
--- /dev/null
+++ b/blt/html/tree.html
@@ -0,0 +1,930 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>tree(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+tree - Create and manage tree data objects.
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>blt::tree
+create </B>?<I>treeName</I>? <P>
+<B>blt::tree destroy</B> <I>treeName</I>... <P>
+<B>blt::tree names</B> ?<I>pattern</I>?
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>tree</B> command creates tree data objects. A <I>tree object</I> is
+general ordered tree of nodes. Each node has both a label and a key-value
+list of data. Data can be heterogeneous, since nodes do not have to contain
+the same data keys. It is associated with a Tcl command that you can use
+to access and modify the its structure and data. Tree objects can also be
+managed via a C API.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+<P>
+
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+<P>
+
+<H2><A NAME="sect5" HREF="#toc5">Syntax</A></H2>
+
+<DL>
+
+<DT><B>tree create</B> ?<I>treeName</I>?
+ </DT>
+<DD>Creates a new tree object. The name of the new tree is returned. If no
+<I>treeName</I> argument is present, then the name of the tree is automatically
+generated in the form "<I>tree0</I>", "<I>tree1</I>", etc. If the substring "<I>#auto</I>" is
+found in <I>treeName</I>, it is automatically substituted by a generated name.
+ For example, the name <I>.foo.#auto.bar</I> will be translated to <I>.foo.tree0.bar</I>. <P>
+A
+new Tcl command (by the same name as the tree) is also created. Another
+Tcl command or tree object can not already exist as <I>treeName</I>. If the Tcl
+command is deleted, the tree will also be freed. The new tree will contain
+just the root node. Trees are by default, created in the current namespace,
+not the global namespace, unless <I>treeName</I> contains a namespace qualifier,
+such as "<I>fred::myTree</I>". </DD>
+
+<DT><B>tree destroy</B> <I>treeName</I>... </DT>
+<DD>Releases one of more trees.
+ The Tcl command associated with <I>treeName</I> is also removed. Trees are reference
+counted. The internal tree data object isn't destroyed until no one else
+is using the tree. </DD>
+
+<DT><B>tree names </B>?<I>pattern</I>? </DT>
+<DD>Returns the names of all tree objects.
+ if a <I>pattern</I> argument is given, then the only those trees whose name matches
+pattern will be listed. </DD>
+</DL>
+
+<H2><A NAME="sect6" HREF="#toc6">Node IDs and Tags</A></H2>
+Nodes in a tree object may be referred
+in either of two ways: by id or by tag. Each node has a unique serial number
+or id that is assigned to that node when it's created. The id of an node
+never changes and id numbers are not re-used. <P>
+A node may also have any number
+of tags associated with it. A tag is just a string of characters, and it
+may take any form except that of an integer. For example, "<I>x123</I>" is valid,
+but "<I>123</I>" isn't. The same tag may be associated with many different nodes.
+This is commonly done to group nodes in various interesting ways. <P>
+There
+are two built-in tags: The tag <B>all</B> is implicitly associated with every node
+in the tree. It may be used to invoke operations on all the nodes in the
+tree. The tag <B>root</B> is managed automatically by the tree object. It applies
+to the node current set as root. <P>
+When specifying nodes in tree object commands,
+if the specifier is an integer then it is assumed to refer to the single
+node with that id. If the specifier is not an integer, then it is assumed
+to refer to all of the nodes in the tree that have a tag matching the specifier.
+ The symbol <I>node</I> is used below to indicate that an argument specifies either
+an id that selects a single node or a tag that selects zero or more nodes.
+ Many tree commands only operate on a single node at a time; if <I>node</I> is
+specified in a way that names multiple items, then an error "refers to
+more than one node" is generated.
+<H2><A NAME="sect7" HREF="#toc7">Node Modifiers</A></H2>
+You can also specify node
+in relation to another node by appending one or more modifiers to the node
+id or tag. A modifier refers to a node in relation to the specified node.
+ For example, "<I>root-&gt;firstchild</I>" selects the first subtree of the root node.
+<P>
+The following modifiers are available: <blockquote>
+<DL>
+
+<DT><B>firstchild</B> </DT>
+<DD>Selects the first child
+of the node. </DD>
+
+<DT><B>lastchild</B> </DT>
+<DD>Selects the last child of the node. </DD>
+
+<DT><B>next</B> </DT>
+<DD>Selects
+the next node in preorder to the node. </DD>
+
+<DT><B>nextsibling</B> </DT>
+<DD>Selects the next sibling
+of the node. </DD>
+
+<DT><B>parent</B> </DT>
+<DD>Selects the parent of the node. </DD>
+
+<DT><B>previous</B> </DT>
+<DD>Selects
+the previous node in preorder to the node. </DD>
+
+<DT><B>prevsibling</B> </DT>
+<DD>Selects the previous
+sibling of the node. </DD>
+
+<DT>"<I>label</I>" </DT>
+<DD>Selects the node whose label is <I>label</I>. Enclosing
+<I>label</I> in quotes indicates to always search for a node by its label (for
+example, even if the node is labeled "parent"). </DD>
+</DL>
+</blockquote>
+<P>
+It's an error the node can't
+be found. For example, <B>lastchild</B> and <B>firstchild</B> will generate errors if
+the node has no children. The exception to this is the <B>index</B> operation.
+You can use <B>index</B> to test if a modifier is valid.
+<H2><A NAME="sect8" HREF="#toc8">Tree Operations</A></H2>
+Once you
+create a tree object, you can use its Tcl command to query or modify it.
+ The general form is <BR>
+<P>
+<CODE><I>treeName</I> <I>operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for trees are listed below.
+<DL>
+
+<DT><I>treeName</I> <B>ancestor</B>
+<I>node1</I> <I>node2</I> </DT>
+<DD>Returns the mutual ancestor of the two nodes <I>node1</I> and <I>node2</I>.
+ The ancestor can be one of the two nodes. For example, if <I>node1</I> and <I>node2</I>
+are the same nodes, their ancestor is <I>node1</I>. </DD>
+
+<DT><I>treeName</I> <B>apply</B> <I>node</I> ?<I>switches</I>?
+</DT>
+<DD>Runs commands for all nodes matching the criteria given by <I>switches</I> for
+the subtree designated by <I>node</I>. By default all nodes match, but you can
+set switches to narrow the match. This operation differs from <B>find</B> in two
+ways: 1) Tcl commands can be invoked both pre- and post-traversal of a node
+and 2) the tree is always traversed in depth first order. <P>
+The <B>-exact</B>, <B>-glob</B>,
+ and <B>-regexp</B> switches indicate both what kind of pattern matching to perform
+and the pattern. Pattern matching is done, by default, against each node's
+label. But if the <B>-path</B> switch is present, it will match the full path of
+the node (a list containing the labels of the node's ancestors too). If
+the <B>-key</B> switch is used, it designates the data field to be matched. <P>
+The
+valid switches are listed below: <blockquote></DD>
+
+<DT><B>-depth</B> <I>number</I> </DT>
+<DD>Descend at most <I>number</I> (a
+non-negative integer) levels If <I>number</I> is <I>1</I> this means only apply the tests
+to the children of <I>node</I>. </DD>
+
+<DT><B>-exact</B> <I>string</I> </DT>
+<DD>Matches each node using <I>string</I>. The
+node must match <I>string</I> exactly. </DD>
+
+<DT><B>-glob</B> <I>string</I> </DT>
+<DD>Test each node to <I>string</I> using
+global pattern matching. Matching is done in a fashion similar to that
+used by the C-shell. </DD>
+
+<DT><B>-invert</B> </DT>
+<DD>Select non-matching nodes. Any node that <I>doesn't</I>
+match the given criteria will be selected. </DD>
+
+<DT><B>-key</B> <I>key</I> </DT>
+<DD>If pattern matching is
+selected (using the <B>-exact</B>, <B>-glob</B>, or <B>-regexp</B> switches), compare the values
+of the data field keyed by <I>key</I> instead of the node's label. If no pattern
+matching switches are set, then any node with this data key will match.
+</DD>
+
+<DT><B>-leafonly</B> </DT>
+<DD>Only test nodes with no children. </DD>
+
+<DT><B>-nocase</B> </DT>
+<DD>Ignore case when matching
+patterns. </DD>
+
+<DT><B>-path</B> </DT>
+<DD>Use the node's full path when comparing nodes. </DD>
+
+<DT><B>-precommand</B> <I>command</I>
+</DT>
+<DD>Invoke <I>command</I> for each matching node. Before <I>command</I> is invoked, the id
+of the node is appended. You can control processing by the return value
+of <I>command</I>. If <I>command</I> generates an error, processing stops and the <B>find</B>
+operation returns an error. But if <I>command</I> returns <B>break</B>, then processing
+stops, no error is generated. If <I>command</I> returns <B>continue</B>, then processing
+stops on that subtree and continues on the next. </DD>
+
+<DT><B>-postcommand</B> <I>command</I> </DT>
+<DD>Invoke
+<I>command</I> for each matching node. Before <I>command</I> is invoked, the id of the
+node is appended. You can control processing by the return value of <I>command</I>.
+ If <I>command</I> generates an error, processing stops and the <B>find</B> operation
+ returns an error. But if <I>command</I> returns <B>break</B>, then processing stops,
+no error is generated. If <I>command</I> returns <B>continue</B>, then processing stops
+on that subtree and continues on the next. </DD>
+
+<DT><B>-regexp</B> <I>string</I> </DT>
+<DD>Test each node
+using <I>string</I> as a regular expression pattern. </DD>
+
+<DT><B>-tag</B> <I>string</I> </DT>
+<DD>Only test nodes
+that have the tag <I>string</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>attach</B> <I>treeObject</I> </DT>
+<DD>Attaches to an existing
+tree object <I>treeObject</I>. This is for cases where the tree object was previously
+created via the C API. The current tree associated with <I>treeName</I> is discarded.
+ In addition, the current set of tags, notifier events, and traces are
+removed. </DD>
+
+<DT><I>treeName</I> <B>children</B> <I>node</I> </DT>
+<DD>Returns a list of children for <I>node</I>. If
+<I>node</I> is a leaf, then an empty string is returned. </DD>
+
+<DT><I>treeName</I> <B>copy</B> <I>srcNode</I>
+?<I>destTree</I>? <I>destNode</I> ?<I>switches</I>? </DT>
+<DD>Copies <I>srcNode</I> into <I>destNode</I>. Both nodes
+<I>srcNode</I> and <I>destNode</I> must already exist. If <I>destTree</I> argument is present,
+it indicates the name of the destination tree. By default both the source
+and destination trees are the same. The valid <I>switches</I> are listed below:
+<blockquote></DD>
+
+<DT><B>-overwrite</B> </DT>
+<DD>Overwrite nodes that already exist. Normally nodes are always
+created, even if there already exists a node by the same name. This switch
+indicates to add or overwrite the node's data fields. </DD>
+
+<DT><B>-recurse</B> </DT>
+<DD>Recursively
+copy all the subtrees of <I>srcNode</I> as well. In this case, <I>srcNode</I> can't be
+an ancestor of <I>destNode</I> as it would result in a cyclic copy. </DD>
+
+<DT><B>-tags</B> </DT>
+<DD>Copy tag
+inforation. Normally the following node is copied: its label and data
+fields. This indicates to copy tags as well. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>degree</B> <I>node</I> </DT>
+<DD>Returns
+the number of children of <I>node</I>. </DD>
+
+<DT><I>treeName</I> <B>delete</B> <I>node</I>... </DT>
+<DD>Recursively deletes
+one or more nodes from the tree. The node and all its descendants are
+removed. The one exception is the root node. In this case, only its descendants
+are removed. The root node will remain. Any tags or traces on the nodes
+are released. </DD>
+
+<DT><I>treeName</I> <B>depth</B> <I>node</I> </DT>
+<DD>Returns the depth of the node. The depth
+is the number of steps from the node to the root of the tree. The depth
+of the root node is <I>0</I>. </DD>
+
+<DT><I>treeName</I> <B>dump</B> <I>node</I> </DT>
+<DD>Returns a list of the paths and
+respective data for <I>node</I> and its descendants. The subtree designated by
+<I>node</I> is traversed returning the following information for each node: 1)
+the node's path relative to <I>node</I>, 2) a sublist key value pairs representing
+the node's data fields, and 3) a sublist of tags. This list returned can
+be used later to copy or restore the tree with the <B>restore</B> operation. </DD>
+
+<DT><I>treeName</I>
+<B>dumpfile</B> <I>node</I> <I>fileName</I> </DT>
+<DD>Writes a list of the paths and respective data for
+<I>node</I> and its descendants to the given file <I>fileName</I>. The subtree designated
+by <I>node</I> is traversed returning the following information for each node:
+1) the node's path relative to <I>node</I>, 2) a sublist key value pairs representing
+the node's data fields, and 3) a sublist of tags. This list returned can
+be used later to copy or restore the tree with the <B>restore</B> operation. </DD>
+
+<DT><I>treeName</I>
+<B>exists</B> <I>node</I> ?<I>key</I>? </DT>
+<DD>Indicates if <I>node</I> exists in the tree. If a <I>key</I> argument
+is present then the command also indicates if the named data field exists.
+</DD>
+
+<DT><I>treeName</I> <B>find</B> <I>node</I> ?<I>switches</I>? </DT>
+<DD>Finds for all nodes matching the criteria
+given by <I>switches</I> for the subtree designated by <I>node</I>. A list of the selected
+ nodes is returned. By default all nodes match, but you can set switches
+to narrow the match. <P>
+The <B>-exact</B>, <B>-glob</B>, and <B>-regexp</B> switches indicate both
+what kind of pattern matching to perform and the pattern. Pattern matching
+is done, by default, against each node's label. But if the <B>-path</B> switch is
+present, it will match the full path of the node. If the <B>-key</B> switch is
+used, it designates the data field to be matched. <P>
+The order in which
+the nodes are traversed is controlled by the <B>-order</B> switch. The possible
+orderings are <B>preorder</B>, <B>postorder</B>, <B>inorder</B>, and <B>breadthfirst</B>. The default
+is <B>postorder</B>. <P>
+The valid switches are listed below: <blockquote></DD>
+
+<DT><B>-addtag</B> <I>string</I> </DT>
+<DD>Add the
+tag <I>string</I> to each selected node. </DD>
+
+<DT><B>-count</B> <I>number</I> </DT>
+<DD>Stop processing after <I>number</I>
+(a positive integer) matches. </DD>
+
+<DT><B>-depth</B> <I>number</I> </DT>
+<DD>Descend at most <I>number</I> (a non-negative
+integer) levels If <I>number</I> is <I>1</I> this means only apply the tests to the children
+of <I>node</I>. </DD>
+
+<DT><B>-exact</B> <I>string</I> </DT>
+<DD>Matches each node using <I>string</I>. The node must match
+<I>string</I> exactly. </DD>
+
+<DT><B>-exec</B> <I>command</I> </DT>
+<DD>Invoke <I>command</I> for each matching node. Before
+<I>command</I> is invoked, the id of the node is appended. You can control processing
+by the return value of <I>command</I>. If <I>command</I> generates an error, processing
+stops and the <B>find</B> operation returns an error. But if <I>command</I> returns
+<B>break</B>, then processing stops, no error is generated. If <I>command</I> returns
+ <B>continue</B>, then processing stops on that subtree and continues on the next.
+</DD>
+
+<DT><B>-glob</B> <I>string</I> </DT>
+<DD>Test each node to <I>string</I> using global pattern matching. Matching
+is done in a fashion similar to that used by the C-shell. </DD>
+
+<DT><B>-invert</B> </DT>
+<DD>Select non-matching
+nodes. Any node that <I>doesn't</I> match the given criteria will be selected. </DD>
+
+<DT><B>-key</B>
+<I>key</I> </DT>
+<DD>If pattern matching is selected (using the <B>-exact</B>, <B>-glob</B>, or <B>-regexp</B> switches),
+compare the values of the data field keyed by <I>key</I> instead of the node's
+label. If no pattern matching switches are set, then any node with this
+data key will match. </DD>
+
+<DT><B>-leafonly</B> </DT>
+<DD>Only test nodes with no children. </DD>
+
+<DT><B>-nocase</B> </DT>
+<DD>Ignore
+case when matching patterns. </DD>
+
+<DT><B>-order</B> <I>string</I> </DT>
+<DD>Traverse the tree and process
+nodes according to <I>string</I>. <I>String</I> can be one of the following: <blockquote></DD>
+
+<DT><B>breadthfirst</B>
+ </DT>
+<DD>Process the node and the subtrees at each sucessive level. Each node on
+a level is processed before going to the next level. </DD>
+
+<DT><B>inorder</B> </DT>
+<DD>Recursively
+process the nodes of the first subtree, the node itself, and any the remaining
+subtrees. </DD>
+
+<DT><B>postorder</B> </DT>
+<DD>Recursively process all subtrees before the node. </DD>
+
+<DT><B>preorder</B>
+ </DT>
+<DD>Recursively process the node first, then any subtrees. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-path</B> </DT>
+<DD>Use the node's
+full path when comparing nodes. </DD>
+
+<DT><B>-regexp</B> <I>string</I> </DT>
+<DD>Test each node using <I>string</I>
+as a regular expression pattern. </DD>
+
+<DT><B>-tag</B> <I>string</I> </DT>
+<DD>Only test nodes that have the
+tag <I>string</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>findchild</B> <I>node</I> <I>label</I> </DT>
+<DD>Searches for a child node Ilabel
+in <I>node</I>. The id of the child node is returned if found. Otherwise <I>-1</I> is
+returned. </DD>
+
+<DT><I>treeName</I> <B>firstchild</B> <I>node</I> </DT>
+<DD>Returns the id of the first child in
+the <I>node</I>'s list of subtrees. If <I>node</I> is a leaf (has no children), then
+<I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I> <B>get</B> <I>node</I> ?<I>key</I>? ?<I>defaultValue</I>? </DT>
+<DD>Returns a list of
+key-value pairs of data for the node. If <I>key</I> is present, then onlyx the
+value for that particular data field is returned. It's normally an error
+if <I>node</I> does not contain the data field <I>key</I>. But if you provide a <I>defaultValue</I>
+argument, this value is returned instead (<I>node</I> will still not contain <I>key</I>).
+ This feature can be used to access a data field of <I>node</I> without first
+testing if it exists. This operation may trigger <B>read</B> data traces. </DD>
+
+<DT><I>treeName</I>
+<B>index</B> <I>node</I> </DT>
+<DD>Returns the id of <I>node</I>. If <I>node</I> is a tag, it can only specify
+one node. If <I>node</I> does not represent a valid node id or tag, or has modifiers
+that are invalid, then <I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I> <B>insert</B> <I>parent</I> ?<I>switches</I>?
+ </DT>
+<DD>Inserts a new node into parent node <I>parent</I>. The id of the new node is
+returned. The following switches are available: <blockquote></DD>
+
+<DT><B>-at</B> <I>number</I> </DT>
+<DD>Inserts the
+node into <I>parent</I>'s list of children at position <I>number</I>. The default is
+to append <I>node</I>. </DD>
+
+<DT><B>-data</B> <I>dataList</I> </DT>
+<DD>Sets the value for each data field in <I>dataList</I>
+for the new node. <I>DataList</I> is a list of key-value pairs. </DD>
+
+<DT><B>-label</B> <I>string</I> </DT>
+<DD>Designates
+the labels of the node as <I>string</I>. By default, nodes are labeled as <I>node0</I>,
+<I>node1</I>, etc. </DD>
+
+<DT><B>-tags</B> <I>tagList</I> </DT>
+<DD>Adds each tag in <I>tagList</I> to the new node. <I>TagList</I>
+is a list of tags, so be careful if a tag has embedded space. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>is</B>
+<I>property</I> <I>args</I> </DT>
+<DD>Indicates the property of a node. Both <I>property</I> and <I>args</I>
+determine the property being tested. Returns <I>1</I> if true and <I>0</I> otherwise.
+ The following <I>property</I> and <I>args</I> are valid: <blockquote></DD>
+
+<DT><B>ancestor</B> <I>node1</I> <I>node2</I> </DT>
+<DD>Indicates
+if <I>node1</I> is an ancestor of <I>node2</I>. </DD>
+
+<DT><B>before</B> <I>node1</I> <I>node2</I> </DT>
+<DD>Indicates if <I>node1</I>
+is before <I>node2</I> in depth first traversal. </DD>
+
+<DT><B>leaf</B> <I>node</I> </DT>
+<DD>Indicates if <I>node</I> is
+a leaf (it has no subtrees). </DD>
+
+<DT><B>root</B> <I>node</I> </DT>
+<DD>Indicates if <I>node</I> is the designated
+root. This can be changed by the <B>root</B> operation. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>label</B> <I>node</I> ?<I>newLabel</I>?
+</DT>
+<DD>Returns the label of the node designated by <I>node</I>. If <I>newLabel</I> is present,
+the node is relabeled using it as the new label. </DD>
+
+<DT><I>treeName</I> <B>lastchild</B> <I>node</I>
+</DT>
+<DD>Returns the id of the last child in the <I>node</I>'s list of subtrees. If <I>node</I>
+is a leaf (has no children), then <I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I> <B>move</B> <I>node</I> <I>newParent</I>
+?<I>switches</I>? </DT>
+<DD>Moves <I>node</I> into <I>newParent</I>. <I>Node</I> is appended to the list children
+of <I>newParent</I>. <I>Node</I> can not be an ancestor of <I>newParent</I>. The valid flags
+for <I>switches</I> are described below. <blockquote></DD>
+
+<DT><B>-after</B> <I>child</I> </DT>
+<DD>Position <I>node</I> after <I>child</I>.
+ The node <I>child</I> must be a child of <I>newParent</I>. </DD>
+
+<DT><B>-at</B> <I>number</I> </DT>
+<DD>Inserts <I>node</I> into
+<I>parent</I>'s list of children at position <I>number</I>. The default is to append the
+node. </DD>
+
+<DT><B>-before</B> <I>child</I> </DT>
+<DD>Position <I>node</I> before <I>child</I>. The node <I>child</I> must be a
+ child of <I>newParent</I>. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>next</B> <I>node</I> </DT>
+<DD>Returns the next node from <I>node</I>
+in a preorder traversal. If <I>node</I> is the last node in the tree, then <I>-1</I> is
+returned. </DD>
+
+<DT><I>treeName</I> <B>nextsibling</B> <I>node</I> </DT>
+<DD>Returns the node representing the next
+subtree from <I>node</I> in its parent's list of children. If <I>node</I> is the last
+child, then <I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I> <B>notify</B> <I>args</I> </DT>
+<DD>Manages notification events
+that indicate that the tree structure has been changed. See the <FONT SIZE=-1><B>NOTIFY
+OPERATIONS</B></FONT>
+ section below. </DD>
+
+<DT><I>treeName</I> <B>parent</B> <I>node</I> </DT>
+<DD>Returns the parent node
+of <I>node</I>. If <I>node</I> is the root of the tree, then <I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I>
+<B>path</B> <I>node</I> </DT>
+<DD>Returns the full path (from root) of <I>node</I>. </DD>
+
+<DT><I>treeName</I> <B>position</B> <I>node</I>
+</DT>
+<DD>Returns the position of the node in its parent's list of children. Positions
+are numbered from 0. The position of the root node is always 0. </DD>
+
+<DT><I>treeName</I>
+<B>previous</B> <I>node</I> </DT>
+<DD>Returns the previous node from <I>node</I> in a preorder traversal.
+If <I>node</I> is the root of the tree, then <I>-1</I> is returned. </DD>
+
+<DT><I>treeName</I> <B>prevsibling</B>
+<I>node</I> </DT>
+<DD>Returns the node representing the previous subtree from <I>node</I> in its
+parent's list of children. If <I>node</I> is the first child, then <I>-1</I> is returned.
+</DD>
+
+<DT><I>treeName</I> <B>restore</B> <I>node</I> <I>dataString</I> <I>switches</I> </DT>
+<DD>Performs the inverse function
+of the <B>dump</B> operation, restoring nodes to the tree. The format of <I>dataString</I>
+is exactly what is returned by the <B>dump</B> operation. It's a list containing
+information for each node to be restored. The information consists of 1)
+the relative path of the node, 2) a sublist of key value pairs representing
+the node's data, and 3) a list of tags for the node. Nodes are created
+ starting from <I>node</I>. Nodes can be listed in any order. If a node's path
+describes ancestor nodes that do not already exist, they are automatically
+created. The valid <I>switches</I> are listed below: <blockquote></DD>
+
+<DT><B>-overwrite</B> </DT>
+<DD>Overwrite nodes
+that already exist. Normally nodes are always created, even if there already
+exists a node by the same name. This switch indicates to add or overwrite
+the node's data fields. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>restorefile</B> <I>node</I> <I>fileName</I> <I>switches</I> </DT>
+<DD>Performs
+the inverse function of the <B>dumpfile</B> operation, restoring nodes to the
+tree from the file <I>fileName</I>. The format of <I>fileName</I> is exactly what is
+returned by the <B>dumpfile</B> operation. It's a list containing information
+for each node to be restored. The information consists of 1) the relative
+path of the node, 2) a sublist of key value pairs representing the node's
+data, and 3) a list of tags for the node. Nodes are created starting
+from <I>node</I>. Nodes can be listed in any order. If a node's path describes
+ancestor nodes that do not already exist, they are automatically created.
+ The valid <I>switches</I> are listed below: <blockquote></DD>
+
+<DT><B>-overwrite</B> </DT>
+<DD>Overwrite nodes that already
+exist. Normally nodes are always created, even if there already exists
+a node by the same name. This switch indicates to add or overwrite the
+node's data fields. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>root</B> ?<I>node</I>? </DT>
+<DD>Returns the id of the root node.
+ Normally this is node <I>0</I>. If a <I>node</I> argument is provided, it will become
+the new root of the tree. This lets you temporarily work within a subset
+of the tree. Changing root affects operations such as <B>next</B>, <B>path</B>, <B>previous</B>,
+etc. </DD>
+
+<DT><I>treeName</I> <B>set</B> <I>node</I> <I>key value</I> ?<I>key value</I>...? </DT>
+<DD>Sets one or more data fields
+in <I>node</I>. <I>Node</I> may be a tag that represents several nodes. <I>Key</I> is the name
+of the data field to be set and <I>value</I> is its respective value. This operation
+may trigger <B>write</B> and <B>create</B> data traces. </DD>
+
+<DT><I>treeName</I> <B>size</B> <I>node</I> </DT>
+<DD>Returns the
+number of nodes in the subtree. This includes the node and all its descendants.
+ The size of a leaf node is 1. </DD>
+
+<DT><I>treeName</I> <B>sort</B> <I>node</I> ?<I>switches</I>? </DT>
+<DD><blockquote></DD>
+
+<DT><B>-ascii</B> </DT>
+<DD>Compare
+strings using the ASCII collation order. </DD>
+
+<DT><B>-command</B> <I>string</I> </DT>
+<DD>Use command <I>string</I>
+as a comparison command. To compare two elements, evaluate a Tcl script
+consisting of command with the two elements appended as additional arguments.
+ The script should return an integer less than, equal to, or greater than
+zero if the first element is to be considered less than, equal to, or greater
+than the second, respectively. </DD>
+
+<DT><B>-decreasing</B> </DT>
+<DD>Sort in decreasing order (largest
+items come first). </DD>
+
+<DT><B>-dictionary</B> </DT>
+<DD>Compare strings using a dictionary-style comparison.
+ This is the same as <B>-ascii</B> except (a) case is ignored except as a tie-breaker
+and (b) if two strings contain embedded numbers, the numbers compare as
+integers, not characters. For example, in <B>-dictionary</B> mode, bigBoy sorts
+between bigbang and bigboy, and x10y sorts between x9y and x11y. </DD>
+
+<DT><B>-integer</B>
+</DT>
+<DD>Compare the nodes as integers. </DD>
+
+<DT><B>-key</B> <I>string</I> </DT>
+<DD>Sort based upon the node's data
+field keyed by <I>string</I>. Normally nodes are sorted according to their label.
+ </DD>
+
+<DT><B>-path</B> </DT>
+<DD>Compare the full path of each node. The default is to compare only
+its label. </DD>
+
+<DT><B>-real</B> </DT>
+<DD>Compare the nodes as real numbers. </DD>
+
+<DT><B>-recurse</B> </DT>
+<DD>Recursively sort
+the entire subtree rooted at <I>node</I>. </DD>
+
+<DT><B>-reorder</B> </DT>
+<DD>Recursively sort subtrees for
+each node. <B>Warning</B>. Unlike the normal flat sort, where a list of nodes
+is returned, this will reorder the tree. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I> <B>tag</B> <I>args</I> </DT>
+<DD>Manages tags
+for the tree object. See the <FONT SIZE=-1><B>TAG OPERATIONS</B></FONT>
+ section below. </DD>
+
+<DT><I>treeName</I> <B>trace</B>
+<I>args</I> </DT>
+<DD>Manages traces for data fields in the tree object. Traces cause Tcl
+commands to be executed whenever a data field of a node is created, read,
+written, or unset. Traces can be set for a specific node or a tag, representing
+possibly many nodes. See the <FONT SIZE=-1><B>TRACE OPERATIONS</B></FONT>
+ section below. </DD>
+
+<DT><I>treeName</I> <B>unset</B>
+<I>node</I> <I>key</I>... </DT>
+<DD>Removes one or more data fields from <I>node</I>. <I>Node</I> may be a tag that
+represents several nodes. <I>Key</I> is the name of the data field to be removed.
+ It's not an error is <I>node</I> does not contain <I>key</I>. This operation may trigger
+<B>unset</B> data traces. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect9" HREF="#toc9">Tag Operations</A></H2>
+Tags are a general means of selecting and
+marking nodes in the tree. A tag is just a string of characters, and it
+may take any form except that of an integer. The same tag may be associated
+with many different nodes. <P>
+There are two built-in tags: The tag <B>all</B> is
+implicitly associated with every node in the tree. It may be used to invoke
+operations on all the nodes in the tree. The tag <B>root</B> is managed automatically
+by the tree object. It specifies the node that is currently set as the
+root of the tree. <P>
+Most tree operations use tags. And several operations
+let you operate on multiple nodes at once. For example, you can use the
+<B>set</B> operation with the tag <B>all</B> to set a data field in for all nodes in
+the tree. <P>
+Tags are invoked by the <B>tag</B> operation. The general form is <BR>
+<P>
+<CODE><I>treeName</I> <B>tag</B> <I>operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for tags are listed below.
+<DL>
+
+<DT><I>treeName</I> <B>tag add</B> <I>string</I>
+<I>node</I>... </DT>
+<DD>Adds the tag <I>string</I> to one of more nodes. </DD>
+
+<DT><I>treeName</I> <B>tag delete</B> <I>string</I>
+<I>node</I>... </DT>
+<DD>Deletes the tag <I>string</I> from one or more nodes. </DD>
+
+<DT><I>treeName</I> <B>tag forget</B>
+<I>string</I> </DT>
+<DD>Removes the tag <I>string</I> from all nodes. It's not an error if no nodes
+are tagged as <I>string</I>. </DD>
+
+<DT><I>treeName</I> <B>tag names</B> ?<I>node</I>? </DT>
+<DD>Returns a list of tags used
+by the tree. If a <I>node</I> argument is present, only those tags used by <I>node</I>
+are returned. </DD>
+
+<DT><I>treeName</I> <B>tag nodes</B> <I>string</I> </DT>
+<DD>Returns a list of nodes that have
+the tag <I>string</I>. If no node is tagged as <I>string</I>, then an empty string is
+returned. </DD>
+</DL>
+
+<H2><A NAME="sect10" HREF="#toc10">Trace Operations</A></H2>
+Data fields can be traced much in the same way
+that you can trace Tcl variables. Data traces cause Tcl commands to be
+executed whenever a particular data field of a node is created, read, written,
+or unset. A trace can apply to one or more nodes. You can trace a specific
+node by using its id, or a group of nodes by a their tag. <P>
+The tree's <B>get</B>,
+<B>set</B>, and <B>unset</B> operations can trigger various traces. The <B>get</B> operation
+can cause a <I>read</I> trace to fire. The <B>set</B> operation causes a <I>write</I> trace
+to fire. And if the data field is written for the first time, you will
+also get a <I>create</I> trace. The <B>unset</B> operation triggers <I>unset</I> traces. <P>
+Data
+traces are invoked by the <B>trace</B> operation. The general form is <BR>
+<P>
+<CODE><I>treeName</I> <B>trace</B> <I>operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for traces are listed below.
+<DL>
+
+<DT><I>treeName</I> <B>trace create</B>
+<I>node</I> <I>key</I> <I>ops</I> <I>command</I> </DT>
+<DD>Creates a trace for <I>node</I> on data field <I>key</I>. <I>Node</I> can
+refer to more than one node (for example, the tag <B>all</B>). If <I>node</I> is a tag,
+any node with that tag can possibly trigger a trace, invoking <I>command</I>.
+ <I>Command</I> is command prefix, typically a procedure name. Whenever a trace
+is triggered, four arguments are appended to <I>command</I> before it is invoked:
+<I>treeName</I>, id of the node, <I>key</I> and, <I>ops</I>. Note that no nodes need have the
+field <I>key</I>. A trace identifier in the form "<I>trace0</I>", "<I>trace1</I>", etc. is returned.
+ <P>
+<I>Ops</I> indicates which operations are of interest, and consists of one or
+more of the following letters: <blockquote></DD>
+
+<DT><B>r</B> </DT>
+<DD>Invoke <I>command</I> whenever <I>key</I> is read. Both
+read and write traces are temporarily disabled when <I>command</I> is executed.
+</DD>
+
+<DT><B>w</B> </DT>
+<DD>Invoke <I>command</I> whenever <I>key</I> is written. Both read and write traces are
+temporarily disabled when <I>command</I> is executed. </DD>
+
+<DT><B>c</B> </DT>
+<DD>Invoke <I>command</I> whenever
+<I>key</I> is created. </DD>
+
+<DT><B>u</B> </DT>
+<DD>Invoke <I>command</I> whenever <I>key</I> is unset. Data fields are
+typically unset with the <B>unset</B> command. Data fields are also unset when
+the tree is released, but all traces are disabled prior to that. <P>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I>
+<B>trace delete</B> <I>traceId</I>... </DT>
+<DD>Deletes one of more traces. <I>TraceId</I> is the trace identifier
+returned by the <B>trace create</B> operation. </DD>
+
+<DT><I>treeName</I> <B>trace info</B> <I>traceId</I> </DT>
+<DD>Returns
+information about the trace <I>traceId</I>. <I>TraceId</I> is a trace identifier previously
+returned by the <B>trace create</B> operation. It's the same information specified
+for the <B>trace create</B> operation. It consists of the node id or tag, data
+field key, a string of letters indicating the operations that are traced
+(it's in the same form as <I>ops</I>) and, the command prefix. </DD>
+
+<DT><I>treeName</I> <B>trace names</B>
+</DT>
+<DD>Returns a list of identifers for all the current traces. </DD>
+</DL>
+
+<H2><A NAME="sect11" HREF="#toc11">Notify Operations</A></H2>
+Tree
+objects can be shared among many clients, such as a <B>hiertable</B> widget. Any
+client can create or delete nodes, sorting the tree, etc. You can request
+to be notified whenever these events occur. Notify events cause Tcl commands
+to be executed whenever the tree structure is changed. <P>
+Notifications are
+handled by the <B>notify</B> operation. The general form is <BR>
+<P>
+<CODE><I>treeName</I> <B>notify</B> <I>operation</I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for events are listed below.
+<DL>
+
+<DT><I>treeName</I> <B>notify create</B>
+?<I>switches</I>? <I>command</I> ?<I>args</I>?... </DT>
+<DD>Creates a notifier for the tree. A notify identifier
+in the form "<I>notify0</I>", "<I>notify1</I>", etc. is returned. <P>
+<I>Command</I> and <I>args</I> are
+saved and invoked whenever the tree structure is changed (according to
+<I>switches</I>). Two arguments are appended to <I>command</I> and <I>args</I> before it's invoked:
+the id of the node and a string representing the type of event that occured.
+One of more switches can be set to indicate the events that are of interest.
+ The valid switches are as follows: <blockquote></DD>
+
+<DT><B>-create</B> </DT>
+<DD>Invoke <I>command</I> whenever a new
+node has been added. </DD>
+
+<DT><B>-delete</B> </DT>
+<DD>Invoke <I>command</I> whenever a node has been deleted.
+</DD>
+
+<DT><B>-move</B> </DT>
+<DD>Invoke <I>command</I> whenever a node has been moved. </DD>
+
+<DT><B>-sort</B> </DT>
+<DD>Invoke <I>command</I>
+whenever the tree has been sorted and reordered. </DD>
+
+<DT><B>-relabel</B> </DT>
+<DD>Invoke <I>command</I>
+whenever a node has been relabeled. </DD>
+
+<DT><B>-allevents</B> </DT>
+<DD>Invoke <I>command</I> whenever any
+of the above events occur. </DD>
+
+<DT><B>-whenidle</B> </DT>
+<DD>When an event occurs don't invoke <I>command</I>
+immediately, but queue it to be run the next time the event loop is entered
+and there are no events to process. If subsequent events occur before
+ the event loop is entered, <I>command</I> will still be invoked only once. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>treeName</I>
+<B>notify delete</B> <I>notifyId</I> </DT>
+<DD>Deletes one or more notifiers from the tree. <I>NotifyId</I>
+is the notifier identifier returned by the <B>notify create</B> operation. </DD>
+
+<DT><I>treeName</I>
+<B>notify info</B> <I>notifyId</I> </DT>
+<DD>Returns information about the notify event <I>notifyId</I>.
+ <I>NotifyId</I> is a notify identifier previously returned by the <B>notify create</B>
+operation. It's the same information specified for the <B>notify create</B> operation.
+It consists of the notify id, a sublist of event flags (it's in the same
+form as <I>flags</I>) and, the command prefix. </DD>
+
+<DT><I>treeName</I> <B>notify names</B> </DT>
+<DD>Returns a
+list of identifers for all the current notifiers. </DD>
+</DL>
+
+<H2><A NAME="sect12" HREF="#toc12">C Language API</A></H2>
+Blt_TreeApply,
+ Blt_TreeApplyBFS, Blt_TreeApplyDFS, Blt_TreeChangeRoot, Blt_TreeCreate,
+ Blt_TreeCreateEventHandler, Blt_TreeCreateNode, Blt_TreeCreateTrace,
+ Blt_TreeDeleteEventHandler, Blt_TreeDeleteNode, Blt_TreeDeleteTrace,
+ Blt_TreeExists, Blt_TreeFindChild, Blt_TreeFirstChild, Blt_TreeFirstKey,
+ Blt_TreeGetNode, Blt_TreeGetToken, Blt_TreeGetValue, Blt_TreeIsAncestor,
+ Blt_TreeIsBefore, Blt_TreeIsLeaf, Blt_TreeLastChild, Blt_TreeMoveNode,
+ Blt_TreeName, Blt_TreeNextKey, Blt_TreeNextNode, Blt_TreeNextSibling,
+ Blt_TreeNodeDegree, Blt_TreeNodeDepth, Blt_TreeNodeId, Blt_TreeNodeLabel,
+ Blt_TreeNodeParent, Blt_TreePrevNode, Blt_TreePrevSibling, Blt_TreeRelabelNode,
+ Blt_TreeReleaseToken, Blt_TreeRootNode, Blt_TreeSetValue, Blt_TreeSize,
+ Blt_TreeSortNode, and Blt_TreeUnsetValue.
+<H2><A NAME="sect13" HREF="#toc13">Keywords</A></H2>
+tree, hiertable, widget
+<P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Node IDs and Tags</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Node Modifiers</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Tree Operations</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Tag Operations</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Trace Operations</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Notify Operations</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">C Language API</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/treeview.html b/blt/html/treeview.html
new file mode 100644
index 00000000000..6db1969a4c2
--- /dev/null
+++ b/blt/html/treeview.html
@@ -0,0 +1,2336 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>treeview(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+treeview - Create and manipulate hierarchical
+table widgets
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>treeview</B> <I>pathName </I>?<I>options</I>?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>treeview</B>
+widget displays a tree of data. It replaces both the <B>hiertable</B> and <B>hierbox</B>
+widgets. The <B>treeview</B> is 100% syntax compatible with the <B>hiertable</B> widget.
+ The <B>hiertable</B> command is retained for sake of script-level compatibility.
+ This widget obsoletes the <B>hierbox</B> widget. It does everything the old <B>hierbox</B>
+widget did, but also provides data sharing (via <I>tree data objects</I>) and
+the ability to tag nodes.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+The <B>treeview</B> widget displays hierarchical
+data. Data is represented as nodes in a general-ordered tree. Each node
+may have sub-nodes and these nodes can in turn has their own children. <P>
+A
+node is displayed as a row entry in the widget. Each entry has a text label
+and icon. When a node has children, its entry is drawn with a small button
+to the left of the label. Clicking the mouse over this button opens or
+closes the node. When a node is <I>open</I>, its children are exposed. When it
+is <I>closed</I>, the children and their descedants are hidden. The button is
+normally a <I>+</I> or <I>-</I> symbol (ala Windows Explorer), but can be replaced with
+a pair of Tk images (open and closed images). <P>
+If the node has data associated
+with it, they can be displayed in columns running vertically on either
+side the tree. You can control the color, font, etc of each entry. Any
+entry label or data field can be edited in-place.
+<H2><A NAME="sect4" HREF="#toc4">Tree Data Object</A></H2>
+The tree
+is not stored inside the widget but in a tree data object (see the <B>tree</B>
+command for a further explanation). Tree data objects can be shared among
+different clients, such as a <B>treeview</B> widget or the <B>tree</B> command. You can
+walk the tree and manage its data with the <B>tree</B> command tree, while displaying
+it with the <B>treeview</B> widget. Whenever the tree is updated, the <B>treeview</B>
+widget is automatically redrawn. <P>
+By default, the <B>treeview</B> widget creates
+its own tree object. The tree initially contains just a root node. But you
+can also display trees created by the <B>tree</B> command using the <B>-tree</B> configuration
+option. <B>Treeview</B> widgets can share the same tree object, possibly displaying
+different views of the same data. <P>
+A tree object has both a Tcl and C API.
+ You can insert or delete nodes using <B>treeview</B> widget or <B>tree</B> command operations,
+but also from C code. For example, you can load the tree from your C code
+while still managing and displaying the tree from Tcl. The widget is automatically
+notified whenever the tree is modified via C or Tcl.
+<H2><A NAME="sect5" HREF="#toc5">Syntax</A></H2>
+<BR>
+<P>
+<CODE><B>treeview <I>pathName </I></B>?<I>option value</I>?...<BR>
+</CODE><P>The <B>treeview</B> command creates a new window <I>pathName</I> and makes it into a
+<B>treeview</B> widget. At the time this command is invoked, there must not exist
+a window named <I>pathName</I>, but <I>pathName</I>'s parent must exist. Additional options
+may be specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the <B>configure</B> operation
+below for the exact details about what <I>option</I> and <I>value</I> pairs are valid.
+<P>
+If successful, <B>treeview</B> returns the path name of the widget. It also creates
+a new Tcl command by the same name. You can use this command to invoke
+various operations that query or modify the widget. The general form is:
+<BR>
+<P>
+<CODE><I>pathName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available are described in the <FONT SIZE=-1><B>TREEVIEW OPERATIONS</B></FONT>
+ section.
+
+<H2><A NAME="sect6" HREF="#toc6">IDs and Tags</A></H2>
+Nodes can be inserted into a tree using the <B>treeview</B> widget
+ <BR>
+<CODE>blt::treeview .t<BR>
+set node [.t insert end root "one"]<BR>
+</CODE><P>or <B>tree</B> command. <BR>
+<CODE>set tree [blt::tree create]<BR>
+set node [$tree insert root "one"]<BR>
+</CODE><P>In both cases, a number identifying the node is returned (the value of
+<I>$node</I>). This serial number or <I>id</I> uniquely identifies the node. Please note
+that you can't infer a location or position of a node from its id. The only
+exception is that the root node is always id <I>0</I>. Since nodes may have the
+same labels or be moved within the tree, ids provide an convenient way
+to identify nodes. If a tree is shared, the ids will be the same regardless
+if you are using by the <B>treeview</B> widget or the <B>tree</B> command. Ids are recycled
+when the node deleted. <P>
+A node may also have any number of <I>tags</I> associated
+with it. A tag is just a string of characters, and it may take any form
+except that of an integer. For example, "<I>x123</I>" is valid, but "<I>123</I>" isn't.
+ The same tag may be associated with many different nodes. This is typically
+done to associate a group of nodes. Many operations in the <B>treeview</B> widget
+take either node ids or tag names as arguments. Using a tag says to apply
+the operation to all nodes with that tag. <P>
+The tag <B>all</B> is implicitly associated
+with every node in the tree. It may be used to invoke operations on all
+the nodes in the tree. <P>
+Tags may be shared, just like trees, between clients.
+ For example, you can use the tags created by the <B>tree</B> command with <B>treeview</B>
+widgets.
+<H2><A NAME="sect7" HREF="#toc7">Special Node IDs</A></H2>
+There are also several special non-numeric ids.
+Special ids differ from tags in that they are always translated to their
+numeric equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to the
+<B>treeview</B> widget.
+<DL>
+
+<DT><B>active</B> </DT>
+<DD>The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon (see the <B>-activeicon</B>
+option). The <B>active</B> id is changed automatically by moving the mouse pointer
+over another node or by using the <B>entry activate</B> operation. Note that there
+can be only one active node at a time. </DD>
+
+<DT><B>anchor</B> </DT>
+<DD>The node representing the
+fixed end of the current selection. The anchor is set by the <B>selection
+anchor</B> operation. </DD>
+
+<DT><B>current</B> </DT>
+<DD>The node where the mouse pointer is currently
+located. But unlike <B>active</B>, this id changes while the selection is dragged.
+ It is used to determine the current node during button drags. </DD>
+
+<DT><B>down</B> </DT>
+<DD>The
+next open node from the current focus. The <B>down</B> of the last open node is
+the same. </DD>
+
+<DT><B>end</B> </DT>
+<DD>The last open node (in depth-first order) on the tree. </DD>
+
+<DT><B>focus</B>
+</DT>
+<DD>The node that currently has focus. When a node has focus, it receives key
+events. To indicate focus, the node is drawn with a dotted line around
+its label. You can change the focus using the <B>focus</B> operation. </DD>
+
+<DT><B>last</B> </DT>
+<DD>The
+last open node from the current focus. But unlike <B>up</B>, when the focus is
+at root, <B>last</B> wraps around to the last open node in the tree. </DD>
+
+<DT><B>mark</B> </DT>
+<DD>The node
+representing the non-fixed end of the current selection. The mark is set
+by the <B>selection mark</B> operation. </DD>
+
+<DT><B>next</B> </DT>
+<DD>The next open node from the current
+focus. But unlike <B>down</B>, when the focus is on last open node, <B>next</B> wraps
+around to the root node. </DD>
+
+<DT><B>nextsibling</B> </DT>
+<DD>The next sibling from the node with
+the current focus. If the node is already the last sibling then it is the
+<B>nextsibling<B>. </DD>
+
+<DT><B>parent</B></B></B> </DT>
+<DD>The parent of the node with the current focus. The <B>parent</B>
+of the root is also the root. </DD>
+
+<DT><B>prevsibling</B> </DT>
+<DD>The previous sibling from the
+node with the current focus. If the node is already the first sibling then
+it is the <B>prevsibling<B>. </DD>
+
+<DT><B>root</B></B></B> </DT>
+<DD>The root node. You can also use id <I>0</I> to indicate
+the root. </DD>
+
+<DT><B>up</B> </DT>
+<DD>The last open node (in depth-first order) from the current focus.
+The <B>up</B> of the root node (i.e. the root has focus) is also the root. </DD>
+
+<DT><B>view.top</B>
+</DT>
+<DD>First node that's current visible in the widget. </DD>
+
+<DT><B>view.bottom</B> </DT>
+<DD>Last node that's
+current visible in the widget. </DD>
+
+<DT><I>path</I> </DT>
+<DD>Absolute path of a node. Path names
+refer to the node name, not their entry labels. Paths don't have to start
+with a separator (see the <B>-separator</B> configuration option), but component
+names must be separated by the designated separator. </DD>
+
+<DT><B>@<I>x<B>,<I>y</I></B></I></B> </DT>
+<DD>Indicates the
+node that covers the point in the treeview window specified by <I>x</I> and <I>y</I>
+(in pixel coordinates). If no part of the entryd covers that point, then
+the closest node to that point is used. </DD>
+</DL>
+<P>
+A node may be specified as an id
+or tag. If the specifier is an integer then it is assumed to refer to the
+single node with that id. If the specifier is not an integer, it's checked
+to see if it's a special id (such as focus). Otherwise, it's assumed to be
+tag. Some operations only operate on a single node at a time; if a tag
+refers to more than one node, then an error is generated.
+<H2><A NAME="sect8" HREF="#toc8">Data Fields</A></H2>
+A node
+in the tree can have <I>data fields</I>. A data field is a name-value pair, used
+to represent arbitrary data in the node. Nodes can contain different fields
+(they aren't required to contain the same fields). You can optionally display
+these fields in the <B>treeview</B> widget in columns running on either side of
+the displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a specific
+field is left blank. Columns can be interactively resized, hidden, or,
+moved.
+<H2><A NAME="sect9" HREF="#toc9">Entry Bindings</A></H2>
+You can bind Tcl commands to be invoked when events
+occur on nodes (much like Tk canvas items). You can bind a node using its
+id or its <I>bindtags</I>. Bindtags are simply names that associate a binding
+with one or more nodes. There is a built-in tag <I>all</I> that all node entries
+automatically have.
+<H2><A NAME="sect10" HREF="#toc10">Treeview Operations</A></H2>
+The <B>treeview</B> operations are the invoked
+by specifying the widget's pathname, the operation, and any arguments that
+pertain to that operation. The general form is: <P>
+<BR>
+<CODE><I>pathName operation </I>?<I>arg arg ...</I>?<BR>
+<P>
+</CODE><P><I>Operation</I> and the <I>arg</I>s determine the exact behavior of the command. The
+following operation are available for <B>treeview</B> widgets:
+<DL>
+
+<DT><I>pathName <B>bbox</B></I> ?<B>-screen</B>?
+<I>tagOrId...</I> </DT>
+<DD>Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more <I>tagOrId</I> arguments.
+ If the <B>-screen</B> flag is given, then the x-y coordinates of the bounding
+box are returned as screen coordinates, not virtual coordinates. Virtual
+coordinates start from <I>0</I> from the root node. The returned list contains
+the following values. <blockquote></DD>
+
+<DT><I>x</I> </DT>
+<DD>X-coordinate of the upper-left corner of the bounding
+box. </DD>
+
+<DT><I>y</I> </DT>
+<DD>Y-coordinate of the upper-left corner of the bounding box. </DD>
+
+<DT><I>width</I> </DT>
+<DD>Width
+of the bounding box. </DD>
+
+<DT><I>height</I> </DT>
+<DD>Height of the bounding box. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for a node with this tag, <I>command</I>
+will be invoked. The syntax is similar to the <B>bind</B> command except that
+it operates on <B>treeview</B> entries, rather than widgets. See the <B>bind</B> manual
+entry for complete details on <I>sequence</I> and the substitutions performed
+on <I>command</I> before invoking it. <P>
+If all arguments are specified then a
+new binding is created, replacing any existing binding for the same <I>sequence</I>
+and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I> augments
+an existing binding rather than replacing it. If no <I>command</I> argument is
+provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button <I>operation</I></B></I> ?<I>args</I>?
+</DT>
+<DD>This command is used to control the button selectors within a <B>treeview</B>
+widget. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>button
+activate</B></I> <I>tagOrId</I> </DT>
+<DD>Designates the node given by <I>tagOrId</I> as active. When
+a node is active it's entry is drawn using its active icon (see the <B>-activeicon</B>
+option). Note that there can be only one active entry at a time. The special
+id <B>active</B> indicates the currently active node. </DD>
+
+<DT><I>pathName <B>button bind</B></I> <I>tagName</I>
+?<I>sequence command</I>? </DT>
+<DD>Associates <I>command</I> with <I>tagName</I> such that whenever the
+event sequence given by <I>sequence</I> occurs for an button of a node entry with
+this tag, <I>command</I> will be invoked. The syntax is similar to the <B>bind</B> command
+except that it operates on <B>treeview</B> buttons, rather than widgets. See the
+<B>bind</B> manual entry for complete details on <I>sequence</I> and the substitutions
+performed on <I>command</I> before invoking it. <P>
+If all arguments are specified
+then a new binding is created, replacing any existing binding for the
+same <I>sequence</I> and <I>tagName</I>. If the first character of <I>command</I> is <I>+</I> then <I>command</I>
+ augments an existing binding rather than replacing it. If no <I>command</I> argument
+is provided then the command currently associated with <I>tagName</I> and <I>sequence</I>
+(it's an error occurs if there's no such binding) is returned. If both <I>command</I>
+and <I>sequence</I> are missing then a list of all the event sequences for which
+bindings have been defined for <I>tagName</I>. </DD>
+
+<DT><I>pathName <B>button cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>button configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the configuration options of the widget. If no <I>option</I> is specified, returns
+a list describing all of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B>
+for information on the format of this list). If <I>option</I> is specified with
+no <I>value</I>, then the command returns a list describing the one named option
+(this list will be identical to the corresponding sublist of the value
+returned if no <I>option</I> is specified). If one or more <I>option-value</I> pairs are
+specified, then the command modifies the given widget option(s) to have
+the given value(s); in this case the command returns an empty string. <I>Option</I>
+and <I>value</I> are described in the section <FONT SIZE=-1><B>BUTTON OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value of the configuration option given
+by <I>option</I>. <I>Option</I> may have any of the values accepted by the <B>configure</B> operation
+described below. </DD>
+
+<DT><I>pathName <B>close </B></I>?<B>-recurse</B>? <I>tagOrId...</I> </DT>
+<DD>Closes the node specified
+by <I>tagOrId</I>. In addition, if a Tcl script was specified by the <B>-closecommand</B>
+option, it is invoked. If the node is already closed, this command has
+no effect. If the <B>-recurse</B> flag is present, each child node is recursively
+closed. </DD>
+
+<DT><I>pathName <B>column <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are available
+for treeview columns. <blockquote></DD>
+
+<DT><I>pathName <B>column activate</B></I> <I>column</I> </DT>
+<DD>Sets the active column
+to <I>column</I>. <I>Column</I> is the name of a column in the widget. When a column is
+active, it's drawn using its <B>-activetitlebackground</B> and <B>-activetitleforeground</B>
+options. If <I>column</I> is the <I>""</I>, then no column will be active. If no column
+argument is provided, then the name of the currently active column is returned.
+</DD>
+
+<DT><I>pathName <B>column cget</B></I> <I>name</I> <I>option</I> </DT>
+<DD>Returns the current value of the column
+configuration option given by <I>option</I> for <I>name</I>. <I>Name</I> is the name of column
+that corresponds to a data field. <I>Option</I> may have any of the values accepted
+by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>column configure</B></I> <I>name</I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the column designated by <I>name</I>. <I>Name</I> is the name of the column corresponding
+to a data field. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>COLUMN OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>column delete</B></I> <I>field</I>
+?<I>field</I>...? </DT>
+<DD>Deletes one of more columns designated by <I>field</I>. Note that this
+does not delete the data fields themselves. </DD>
+
+<DT><I>pathName <B>column insert</B></I> <I>position</I>
+<I>field</I> ?<I>options</I>...? </DT>
+<DD>Inserts one of more columns designated by <I>field</I>. A column
+displays each node's data field by the same name. If the node doesn't have
+the given field, the cell is left blank. <I>Position</I> indicates where in the
+list of columns to add the new column. It may be either a number or <I>end</I>.
+ </DD>
+
+<DT><I>pathName <B>column invoke</B></I> <I>field</I> </DT>
+<DD>Invokes the Tcl command associated with the
+column <I>field</I>, if there is one (using the column's <B>-command</B> option). The
+command is ignored if the column's <B>-state</B> option set to <I>disabled</I>. </DD>
+
+<DT><I>pathName
+<B>column move <I>name</I></B></I> <I>dest</I> </DT>
+<DD>Moves the column <I>name</I> to the destination position.
+ <I>Dest</I> is the name of another column or a screen position in the form <I>@<I>x<I>,<I>y</I></I></I></I>.
+</DD>
+
+<DT><I>pathName <B>column names</B></I> </DT>
+<DD>Returns a list of the names of all columns in the
+widget. The list is ordered as the columns are drawn from left-to-right. </DD>
+
+<DT><I>pathName
+<B>column nearest</B></I> <I>x</I> ?<I>y</I>? </DT>
+<DD>Returns the name of the column closest to the given
+X-Y screen coordinate. If you provide a <I>y</I> argument (it's optional), a name
+is returned only when if the point is over a column's title. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>configure</B></I>
+?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options
+of the widget. If no <I>option</I> is specified, returns a list describing all
+of the available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is specified with no <I>value</I>, then
+the command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described in the section <FONT SIZE=-1><B>TREEVIEW OPTIONS</B></FONT>
+ below. </DD>
+
+<DT><I>pathName <B>curselection</B></I>
+</DT>
+<DD>Returns a list containing the ids of all of the entries that are currently
+selected. If there are no entries selected, then the empty string is returned.
+</DD>
+
+<DT><I>pathName <B>delete <I>tagOrId</I></B></I>... </DT>
+<DD>Deletes one or more entries given by <I>tagOrId</I> and
+its children. </DD>
+
+<DT><I>pathName <B>entry <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>The following operations are
+available for treeview entries. <blockquote></DD>
+
+<DT><I>pathName <B>entry activate</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the
+active entry to the one specified by <I>tagOrId</I>. When an entry is active
+it is drawn using its active icon (see the <B>-activeicon</B> option). Note that
+there can be only one active node at a time. The special id of the currently
+active node is <B>active</B>. </DD>
+
+<DT><I>pathName <B>entry cget</B></I> <I>option</I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>entry
+children</B></I> <I>tagOrId</I> ?<I>first</I>? ?<I>last</I>? </DT>
+<DD>Returns a list of ids for the given range
+of children of <I>tagOrId</I>. <I>TagOrId</I> is the id or tag of the node to be examined.
+If only a <I>first</I> argument is present, then the id of the that child at
+that numeric position is returned. If both <I>first</I> and <I>last</I> arguments are
+given, then the ids of all the children in that range are returned. Otherwise
+the ids of all children are returned. </DD>
+
+<DT><I>pathName <B>entry configure</B></I> ?<I>option</I>?
+?<I>value option value ...</I>? </DT>
+<DD>Query or modify the configuration options of the
+widget. If no <I>option</I> is specified, returns a list describing all of the
+available options for <I>pathName</I> (see <B>Tk_ConfigureInfo</B> for information on
+the format of this list). If <I>option</I> is specified with no <I>value</I>, then the
+command returns a list describing the one named option (this list will
+be identical to the corresponding sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have the given value(s);
+ in this case the command returns an empty string. <I>Option</I> and <I>value</I> are
+described below: </DD>
+
+<DT><I>pathName <B>entry delete</B></I> <I>tagOrId</I> ?<I>first</I> ?<I>last</I>? </DT>
+<DD>Deletes the
+one or more children nodes of the parent <I>tagOrId</I>. If <I>first</I> and <I>last</I> arguments
+are present, they are positions designating a range of children nodes to
+be deleted. </DD>
+
+<DT><I>pathName <B>entry isbefore <I>tagOrId1</I></B></I> <I>tagOrId2</I> </DT>
+<DD>Returns 1 if <I>tagOrId1</I>
+is before <I>tagOrId2</I> and 0 otherwise. </DD>
+
+<DT><I>pathName <B>entry ishidden <I>tagOrId</I></B></I> </DT>
+<DD>Returns
+1 if the node is currently hidden and 0 otherwise. A node is also hidden
+if any of its ancestor nodes are closed or hidden. </DD>
+
+<DT><I>pathName <B>entry isopen
+<I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the node is currently open and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>entry size</B></I> <B>-recurse</B> <I>tagOrId</I> </DT>
+<DD>Returns the number of children for parent node
+<I>tagOrId</I>. If the <B>-recurse</B> flag is set, the number of all its descendants
+is returned. The node itself is not counted. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>find </B></I>?<I>flags</I>? <I>first</I>
+<I>last</I> </DT>
+<DD>Finds for all entries matching the criteria given by <I>flags</I>. A list
+of ids for all matching nodes is returned. <I>First</I> and <I>last</I> are ids designating
+the range of the search in depth-first order. If <I>last</I> is before <I>first</I>, then
+nodes are searched in reverse order. The valid flags are: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B>
+</DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern
+to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match
+against the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Patterns must match
+exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching
+is done in a fashion similar to that used by the C-shell. For the two
+strings to match, their contents must be identical except that the following
+ special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of
+ characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character
+in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a
+sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I>
+and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This
+provides a way of avoiding the special interpretation of the characters
+<I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern matching (i.e.
+the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Pick entries
+that don't match. </DD>
+
+<DT><B>-exec<I> string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked for
+each matching node. Percent substitutions are performed on <I>string</I> before
+ it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-count<I> number</I></B> </DT>
+<DD>Stop
+searching after <I>number</I> matches. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>focus
+</B></I> <I>tagOrId</I> </DT>
+<DD>Sets the focus to the node given by <I>tagOrId</I>. When a node has focus,
+it can receive keyboard events. The special id <B>focus</B> designates the node
+that currently has focus. </DD>
+
+<DT><I>pathName <B>get </B></I>?<B>-full</B>? <I>tagOrId</I> <I>tagOrId</I>... </DT>
+<DD>Translates
+one or more ids to their node entry names. It returns a list of names
+for all the ids specified. If the <B>-full</B> flag is set, then the full pathnames
+are returned. </DD>
+
+<DT><I>pathName <B>hide </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Hides all nodes matching the
+criteria given by <I>flags</I>. The search is performed recursively for each node
+given by <I>tagOrId</I>. The valid flags are described below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies
+pattern to match against node names. </DD>
+
+<DT><B>-full<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match
+against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B> </DT>
+<DD>Specifies pattern to match against
+the node entry's configuration option. </DD>
+
+<DT><B>-exact</B> </DT>
+<DD>Match patterns exactly. The
+is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD>Use global pattern matching. Matching is done in a
+fashion similar to that used by the C-shell. For the two strings to match,
+their contents must be identical except that the following special sequences
+ may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches any sequence of characters in string,
+including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I>
+</DT>
+<DD>Matches any character in the set given by <I>chars</I>. If a sequence of the form
+<I>x</I>-<I>y</I> appears in <I>chars</I>, then any character between <I>x</I> and <I>y</I>, inclusive, will
+match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single character <I>x</I>. This provides a way of avoiding
+ the special interpretation of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B>
+</DT>
+<DD>Use regular expression pattern matching (i.e. the same as implemented by
+the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B> </DT>
+<DD>Hide nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates
+the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>index </B></I>?<B>-at</B> <I>tagOrId</I>? <I>string</I> </DT>
+<DD>Returns the id of
+the node specified by <I>string</I>. <I>String</I> may be a tag or node id. Some special
+ids are normally relative to the node that has focus. The <B>-at</B> flag lets
+you select another node. </DD>
+
+<DT><I>pathName <B>insert </B></I>?<B>-at <I>tagOrId</I></B>? <I>position</I> <I>path</I> ?<I>options...</I>?
+?<I>path</I>? ?<I>options...</I>? </DT>
+<DD>Inserts one or more nodes at <I>position</I>. <I>Position</I> is the
+location (number or <I>end</I>) where the new nodes are added to the parent node.
+ <I>Path</I> is the pathname of the new node. Pathnames can be formated either
+as a Tcl list (each element is a path component) or as a string separated
+by a special character sequence (using the <B>-separator</B> option). Pathnames
+are normally absolute, but the <B>-at</B> switch lets you select a relative starting
+point. Its value is the id of the starting node. <P>
+All ancestors of the
+new node must already exist, unless the <B>-autocreate</B> option is set. It is
+also an error if a node already exists, unless the <B>-allowduplicates</B> option
+is set. <P>
+<I>Option</I> and <I>value</I> may have any of the values accepted by the <B>entry
+configure</B> operation described in the <FONT SIZE=-1><B>ENTRY OPERATIONS</B></FONT>
+ section below. This
+command returns a list of the ids of the new entries. </DD>
+
+<DT><I>pathName <B>move <I>tagOrId</I></B></I>
+<I>how</I> <I>destId</I> </DT>
+<DD>Moves the node given by <I>tagOrId</I> to the destination node. The
+node can not be an ancestor of the destination. <I>DestId</I> is the id of the
+destination node and can not be the root of the tree. In conjunction with
+<I>how</I>, it describes how the move is performed. <blockquote></DD>
+
+<DT><I>before</I> </DT>
+<DD>Moves the node before
+the destination node. </DD>
+
+<DT><I>after</I> </DT>
+<DD>Moves the node after the destination node. </DD>
+
+<DT><I>into</I>
+</DT>
+<DD>Moves the node to the end of the destination's list of children. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>nearest <I>x y</I></B></I> ?<I>varName</I>? </DT>
+<DD>Returns the id of the node entry closest to the given
+X-Y screen coordinate. The optional argument <I>varName</I> is the name of variable
+which is set to either <I>button</I> or <I>select</I> to indicate over what part of the
+node the coordinate lies. If the coordinate is not directly over any node,
+then <I>varName</I> will contain the empty string. </DD>
+
+<DT><I>pathName <B>open </B></I>?<B>-recurse</B>? <I>tagOrId...</I>
+</DT>
+<DD>Opens the one or more nodes specified by <I>tagOrId</I>. If a node is not already
+open, the Tcl script specified by the <B>-opencommand</B> option is invoked. If
+the <B>-recurse</B> flag is present, then each descendant is recursively opened.
+ </DD>
+
+<DT><I>pathName <B>range</B></I> ?<B>-open</B>? <I>first last</I> </DT>
+<DD>Returns the ids in depth-first order
+of the nodes between the <I>first</I> and <I>last</I> ids. If the <B>-open</B> flag is present,
+it indicates to consider only open nodes. If <I>last</I> is before <I>first</I>, then
+the ids are returned in reverse order. </DD>
+
+<DT><I>pathName <B>scan</B></I> <I>option args</I> </DT>
+<DD>This command
+implements scanning. It has two forms, depending on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>scan
+mark <I>x y</I></B></I> </DT>
+<DD>Records <I>x</I> and <I>y</I> and the current view in the treeview window;
+used in conjunction with later <B>scan dragto</B> commands. Typically this command
+is associated with a mouse button press in the widget. It returns an empty
+string. </DD>
+
+<DT><I>pathName <B>scan dragto <I>x y</I></B></I>. </DT>
+<DD>Computes the difference between its <I>x</I> and
+<I>y</I> arguments and the <I>x</I> and <I>y</I> arguments to the last <B>scan mark</B> command for
+the widget. It then adjusts the view by 10 times the difference in coordinates.
+ This command is typically associated with mouse motion events in the widget,
+to produce the effect of dragging the list at high speed through the window.
+ The return value is an empty string. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>see</B></I> ?<B>-anchor <I>anchor</I></B>? <I>tagOrId</I>
+</DT>
+<DD>Adjusts the view of entries so that the node given by <I>tagOrId</I> is visible
+in the widget window. It is an error if <B>tagOrId</B> is a tag that refers to
+more than one node. By default the node's entry is displayed in the middle
+of the window. This can changed using the <B>-anchor</B> flag. Its value is a Tk
+anchor position. </DD>
+
+<DT><I>pathName <B>selection <I>option arg</I></B></I> </DT>
+<DD>This command is used to adjust
+the selection within a <B>treeview</B> widget. It has several forms, depending
+on <I>option</I>: <blockquote></DD>
+
+<DT><I>pathName <B>selection anchor <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection anchor
+to the node given by <I>tagOrId</I>. If <I>tagOrId</I> refers to a non-existent node, then
+the closest node is used. The selection anchor is the end of the selection
+that is fixed while dragging out a selection with the mouse. The special
+id <B>anchor</B> may be used to refer to the anchor node. </DD>
+
+<DT><I>pathName <B>selection cancel</B></I>
+</DT>
+<DD>Clears the temporary selection of entries back to the current anchor. Temporary
+selections are created by the <B>selection mark</B> operation. </DD>
+
+<DT><I>pathName <B>selection
+clear <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Removes the entries between <I>first</I> and <I>last</I> (inclusive)
+from the selection. Both <I>first</I> and <I>last</I> are ids representing a range of
+entries. If <I>last</I> isn't given, then only <I>first</I> is deselected. Entries outside
+the selection are not affected. </DD>
+
+<DT><I>pathName <B>selection clearall</B></I> </DT>
+<DD>Clears the entire
+selection. </DD>
+
+<DT><I>pathName <B>selection mark <I>tagOrId</I></B></I> </DT>
+<DD>Sets the selection mark to
+the node given by <I>tagOrId</I>. This causes the range of entries between the
+anchor and the mark to be temporarily added to the selection. The selection
+mark is the end of the selection that is fixed while dragging out a selection
+with the mouse. The special id <B>mark</B> may be used to refer to the current
+ mark node. If <I>tagOrId</I> refers to a non-existent node, then the mark is ignored.
+Resetting the mark will unselect the previous range. Setting the anchor
+finalizes the range. </DD>
+
+<DT><I>pathName <B>selection includes <I>tagOrId</I></B></I> </DT>
+<DD>Returns 1 if the
+node given by <I>tagOrId</I> is currently selected, 0 if it isn't. </DD>
+
+<DT><I>pathName <B>selection
+present</B></I> </DT>
+<DD>Returns 1 if any nodes are currently selected and 0 otherwise. </DD>
+
+<DT><I>pathName
+<B>selection set <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects all of the nodes in the range between
+<I>first</I> and <I>last</I>, inclusive, without affecting the selection state of nodes
+outside that range. </DD>
+
+<DT><I>pathName <B>selection toggle <I>first </I></B></I>?<I>last</I>? </DT>
+<DD>Selects/deselects
+nodes in the range between <I>first</I> and <I>last</I>, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and visa versa.
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>show </B></I>?<B>flags</B>? <I>tagOrId</I>... </DT>
+<DD>Exposes all nodes matching the criteria given
+by <I>flags</I>. This is the inverse of the <B>hide</B> operation. The search is performed
+recursively for each node given by <I>tagOrId</I>. The valid flags are described
+below: <blockquote></DD>
+
+<DT><B>-name<I> pattern</I></B> </DT>
+<DD>Specifies pattern to match against node names. </DD>
+
+<DT><B>-full<I>
+pattern</I></B> </DT>
+<DD>Specifies pattern to match against node pathnames. </DD>
+
+<DT><B>-<I>option<I> pattern</I></I></B>
+</DT>
+<DD>Specifies pattern to match against the entry's configuration option. </DD>
+
+<DT><B>-exact</B>
+</DT>
+<DD>Match patterns exactly. The is the default. </DD>
+
+<DT><B>-glob</B> </DT>
+<DD><B>-glob</B> Use global pattern
+matching. Matching is done in a fashion similar to that used by the C-shell.
+ For the two strings to match, their contents must be identical except
+that the following special sequences may appear in pattern: <blockquote></DD>
+
+<DT><I>*</I> </DT>
+<DD>Matches
+ any sequence of characters in string, including a null string. </DD>
+
+<DT><I>?</I> </DT>
+<DD>Matches
+any single character in string. </DD>
+
+<DT><I>[<I>chars<I>]</I></I></I> </DT>
+<DD>Matches any character in the set
+given by <I>chars</I>. If a sequence of the form <I>x</I>-<I>y</I> appears in <I>chars</I>, then any
+character between <I>x</I> and <I>y</I>, inclusive, will match. </DD>
+
+<DT><I>\<I>x</I></I> </DT>
+<DD>Matches the single
+ character <I>x</I>. This provides a way of avoiding the special interpretation
+of the characters <I>*?[]\</I> in the pattern. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-regexp</B> </DT>
+<DD>Use regular expression pattern
+matching (i.e. the same as implemented by the <B>regexp</B> command). </DD>
+
+<DT><B>-nonmatching</B>
+</DT>
+<DD>Expose nodes that don't match. </DD>
+
+<DT><B>--</B> </DT>
+<DD>Indicates the end of flags. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort</B></I>
+?<I>operation</I>? <I>args...</I> </DT>
+<DD><blockquote></DD>
+
+<DT><I>pathName <B>sort auto</B></I> ?<I>boolean</I> </DT>
+<DD>Turns on/off automatic sorting
+of node entries. If <I>boolean</I> is true, entries will be automatically sorted
+as they are opened, closed, inserted, or deleted. If no <I>boolean</I> argument
+is provided, the current state is returned. </DD>
+
+<DT><I>pathName <B>sort cget</B></I> <I>option</I> </DT>
+<DD>Returns
+the current value of the configuration option given by <I>option</I>. <I>Option</I> may
+have any of the values accepted by the <B>configure</B> operation described below.
+</DD>
+
+<DT><I>pathName <B>sort configure</B></I> ?<I>option</I>? ?<I>value option value ...</I>? </DT>
+<DD>Query or modify
+the sorting configuration options of the widget. If no <I>option</I> is specified,
+returns a list describing all of the available options for <I>pathName</I> (see
+<B>Tk_ConfigureInfo</B> for information on the format of this list). If <I>option</I>
+is specified with no <I>value</I>, then the command returns a list describing
+the one named option (this list will be identical to the corresponding
+sublist of the value returned if no <I>option</I> is specified). If one or more
+<I>option-value</I> pairs are specified, then the command modifies the given sorting
+option(s) to have the given value(s); in this case the command returns
+an empty string. <I>Option</I> and <I>value</I> are described below: <blockquote></DD>
+
+<DT><B>-column<I> string</I></B> </DT>
+<DD>Specifies
+the column to sort. Entries in the widget are rearranged according to this
+column. If <I>column</I> is <I>""</I> then no sort is performed. </DD>
+
+<DT><B>-command<I> string</I></B> </DT>
+<DD>Specifies
+a Tcl procedure to be called when sorting nodes. The procedure is called
+with three arguments: the pathname of the widget and the fields of two
+entries. The procedure returns 1 if the first node is greater than the
+second, -1 is the second is greater, and 0 if equal. </DD>
+
+<DT><B>-decreasing<I> boolean</I></B>
+</DT>
+<DD>Indicates to sort in ascending/descending order. If <I>boolean</I> is true, then
+the entries as in descending order. The default is <I>no</I>. </DD>
+
+<DT><B>-mode<I> string</I></B> </DT>
+<DD>Specifies
+how to compare entries when sorting. <I>String</I> may be one of the following:
+<blockquote></DD>
+
+<DT><I>ascii</I> </DT>
+<DD>Use string comparison based upon the ASCII collation order. </DD>
+
+<DT><I>dictionary</I>
+ </DT>
+<DD>Use dictionary-style comparison. This is the same as <I>ascii</I> except (a) case
+is ignored except as a tie-breaker and (b) if two strings contain embedded
+numbers, the numbers compare as integers, not characters. For example,
+"bigBoy" sorts between "bigbang" and "bigboy", and "x10y" sorts between
+"x9y" and "x11y". </DD>
+
+<DT><I>integer</I> </DT>
+<DD>Compares fields as integers. </DD>
+
+<DT><I>real</I> </DT>
+<DD>Compares fields
+as floating point numbers. </DD>
+
+<DT><I>command</I> </DT>
+<DD>Use the Tcl proc specified by the <B>-command</B>
+option to compare entries when sorting. If no command is specified, the
+sort reverts to <I>ascii</I> sorting. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>sort once</B></I> ?<I>flags</I>? <I>tagOrId...</I> </DT>
+<DD>Sorts
+the children for each entries specified by <I>tagOrId</I>. By default, entries
+are sorted by name, but you can specify a Tcl proc to do your own comparisons.
+<blockquote></DD>
+
+<DT><B>-recurse</B> </DT>
+<DD>Recursively sort the entire branch, not just the children. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName
+<B>tag <I>operation args</I></B></I> </DT>
+<DD>Tags are a general means of selecting and marking nodes
+in the tree. A tag is just a string of characters, and it may take any form
+except that of an integer. The same tag may be associated with many different
+nodes. <P>
+Both <I>operation</I> and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below. <blockquote></DD>
+
+<DT><I>pathName</I>
+<B>tag add</B> <I>string</I> <I>id</I>... </DT>
+<DD>Adds the tag <I>string</I> to one of more entries. </DD>
+
+<DT><I>pathName</I> <B>tag
+delete</B> <I>string</I> <I>id</I>... </DT>
+<DD>Deletes the tag <I>string</I> from one or more entries. </DD>
+
+<DT><I>pathName</I>
+<B>tag forget</B> <I>string</I> </DT>
+<DD>Removes the tag <I>string</I> from all entries. It's not an error
+if no entries are tagged as <I>string</I>. </DD>
+
+<DT><I>pathName</I> <B>tag names</B> ?<I>id</I>? </DT>
+<DD>Returns a list
+of tags used. If an <I>id</I> argument is present, only those tags used by the
+node designated by <I>id</I> are returned. </DD>
+
+<DT><I>pathName</I> <B>tag nodes</B> <I>string</I> </DT>
+<DD>Returns a
+list of ids that have the tag <I>string</I>. If no node is tagged as <I>string</I>, then
+an empty string is returned. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text <I>operation</I></B></I> ?<I>args</I>? </DT>
+<DD>This operation
+is used to provide text editing for cells (data fields in a column) or
+entry labels. It has several forms, depending on <I>operation</I>: <blockquote></DD>
+
+<DT><I>pathName <B>text
+apply</B></I> </DT>
+<DD>Applies the edited buffer, replacing the entry label or data field.
+The edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cancel</B></I> </DT>
+<DD>Cancels the editing operation,
+reverting the entry label or data value back to the previous value. The
+edit window is hidden. </DD>
+
+<DT><I>pathName <B>text cget<I> value</I></B></I> </DT>
+<DD>Returns the current value
+of the configuration option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>configure</B> operation described below. </DD>
+
+<DT><I>pathName <B>text
+configure</B></I> ?<I>option value</I>? </DT>
+<DD>Query or modify the configuration options of the
+edit window. If no <I>option</I> is specified, returns a list describing all of
+the available options (see <B>Tk_ConfigureInfo</B> for information on the format
+of this list). If <I>option</I> is specified with no <I>value</I>, then the command returns
+a list describing the one named option (this list will be identical to
+the corresponding sublist of the value returned if no <I>option</I> is specified).
+ If one or more <I>option-value</I> pairs are specified, then the command modifies
+the given widget option(s) to have the given value(s); in this case the
+command returns an empty string. <I>Option</I> and <I>value</I> are described in the section
+ <FONT SIZE=-1><B>TEXT EDITING OPTIONS</B></FONT>
+ below. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>text delete<I> first last</I></B></I> </DT>
+<DD>Deletes the
+characters in the edit buffer between the two given character positions.
+ </DD>
+
+<DT><I>pathName <B>text get</B></I> ?<I>-root</I>? <I>x y</I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text icursor<I> index</I></B></I> </DT>
+<DD></DD>
+
+<DT><I>pathName <B>text
+index<I> index</I></B></I> </DT>
+<DD>Returns the text index of given <I>index</I>. </DD>
+
+<DT><I>pathName <B>text insert<I>
+index string</I></B></I> </DT>
+<DD>Insert the text string <I>string</I> into the edit buffer at the
+index <I>index</I>. For example, the index 0 will prepend the buffer. </DD>
+
+<DT><I>pathName
+<B>text selection<I> args</I></B></I> </DT>
+<DD>This operation controls the selection of the editing
+window. Note that this differs from the selection of entries. It has the
+following forms: <blockquote></DD>
+
+<DT><I>pathName <B>text selection adjust<I> index</I></B></I> </DT>
+<DD>Adjusts either the
+first or last index of the selection. </DD>
+
+<DT><I>pathName <B>text selection clear</B></I> </DT>
+<DD>Clears
+the selection. </DD>
+
+<DT><I>pathName <B>text selection from<I> index</I></B></I> </DT>
+<DD>Sets the anchor of the
+selection. </DD>
+
+<DT><I>pathName <B>text selection present</B></I> </DT>
+<DD>Indicates if a selection is present.
+</DD>
+
+<DT><I>pathName <B>text selection range<I> start end</I></B></I> </DT>
+<DD>Sets both the anchor and mark of
+the selection. </DD>
+
+<DT><I>pathName <B>text selection to<I> index</I></B></I> </DT>
+<DD>Sets the unanchored end
+(mark) of the selection. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>toggle <I>tagOrId</I></B></I> </DT>
+<DD>Opens or closes the node
+given by <I>tagOrId</I>. If the corresponding <B>-opencommand</B> or <B>-closecommand</B> option
+is set, then that command is also invoked. </DD>
+
+<DT><I>pathName <B>xview <I>args</I></B></I> </DT>
+<DD>This command
+is used to query and change the horizontal position of the information
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>xview</B></I> </DT>
+<DD>Returns a list containing two elements. Each element is a real fraction
+between 0 and 1; together they describe the horizontal span that is visible
+in the window. For example, if the first element is .2 and the second element
+is .6, 20% of the <B>treeview</B> widget's text is off-screen to the left, the middle
+40% is visible in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the <B>-xscrollcommand</B> option.
+</DD>
+
+<DT><I>pathName <B>xview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the window so that the character
+position given by <I>tagOrId</I> is displayed at the left edge of the window. Character
+positions are defined by the width of the character <B>0</B>. </DD>
+
+<DT><I>pathName <B>xview moveto<I>
+fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so that <I>fraction</I> of the total width
+of the <B>treeview</B> widget's text is off-screen to the left. <I>fraction</I> must be
+a fraction between 0 and 1. </DD>
+
+<DT><I>pathName <B>xview scroll <I>number what</I></B></I> </DT>
+<DD>This command
+shifts the view in the window left or right according to <I>number</I> and <I>what</I>.
+<I>Number</I> must be an integer. <I>What</I> must be either <B>units</B> or <B>pages</B> or an abbreviation
+of one of these. If <I>what</I> is <B>units</B>, the view adjusts left or right by <I>number</I>
+character units (the width of the <B>0</B> character) on the display; if it is
+<B>pages</B> then the view adjusts by <I>number</I> screenfuls. If <I>number</I> is negative
+then characters farther to the left become visible; if it is positive
+then characters farther to the right become visible. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>pathName <B>yview <I>?args</I></B></I>?
+</DT>
+<DD>This command is used to query and change the vertical position of the text
+in the widget's window. It can take any of the following forms: <blockquote></DD>
+
+<DT><I>pathName
+<B>yview</B></I> </DT>
+<DD>Returns a list containing two elements, both of which are real fractions
+between 0 and 1. The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means it is halfway
+through the treeview window, for example). The second element gives the
+position of the node just after the last one in the window, relative to
+the widget as a whole. These are the same values passed to scrollbars via
+the <B>-yscrollcommand</B> option. </DD>
+
+<DT><I>pathName <B>yview</B></I> <I>tagOrId</I> </DT>
+<DD>Adjusts the view in the
+window so that the node given by <I>tagOrId</I> is displayed at the top of the
+window. </DD>
+
+<DT><I>pathName <B>yview moveto<I> fraction</I></B></I> </DT>
+<DD>Adjusts the view in the window so
+that the node given by <I>fraction</I> appears at the top of the window. <I>Fraction</I>
+is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates
+the node one-third the way through the <B>treeview</B> widget, and so on. </DD>
+
+<DT><I>pathName
+<B>yview scroll <I>number what</I></B></I> </DT>
+<DD>This command adjusts the view in the window up
+or down according to <I>number</I> and <I>what</I>. <I>Number</I> must be an integer. <I>What</I> must
+be either <B>units</B> or <B>pages</B>. If <I>what</I> is <B>units</B>, the view adjusts up or down
+by <I>number</I> lines; if it is <B>pages</B> then the view adjusts by <I>number</I> screenfuls.
+If <I>number</I> is negative then earlier nodes become visible; if it is positive
+then later nodes become visible. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect11" HREF="#toc11">Treeview Options</A></H2>
+In addition to the <B>configure</B>
+operation, widget configuration options may also be set by the Tk <B>option</B>
+command. The class resource name is <I>TreeView</I>. <BR>
+<CODE>option add *TreeView.Foreground white<BR>
+option add *TreeView.Background blue<BR>
+</CODE><P>The following widget options are available:
+<DL>
+
+<DT><B>-activebackground <I>color</I></B> </DT>
+<DD>Sets
+the background color for active entries. A node is active when the mouse
+passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of the active node. A node is active when
+the mouse passes over it's entry or using the <B>activate</B> operation. </DD>
+
+<DT><B>-activeicons
+<I>images</I></B> </DT>
+<DD>Specifies images to be displayed for an entry's icon when it is active.
+<I>Images</I> is a list of two Tk images: the first image is displayed when the
+node is open, the second when it is closed. </DD>
+
+<DT><B>-autocreate <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I>
+is true, automatically create missing ancestor nodes when inserting new
+nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-allowduplicates <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is true, allow nodes with duplicate pathnames when inserting
+new nodes. Otherwise flag an error. The default is <I>no</I>. </DD>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets
+the background color of the widget. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B>
+</DT>
+<DD>Sets the width of the 3-D border around the outside edge of the widget.
+The <B>-relief</B> option determines if the border is to be drawn. The default
+is <I>2</I>. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked when a node
+is closed. You can overrider this for individual entries using the entry's
+<B>-closecommand</B> option. The default is <I>""</I>. Percent substitutions are performed
+on <I>string</I> before it is executed. The following substitutions are valid:
+<blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-cursor
+<I>cursor</I></B> </DT>
+<DD>Specifies the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-dashes <I>number</I></B>
+</DT>
+<DD>Sets the dash style of the horizontal and vertical lines drawn connecting
+ entries. <I>Number</I> is the length in pixels of the dashes and gaps in the line.
+If <I>number</I> is <I>0</I>, solid lines will be drawn. The default is <I>1</I> (dotted). </DD>
+
+<DT><B>-exportselection
+<I>boolean</I></B> </DT>
+<DD>Indicates if the selection is exported. If the widget is exporting
+its selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type <B>STRING</B>; the value of the
+selection will be the label of the selected nodes, separated by newlines.
+ The default is <I>no</I>. </DD>
+
+<DT><B>-flat <I>boolean</I></B> </DT>
+<DD>Indicates whether to display the tree as
+a flattened list. If <I>boolean</I> is true, then the hierarchy will be a list
+of full paths for the nodes. This option also has affect on sorting. See
+the <FONT SIZE=-1><B>SORT OPERATIONS</B></FONT>
+ section for more information. The default is <I>no</I>. </DD>
+
+<DT><B>-focusdashes
+<I>dashList</I></B> </DT>
+<DD>Sets the dash style of the outline rectangle drawn around the
+entry label of the node that current has focus. <I>Number</I> is the length in
+pixels of the dashes and gaps in the line. If <I>number</I> is <I>0</I>, a solid line
+will be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-focusforeground <I>color</I></B> </DT>
+<DD>Sets the color of
+the focus rectangle. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Specifies the
+font for entry labels. You can override this for individual entries with
+the entry's <B>-font</B> configuration option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of entry labels. You can override
+this for individual entries with the entry's <B>-foreground</B> configuration option.
+ The default is <I>black</I>. </DD>
+
+<DT><B>-height <I>pixels</I></B> </DT>
+<DD>Specifies the requested height of
+widget. The default is <I>400</I>. </DD>
+
+<DT><B>-hideroot <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, it indicates
+that no entry for the root node should be displayed. The default is <I>no</I>.
+</DD>
+
+<DT><B>-highlightbackground <I>color</I></B> </DT>
+<DD>Specifies the normal color of the traversal
+highlight region when the widget does not have the input focus. </DD>
+
+<DT><B>-highlightcolor
+<I>color</I></B> </DT>
+<DD>Specifies the color of the traversal highlight rectangle when the
+widget has the input focus. The default is <I>black</I>. </DD>
+
+<DT><B>-highlightthickness <I>pixels</I></B>
+</DT>
+<DD>Specifies the width of the highlight rectangle indicating when the widget
+has input focus. The value may have any of the forms acceptable to <B>Tk_GetPixels</B>.
+ If the value is zero, no focus highlight will be displayed. The default
+is <I>2</I>. </DD>
+
+<DT><B>-icons <I>images</I></B> </DT>
+<DD>Specifies images for the entry's icon. <I>Images</I> is a list
+of two Tk images: the first image is displayed when the node is open,
+the second when it is closed. </DD>
+
+<DT><B>-linecolor <I>color</I></B> </DT>
+<DD>Sets the color of the connecting
+lines drawn between entries. The default is <I>black</I>. </DD>
+
+<DT><B>-linespacing <I>pixels</I></B> </DT>
+<DD>Sets
+the number of pixels spacing between entries. The default is <I>0</I>. </DD>
+
+<DT><B>-linewidth
+<I>pixels</I></B> </DT>
+<DD>Set the width of the lines drawn connecting entries. If <I>pixels</I> is
+<I>0</I>, no vertical or horizontal lines are drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-newtags
+<I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, when sharing a tree object (see the <B>-tree</B> option),
+don't share its tags too. The default is <I>0</I>. </DD>
+
+<DT><B>-opencommand <I>string</I></B> </DT>
+<DD>Specifies
+a Tcl script to be invoked when a node is open. You can override this
+for individual entries with the entry's <B>-opencommand</B> configuration option.
+ The default is <I>""</I>. Percent substitutions are performed on <I>string</I> before
+ it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname
+of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname of the node.
+</DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect for the widget. <I>Relief</I> specifies how the <B>treeview</B> widget
+should appear relative to widget it is packed into; for example, <I>raised</I>
+means the <B>treeview</B> widget should appear to protrude. The default is <I>sunken</I>.
+</DD>
+
+<DT><B>-scrollmode <I>mode</I></B> </DT>
+<DD>Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is <I>hierbox</I>. <blockquote></DD>
+
+<DT><I>listbox</I> </DT>
+<DD>Like the <B>listbox</B>
+widget, the last entry can always be scrolled to the top of the widget
+window. This allows the scrollbar thumb to shrink as the last entry is
+scrolled upward. </DD>
+
+<DT><I>hierbox</I> </DT>
+<DD>Like the <B>hierbox</B> widget, the last entry can only
+be viewed at the bottom of the widget window. The scrollbar stays a constant
+size. </DD>
+
+<DT><I>canvas</I> </DT>
+<DD>Like the <B>canvas</B> widget, the entries are bound within the
+scrolling area. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-selectbackground <I>color</I></B> </DT>
+<DD>Sets the background color selected
+node entries. The default is <I>#ffffea</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the raised 3-D border drawn around the labels of selected entries. The
+default is <I>0</I>. <B>-selectcommand <I>string</I></B> Specifies a Tcl script to invoked when
+the set of selected nodes changes. The default is <I>""</I>. </DD>
+
+<DT><B>-selectforeground <I>color<B>
+</B></I></B></DT>
+<DD>Sets the color of the labels of selected node entries. The default is <I>black</I>.
+</DD>
+
+<DT><B>-selectmode <I>mode</I></B> </DT>
+<DD>Specifies the selection mode. If <I>mode</I> is <I>single</I>, only one
+node can be selected at a time. If <I>multiple</I> more than one node can be selected.
+The default is <I>single</I>. </DD>
+
+<DT><B>-separator <I>string</I></B> </DT>
+<DD>Specifies the character sequence
+to use when spliting the path components. The separator may be several
+characters wide (such as "::") Consecutive separators in a pathname are
+treated as one. If <I>string</I> is the empty string, the pathnames are Tcl lists.
+ Each element is a path component. The default is <I>""</I>. </DD>
+
+<DT><B>-showtitles <I>boolean</I></B>
+</DT>
+<DD>If <I>boolean</I> is false, column titles are not be displayed. The default is
+<I>yes</I>. </DD>
+
+<DT><B>-sortselection <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, nodes in the selection are
+ordered as they are currently displayed (depth-first or sorted), not in
+the order they were selected. The default is <I>no</I>. </DD>
+
+<DT><B>-takefocus</B> <I>focus</I> </DT>
+<DD>Provides
+information used when moving the focus from window to window via keyboard
+traversal (e.g., Tab and Shift-Tab). If <I>focus</I> is <I>0</I>, this means that this window
+should be skipped entirely during keyboard traversal. <I>1</I> means that the
+this window should always receive the input focus. An empty value means
+that the traversal scripts make the decision whether to focus on the window.
+The default is <I>"1"</I>. </DD>
+
+<DT><B>-trim <I>string</I></B> </DT>
+<DD>Specifies a string leading characters to
+trim from entry pathnames before parsing. This only makes sense if the
+<B>-separator</B> is also set. The default is <I>""</I>. </DD>
+
+<DT><B>-width <I>pixels</I></B> </DT>
+<DD>Sets the requested
+width of the widget. If <I>pixels</I> is 0, then the with is computed from the
+contents of the <B>treeview</B> widget. The default is <I>200</I>. </DD>
+
+<DT><B>-xscrollcommand <I>string</I></B>
+</DT>
+<DD>Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window changes,
+the widget will generate a Tcl command by concatenating the scroll command
+and two numbers. If this option is not specified, then no command will
+be executed. </DD>
+
+<DT><B>-xscrollincrement</B> <I>pixels</I> </DT>
+<DD>Sets the horizontal scrolling distance.
+The default is 20 pixels. </DD>
+
+<DT><B>-yscrollcommand <I>string</I></B> </DT>
+<DD>Specifies the prefix for
+a command used to communicate with vertical scrollbars. Whenever the vertical
+view in the widget's window changes, the widget will generate a Tcl command
+by concatenating the scroll command and two numbers. If this option is
+not specified, then no command will be executed. </DD>
+
+<DT><B>-yscrollincrement</B> <I>pixels</I>
+</DT>
+<DD>Sets the vertical scrolling distance. The default is 20 pixels. </DD>
+</DL>
+
+<H2><A NAME="sect12" HREF="#toc12">Entry Options</A></H2>
+Many
+widget configuration options have counterparts in entries. For example,
+there is a <B>-closecommand</B> configuration option for both widget itself and
+for individual entries. Options set at the widget level are global for
+all entries. If the entry configuration option is set, then it overrides
+the widget option. This is done to avoid wasting memory by replicated options.
+ Most entries will have redundant options. <P>
+There is no resource class or
+name for entries.
+<DL>
+
+<DT><B>-activeicons <I>images</I></B> </DT>
+<DD>Specifies images to be displayed as
+the entry's icon when it is active. This overrides the global <B>-activeicons</B>
+configuration option for the specific entry. <I>Images</I> is a list of two Tk
+images: the first image is displayed when the node is open, the second
+when it is closed. </DD>
+
+<DT><B>-bindtags <I>tagList</I></B> </DT>
+<DD>Specifies the binding tags for nodes.
+ <I>TagList</I> is a list of binding tag names. The tags and their order will
+determine how events are handled for nodes. Each tag in the list matching
+the current event sequence will have its Tcl command executed. The default
+value is <I>all</I>. </DD>
+
+<DT><B>-button <I>string</I></B> </DT>
+<DD>Indicates whether a button should be displayed
+on the left side of the node entry. <I>String</I> can be <I>yes</I>, <I>no</I>, or <I>auto</I>. If
+<I>auto</I>, then a button is automatically displayed if the node has children.
+ This is the default. </DD>
+
+<DT><B>-closecommand <I>string</I></B> </DT>
+<DD>Specifies a Tcl script to be invoked
+when the node is closed. This overrides the global <B>-closecommand</B> option
+for this entry. The default is <I>""</I>. Percent substitutions are performed on
+<I>string</I> before it is executed. The following substitutions are valid: <blockquote></DD>
+
+<DT><I>%W</I>
+</DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The full pathname
+of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single percent. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>-data
+<I>string</I></B> </DT>
+<DD>Sets data fields for the node. <I>String</I> is a list of name-value pairs
+to be set. The default is <I>""</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B> </DT>
+<DD>Sets the font for entry labels.
+ This overrides the widget's <B>-font</B> option for this node. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets the text color of the entry label. This overrides
+the widget's <B>-foreground</B> configuration option. The default is <I>""</I>. </DD>
+
+<DT><B>-icons <I>images</I></B>
+</DT>
+<DD>Specifies images to be displayed for the entry's icon. This overrides the
+global <B>-icons</B> configuration option. <I>Images</I> is a list of two Tk images: the
+first image is displayed when the node is open, the second when it is closed.
+</DD>
+
+<DT><B>-label <I>string</I></B> </DT>
+<DD>Sets the text for the entry's label. If not set, this defaults
+to the name of the node. The default is <I>""</I>. </DD>
+
+<DT><B>-opencommand <I>string</I></B> </DT>
+<DD>Specifies
+a Tcl script to be invoked when the entry is opened. This overrides the
+widget's <B>-opencommand</B> option for this node. The default is <I>""</I>. Percent substitutions
+are performed on <I>string</I> before it is executed. The following substitutions
+are valid: <blockquote></DD>
+
+<DT><I>%W</I> </DT>
+<DD>The pathname of the widget. </DD>
+
+<DT><I>%p</I> </DT>
+<DD>The name of the node. </DD>
+
+<DT><I>%P</I> </DT>
+<DD>The
+full pathname of the node. </DD>
+
+<DT><I>%#</I> </DT>
+<DD>The id of the node. </DD>
+
+<DT><I>%%</I> </DT>
+<DD>Translates to a single
+percent. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect13" HREF="#toc13">Button Options</A></H2>
+Button configuration options may also be set by the
+<B>option</B> command. The resource subclass is <I>Button</I>. The resource name is always
+<I>button</I>. <BR>
+<CODE>option add *TreeView.Button.Foreground white<BR>
+option add *TreeView.button.Background blue<BR>
+</CODE><P>The following are the configuration options available for buttons.
+<DL>
+
+<DT><B>-activebackground
+<I>color</I></B> </DT>
+<DD>Sets the background color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-activeforeground
+<I>color</I></B> </DT>
+<DD>Sets the foreground color of active buttons. A button is made active
+when the mouse passes over it or by the <B>button activate</B> operation. </DD>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background of the button. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth
+<I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around the button. The <B>-relief</B> option
+determines if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-closerelief <I>relief</I></B>
+</DT>
+<DD>Specifies the 3-D effect for the closed button. <I>Relief</I> indicates how the
+button should appear relative to the widget; for example, <I>raised</I> means
+the button should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-cursor <I>cursor</I></B>
+</DT>
+<DD>Sets the widget's cursor. The default cursor is <I>""</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Sets
+the foreground color of buttons. The default is <I>black</I>. </DD>
+
+<DT><B>-images <I>images</I></B> </DT>
+<DD>Specifies
+images to be displayed for the button. <I>Images</I> is a list of two Tk images:
+ the first image is displayed when the button is open, the second when
+it is closed. If the <I>images</I> is the empty string, then a plus/minus gadget
+is drawn. The default is <I>""</I>. </DD>
+
+<DT><B>-openrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of
+the open button. <I>Relief</I> indicates how the button should appear relative
+to the widget; for example, <I>raised</I> means the button should appear to protrude.
+ The default is <I>flat</I>. </DD>
+
+<DT><B>-size <I>pixels</I></B> </DT>
+<DD>Sets the requested size of the button.
+ The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect14" HREF="#toc14">Column Options</A></H2>
+Column configuration options may also
+be set by the <B>option</B> command. The resource subclass is <I>Column</I>. The resource
+name is the name of the column. <BR>
+<CODE>option add *TreeView.Column.Foreground white<BR>
+option add *TreeView.treeView.Background blue<BR>
+</CODE><P>The following configuration options are available for columns.
+<DL>
+
+<DT><B>-background
+<I>color</I></B> </DT>
+<DD>Sets the background color of the column. This overrides the widget's
+<B>-background</B> option. The default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width
+of the 3-D border of the column. The <B>-relief</B> option determines if a border
+is to be drawn. The default is <I>0</I>. </DD>
+
+<DT><B>-edit <I>boolean</I></B> </DT>
+<DD>Indicates if the column's
+data fields can be edited. If <I>boolean</I> is false, the data fields in the
+column may not be edited. The default is <I>yes</I>. </DD>
+
+<DT><B>-foreground <I>color</I></B> </DT>
+<DD>Specifies
+the foreground color of the column. You can override this for individual
+entries with the entry's <B>-foreground</B> option. The default is <I>black</I>. </DD>
+
+<DT><B>-font <I>fontName</I></B>
+ </DT>
+<DD>Sets the font for a column. You can override this for individual entries
+with the entry's <B>-font</B> option. The default is <I>*-Helvetica-Bold-R-Normal-*-12-120-*</I>.
+</DD>
+
+<DT><B>-hide <I>boolean</I></B> </DT>
+<DD>If <I>boolean</I> is true, the column is not displayed. The default
+is <I>yes</I>. </DD>
+
+<DT><B>-justify <I>justify</I></B> </DT>
+<DD>Specifies how the column data fields title should
+be justified within the column. This matters only when the column is wider
+than the data field to be display. <I>Justify</I> must be <I>left</I>, <I>right</I>, or <I>center</I>.
+ The default is <I>left</I>. </DD>
+
+<DT><B>-pad <I>pad</I></B> </DT>
+<DD>Specifies how much padding for the left and
+right sides of the column. <I>Pad</I> is a list of one or two screen distances.
+ If <I>pad</I> has two elements, the left side of the column is padded by the
+first distance and the right side by the second. If <I>pad</I> has just one distance,
+both the left and right sides are padded evenly. The default is <I>2</I>. </DD>
+
+<DT><B>-relief
+<I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the column. <I>Relief</I> specifies how the
+column should appear relative to the widget; for example, <I>raised</I> means
+the column should appear to protrude. The default is <I>flat</I>. </DD>
+
+<DT><B>-state <I>state</I></B> </DT>
+<DD>Sets
+the state of the column. If <I>state</I> is <I>disable</I> then the column title can not
+be activated nor invoked. The default is <I>normal</I>. </DD>
+
+<DT><B>-text <I>string</I></B> </DT>
+<DD>Sets the title
+for the column. The default is <I>""</I>. </DD>
+
+<DT><B>-titleforeground <I>color</I></B> </DT>
+<DD>Sets the foreground
+color of the column title. The default is <I>black</I>. </DD>
+
+<DT><B>-titleshadow <I>color</I></B> </DT>
+<DD>Sets
+the color of the drop shadow of the column title. The default is <I>""</I>. </DD>
+
+<DT><B>-width
+<I>pixels</I></B> </DT>
+<DD>Sets the requested width of the column. This overrides the computed
+with of the column. If <I>pixels</I> is 0, the width is computed as from the contents
+of the column. The default is <I>0</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect15" HREF="#toc15">Text Editing Options</A></H2>
+Text edit window configuration
+options may also be set by the <B>option</B> command. The resource class is <I>TreeViewEditor</I>.
+The resource name is always <I>edit</I>. <BR>
+<CODE>option add *TreeViewEditor.Foreground white<BR>
+option add *edit.Background blue<BR>
+</CODE><P>The following are the configuration options available for the text editing
+window.
+<DL>
+
+<DT><B>-background <I>color</I></B> </DT>
+<DD>Sets the background of the text edit window. The
+default is <I>white</I>. </DD>
+
+<DT><B>-borderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the edit window. The <B>-relief</B> option determines if a border is to be drawn.
+ The default is <I>1</I>. </DD>
+
+<DT><B>-exportselection <I>boolean</I></B> </DT>
+<DD>Indicates if the text selection
+is exported. If the edit window is exporting its selection then it will
+observe the standard X11 protocols for handling the selection. Selections
+are available as type <B>STRING</B>. The default is <I>no</I>. </DD>
+
+<DT><B>-relief <I>relief</I></B> </DT>
+<DD>Specifies
+the 3-D effect of the edit window. <I>Relief</I> indicates how the background should
+appear relative to the edit window; for example, <I>raised</I> means the background
+should appear to protrude. The default is <I>solid</I>. </DD>
+
+<DT><B>-selectbackground <I>color</I></B>
+</DT>
+<DD>Sets the background of the selected text in the edit window. The default
+is <I>white</I>. </DD>
+
+<DT><B>-selectborderwidth <I>pixels</I></B> </DT>
+<DD>Sets the width of the 3-D border around
+the selected text in the edit window. The <B>-selectrelief</B> option determines
+if a border is to be drawn. The default is <I>1</I>. </DD>
+
+<DT><B>-selectforeground <I>color</I></B> </DT>
+<DD>Sets
+the foreground of the selected text in the edit window. The default is
+<I>white</I>. </DD>
+
+<DT><B>-selectrelief <I>relief</I></B> </DT>
+<DD>Specifies the 3-D effect of the selected text
+in the edit window. <I>Relief</I> indicates how the text should appear relative
+to the edit window; for example, <I>raised</I> means the text should appear to
+protrude. The default is <I>flat</I>. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect16" HREF="#toc16">Default Bindings</A></H2>
+Tk automatically creates
+class bindings for treeviews that give them Motif-like behavior. Much of
+the behavior of a <B>treeview</B> widget is determined by its <B>-selectmode</B> option,
+which selects one of two ways of dealing with the selection. <P>
+If the selection
+mode is <B>single</B>, only one node can be selected at a time. Clicking button
+1 on an node selects it and deselects any other selected item. <P>
+If the selection
+mode is <B>multiple</B>, any number of entries may be selected at once, including
+discontiguous ranges. Clicking Control-Button-1 on a node entry toggles its
+selection state without affecting any other entries. Pressing Shift-Button-1
+on a node entry selects it, extends the selection.
+<OL>
+<LI>In <B>extended</B> mode, the
+selected range can be adjusted by pressing button 1 with the Shift key
+down: this modifies the selection to consist of the entries between the
+anchor and the entry under the mouse, inclusive. The un-anchored end of this
+new selection can also be dragged with the button down. </LI><LI>In <B>extended</B> mode,
+pressing button 1 with the Control key down starts a toggle operation:
+the anchor is set to the entry under the mouse, and its selection state
+is reversed. The selection state of other entries isn't changed. If the mouse
+is dragged with button 1 down, then the selection state of all entries
+between the anchor and the entry under the mouse is set to match that of
+the anchor entry; the selection state of all other entries remains what
+it was before the toggle operation began. </LI><LI>If the mouse leaves the treeview
+window with button 1 down, the window scrolls away from the mouse, making
+information visible that used to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the button
+is released, or the end of the hierarchy is reached. </LI><LI>Mouse button 2 may
+be used for scanning. If it is pressed and dragged over the <B>treeview</B> widget,
+the contents of the hierarchy drag at high speed in the direction the mouse
+moves. </LI><LI>If the Up or Down key is pressed, the location cursor (active entry)
+moves up or down one entry. If the selection mode is <B>browse</B> or <B>extended</B>
+then the new active entry is also selected and all other entries are deselected.
+In <B>extended</B> mode the new active entry becomes the selection anchor. </LI><LI>In <B>extended</B>
+mode, Shift-Up and Shift-Down move the location cursor (active entry) up
+or down one entry and also extend the selection to that entry in a fashion
+similar to dragging with mouse button 1. </LI><LI>The Left and Right keys scroll
+the <B>treeview</B> widget view left and right by the width of the character <B>0</B>.
+Control-Left and Control-Right scroll the <B>treeview</B> widget view left and right
+by the width of the window. Control-Prior and Control-Next also scroll left
+and right by the width of the window. </LI><LI>The Prior and Next keys scroll the
+<B>treeview</B> widget view up and down by one page (the height of the window).
+</LI><LI>The Home and End keys scroll the <B>treeview</B> widget horizontally to the left
+and right edges, respectively. </LI><LI>Control-Home sets the location cursor to the
+the first entry, selects that entry, and deselects everything else in
+the widget. </LI><LI>Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else in the widget. </LI><LI>In <B>extended</B>
+mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End
+extends the selection to the last entry. </LI><LI>In <B>multiple</B> mode, Control-Shift-Home
+moves the location cursor to the first entry and Control-Shift-End moves
+the location cursor to the last entry. </LI><LI>The space and Select keys make a
+selection at the location cursor (active entry) just as if mouse button
+1 had been pressed over this entry. </LI><LI>In <B>extended</B> mode, Control-Shift-space
+and Shift-Select extend the selection to the active entry just as if button
+1 had been pressed with the Shift key down. </LI><LI>In <B>extended</B> mode, the Escape
+key cancels the most recent selection and restores all the entries in the
+selected range to their previous selection state. </LI><LI>Control-slash selects everything
+in the widget, except in <B>single</B> and <B>browse</B> modes, in which case it selects
+the active entry and deselects everything else. </LI><LI>Control-backslash deselects
+everything in the widget, except in <B>browse</B> mode where it has no effect.
+</LI><LI>The F16 key (labelled Copy on many Sun workstations) or Meta-w copies the
+selection in the widget to the clipboard, if there is a selection. </LI>
+</OL>
+<P>
+The behavior
+of <B>treeview</B> widgets can be changed by defining new bindings for individual
+widgets or by redefining the class bindings.
+<H3><A NAME="sect17" HREF="#toc17">Widget Bindings</A></H3>
+In addition
+to the above behavior, the following additional behavior is defined by
+the default widget class (TreeView) bindings.
+<DL>
+
+<DT><I>&lt;ButtonPress-2&gt;</I></DT>
+<DD>Starts scanning.
+ </DD>
+
+<DT><I>&lt;B2-Motion&gt;</I></DT>
+<DD>Adjusts the scan. </DD>
+
+<DT><I>&lt;ButtonRelease-2&gt;</I></DT>
+<DD>Stops scanning. </DD>
+
+<DT><I>&lt;B1-Leave&gt;</I></DT>
+<DD>Starts auto-scrolling.
+</DD>
+
+<DT><I>&lt;B1-Enter&gt;</I></DT>
+<DD>Starts auto-scrolling </DD>
+
+<DT><I>&lt;KeyPress-Up&gt;</I></DT>
+<DD>Moves the focus to the previous
+entry. </DD>
+
+<DT><I>&lt;KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the next entry. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Up&gt;</I></DT>
+<DD>Moves
+the focus to the previous sibling. </DD>
+
+<DT><I>&lt;Shift-KeyPress-Down&gt;</I></DT>
+<DD>Moves the focus to the
+next sibling. </DD>
+
+<DT><I>&lt;KeyPress-Prior&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or hidden
+entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Next&gt;</I></DT>
+<DD>Move the focus to the last entry. Closed
+or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-Left&gt;</I></DT>
+<DD>Closes the entry. It is not an
+error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-Right&gt;</I></DT>
+<DD>Opens the entry, displaying
+its children. It is not an error if the entry has no children. </DD>
+
+<DT><I>&lt;KeyPress-space&gt;</I></DT>
+<DD>In
+"single" select mode this selects the entry. In "multiple" mode, it toggles
+the entry (if it was previous selected, it is not deselected). </DD>
+
+<DT><I>&lt;KeyRelease-space&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress-Return&gt;</I></DT>
+<DD>Sets the focus to the current entry. </DD>
+
+<DT><I>&lt;KeyRelease-Return&gt;</I></DT>
+<DD>Turns
+off select mode. </DD>
+
+<DT><I>&lt;KeyPress&gt;</I></DT>
+<DD>Moves to the next entry whose label starts with
+the letter typed. </DD>
+
+<DT><I>&lt;KeyPress-Home&gt;</I></DT>
+<DD>Moves the focus to first entry. Closed or
+hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-End&gt;</I></DT>
+<DD>Move the focus to the last entry.
+Closed or hidden entries are ignored. </DD>
+
+<DT><I>&lt;KeyPress-F1&gt;</I></DT>
+<DD>Opens all entries. </DD>
+
+<DT><I>&lt;KeyPress-F2&gt;</I></DT>
+<DD>Closes
+all entries (except root). </DD>
+</DL>
+
+<H3><A NAME="sect18" HREF="#toc18">Button Bindings</A></H3>
+Buttons have bindings. There are
+associated with the "all" bindtag (see the entry's -bindtag option). You
+can use the <B>bind</B> operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the button of
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the button back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the view so that the current entry is visible. </DD>
+</DL>
+
+<H3><A NAME="sect19" HREF="#toc19">Entry Bindings</A></H3>
+Entries have
+default bindings. There are associated with the "all" bindtag (see the
+entry's -bindtag option). You can use the <B>bind</B> operation to modify them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current entry. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the entry back to its normal state. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets
+the selection anchor the current entry. </DD>
+
+<DT><I>&lt;Double-ButtonPress-1&gt;</I></DT>
+<DD>Toggles the selection
+of the current entry. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>For "multiple" mode only. Saves the current
+location of the pointer for auto-scrolling. Resets the selection mark.
+</DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>For "multiple" mode only. Sets the selection anchor to the
+ current entry. </DD>
+
+<DT><I>&lt;Shift-ButtonPress-1&gt;</I></DT>
+<DD>For "multiple" mode only. Extends the selection.
+</DD>
+
+<DT><I>&lt;Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder.
+Does nothing. </DD>
+
+<DT><I>&lt;Shift-ButtonRelease-1&gt;</I></DT>
+<DD>Stop auto-scrolling. </DD>
+
+<DT><I>&lt;Control-ButtonPress-1&gt;</I></DT>
+<DD>For
+"multiple" mode only. Toggles and extends the selection. </DD>
+
+<DT><I>&lt;Control-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-ButtonRelease-1&gt;</I></DT>
+<DD>Stops
+auto-scrolling. </DD>
+
+<DT><I>&lt;Control-Shift-ButtonPress-1&gt;</I></DT>
+<DD>??? </DD>
+
+<DT><I>&lt;Control-Shift-Double-ButtonPress-1&gt;</I></DT>
+<DD>Place
+holder. Does nothing. </DD>
+
+<DT><I>&lt;Control-Shift-B1-Motion&gt;</I></DT>
+<DD>Place holder. Does nothing. </DD>
+</DL>
+
+<H3><A NAME="sect20" HREF="#toc20">Column
+Bindings</A></H3>
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the <B>column bind</B>
+operation to change them.
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights the current column title. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns
+the column back to its normal state. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Invokes the command (see
+the column's -command option) if one if specified. </DD>
+</DL>
+
+<H3><A NAME="sect21" HREF="#toc21">Column Rule Bindings</A></H3>
+
+<DL>
+
+<DT><I>&lt;Enter&gt;</I></DT>
+<DD>Highlights
+the current and activates the ruler. </DD>
+
+<DT><I>&lt;Leave&gt;</I></DT>
+<DD>Returns the column back to its
+normal state. Deactivates the ruler. </DD>
+
+<DT><I>&lt;ButtonPress-1&gt;</I></DT>
+<DD>Sets the resize anchor for
+the column. </DD>
+
+<DT><I>&lt;B1-Motion&gt;</I></DT>
+<DD>Sets the resize mark for the column. </DD>
+
+<DT><I>&lt;ButtonRelease-1&gt;</I></DT>
+<DD>Adjust
+the size of the column, based upon the resize anchor and mark positions.
+</DD>
+</DL>
+
+<H2><A NAME="sect22" HREF="#toc22">Example</A></H2>
+The <B>treeview</B> command creates a new widget. <BR>
+<CODE>treeview .h -bg white<BR>
+</CODE><P>A new Tcl command <I>.h</I> is also created. This command can be used to query
+and modify the <B>treeview</B> widget. For example, to change the background
+color of the table to "green", you use the new command and the widget's
+<B>configure</B> operation. <BR>
+<CODE># Change the background color.<BR>
+.h configure -background "green"<BR>
+</CODE><P>By default, the <B>treeview</B> widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the widget.
+ Above, the new tree object name is ".h". But you can use the <B>-tree</B> option
+to specify the name of another tree. <BR>
+<CODE># View the tree "myTree".<BR>
+.h configure -tree "myTree"<BR>
+</CODE><P>When a new tree is created, it contains only a root node. The node is automatically
+opened. The id of the root node is always <I>0</I> (you can use also use the special
+id <I>root</I>). The <B>insert</B> operation lets you insert one or more new entries into
+the tree. The last argument is the node's <I>pathname</I>. <BR>
+<CODE># Create a new entry named "myEntry"<BR>
+set id [.h insert end "myEntry"]<BR>
+</CODE><P>This appends a new node named "myEntry". It will positioned as the last
+child of the root of the tree (using the position "end"). You can supply
+another position to order the node within its siblings. <BR>
+<CODE># Prepend "fred".<BR>
+set id [.h insert 0 "fred"]<BR>
+</CODE><P>Entry names do not need to be unique. By default, the node's label is its
+name. To supply a different text label, add the <B>-label</B> option. <BR>
+<CODE># Create a new node named "fred"<BR>
+set id [.h insert end "fred" -label "Fred Flintstone"]<BR>
+</CODE><P>The <B>insert</B> operation returns the id of the new node. You can also use the
+<B>index</B> operation to get this information. <BR>
+<CODE># Get the id of "fred"<BR>
+.h index "fred"<BR>
+</CODE><P>To insert a node somewhere other than root, use the <B>-at</B> switch. It takes
+the id of the node where the new child will be added. <BR>
+<CODE># Create a new node "barney" in "fred".<BR>
+.h insert -at $id end "barney" <BR>
+</CODE><P>A pathname describes the path to an entry in the hierarchy. It's a list
+of entry names that compose the path in the tree. Therefore, you can also
+add "barney" to "fred" as follows. <BR>
+<CODE># Create a new sub-entry of "fred"<BR>
+.h insert end "fred barney" <BR>
+</CODE><P>Every name in the list is ancestor of the next. All ancestors must already
+exist. That means that an entry "fred" is an ancestor of "barney" and must
+already exist. But you can use the <B>-autocreate</B> configuration option to force
+the creation of ancestor nodes. <BR>
+<CODE># Force the creation of ancestors.<BR>
+.h configure -autocreate yes <BR>
+.h insert end "fred barney wilma betty" <BR>
+</CODE><P>Sometimes the pathname is already separated by a character sequence rather
+than formed as a list. A file name is a good example of this. You can use
+the <B>-separator</B> option to specify a separator string to split the path into
+its components. Each pathname inserted is automatically split using the
+separator string as a separator. Multiple separators are treated as one.
+<BR>
+<CODE>.h configure -separator /<BR>
+.h insert end "/usr/local/tcl/bin" <BR>
+</CODE><P>If the path is prefixed by extraneous characters, you can automatically
+trim it off using the <B>-trim</B> option. It removed the string from the path
+before it is parsed. <BR>
+<CODE>.h configure -trim C:/windows -separator /<BR>
+.h insert end "C:/window/system" <BR>
+</CODE><P>You can insert more than one entry at a time with the <B>insert</B> operation.
+ This can be much faster than looping over a list of names. <BR>
+<CODE># The slow way<BR>
+foreach f [glob $dir/*] {<BR>
+ .h insert end $f<BR>
+}<BR>
+# The fast way<BR>
+eval .h insert end [glob $dir/*]<BR>
+</CODE><P>In this case, the <B>insert</B> operation will return a list of ids of the new
+entries. <P>
+You can delete entries with the <B>delete</B> operation. It takes one
+or more tags of ids as its argument. It deletes the entry and all its children.
+<BR>
+<CODE>.h delete $id<BR>
+</CODE><P>Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the <B>-label</B> option that
+sets the entry's text label. The <B>entry configure</B> operation lets you set
+or modify an entry's configuration options. <BR>
+<CODE>.h entry configure $id -color red -font fixed<BR>
+</CODE><P>You can hide an entry and its children using the <B>-hide</B> option. <BR>
+<CODE>.h entry configure $id -hide yes<BR>
+</CODE><P>More that one entry can be configured at once. All entries specified are
+configured with the same options. <BR>
+<CODE>.h entry configure $i1 $i2 $i3 $i4 -color brown <BR>
+</CODE><P>An icon is displayed for each entry. It's a Tk image drawn to the left of
+the label. You can set the icon with the entry's <B>-icons</B> option. It takes
+a list of two image names: one to represent the open entry, another when
+it is closed. <BR>
+<CODE>set im1 [image create photo -file openfolder.gif]<BR>
+set im2 [image create photo -file closefolder.gif]<BR>
+.h entry configure $id -icons "$im1 $im2"<BR>
+</CODE><P>If <B>-icons</B> is set to the empty string, no icons are display. <P>
+If an entry has
+children, a button is displayed to the left of the icon. Clicking the mouse
+on this button opens or closes the sub-hierarchy. The button is normally
+a <I>+</I> or <I>-</I> symbol, but can be configured in a variety of ways using the <B>button
+configure</B> operation. For example, the <I>+</I> and <I>-</I> symbols can be replaced with
+Tk images. <BR>
+<CODE>set im1 [image create photo -file closefolder.gif]<BR>
+set im2 [image create photo -file downarrow.gif]<BR>
+.h button configure $id -images "$im1 $im2" \<BR>
+ -openrelief raised -closerelief raised<BR>
+</CODE><P>Entries can contain an arbitrary number of <I>data fields</I>. Data fields are
+name-value pairs. Both the value and name are strings. The entry's <B>-data</B> option
+lets you set data fields. <BR>
+<CODE>.h entry configure $id -data {mode 0666 group users}<BR>
+</CODE><P>The <B>-data</B> takes a list of name-value pairs. <P>
+You can display these data fields
+as <I>columns</I> in the <B>treeview</B> widget. You can create and configure columns
+with the <B>column</B> operation. For example, to add a new column to the widget,
+use the <B>column insert</B> operation. The last argument is the name of the data
+field that you want to display. <BR>
+<CODE>.h column insert end "mode"<BR>
+</CODE><P>The column title is displayed at the top of the column. By default, it's
+is the field name. You can override this using the column's <B>-text</B> option.
+<BR>
+<CODE>.h column insert end "mode" -text "File Permissions"<BR>
+</CODE><P>Columns have several configuration options. The <B>column configure</B> operation
+lets you query or modify column options. <BR>
+<CODE>.h column configure "mode" -justify left<BR>
+</CODE><P>The <B>-justify</B> option says how the data is justified within in the column.
+ The <B>-hide</B> option indicates whether the column is displayed. <BR>
+<CODE>.h column configure "mode" -hide yes<BR>
+</CODE><P>Entries can be selected by clicking on the mouse. Selected entries are
+drawn using the colors specified by the <B>-selectforeground</B> and <B>-selectbackground</B>
+configuration options. The selection itself is managed by the <B>selection</B>
+operation. <BR>
+<CODE># Clear all selections<BR>
+.h selection clear 0 end<BR>
+# Select the root node<BR>
+.h selection set 0 <BR>
+</CODE><P>The <B>curselection</B> operation returns a list of ids of all the selected entries.
+<BR>
+<CODE>set ids [.h curselection]<BR>
+</CODE><P>You can use the <B>get</B> operation to convert the ids to their pathnames. <BR>
+<CODE>set names [eval .h get -full $ids]<BR>
+</CODE><P>If a treeview is exporting its selection (using the <B>-exportselection</B> option),
+then it will observe the standard X11 protocols for handling the selection.
+ Treeview selections are available as type <B>STRING</B>; the value of the selection
+will be the pathnames of the selected entries, separated by newlines. <P>
+The
+<B>treeview</B> supports two modes of selection: <I>single</I> and <I>multiple</I>. In single
+select mode, only one entry can be selected at a time, while multiple select
+mode allows several entries to be selected. The mode is set by the widget's
+<B>-selectmode</B> option. <BR>
+<CODE>.h configure -selectmode "multiple"<BR>
+</CODE><P>You can be notified when the list of selected entries changes. The widget's
+<B>-selectcommand</B> specifies a Tcl procedure that is called whenever the selection
+changes. <BR>
+<CODE>proc SelectNotify { widget } {<BR>
+ set ids [$widget curselection]<BR>
+}<BR>
+.h configure -selectcommand "SelectNotify .h"<BR>
+</CODE><P>The widget supports the standard Tk scrolling and scanning operations. The
+<B>treeview</B> can be both horizontally and vertically. You can attach scrollbars
+to the <B>treeview</B> the same way as the listbox or canvas widgets. <BR>
+<CODE>scrollbar .xbar -orient horizontal -command ".h xview"<BR>
+scrollbar .ybar -orient vertical -command ".h yview"<BR>
+.h configure -xscrollcommand ".xbar set" \<BR>
+ -yscrollcommand ".ybar set"<BR>
+</CODE><P>There are three different modes of scrolling: <I>listbox</I>, <I>canvas</I>, and <I>hierbox</I>.
+ In <I>listbox</I> mode, the last entry can always be scrolled to the top of the
+widget. In <I>hierbox</I> mode, the last entry is always drawn at the bottom of
+the widget. The scroll mode is set by the widget's <B>-selectmode</B> option. <BR>
+<CODE>.h configure -scrollmode "listbox"<BR>
+</CODE><P>Entries can be programmatically opened or closed using the <B>open</B> and <B>close</B>
+operations respectively. <BR>
+<CODE>.h open $id<BR>
+.h close $id<BR>
+</CODE><P>When an entry is opened, a Tcl procedure can be automatically invoked. The
+<B>-opencommand</B> option specifies this procedure. This procedure can lazily
+insert entries as needed. <BR>
+<CODE>proc AddEntries { dir } {<BR>
+ eval .h insert end [glob -nocomplain $dir/*] <BR>
+}<BR>
+.h configure -opencommand "AddEntries %P"<BR>
+</CODE><P>Now when an entry is opened, the procedure <I>AddEntries</I> is called and adds
+children to the entry. Before the command is invoked, special "%" substitutions
+(like <B>bind</B>) are performed. Above, <I>%P</I> is translated to the pathname of the
+entry. <P>
+The same feature exists when an entry is closed. The <B>-closecommand</B>
+option specifies the procedure. <BR>
+<CODE>proc DeleteEntries { id } {<BR>
+ .h entry delete $id 0 end<BR>
+}<BR>
+.h configure -closecommand "DeleteEntries %#"<BR>
+</CODE><P>When an entry is closed, the procedure <I>DeleteEntries</I> is called and deletes
+the entry's children using the <B>entry delete</B> operation (<I>%#</I> is the id of entry).
+
+<H2><A NAME="sect23" HREF="#toc23">Keywords</A></H2>
+treeview, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Tree Data Object</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">IDs and Tags</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Special Node IDs</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Data Fields</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">Entry Bindings</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Treeview Operations</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Treeview Options</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Entry Options</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Button Options</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Column Options</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Text Editing Options</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Default Bindings</A></LI>
+<UL>
+<LI><A NAME="toc17" HREF="#sect17">Widget Bindings</A></LI>
+<LI><A NAME="toc18" HREF="#sect18">Button Bindings</A></LI>
+<LI><A NAME="toc19" HREF="#sect19">Entry Bindings</A></LI>
+<LI><A NAME="toc20" HREF="#sect20">Column Bindings</A></LI>
+<LI><A NAME="toc21" HREF="#sect21">Column Rule Bindings</A></LI>
+</UL>
+<LI><A NAME="toc22" HREF="#sect22">Example</A></LI>
+<LI><A NAME="toc23" HREF="#sect23">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/vector.html b/blt/html/vector.html
new file mode 100644
index 00000000000..c89a044c645
--- /dev/null
+++ b/blt/html/vector.html
@@ -0,0 +1,1124 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>vector(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+vector - Vector data type for Tcl
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>vector
+create <I>vecName </I></B>?<I>vecName</I>...? ?<I>switches</I>? <P>
+<B>vector destroy <I>vecName </I></B>?<I>vecName</I>...? <P>
+<B>vector
+expr <I>expression</I></B> <P>
+<B>vector names </B>?<I>pattern</I>...?
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>vector</B> command creates
+a vector of floating point values. The vector's components can be manipulated
+in three ways: through a Tcl array variable, a Tcl command, or the C API.
+
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+A vector is simply an ordered set of numbers. The components
+of a vector are real numbers, indexed by counting numbers. <P>
+Vectors are common
+data structures for many applications. For example, a graph may use two
+vectors to represent the X-Y coordinates of the data plotted. The graph
+will automatically be redrawn when the vectors are updated or changed. By
+using vectors, you can separate data analysis from the graph widget. This
+makes it easier, for example, to add data transformations, such as splines.
+ It's possible to plot the same data to in multiple graphs, where each graph
+presents a different view or scale of the data. <P>
+You could try to use Tcl's
+associative arrays as vectors. Tcl arrays are easy to use. You can access
+individual elements randomly by specifying the index, or the set the entire
+array by providing a list of index and value pairs for each element. The
+disadvantages of associative arrays as vectors lie in the fact they are
+implemented as hash tables.
+<UL>
+&#183;<LI>There's no implied ordering to the associative
+arrays. If you used vectors for plotting, you would want to insure the
+second component comes after the first, an so on. This isn't possible since
+arrays are actually hash tables. For example, you can't get a range of values
+between two indices. Nor can you sort an array. </LI>&#183;<LI>Arrays consume lots of memory
+when the number of elements becomes large (tens of thousands). This is
+because each element's index and value are stored as strings in the hash
+table. </LI>&#183;<LI>The C programming interface is unwieldy. Normally with vectors, you
+would like to view the Tcl array as you do a C array, as an array of floats
+or doubles. But with hash tables, you must convert both the index and value
+to and from decimal strings, just to access an element in the array. This
+makes it cumbersome to perform operations on the array as a whole. </LI>
+</UL>
+<P>
+The <B>vector</B>
+command tries to overcome these disadvantages while still retaining the
+ease of use of Tcl arrays. The <B>vector</B> command creates both a new Tcl command
+and associate array which are linked to the vector components. You can
+randomly access vector components though the elements of array. Not have
+all indices are generated for the array, so printing the array (using the
+<B>parray</B> procedure) does not print out all the component values. You can
+use the Tcl command to access the array as a whole. You can copy, append,
+or sort vector using its command. If you need greater performance, or customized
+behavior, you can write your own C code to manage vectors.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+You create
+vectors using the <B>vector</B> command and its <B>create</B> operation. <BR>
+<CODE># Create a new vector. <BR>
+vector create y(50)<BR>
+</CODE><P>This creates a new vector named <I>y</I>. It has fifty components, by default,
+initialized to <I>0.0</I>. In addition, both a Tcl command and array variable,
+both named <I>y</I>, are created. You can use either the command or variable to
+query or modify components of the vector. <BR>
+<CODE># Set the first value. <BR>
+set y(0) 9.25<BR>
+puts "y has [y length] components"<BR>
+</CODE><P>The array <I>y</I> can be used to read or set individual components of the vector.
+ Vector components are indexed from zero. The array index must be a number
+less than the number of components. For example, it's an error if you try
+to set the 51st element of <I>y</I>. <BR>
+<CODE># This is an error. The vector only has 50 components.<BR>
+set y(50) 0.02<BR>
+</CODE><P>You can also specify a range of indices using a colon (:) to separate the
+first and last indices of the range. <BR>
+<CODE># Set the first six components of y <BR>
+set y(0:5) 25.2<BR>
+</CODE><P>If you don't include an index, then it will default to the first and/or
+last component of the vector. <BR>
+<CODE># Print out all the components of y <BR>
+puts "y = $y(:)"<BR>
+</CODE><P>There are special non-numeric indices. The index <I>end</I>, specifies the last
+component of the vector. It's an error to use this index if the vector is
+empty (length is zero). The index <I>++end</I> can be used to extend the vector
+by one component and initialize it to a specific value. You can't read
+from the array using this index, though. <BR>
+<CODE># Extend the vector by one component.<BR>
+set y(++end) 0.02<BR>
+</CODE><P>The other special indices are <I>min</I> and <I>max</I>. They return the current smallest
+and largest components of the vector. <BR>
+<CODE># Print the bounds of the vector<BR>
+puts "min=$y(min) max=$y(max)"<BR>
+</CODE><P>To delete components from a vector, simply unset the corresponding array
+element. In the following example, the first component of <I>y</I> is deleted.
+All the remaining components of <I>y</I> will be moved down by one index as the
+length of the vector is reduced by one. <BR>
+<CODE># Delete the first component<BR>
+unset y(0)<BR>
+puts "new first element is $y(0)"<BR>
+</CODE><P>The vector's Tcl command can also be used to query or set the vector. <BR>
+<CODE># Create and set the components of a new vector<BR>
+vector create x<BR>
+x set { 0.02 0.04 0.06 0.08 0.10 0.12 0.14 0.16 0.18 0.20 }<BR>
+</CODE><P>Here we've created a vector <I>x</I> without a initial length specification. In
+this case, the length is zero. The <B>set</B> operation resets the vector, extending
+it and setting values for each new component. <P>
+There are several operations
+for vectors. The <B>range</B> operation lists the components of a vector between
+two indices. <BR>
+<CODE># List the components <BR>
+puts "x = [x range 0 end]"<BR>
+</CODE><P>You can search for a particular value using the <B>search</B> operation. It returns
+a list of indices of the components with the same value. If no component
+has the same value, it returns <I>""</I>. <BR>
+<CODE># Find the index of the biggest component<BR>
+set indices [x search $x(max)]<BR>
+</CODE><P>Other operations copy, append, or sort vectors. You can append vectors
+or new values onto an existing vector with the <B>append</B> operation. <BR>
+<CODE># Append assorted vectors and values to x<BR>
+x append x2 x3 { 2.3 4.5 } x4<BR>
+</CODE><P>The <B>sort</B> operation sorts the vector. If any additional vectors are specified,
+they are rearranged in the same order as the vector. For example, you could
+use it to sort data points represented by x and y vectors. <BR>
+<CODE># Sort the data points<BR>
+x sort y<BR>
+</CODE><P>The vector <I>x</I> is sorted while the components of <I>y</I> are rearranged so that
+the original x,y coordinate pairs are retained. <P>
+The <B>expr</B> operation lets
+you perform arithmetic on vectors. The result is stored in the vector.
+<BR>
+<CODE># Add the two vectors and a scalar<BR>
+x expr { x + y }<BR>
+x expr { x * 2 }<BR>
+</CODE><P>When a vector is modified, resized, or deleted, it may trigger call-backs
+to notify the clients of the vector. For example, when a vector used in
+the <B>graph</B> widget is updated, the vector automatically notifies the widget
+that it has changed. The graph can then redrawn itself at the next idle
+point. By default, the notification occurs when Tk is next idle. This way
+you can modify the vector many times without incurring the penalty of the
+graph redrawing itself for each change. You can change this behavior using
+the <B>notify</B> operation. <BR>
+<CODE># Make vector x notify after every change<BR>
+x notify always<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;...<BR>
+# Never notify<BR>
+x notify never<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;...<BR>
+# Force notification now<BR>
+x notify now<BR>
+</CODE><P>To delete a vector, use the <B>vector delete</B> command. Both the vector and
+its corresponding Tcl command are destroyed. <BR>
+<CODE># Remove vector x<BR>
+vector destroy x<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Syntax</A></H2>
+Vectors are created using the <B>vector create</B> operation. Th <B>create</B>
+operation can be invoked in one of three forms:
+<DL>
+
+<DT><B>vector create <I>vecName</I></B> </DT>
+<DD>This
+creates a new vector <I>vecName</I> which initially has no components. </DD>
+
+<DT><B>vector create
+<I>vecName</I></B>(<I>size</I>) </DT>
+<DD>This second form creates a new vector which will contain
+<I>size</I> number of components. The components will be indexed starting from
+zero (0). The default value for the components is <I>0.0</I>. </DD>
+
+<DT><B>vector create <I>vecName</I></B>(<I>first</I>:<I>last</I>)
+</DT>
+<DD>The last form creates a new vector of indexed <I>first</I> through <I>last</I>. <I>First</I>
+and <I>last</I> can be any integer value so long as <I>first</I> is less than <I>last</I>. </DD>
+</DL>
+<P>
+Vector
+names must start with a letter and consist of letters, digits, or underscores.
+ <BR>
+<CODE># Error: must start with letter<BR>
+vector create 1abc<BR>
+</CODE><P>You can automatically generate vector names using the "<I>#auto</I>" vector name.
+ The <B>create</B> operation will generate a unique vector name. <BR>
+<CODE>set vec [vector create #auto]<BR>
+puts "$vec has [$vec length] components"<BR>
+
+<H3><A NAME="sect6" HREF="#toc6"></CODE><P>Vector Indices</A></H3>
+Vectors are indexed by integers. You can access the individual
+vector components via its array variable or Tcl command. The string representing
+the index can be an integer, a numeric expression, a range, or a special
+keyword. <P>
+The index must lie within the current range of the vector, otherwise
+an an error message is returned. Normally the indices of a vector are start
+from 0. But you can use the <B>offset</B> operation to change a vector's indices
+on-the-fly. <BR>
+<CODE>puts $vecName(0)<BR>
+vecName offset -5<BR>
+puts $vecName(-5)<BR>
+</CODE><P>You can also use numeric expressions as indices. The result of the expression
+must be an integer value. <BR>
+<CODE>set n 21<BR>
+set vecName($n+3) 50.2<BR>
+</CODE><P>The following special non-numeric indices are available: <I>min</I>, <I>max</I>, <I>end</I>,
+and <I>++end</I>. <BR>
+<CODE>puts "min = $vecName($min)"<BR>
+set vecName(end) -1.2<BR>
+</CODE><P>The indices <I>min</I> and <I>max</I> will return the minimum and maximum values of the
+vector. The index <I>end</I> returns the value of the last component in the vector.
+ The index <I>++end</I> is used to append new value onto the vector. It automatically
+extends the vector by one component and sets its value. <BR>
+<CODE># Append an new component to the end<BR>
+set vecName(++end) 3.2<BR>
+</CODE><P>A range of indices can be indicated by a colon (:). <BR>
+<CODE># Set the first six components to 1.0<BR>
+set vecName(0:5) 1.0<BR>
+</CODE><P>If no index is supplied the first or last component is assumed. <BR>
+<CODE># Print the values of all the components<BR>
+puts $vecName(:)<BR>
+
+<H2><A NAME="sect7" HREF="#toc7"></CODE><P>Vector Operations</A></H2>
+
+<DL>
+
+<DT><B>vector create <I>vecName</I></B>?(<I>size</I>)?... ?<I>switches</I>? </DT>
+<DD>The <B>create</B> operation
+creates a new vector <I>vecName</I>. Both a Tcl command and array variable <I>vecName</I>
+are also created. The name <I>vecName</I> must be unique, so another Tcl command
+or array variable can not already exist in that scope. You can access the
+components of the vector using its variable. If you change a value in the
+array, or unset an array element, the vector is updated to reflect the
+changes. When the variable <I>vecName</I> is unset, the vector and its Tcl command
+are also destroyed. <P>
+The vector has optional switches that affect how the
+vector is created. They are as follows: <blockquote></DD>
+
+<DT><B>-variable <I>varName</I></B> </DT>
+<DD>Specifies the name
+of a Tcl variable to be mapped to the vector. If the variable already exists,
+it is first deleted, then recreated. If <I>varName</I> is the empty string, then
+no variable will be mapped. You can always map a variable back to the vector
+using the vector's <B>variable</B> operation. </DD>
+
+<DT><B>-command <I>cmdName</I></B> </DT>
+<DD>Maps a Tcl command
+to the vector. The vector can be accessed using <I>cmdName</I> and one of the
+vector instance operations. A Tcl command by that name cannot already
+exist. If <I>cmdName</I> is the empty string, no command mapping will be made. </DD>
+
+<DT><B>-watchunset
+<I>boolean</I></B> </DT>
+<DD>Indicates that the vector should automatically delete itself if
+the variable associated with the vector is unset. By default, the vector
+will not be deleted. This is different from previous releases. Set <I>boolean</I>
+to "true" to get the old behavior. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>vector destroy <I>vecName</I></B> ?<I>vecName...</I>? </DT>
+<DD></DD>
+
+<DT><B>vector
+expr <I>expression</I></B> </DT>
+<DD><blockquote>All binary operators take vectors as operands (remember
+that numbers are treated as one-component vectors). The exact action of
+binary operators depends upon the length of the second operand. If the
+second operand has only one component, then each element of the first vector
+operand is computed by that value. For example, the expression "x * 2"
+multiples all elements of the vector x by 2. If the second operand has
+more than one component, both operands must be the same length. Each pair
+of corresponding elements are computed. So "x + y" adds the the first components
+of x and y together, the second, and so on. <P>
+The valid operators are listed
+below, grouped in decreasing order of precedence: </DD>
+
+<DT><B>- !</B> </DT>
+<DD>Unary minus and logical
+NOT. The unary minus flips the sign of each component in the vector. The
+logical not operator returns a vector of whose values are 0.0 or 1.0. For
+each non-zero component 1.0 is returned, 0.0 otherwise. </DD>
+
+<DT><B>^</B> </DT>
+<DD>Exponentiation. </DD>
+
+<DT><B>*
+ / %</B> </DT>
+<DD>Multiply, divide, remainder. </DD>
+
+<DT><B>+ -</B> </DT>
+<DD>Add and subtract. </DD>
+
+<DT><B>&lt;&lt; &gt;&gt;</B> </DT>
+<DD>Left and
+right shift. Circularly shifts the values of the vector (not implemented
+yet). </DD>
+
+<DT><B>&lt; &gt; &lt;= &gt;=</B> </DT>
+<DD>Boolean less, greater, less than or equal, and greater than
+or equal. Each operator returns a vector of ones and zeros. If the condition
+is true, 1.0 is the component value, 0.0 otherwise. </DD>
+
+<DT><B>== !=</B> </DT>
+<DD>Boolean equal
+and not equal. Each operator returns a vector of ones and zeros. If the
+condition is true, 1.0 is the component value, 0.0 otherwise. </DD>
+
+<DT><B>|</B> </DT>
+<DD>Bit-wise OR.
+ (Not implemented). </DD>
+
+<DT><B>&amp;&amp;</B> </DT>
+<DD>Logical AND. Produces a 1 result if both operands are
+non-zero, 0 otherwise. </DD>
+
+<DT><B>||</B> </DT>
+<DD>Logical OR. Produces a 0 result if both operands
+are zero, 1 otherwise. </DD>
+
+<DT><I>x<B>?<I>y<B>:<I>z</I></B></I></B></I> </DT>
+<DD>If-then-else, as in C. (Not implemented yet).
+</DD>
+</DL>
+<P>
+See the C manual for more details on the results produced by each operator.
+ All of the binary operators group left-to-right within the same precedence
+level. <P>
+Several mathematical functions are supported for vectors. Each
+of the following functions invokes the math library function of the same
+name; see the manual entries for the library functions for details on what
+they do. The operation is applied to all elements of the vector returning
+the results. <BR>
+<CODE><P>
+<B>acos</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>cos</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>hypot</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>sinh</B> <BR>
+<B>asin</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>cosh</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>log</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>sqrt</B> <BR>
+<B>atan</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>exp</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>log10</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tan</B> <BR>
+<B>ceil</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>floor</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>sin</B><tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<B>tanh</B> <BR>
+</CODE><P>Additional functions are:
+<DL>
+
+<DT><B>abs</B> </DT>
+<DD>Returns the absolute value of each component.
+</DD>
+
+<DT><B>random</B> </DT>
+<DD>Returns a vector of non-negative values uniformly distributed between
+[0.0, 1.0) using <I>drand48</I>. The seed comes from the internal clock of the machine
+or may be set manual with the srandom function. </DD>
+
+<DT><B>round</B> </DT>
+<DD>Rounds each component
+of the vector. </DD>
+
+<DT><B>srandom</B> </DT>
+<DD>Initializes the random number generator using <I>srand48</I>.
+The high order 32-bits are set using the integral portion of the first
+vector component. All other components are ignored. The low order 16-bits
+ are set to an arbitrary value. </DD>
+</DL>
+<P>
+The following functions return a single
+value.
+<DL>
+
+<DT><B>adev</B> </DT>
+<DD>Returns the average deviation (defined as the sum of the absolute
+values of the differences between component and the mean, divided by the
+length of the vector). </DD>
+
+<DT><B>kurtosis</B> </DT>
+<DD>Returns the degree of peakedness (fourth
+moment) of the vector. </DD>
+
+<DT><B>length</B> </DT>
+<DD>Returns the number of components in the vector.
+</DD>
+
+<DT><B>max</B> </DT>
+<DD>Returns the vector's maximum value. </DD>
+
+<DT><B>mean</B> </DT>
+<DD>Returns the mean value of the
+vector. </DD>
+
+<DT><B>median</B> </DT>
+<DD>Returns the median of the vector. </DD>
+
+<DT><B>min</B> </DT>
+<DD>Returns the vector's
+minimum value. </DD>
+
+<DT><B>q1</B> </DT>
+<DD>Returns the first quartile of the vector. </DD>
+
+<DT><B>q3</B> </DT>
+<DD>Returns the
+third quartile of the vector. </DD>
+
+<DT><B>prod</B> </DT>
+<DD>Returns the product of the components.
+</DD>
+
+<DT><B>sdev</B> </DT>
+<DD>Returns the standard deviation (defined as the square root of the
+variance) of the vector. </DD>
+
+<DT><B>skew</B> </DT>
+<DD>Returns the skewness (or third moment) of
+the vector. This characterizes the degree of asymmetry of the vector about
+the mean. </DD>
+
+<DT><B>sum</B> </DT>
+<DD>Returns the sum of the components. </DD>
+
+<DT><B>var</B> </DT>
+<DD>Returns the variance
+of the vector. The sum of the squared differences between each component
+and the mean is computed. The variance is the sum divided by the length
+of the vector minus 1. </DD>
+</DL>
+<P>
+The last set returns a vector of the same length
+as the argument.
+<DL>
+
+<DT><B>norm</B> </DT>
+<DD>Scales the values of the vector to lie in the range
+[0.0..1.0]. </DD>
+
+<DT><B>sort</B> </DT>
+<DD>Returns the vector components sorted in ascending order. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>vector
+names </B>?<I>pattern</I>? </DT>
+<DD></DD>
+</DL>
+
+<H2><A NAME="sect8" HREF="#toc8">Instance Operations</A></H2>
+You can also use the vector's Tcl command
+to query or modify it. The general form is <BR>
+<P>
+<CODE><I>vecName <I>operation</I></I> ?<I>arg</I>?...<BR>
+</CODE><P>Both <I>operation</I> and its arguments determine the exact behavior of the command.
+ The operations available for vectors are listed below.
+<DL>
+
+<DT><I>vecName <B>append</B></I> <I>item</I>
+?<I>item</I>?... </DT>
+<DD>Appends the component values from <I>item</I> to <I>vecName</I>. <I>Item</I> can be either
+the name of a vector or a list of numeric values. </DD>
+
+<DT><I>vecName <B>clear</B></I> </DT>
+<DD>Clears
+the element indices from the array variable associated with <I>vecName</I>. This
+doesn't affect the components of the vector. By default, the number of entries
+in the Tcl array doesn't match the number of components in the vector. This
+is because its too expensive to maintain decimal strings for both the index
+and value for each component. Instead, the index and value are saved only
+when you read or write an element with a new index. This command removes
+the index and value strings from the array. This is useful when the vector
+is large. </DD>
+
+<DT><I>vecName <B>delete</B></I> <I>index</I> ?<I>index</I>?... </DT>
+<DD>Deletes the <I>index</I>th component from
+the vector <I>vecName</I>. <I>Index</I> is the index of the element to be deleted. This
+is the same as unsetting the array variable element <I>index</I>. The vector is
+compacted after all the indices have been deleted. </DD>
+
+<DT><I>vecName <B>dup</B></I> <I>destName</I>
+ </DT>
+<DD>Copies <I>vecName</I> to <I>destName</I>. <I>DestName</I> is the name of a destination vector.
+ If a vector <I>destName</I> already exists, it is overwritten with the components
+of <I>vecName</I>. Otherwise a new vector is created. </DD>
+
+<DT><I>vecName <B>expr</B></I> <I>expression</I>
+</DT>
+<DD>Computes the expression and resets the values of the vector accordingly.
+Both scalar and vector math operations are allowed. All values in expressions
+are either real numbers or names of vectors. All numbers are treated as
+one component vectors. </DD>
+
+<DT><I>vecName <B>length</B></I> ?<I>newSize</I>? </DT>
+<DD>Queries or resets the number
+of components in <I>vecName</I>. <I>NewSize</I> is a number specifying the new size of
+the vector. If <I>newSize</I> is smaller than the current size of <I>vecName</I>, <I>vecName</I>
+is truncated. If <I>newSize</I> is greater, the vector is extended and the new
+components are initialized to <I>0.0</I>. If no <I>newSize</I> argument is present, the
+current length of the vector is returned. </DD>
+
+<DT><I>vecName <B>merge</B></I> <I>srcName</I> ?<I>srcName</I>?...
+</DT>
+<DD>Merges the named vectors into a single vector. The resulting vector is
+formed by merging the components of each source vector one index at a
+time. </DD>
+
+<DT><I>vecName <B>notify</B></I> <I>keyword</I> </DT>
+<DD>Controls how vector clients are notified of
+changes to the vector. The exact behavior is determined by <I>keyword</I>. <blockquote></DD>
+
+<DT><I>always</I>
+ </DT>
+<DD>Indicates that clients are to be notified immediately whenever the vector
+is updated. </DD>
+
+<DT><I>never</I> </DT>
+<DD>Indicates that no clients are to be notified. </DD>
+
+<DT><I>whenidle</I>
+</DT>
+<DD>Indicates that clients are to be notified at the next idle point whenever
+the vector is updated. </DD>
+
+<DT><I>now</I> </DT>
+<DD>If any client notifications is currently pending,
+they are notified immediately. </DD>
+
+<DT><I>cancel</I> </DT>
+<DD>Cancels pending notifications of clients
+using the vector. </DD>
+
+<DT><I>pending</I> </DT>
+<DD>Returns <I>1</I> if a client notification is pending,
+and <I>0</I> otherwise. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><I>vecName <B>offset</B></I> ?<I>value</I>? </DT>
+<DD>Shifts the indices of the vector
+by the amount specified by <I>value</I>. <I>Value</I> is an integer number. If no <I>value</I>
+argument is given, the current offset is returned. </DD>
+
+<DT><I>vecName <B>populate</B></I> <I>destName</I>
+?<I>density</I>? </DT>
+<DD>Creates a vector <I>destName</I> which is a superset of <I>vecName</I>. <I>DestName</I>
+will include all the components of <I>vecName</I>, in addition the interval between
+each of the original components will contain a <I>density</I> number of new components,
+whose values are evenly distributed between the original components values.
+ This is useful for generating abscissas to be interpolated along a spline.
+</DD>
+
+<DT><I>vecName <B>range</B></I> <I>firstIndex</I> ?<I>lastIndex</I>?... </DT>
+<DD>Returns a list of numeric values representing
+the vector components between two indices. Both <I>firstIndex</I> and <I>lastIndex</I>
+are indices representing the range of components to be returned. If <I>lastIndex</I>
+is less than <I>firstIndex</I>, the components are listed in reverse order. </DD>
+
+<DT><I>vecName
+<B>search</B></I> <I>value</I> ?<I>value</I>? </DT>
+<DD>Searches for a value or range of values among the
+components of <I>vecName</I>. If one <I>value</I> argument is given, a list of indices
+of the components which equal <I>value</I> is returned. If a second <I>value</I> is also
+provided, then the indices of all components which lie within the range
+of the two values are returned. If no components are found, then <I>""</I> is returned.
+</DD>
+
+<DT><I>vecName <B>set</B></I> <I>item</I> </DT>
+<DD>Resets the components of the vector to <I>item</I>. <I>Item</I> can be
+either a list of numeric expressions or another vector. </DD>
+
+<DT><I>vecName <B>seq</B></I> <I>start</I>
+?<I>finish</I>? ?<I>step</I>? </DT>
+<DD>Generates a sequence of values starting with the value
+<I>start</I>. <I>Finish</I> indicates the terminating value of the sequence. The vector
+is automatically resized to contain just the sequence. If three arguments
+are present, <I>step</I> designates the interval. <P>
+With only two arguments (no
+<I>finish</I> argument), the sequence will continue until the vector is filled.
+ With one argument, the interval defaults to 1.0. </DD>
+
+<DT><I>vecName <B>sort</B></I> ?<B>-reverse</B>?
+?<I>argName</I>?... </DT>
+<DD>Sorts the vector <I>vecName</I> in increasing order. If the <B>-reverse</B>
+flag is present, the vector is sorted in decreasing order. If other arguments
+<I>argName</I> are present, they are the names of vectors which will be rearranged
+in the same manner as <I>vecName</I>. Each vector must be the same length as <I>vecName</I>.
+You could use this to sort the x vector of a graph, while still retaining
+the same x,y coordinate pairs in a y vector. </DD>
+
+<DT><I>vecName <B>variable</B></I> <I>varName</I> </DT>
+<DD>Maps
+a Tcl variable to the vector, creating another means for accessing the
+vector. The variable <I>varName</I> can't already exist. This overrides any current
+variable mapping the vector may have. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect9" HREF="#toc9">C Language API</A></H2>
+You can create, modify,
+and destroy vectors from C code, using library routines. You need to
+include the header file <I>blt.h</I>. It contains the definition of the structure
+<B>Blt_Vector</B>, which represents the vector. It appears below. <BR>
+<CODE>typedef struct {<BR>
+ double *<I>valueArr</I>; <BR>
+ int <I>numValues</I>; <BR>
+ int <I>arraySize</I>; <BR>
+ double <I>min</I>, <I>max</I>; <BR>
+} <B>Blt_Vector</B>;<BR>
+</CODE><P>The field <I>valueArr</I> points to memory holding the vector components. The
+components are stored in a double precision array, whose size size is represented
+by <I>arraySize</I>. <I>NumValues</I> is the length of vector. The size of the array
+is always equal to or larger than the length of the vector. <I>Min</I> and <I>max</I>
+are minimum and maximum component values.
+<H2><A NAME="sect10" HREF="#toc10">Library Routines</A></H2>
+The following
+routines are available from C to manage vectors. Vectors are identified
+by the vector name. <P>
+<B>Blt_CreateVector</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_CreateVector</B> (<I>interp</I>, <I>vecName</I>, <I>length</I>, <I>vecPtrPtr</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+char *<I>vecName</I>;<BR>
+int <I>length</I>;<BR>
+Blt_Vector **<I>vecPtrPtr</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Creates a new vector <I>vecName</I> with a length of <I>length</I>. <B>Blt_CreateVector</B>
+creates both a new Tcl command and array variable <I>vecName</I>. Neither a command
+nor variable named <I>vecName</I> can already exist. A pointer to the vector
+is placed into <I>vecPtrPtr</I>. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is successfully
+created. If <I>length</I> is negative, a Tcl variable or command <I>vecName</I> already
+exists, or memory cannot be allocated for the vector, then <I>TCL_ERROR</I> is
+returned and <I>interp-&gt;result</I> will contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_DeleteVectorByName</B>
+ <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_DeleteVectorByName</B> (<I>interp</I>, <I>vecName</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+char *<I>vecName</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Removes the vector <I>vecName</I>. <I>VecName</I> is the name of a vector
+which must already exist. Both the Tcl command and array variable <I>vecName</I>
+are destroyed. All clients of the vector will be notified immediately that
+the vector has been destroyed. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is
+successfully deleted. If <I>vecName</I> is not the name a vector, then <I>TCL_ERROR</I>
+is returned and <I>interp-&gt;result</I> will contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_DeleteVector</B>
+ <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_DeleteVector</B> (<I>vecPtr</I>)<BR>
+<blockquote>Blt_Vector *<I>vecPtr</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Removes the vector pointed to by <I>vecPtr</I>. <I>VecPtr</I> is a pointer
+to a vector, typically set by <B>Blt_GetVector</B> or <B>Blt_CreateVector</B>. Both the
+Tcl command and array variable of the vector are destroyed. All clients
+of the vector will be notified immediately that the vector has been destroyed.
+</DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is successfully deleted. If <I>vecName</I>
+is not the name a vector, then <I>TCL_ERROR</I> is returned and <I>interp-&gt;result</I> will
+contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_GetVector</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_GetVector</B> (<I>interp</I>, <I>vecName</I>, <I>vecPtrPtr</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+char *<I>vecName</I>;<BR>
+Blt_Vector **<I>vecPtrPtr</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Retrieves the vector <I>vecName</I>. <I>VecName</I> is the name of a vector
+which must already exist. <I>VecPtrPtr</I> will point be set to the address of
+the vector. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is successfully retrieved.
+ If <I>vecName</I> is not the name of a vector, then <I>TCL_ERROR</I> is returned and
+<I>interp-&gt;result</I> will contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_ResetVector</B> <P>
+<blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_ResetVector</B> (<I>vecPtr</I>, <I>dataArr</I>, <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<I>numValues</I>, <I>arraySize</I>, <I>freeProc</I>)<BR>
+<blockquote>Blt_Vector *<I>vecPtr</I>;<BR>
+double *<I>dataArr</I>;<BR>
+int *<I>numValues</I>;<BR>
+int *<I>arraySize</I>;<BR>
+Tcl_FreeProc *<I>freeProc</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Resets the components of the vector pointed to by <I>vecPtr</I>.
+Calling <B>Blt_ResetVector</B> will trigger the vector to dispatch notifications
+to its clients. <I>DataArr</I> is the array of doubles which represents the vector
+data. <I>NumValues</I> is the number of elements in the array. <I>ArraySize</I> is the
+actual size of the array (the array may be bigger than the number of values
+stored in it). <I>FreeProc</I> indicates how the storage for the vector component
+array (<I>dataArr</I>) was allocated. It is used to determine how to reallocate
+memory when the vector is resized or destroyed. It must be <I>TCL_DYNAMIC</I>,
+<I>TCL_STATIC</I>, <I>TCL_VOLATILE</I>, or a pointer to a function to free the memory
+allocated for the vector array. If <I>freeProc</I> is <I>TCL_VOLATILE</I>, it indicates
+that <I>dataArr</I> must be copied and saved. If <I>freeProc</I> is <I>TCL_DYNAMIC</I>, it indicates
+that <I>dataArr</I> was dynamically allocated and that Tcl should free <I>dataArr</I>
+if necessary. <I>Static</I> indicates that nothing should be done to release storage
+for <I>dataArr</I>. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is successfully resized.
+ If <I>newSize</I> is negative, a vector <I>vecName</I> does not exist, or memory cannot
+be allocated for the vector, then <I>TCL_ERROR</I> is returned and <I>interp-&gt;result</I>
+will contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_ResizeVector</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_ResizeVector</B> (<I>vecPtr</I>, <I>newSize</I>)<BR>
+<blockquote>Blt_Vector *<I>vecPtr</I>;<BR>
+int <I>newSize</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Resets the length of the vector pointed to by <I>vecPtr</I> to <I>newSize</I>.
+ If <I>newSize</I> is smaller than the current size of the vector, it is truncated.
+ If <I>newSize</I> is greater, the vector is extended and the new components are
+initialized to <I>0.0</I>. Calling <B>Blt_ResetVector</B> will trigger the vector to dispatch
+notifications. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the vector is successfully resized.
+ If <I>newSize</I> is negative or memory can not be allocated for the vector,
+ then <I>TCL_ERROR</I> is returned and <I>interp-&gt;result</I> will contain an error message.
+<P>
+</DD>
+</DL>
+<P>
+<B>Blt_VectorExists</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>int <B>Blt_VectorExists</B> (<I>interp</I>, <I>vecName</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+char *<I>vecName</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Indicates if a vector named <I>vecName</I> exists in <I>interp</I>. </DD>
+
+<DT>Results:
+</DT>
+<DD>Returns <I>1</I> if a vector <I>vecName</I> exists and <I>0</I> otherwise. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+If your application
+needs to be notified when a vector changes, it can allocate a unique <I>client
+identifier</I> for itself. Using this identifier, you can then register a call-back
+to be made whenever the vector is updated or destroyed. By default, the
+call-backs are made at the next idle point. This can be changed to occur
+at the time the vector is modified. An application can allocate more than
+one identifier for any vector. When the client application is done with
+the vector, it should free the identifier. <P>
+The call-back routine must of
+the following type. <BR>
+<blockquote><BR>
+<CODE>typedef void (<B>Blt_VectorChangedProc</B>) (Tcl_Interp *<I>interp</I>, <BR>
+<blockquote>ClientData <I>clientData</I>, Blt_VectorNotify <I>notify</I>);<BR>
+</blockquote>
+<BR>
+</blockquote>
+</PRE></CODE><P><I>ClientData</I> is passed to this routine whenever it is called. You can use
+this to pass information to the call-back. The <I>notify</I> argument indicates
+whether the vector has been updated of destroyed. It is an enumerated type.
+<BR>
+<blockquote><BR>
+<CODE>typedef enum {<BR>
+ <I>BLT_VECTOR_NOTIFY_UPDATE</I>=1,<BR>
+ <I>BLT_VECTOR_NOTIFY_DESTROY</I>=2<BR>
+} <B>Blt_VectorNotify</B>;<BR>
+<BR>
+</blockquote>
+<P>
+</CODE><P><B>Blt_AllocVectorId</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>Blt_VectorId <B>Blt_AllocVectorId</B> (<I>interp</I>, <I>vecName</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+char *<I>vecName</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Allocates an client identifier for with the vector <I>vecName</I>.
+This identifier can be used to specify a call-back which is triggered when
+the vector is updated or destroyed. </DD>
+
+<DT>Results: </DT>
+<DD>Returns a client identifier
+if successful. If <I>vecName</I> is not the name of a vector, then <I>NULL</I> is returned
+and <I>interp-&gt;result</I> will contain an error message. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_GetVectorById</B> <blockquote>
+<DL>
+
+<DT>Synopsis:
+</DT>
+<DD><BR>
+<CODE>int <B>Blt_GetVector</B> (<I>interp</I>, <I>clientId</I>, <I>vecPtrPtr</I>)<BR>
+<blockquote>Tcl_Interp *<I>interp</I>;<BR>
+Blt_VectorId <I>clientId</I>;<BR>
+Blt_Vector **<I>vecPtrPtr</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Retrieves the vector used by <I>clientId</I>. <I>ClientId</I> is a valid
+vector client identifier allocated by <B>Blt_AllocVectorId</B>. <I>VecPtrPtr</I> will
+point be set to the address of the vector. </DD>
+
+<DT>Results: </DT>
+<DD>Returns <I>TCL_OK</I> if the
+vector is successfully retrieved. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_SetVectorChangedProc</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>void <B>Blt_SetVectorChangedProc</B> (<I>clientId</I>, <I>proc</I>, <I>clientData</I>);<BR>
+<blockquote>Blt_VectorId <I>clientId</I>;<BR>
+Blt_VectorChangedProc *<I>proc</I>;<BR>
+ClientData *<I>clientData</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Specifies a call-back routine to be called whenever the vector
+associated with <I>clientId</I> is updated or deleted. <I>Proc</I> is a pointer to call-back
+routine and must be of the type <B>Blt_VectorChangedProc</B>. <I>ClientData</I> is a
+one-word value to be passed to the routine when it is invoked. If <I>proc</I> is
+<I>NULL</I>, then the client is not notified. </DD>
+
+<DT>Results: </DT>
+<DD>The designated call-back
+procedure will be invoked when the vector is updated or destroyed. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_FreeVectorId</B>
+<blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>void <B>Blt_FreeVectorId</B> (<I>clientId</I>);<BR>
+<blockquote>Blt_VectorId <I>clientId</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Frees the client identifier. Memory allocated for the identifier
+ is released. The client will no longer be notified when the vector is
+modified. </DD>
+
+<DT>Results: </DT>
+<DD>The designated call-back procedure will be no longer be
+invoked when the vector is updated or destroyed. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_NameOfVectorId</B> <blockquote>
+<DL>
+
+<DT>Synopsis:
+</DT>
+<DD><BR>
+<CODE>char *<B>Blt_NameOfVectorId</B> (<I>clientId</I>);<BR>
+<blockquote>Blt_VectorId <I>clientId</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Retrieves the name of the vector associated with the client
+identifier <I>clientId</I>. </DD>
+
+<DT>Results: </DT>
+<DD>Returns the name of the vector associated
+with <I>clientId</I>. If <I>clientId</I> is not an identifier or the vector has been
+destroyed, <I>NULL</I> is returned. </DD>
+</DL>
+</blockquote>
+<P>
+<P>
+<B>Blt_InstallIndexProc</B> <blockquote>
+<DL>
+
+<DT>Synopsis: </DT>
+<DD><BR>
+<CODE>void <B>Blt_InstallIndexProc</B> (<I>indexName</I>, <I>procPtr</I>)<BR>
+<blockquote>char *<I>indexName</I>;<BR>
+Blt_VectorIndexProc *<I>procPtr</I>;<BR>
+</DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT></CODE><P>Description: </DT>
+<DD>Registers a function to be called to retrieved the index
+<I>indexName</I> from the vector's array variable. <P>
+typedef double Blt_VectorIndexProc(Vector
+*vecPtr); <P>
+The function will be passed a pointer to the vector. The function
+must return a double representing the value at the index. </DD>
+
+<DT>Results: </DT>
+<DD>The new
+index is installed into the vector. </DD>
+</DL>
+</blockquote>
+</blockquote>
+
+<H2><A NAME="sect11" HREF="#toc11">C API Example</A></H2>
+The following example opens
+a file of binary data and stores it in an array of doubles. The array size
+is computed from the size of the file. If the vector "data" exists, calling
+<B>Blt_VectorExists</B>, <B>Blt_GetVector</B> is called to get the pointer to the vector.
+Otherwise the routine <B>Blt_CreateVector</B> is called to create a new vector
+and returns a pointer to it. Just like the Tcl interface, both a new Tcl
+command and array variable are created when a new vector is created. It
+doesn't make any difference what the initial size of the vector is since
+it will be reset shortly. The vector is updated when <B>lt_ResetVector</B> is called.
+ Blt_ResetVector makes the changes visible to the Tcl interface and other
+vector clients (such as a graph widget). <P>
+<BR>
+<CODE>#include &lt;tcl.h&gt;<BR>
+#include &lt;blt.h&gt;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;<BR>
+
+<!--
+
+Blt_Vector *vecPtr;<BR>
+double *newArr;<BR>
+FILE *f;<BR>
+struct stat statBuf;<BR>
+int numBytes, numValues;<BR>
+<P>
+f = fopen("binary.dat", "r");<BR>
+fstat(fileno(f), &amp;statBuf);<BR>
+numBytes = (int)statBuf.st_size;<BR>
+<P>
+/* Allocate an array big enough to hold all the data */<BR>
+newArr = (double *)m<A HREF="alloc.n.html">alloc(numBytes)</A>
+;<BR>
+numValues = numBytes / sizeof(double);<BR>
+fread((void *)newArr, numValues, sizeof(double), f);<BR>
+fclose(f);<BR>
+<P>
+if (Blt_VectorExists(interp, "data")) {<BR>
+ if (Blt_GetVector(interp, "data", &amp;vecPtr) != TCL_OK) {<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;return TCL_ERROR;<BR>
+ }<BR>
+} else {<BR>
+ if (Blt_CreateVector(interp, "data", 0, &amp;vecPtr) != TCL_OK) {<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;return TCL_ERROR;<BR>
+ }<BR>
+}<BR>
+/* <BR>
+ * Reset the vector. Clients will be notified when Tk is idle. <BR>
+ * TCL_DYNAMIC tells the vector to free the memory allocated <BR>
+ * if it needs to reallocate or destroy the vector.<BR>
+ */<BR>
+if (Blt_ResetVector(vecPtr, newArr, numValues, numValues, <BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;TCL_DYNAMIC) != TCL_OK) {<BR>
+ return TCL_ERROR;<BR>
+}<BR>
+
+-->
+
+<H2><A NAME="sect12" HREF="#toc12"></CODE><P>Incompatibilities</A></H2>
+In previous versions, if the array variable isn't global
+ (i.e. local to a Tcl procedure), the vector is automatically destroyed
+when the procedure returns. <BR>
+<CODE>proc doit {} {<BR>
+ # Temporary vector x<BR>
+ vector x(10)<BR>
+ set <A HREF="x.9.html">x(9)</A>
+ 2.0<BR>
+ ...<BR>
+}<BR>
+<P>
+</CODE><P>This has changed. Variables are not automatically destroyed when their
+variable is unset. You can restore the old behavior by setting the "-watchunset"
+switch.
+<H2><A NAME="sect13" HREF="#toc13"></CODE><P>Keywords</A></H2>
+vector, graph, widget <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Syntax</A></LI>
+<UL>
+<LI><A NAME="toc6" HREF="#sect6">Vector Indices</A></LI>
+</UL>
+<LI><A NAME="toc7" HREF="#sect7">Vector Operations</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Instance Operations</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">C Language API</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Library Routines</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">C API Example</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Incompatibilities</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/watch.html b/blt/html/watch.html
new file mode 100644
index 00000000000..2059c0723a7
--- /dev/null
+++ b/blt/html/watch.html
@@ -0,0 +1,140 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>watch(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+watch - call Tcl procedures before and after each
+command
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>watch create</B> <I>watchName</I> ?<I>options</I>? <P>
+<B>watch activate</B> <I>watchName</I>
+<P>
+<B>watch deactivate</B> <I>watchName</I> <P>
+<B>watch delete</B> <I>watchName</I> <P>
+<B>watch configure</B> <I>watchName</I>
+?<I>options</I> <P>
+<B>watch info</B> <I>watchName</I> <P>
+<B>watch names</B>
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>watch</B> command
+arranges for Tcl procedures to be invoked before and after the execution
+of each Tcl command.
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+When an error occurs in Tcl, the global
+variable <I>errorInfo</I> will contain a stack-trace of the active procedures when
+the error occured. Sometimes, however, the stack trace is insufficient.
+You may need to know exactly where in the program's execution the error
+occured. In cases like this, a more general tracing facility would be useful.
+<P>
+The <B>watch</B> command lets you designate Tcl procedures to be invoked before
+and after the execution of each Tcl command. This means you can display
+the command line and its results for each command as it executes. Another
+use is to profile your Tcl commands. You can profile any Tcl command (like
+<B>if</B> and <B>set</B>), not just Tcl procedures.
+<H2><A NAME="sect4" HREF="#toc4">Example</A></H2>
+The following example use <B>watch</B>
+to trace Tcl commands (printing to standard error) both before and after
+they are executed. <BR>
+<CODE>proc preCmd { level command argv } {<BR>
+ set name [lindex $argv 0]<BR>
+ puts stderr "$level $name =&gt; $command"<BR>
+}<BR>
+<P>
+proc postCmd { level command argv retcode results } {<BR>
+ set name [lindex $argv 0]<BR>
+ puts stderr "$level $name =&gt; $argv0= ($retcode) $results"<BR>
+}<BR>
+watch create trace \<BR>
+<tt>&#32;</tt>&nbsp;<tt>&#32;</tt>&nbsp;-postcmd postCmd -precmd preCmd<BR>
+
+<H2><A NAME="sect5" HREF="#toc5"></CODE><P>Operations</A></H2>
+The following operations are available for the <B>watch</B> command:
+
+<DL>
+
+<DT><B>watch activate <I>watchName</I></B> </DT>
+<DD>Activates the watch, causing Tcl commands the
+be traced to the maximum depth selected. </DD>
+
+<DT><B>watch create <I>watchName</I></B> ?<I>options</I>?...
+</DT>
+<DD>Creates a new watch <I>watchName</I>. It's an error if another watch <I>watchName</I>
+already exists and an error message will be returned. <I>Options</I> may have any
+of the values accepted by the <B>watch configure</B> command. This command returns
+the empty string. </DD>
+
+<DT><B>watch configure <I>watchName</I></B> ?<I>options...</I>? </DT>
+<DD>Queries or modifies
+the configuration options of the watch <I>watchName</I>. <I>WatchName</I> is the name
+of a watch. <I>Options</I> may have any of the following values: <blockquote></DD>
+
+<DT><B>-active <I>boolean</I></B>
+</DT>
+<DD>Specifies if the watch is active. By default, watches are active when created.
+</DD>
+
+<DT><B>-postcmd <I>string</I></B> </DT>
+<DD>Specifies a Tcl procedure to be called immediately after
+each Tcl command. <I>String</I> is name of a Tcl procedure and any extra arguments
+to be passed to it. Before <I>string</I> is invoked, five more arguments are appended:
+1) the current level 2) the current command line 3) a list containing the
+command after substitutions and split into words 4) the return code of
+the command, and 5) the results of the command. The return status of the
+postcmd procedure is always ignored. </DD>
+
+<DT><B>-precmd <I>string</I></B> </DT>
+<DD>Specifies a Tcl procedure
+to be called immediately before each Tcl command. <I>String</I> is name of a Tcl
+procedure and any extra arguments to be passed to it. Before <I>string</I> is
+invoked, three arguments are appended: 1) the current level 2) the current
+command line, and 3) a list containing the command after substitutions
+and split into words. The return status of the <B>-precmd</B> procedure is always
+ignored. </DD>
+
+<DT><B>-maxlevel <I>number</I></B> </DT>
+<DD>Specifies the maximum evaluation depth to watch
+Tcl commands. The default maximum level is 10000. </DD>
+</DL>
+</blockquote>
+
+<DL>
+
+<DT><B>watch deactivate <I>watchName</I></B>
+ </DT>
+<DD>Deactivates the watch. The <B>-precmd</B> and <B>-postcmd</B> procedures will no longer
+be invoked. </DD>
+
+<DT><B>watch info <I>watchName</I></B> </DT>
+<DD>Returns the configuration information
+associated with the watch <I>watchName</I>. <I>WatchName</I> is the name of a watch.
+</DD>
+
+<DT><B>watch names</B> ?<I>state</I>? </DT>
+<DD>Lists the names of the watches for a given state. <I>State</I>
+may be one of the following: <I>active</I>, <I>idle</I>, or <I>ignore</I>. If a <I>state</I> argument
+isn't specified, all watches are<BR>
+ listed. </DD>
+</DL>
+</blockquote>
+
+<H2><A NAME="sect6" HREF="#toc6">Keywords</A></H2>
+debug, profile <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Example</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Operations</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/html/winop.html b/blt/html/winop.html
new file mode 100644
index 00000000000..396a41f1c95
--- /dev/null
+++ b/blt/html/winop.html
@@ -0,0 +1,124 @@
+ <!-- manual page source format generated by PolyglotMan v3.0.8+XFree86, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>winop(n) manual page</TITLE>
+</HEAD>
+<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">
+<A HREF="#toc">Table of Contents</A><P>
+
+<H2><A NAME="sect0" HREF="#toc0">Name</A></H2>
+winop - Perform assorted window operations
+<H2><A NAME="sect1" HREF="#toc1">Synopsis</A></H2>
+<B>winop
+lower</B> ?<I>window</I>?... <P>
+<B>winop map</B> ?<I>window</I>?... <P>
+<B>winop move <I>window x y</I></B> <P>
+<B>winop raise</B> ?<I>window</I>?...
+<P>
+<B>winop snap <I>window photoName</I></B> <P>
+<B>winop unmap</B> ?<I>window</I>?... <P>
+<B>winop warpto</B> ?<I>window</I>?
+
+<H2><A NAME="sect2" HREF="#toc2">Description</A></H2>
+The <B>winop</B> command performs various window operations on Tk windows
+using low-level Xlib function calls to work around window manager pecularities.
+
+<H2><A NAME="sect3" HREF="#toc3">Introduction</A></H2>
+Tk has several commands for manipulating its windows: <B>raise</B>,
+<B>lower</B>, <B>wm</B>, etc. These commands ask the window manager to perform operations
+on Tk windows. In some cases, a particular window manager won't perform
+the operation as expected. <P>
+For example, if you positioned a toplevel window
+using <B>wm geometry</B>, the window may not actually be at those particular coordinates.
+ The position of the window may be offset by dimensions of the title bar
+added by the window manager. <P>
+In situations like these, the <B>winop</B> command
+can be used to workaround these difficulties. Instead, it makes low-level
+Xlib (such <B>XRaiseWindow</B> and <B>XMapWindow</B>) calls to perform these operations.
+<BR>
+<CODE>toplevel .top<BR>
+wm withdraw .top<BR>
+<P>
+# Set the geometry to make the window manager <BR>
+# place the window.<BR>
+wm geometry .top +100+100<BR>
+<P>
+# Move the window to the desired location<BR>
+# and "update" to force the window manager<BR>
+# to recognize it.<BR>
+winop move .top 100 100<BR>
+update <BR>
+<P>
+wm deiconify .top<BR>
+winop move .top 100 100<BR>
+
+<H2><A NAME="sect4" HREF="#toc4"></CODE><P>Operations</A></H2>
+The following operations are available for the <B>winop</B> command:
+
+<DL>
+
+<DT><B>winop lower</B> ?<I>window</I>?... </DT>
+<DD>Lowers <I>window</I> to the bottom of the X window stack.
+ <I>Window</I> is the path name of a Tk window. </DD>
+
+<DT><B>winop map</B> ?<I>window</I>?... </DT>
+<DD>Maps <I>window</I>
+on the screen. <I>Window</I> is the path name of a Tk window. If <I>window</I> is already
+mapped, this command has no effect. </DD>
+
+<DT><B>winop move <I>window x y</I></B> </DT>
+<DD>Move <I>window</I>
+to the screen location specified by <I>x</I> and <I>y</I>. <I>Window</I> is the path name of
+a Tk window, while <I>x</I> and <I>y</I> are screen coordinates. This command returns
+ the empty string. </DD>
+
+<DT><B>winop raise</B> ?<I>window</I>?... </DT>
+<DD>Raises <I>window</I> to the top of the
+X window stack. <I>Window</I> must be a valid path name of a Tk window. This command
+returns the empty string. </DD>
+
+<DT><B>winop snap <I>window photoName</I></B> </DT>
+<DD>Takes a snapshot of
+the <I>window</I> and stores the contents in the photo image <I>photoName</I>. <I>Window</I>
+is the valid path name of a Tk window which must be totally visible (unobscured).
+ <I>PhotoName</I> is the name of a Tk photo image which must already exist. This
+command can fail if the window is obscured in any fashion, such as covered
+by another window or partially offscreen. In that case, an error message
+is returned. </DD>
+
+<DT><B>winop unmap</B> ?<I>window</I>?... </DT>
+<DD>Unmaps <I>window</I> from the screen. <I>Window</I> is
+the path name of a Tk window. </DD>
+
+<DT><B>winop warpto</B> ?<I>window</I>? </DT>
+<DD>Warps the pointer to
+<I>window</I>. <I>Window</I> is the path name of a Tk window which must be mapped. If <I>window</I>
+is in the form <I>@x,y</I>, where <I>x</I> and <I>y</I> are root screen coordinates, the pointer
+is warped to that location on the screen. <P>
+[<I>I've never heard a good case for
+warping the pointer in an application. It can be useful for testing, but
+in applications, it's always a bad idea. Simply stated, the user owns the
+pointer, not the application. If you have an application that needs it,
+I'd like to hear about it.</I>] <P>
+If no <I>window</I> argument is present the current
+location of the pointer is returned. The location is returned as a list
+in the form "<I>x y</I>", where <I>x</I> and <I>y</I> are the current coordinates of the pointer.
+</DD>
+</DL>
+
+<H2><A NAME="sect5" HREF="#toc5">Keywords</A></H2>
+window, map, raise, lower, pointer, warp <P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Description</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Introduction</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Operations</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Keywords</A></LI>
+</UL>
+</BODY></HTML>
diff --git a/blt/library/Makefile.cyg b/blt/library/Makefile.cyg
new file mode 100644
index 00000000000..8e5ccd4f90d
--- /dev/null
+++ b/blt/library/Makefile.cyg
@@ -0,0 +1,70 @@
+# ------------------------------------------------------------------------
+# Makefile for library files and directories of BLT library
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+version = $(BLT_MAJOR_VERSION).$(BLT_MINOR_VERSION)
+pkgdir = $(libdir)/tcl$(v1)/blt$(version)
+srcdir = .
+
+cursors = treeview.cur
+
+miscFiles = bltCanvEps.pro \
+ bltGraph.pro \
+ dnd.tcl \
+ dragdrop.tcl \
+ graph.tcl \
+ hierbox.tcl \
+ tabnotebook.tcl \
+ tabset.tcl \
+ treeview.tcl \
+ tclIndex \
+ $(cursors)
+
+ddFiles = dd-color.tcl \
+ dd-file.tcl \
+ dd-number.tcl \
+ dd-text.tcl \
+ tclIndex
+
+instdirs = $(prefix) $(exec_prefix) $(libdir) \
+ $(scriptdir) $(scriptdir)/dd_protocols $(libdir)/tcl$(v1) \
+ $(pkgdir)
+
+all: build-pkgindex
+
+install: install-dirs install-ddfiles install-files install-pkgindex
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else mkdir "$$i" ; fi ; \
+ done
+
+install-ddfiles: install-dirs
+ for i in $(ddFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/dd_protocols/$$i $(scriptdir)/dd_protocols ; \
+ done
+
+install-files: install-dirs
+ for i in $(miscFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/$$i $(scriptdir) ; \
+ done
+
+pkgIndex.tcl: build-pkgindex
+
+build-pkgindex:
+ rm -f pkgIndex.tcl
+ sed -e 's/%VERSION%/$(version)/' $(srcdir)/pkgIndex.tcl.in | \
+ sed -e 's;%LIB_DIR%;$(libdir);' > pkgIndex.tcl
+
+install-pkgindex: pkgIndex.tcl
+ $(INSTALL_DATA) pkgIndex.tcl $(scriptdir)
+ $(INSTALL_DATA) pkgIndex.tcl $(pkgdir)
+
+clean:
+ $(RM) pkgIndex.tcl
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) Makefile
diff --git a/blt/library/Makefile.in b/blt/library/Makefile.in
new file mode 100644
index 00000000000..fef6a48a24c
--- /dev/null
+++ b/blt/library/Makefile.in
@@ -0,0 +1,74 @@
+# ------------------------------------------------------------------------
+# Makefile for library files and directories of BLT library
+# ------------------------------------------------------------------------
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+version = @BLT_VERSION@
+srcdir = @srcdir@
+libdir = @libdir@
+scriptdir = $(prefix)/lib/blt$(version)
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+RM = rm -f
+SHELL = /bin/sh
+
+cursors = treeview.xbm \
+ treeview_m.xbm
+
+miscFiles = \
+ bltCanvEps.pro \
+ bltGraph.pro \
+ dnd.tcl \
+ dragdrop.tcl \
+ graph.tcl \
+ hierbox.tcl \
+ tabnotebook.tcl \
+ tabset.tcl \
+ treeview.tcl \
+ tclIndex \
+ $(cursors)
+
+ddFiles = dd-color.tcl \
+ dd-file.tcl \
+ dd-number.tcl \
+ dd-text.tcl \
+ tclIndex
+
+instdirs = $(prefix) $(exec_prefix) \
+ $(libdir) $(prefix)/lib $(scriptdir) $(scriptdir)/dd_protocols
+
+all: pkgIndex
+
+pkgIndex:
+ rm -f pkgIndex.tcl
+ sed -e 's/%VERSION%/$(version)/' $(srcdir)/pkgIndex.tcl.in | \
+ sed -e 's;%LIB_DIR%;$(libdir);' > pkgIndex.tcl
+
+install: mkdirs pkgIndex
+ for i in $(ddFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/dd_protocols/$$i \
+ $(INSTALL_ROOT)$(scriptdir)/dd_protocols ; \
+ done
+ for i in $(miscFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/$$i $(INSTALL_ROOT)$(scriptdir) ; \
+ done
+ $(INSTALL_DATA) pkgIndex.tcl $(scriptdir)
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)$$i ; then \
+ : ; \
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)$$i ; \
+ fi ; \
+ done
+
+clean:
+ $(RM) pkgIndex.tcl
+
+distclean: clean
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"* Makefile
diff --git a/blt/library/Makefile.vc b/blt/library/Makefile.vc
new file mode 100644
index 00000000000..2c3e0f8a07d
--- /dev/null
+++ b/blt/library/Makefile.vc
@@ -0,0 +1,71 @@
+
+# ------------------------------------------------------------------------
+# Makefile for library files and directories of BLT library
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+version = $(BLT_MAJOR_VERSION).$(BLT_MINOR_VERSION)
+pkgdir = $(libdir)/tcl$(v1)/blt$(version)
+srcdir = .
+
+cursors = treeview.cur
+
+miscFiles = bltCanvEps.pro \
+ bltGraph.pro \
+ dnd.tcl \
+ dragdrop.tcl \
+ graph.tcl \
+ hierbox.tcl \
+ tabnotebook.tcl \
+ tabset.tcl \
+ treeview.tcl \
+ tclIndex \
+ $(cursors)
+
+ddFiles = dd-color.tcl \
+ dd-file.tcl \
+ dd-number.tcl \
+ dd-text.tcl \
+ tclIndex
+
+instdirs = $(prefix) $(exec_prefix) $(libdir) \
+ $(scriptdir) $(scriptdir)/dd_protocols $(libdir)/tcl$(v1) \
+ $(pkgdir)
+
+all: build-pkgindex
+
+install: install-dirs install-ddfiles install-files install-pkgindex
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test -d "$$i" ; then : ; else mkdir "$$i" ; fi ; \
+ done
+
+install-ddfiles: install-dirs
+ for i in $(ddFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/dd_protocols/$$i $(scriptdir)/dd_protocols ; \
+ done
+
+install-files: install-dirs
+ for i in $(miscFiles) ; do \
+ $(INSTALL_DATA) $(srcdir)/$$i $(scriptdir) ; \
+ done
+
+pkgIndex.tcl: build-pkgindex
+
+build-pkgindex:
+ rm -f pkgIndex.tcl
+ sed -e 's/%VERSION%/$(version)/' $(srcdir)/pkgIndex.tcl.in | \
+ sed -e 's;%LIB_DIR%;$(libdir);' > pkgIndex.tcl
+
+install-pkgindex: pkgIndex.tcl
+ $(INSTALL_DATA) pkgIndex.tcl $(scriptdir)
+ $(INSTALL_DATA) pkgIndex.tcl $(pkgdir)
+
+clean:
+ $(RM) pkgIndex.tcl
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) Makefile
diff --git a/blt/library/ZoomStack.itcl b/blt/library/ZoomStack.itcl
new file mode 100644
index 00000000000..09601d7abff
--- /dev/null
+++ b/blt/library/ZoomStack.itcl
@@ -0,0 +1,359 @@
+import add itcl
+
+class ZoomStackGraph {
+
+ # The name of graph (nee the namespace path)
+ variable graph ""
+
+ # Indicates which corner of the rectangular zoom region
+ # is currently being choosen.
+ variable corner "first"
+
+ # Coordinates of the current zoom region. They represent the
+ # two corners of a rectangular area. The two points are order
+ # independent.
+ variable x1
+ variable y1
+ variable x2
+ variable y2
+
+ # A list of axis configuration commmands. Acts as a stack to
+ # unzoom the graph back to previous axis limits.
+ variable stack {}
+
+ constructor { args } {
+ # This will need to change when we start using inheritance.
+ set graph [info namespace tail $this]
+
+ # What about collisions between the blt::graph instance
+ # command and the ZoomStackGraph instance command?
+ blt::graph $graph
+
+ if { [llength $args] > 0 } {
+ $graph configure $args
+ }
+ # Set up the bindings to select/deselect the zoom region
+ bind $graph <1> [code $this SelectPoint %x %y]
+ bind $graph <3> [code $this ClearZoom]
+ # The particular mouse buttons should be configurable.
+ }
+ destructor {
+ if { [winfo exists $graph] } {
+ destroy $graph
+ }
+ }
+
+ # These methods are used internally, within this class, to manage the
+ # zoom stack.
+ private method SaveCoords { x y }
+ private method Zoom {}
+ private method Unzoom {}
+ private method Push { cmd }
+ private method Pop {}
+ private method MarkPoint { x y }
+ private method SetTitle { title }
+ private method DrawBox { }
+
+ # These methods are called by "bind" and "after" from the Tk
+ # event loop. Is there any way of hiding them, so that it
+ # doesn't look to the user as part of the public interface?
+ method ClearZoom {}
+ method ClearTitle {}
+ method UpdateOutline { x y }
+ method SelectPoint { x y }
+}
+
+# ----------------------------------------------------------------------
+#
+# SaveCoords --
+#
+# Given a point on the screen, transforms the point into graph
+# coordinates and saves it as one of the points representing a
+# corner of the zoom region.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::SaveCoords { x y } {
+
+ set coords [$graph invtransform $x $y]
+ set x [lindex $coords 0]
+ set y [lindex $coords 1]
+
+ scan [$graph xaxis limits] "%s %s" min max
+ if { $x > $max } {
+ set x $max
+ } elseif { $x < $min } {
+ set x $min
+ }
+
+ scan [$graph yaxis limits] "%s %s" min max
+ if { $y > $max } {
+ set y $max
+ } elseif { $y < $min } {
+ set y $min
+ }
+
+ if { $corner == "first" } {
+ set x1 $x ; set y1 $y
+ } else {
+ set x2 $x ; set y2 $y
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# MarkPoint --
+#
+# Adds text around one of the corners of the zoom region.
+# The text consists of the x,y graph coordinates of the
+# corner.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::MarkPoint { x y } {
+
+ set marker "bltZoom_text_$corner"
+ set text [format "x=%.4g\ny=%.4g" $x $y]
+
+ if [$graph marker exists $marker] {
+ $graph marker configure $marker -coords { $x $y } -text $text
+ } else {
+ $graph marker create text -coords { $x $y } -name $marker \
+ -font *lucida*-r-*-10-* \
+ -text $text -anchor center -bg {} -justify left
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# Empty --
+#
+# Indicates if the stack of axis configuration commands is
+# empty.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::Empty { } {
+ return [llength $stack]
+}
+
+
+# ----------------------------------------------------------------------
+#
+# Push --
+#
+# Appends a command on the list "stack" which can be used
+# to return to previous graph x and y axis ranges.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::Push { cmd } {
+ lappend stack $cmd
+}
+
+# ----------------------------------------------------------------------
+#
+# Pop --
+#
+# Remove the last item pushed onto the stack and returns it.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::Pop { } {
+ set cmd [lindex $stack end]
+ set stack [lreplace $stack end end]
+ return $cmd
+}
+
+# ----------------------------------------------------------------------
+#
+# ClearTitle --
+#
+# Clears the zoom title (displayed in the upper left corner
+# of the graph). This routine is called from the event queue
+# using "after".
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::ClearTitle {} {
+ $graph marker delete "bltZoom_title"
+}
+
+# ----------------------------------------------------------------------
+#
+# Unzoom --
+#
+# Reverts to a previous zoom. Resets the x and y axis limits
+# back to a previous setting. First checks if there's anything
+# to pop back to. In addition, displays a title in the upper
+# left corner showing the current zoom level.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::Unzoom { } {
+
+ if ![Empty] {
+
+ # Reset the x and y axis limits, by invoking the saved graph
+ # command.
+ eval [Pop]
+
+ # Cheat: Using "Empty" to get the number of entries on the stack.
+ set level [Empty]
+ if { $level > 0 } {
+ SetTitle "Zoom #$level"
+ }
+
+ blt::busy hold $graph
+ update
+ if { $corner == "first" } {
+ # Remember to remove the zoom title in a couple of seconds
+ after 2000 [code $this ClearTitle]
+ }
+ blt::busy release $graph
+ } else {
+ $graph marker delete "bltZoom_title"
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# Zoom --
+#
+# Push the old axis limits on the stack and set them to the
+# zoom region.
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::Zoom { } {
+ $graph marker delete "bltZoom_*"
+
+ if { ($x1 == $x2) && ($y1 == $y2) } {
+ # The first and last points of the zoom region are the same.
+ # Revert back to the start.
+ return
+ }
+
+ # Put a command on the stack that lets us revert back to the current
+ # axis limits.
+ set cmd [format {
+ %s xaxis configure -min "%s" -max "%s"
+ %s yaxis configure -min "%s" -max "%s"
+ } $graph [$graph xaxis cget -min] [$graph xaxis cget -max] \
+ $graph [$graph yaxis cget -min] [$graph yaxis cget -max] ]
+ Push $cmd
+
+ # The first and last corners of the zoom region don't have to be
+ # selected in ascending order. So consider their relative positions
+ # when setting min and max axis limits.
+
+ if { $x1 > $x2 } {
+ $graph xaxis configure -min $x2 -max $x1
+ } elseif { $x1 < $x2 } {
+ $graph xaxis configure -min $x1 -max $x2
+ }
+ if { $y1 > $y2 } {
+ $graph yaxis configure -min $y2 -max $y1
+ } elseif { $y1 < $y2 } {
+ $graph yaxis configure -min $y1 -max $y2
+ }
+
+ # Call "update" explicitly here after the graph is made busy.
+ # This prevents the user from inadvertantly selecting another zoom
+ # region when the graph is recalculating and redrawing itself.
+
+ blt::busy hold $graph
+ update
+ blt::busy release $graph
+}
+
+# ----------------------------------------------------------------------
+#
+# ClearZoom --
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::ClearZoom { } {
+
+ $graph marker delete "bltZoom_*"
+ if { $corner == "first" } {
+ # We're haven't started to select a zoom region, so assume
+ # that we want to revert back to a previous zoom level.
+ Unzoom
+ } else {
+ # Let the user re-pick the first corner again. So reset the
+ # indicator "corner" and turn off the <Motion> binding.
+ set corner "first"
+ bind $graph <Motion> {}
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# SetTitle --
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::SetTitle { title } {
+
+ $graph marker create text -name "bltZoom_title" -text $title \
+ -coords {-Inf Inf} -anchor nw -bg {}
+}
+
+# ----------------------------------------------------------------------
+#
+# UpdateOutline --
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::UpdateOutline { x y } {
+ SaveCoords $x $y
+ MarkPoint $x2 $y2
+ DrawBox
+}
+
+# ----------------------------------------------------------------------
+#
+# SelectPoint --
+#
+# Invoked from the binding to ButtonPress-1 events. Saves
+# a corner of zoom region.
+#
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::SelectPoint { x y } {
+
+ SaveCoords $x $y
+ if { $corner == "first" } {
+ MarkPoint $x1 $y1
+
+ # Display a new title indicating zoom pick is active
+ set level [expr [llength $stack] + 1]
+ SetTitle "Zoom #$level"
+
+ # Start watching now for motion events, drawing an outline
+ bind $graph <Any-Motion> [code $this UpdateOutline %x %y]
+
+ # Indicate the next corner is the last
+ set corner last
+ } else {
+
+ # Stop watching motion events
+ bind $graph <Any-Motion> {}
+
+ # Zoom into the new region defined by the outline
+ Zoom
+
+ # Reset to select the first corner, again
+ set corner first
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# DrawBox --
+#
+# ----------------------------------------------------------------------
+body ZoomStackGraph::DrawBox { } {
+
+ set coords {
+ $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1
+ }
+ if [$graph marker exists "bltZoom_outline"] {
+ $graph marker configure "bltZoom_outline" -coords $coords
+ } else {
+ $graph marker create line -coords $coords -name "bltZoom_outline" \
+ -dashes { 4 2 }
+ }
+ $graph marker before "bltZoom_outline"
+}
+
diff --git a/blt/library/bltCanvEps.pro b/blt/library/bltCanvEps.pro
new file mode 100644
index 00000000000..7b275aa8fb5
--- /dev/null
+++ b/blt/library/bltCanvEps.pro
@@ -0,0 +1,78 @@
+%
+% PostScript encapulator prolog file of the BLT "eps" canvas item.
+%
+% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.
+%
+% Permission to use, copy, modify, and distribute this software and its
+% documentation for any purpose and without fee is hereby granted, provided
+% that the above copyright notice appear in all copies and that both that the
+% copyright notice and warranty disclaimer appear in supporting documentation,
+% and that the names of Lucent Technologies any of their entities not be used
+% in advertising or publicity pertaining to distribution of the software
+% without specific, written prior permission.
+%
+% Lucent Technologies disclaims all warranties with regard to this software,
+% including all implied warranties of merchantability and fitness. In no event
+% shall Lucent Technologies be liable for any special, indirect or
+% consequential damages or any damages whatsoever resulting from loss of use,
+% data or profits, whether in an action of contract, negligence or other
+% tortuous action, arising out of or in connection with the use or performance
+% of this software.
+%
+
+%
+% The definitions of the next two macros are from Appendix H of
+% Adobe's "PostScript Language Reference Manual" pp. 709-736.
+%
+
+% Prepare for EPS file
+
+/BeginEPSF {
+ /beforeInclusionState save def
+ /dictCount countdictstack def % Save the # objects in the dictionary
+ /opCount count 1 sub def % Count object on operator stack
+ userdict begin % Make "userdict" the current
+ % dictionary
+ /showpage {} def % Redefine showpage to be null
+ 0 setgray
+ 0 setlinecap
+ 1 setlinewidth
+ 0 setlinejoin
+ 10 setmiterlimit
+ [] 0 setdash
+ newpath
+ /languagellevel where {
+ pop languagelevel
+ 1 ne {
+ false setstrokeadjust false setoverprint
+ } if
+ } if
+ % note: no "end"
+} bind def
+
+/EndEPSF { %def
+ count opCount sub {
+ pop
+ } repeat
+ countdictstack dictCount sub {
+ end % Clean up dictionary stack
+ } repeat
+ beforeInclusionState restore
+} bind def
+
+
+%
+% Set up a clip region based upon a bounding box (x1, y1, x2, y2).
+%
+/SetClipRegion {
+ % Stack: x1 y1 x2 y2
+ newpath
+ 4 2 roll moveto
+ 1 index 0 rlineto
+ 0 exch rlineto
+ neg 0 rlineto
+ closepath
+ clip
+ newpath
+} def
+
diff --git a/blt/library/bltGraph.pro b/blt/library/bltGraph.pro
new file mode 100644
index 00000000000..a171f2cf1c9
--- /dev/null
+++ b/blt/library/bltGraph.pro
@@ -0,0 +1,462 @@
+%
+% PostScript prolog file of the BLT graph widget.
+%
+% Copyright 1989-1992 Regents of the University of California.
+% Permission to use, copy, modify, and distribute this
+% software and its documentation for any purpose and without
+% fee is hereby granted, provided that the above copyright
+% notice appear in all copies. The University of California
+% makes no representations about the suitability of this
+% software for any purpose. It is provided "as is" without
+% express or implied warranty.
+%
+% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.
+%
+% Permission to use, copy, modify, and distribute this software and its
+% documentation for any purpose and without fee is hereby granted, provided
+% that the above copyright notice appear in all copies and that both that the
+% copyright notice and warranty disclaimer appear in supporting documentation,
+% and that the names of Lucent Technologies any of their entities not be used
+% in advertising or publicity pertaining to distribution of the software
+% without specific, written prior permission.
+%
+% Lucent Technologies disclaims all warranties with regard to this software,
+% including all implied warranties of merchantability and fitness. In no event
+% shall Lucent Technologies be liable for any special, indirect or
+% consequential damages or any damages whatsoever resulting from loss of use,
+% data or profits, whether in an action of contract, negligence or other
+% tortuous action, arising out of or in connection with the use or performance
+% of this software.
+%
+
+200 dict begin
+
+/BaseRatio 1.3467736870885982 def % Ratio triangle base / symbol size
+/BgColorProc 0 def % Background color routine (symbols)
+/DrawSymbolProc 0 def % Routine to draw symbol outline/fill
+/StippleProc 0 def % Stipple routine (bar segments)
+/DashesProc 0 def % Dashes routine (line segments)
+
+% Define the array ISOLatin1Encoding (which specifies how characters are
+% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript
+% level 2 is supposed to define it, but level 1 doesn't).
+
+systemdict /ISOLatin1Encoding known not {
+ /ISOLatin1Encoding [
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /space /exclam /quotedbl /numbersign /dollar /percent /ampersand
+ /quoteright
+ /parenleft /parenright /asterisk /plus /comma /minus /period /slash
+ /zero /one /two /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less /equal /greater /question
+ /at /A /B /C /D /E /F /G
+ /H /I /J /K /L /M /N /O
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+ /quoteleft /a /b /c /d /e /f /g
+ /h /i /j /k /l /m /n /o
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde /space
+ /space /space /space /space /space /space /space /space
+ /space /space /space /space /space /space /space /space
+ /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
+ /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron
+ /space /exclamdown /cent /sterling /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen
+ /registered /macron
+ /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph
+ /periodcentered
+ /cedillar /onesuperior /ordmasculine /guillemotright /onequarter
+ /onehalf /threequarters /questiondown
+ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex
+ /Idieresis
+ /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn
+ /germandbls
+ /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex
+ /idieresis
+ /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn
+ /ydieresis
+ ] def
+} if
+
+% font ISOEncode font
+% This procedure changes the encoding of a font from the default
+% Postscript encoding to ISOLatin1. It is typically invoked just
+% before invoking "setfont". The body of this procedure comes from
+% Section 5.6.1 of the Postscript book.
+
+/ISOEncode {
+ dup length dict
+ begin
+ {1 index /FID ne {def} {pop pop} ifelse} forall
+ /Encoding ISOLatin1Encoding def
+ currentdict
+ end
+
+ % I'm not sure why it's necessary to use "definefont" on this new
+ % font, but it seems to be important; just use the name "Temporary"
+ % for the font.
+
+ /Temporary exch definefont
+} bind def
+
+/Stroke {
+ gsave
+ stroke
+ grestore
+} def
+
+/Fill {
+ gsave
+ fill
+ grestore
+} def
+
+/SetFont {
+ % Stack: pointSize fontName
+ findfont exch scalefont ISOEncode setfont
+} def
+
+/Box {
+ % Stack: x y width height
+ newpath
+ exch 4 2 roll moveto
+ dup 0 rlineto
+ exch 0 exch rlineto
+ neg 0 rlineto
+ closepath
+} def
+
+/SetFgColor {
+ % Stack: red green blue
+ CL 0 eq {
+ pop pop pop 0 0 0
+ } if
+ setrgbcolor
+ CL 1 eq {
+ currentgray setgray
+ } if
+} def
+
+/SetBgColor {
+ % Stack: red green blue
+ CL 0 eq {
+ pop pop pop 1 1 1
+ } if
+ setrgbcolor
+ CL 1 eq {
+ currentgray setgray
+ } if
+} def
+
+% The next two definitions are taken from "$tk_library/prolog.ps"
+
+% desiredSize EvenPixels closestSize
+%
+% The procedure below is used for stippling. Given the optimal size
+% of a dot in a stipple pattern in the current user coordinate system,
+% compute the closest size that is an exact multiple of the device's
+% pixel size. This allows stipple patterns to be displayed without
+% aliasing effects.
+
+/EvenPixels {
+ % Compute exact number of device pixels per stipple dot.
+ dup 0 matrix currentmatrix dtransform
+ dup mul exch dup mul add sqrt
+
+ % Round to an integer, make sure the number is at least 1, and compute
+ % user coord distance corresponding to this.
+ dup round dup 1 lt {pop 1} if
+ exch div mul
+} bind def
+
+% width height string filled StippleFill --
+%
+% Given a path and other graphics information already set up, this
+% procedure will fill the current path in a stippled fashion. "String"
+% contains a proper image description of the stipple pattern and
+% "width" and "height" give its dimensions. If "filled" is true then
+% it means that the area to be stippled is gotten by filling the
+% current path (e.g. the interior of a polygon); if it's false, the
+% area is gotten by stroking the current path (e.g. a wide line).
+% Each stipple dot is assumed to be about one unit across in the
+% current user coordinate system.
+
+% width height string StippleFill --
+%
+% Given a path already set up and a clipping region generated from
+% it, this procedure will fill the clipping region with a stipple
+% pattern. "String" contains a proper image description of the
+% stipple pattern and "width" and "height" give its dimensions. Each
+% stipple dot is assumed to be about one unit across in the current
+% user coordinate system. This procedure trashes the graphics state.
+
+/StippleFill {
+ % The following code is needed to work around a NeWSprint bug.
+
+ /tmpstip 1 index def
+
+ % Change the scaling so that one user unit in user coordinates
+ % corresponds to the size of one stipple dot.
+ 1 EvenPixels dup scale
+
+ % Compute the bounding box occupied by the path (which is now
+ % the clipping region), and round the lower coordinates down
+ % to the nearest starting point for the stipple pattern. Be
+ % careful about negative numbers, since the rounding works
+ % differently on them.
+
+ pathbbox
+ 4 2 roll
+ 5 index div dup 0 lt {1 sub} if cvi 5 index mul 4 1 roll
+ 6 index div dup 0 lt {1 sub} if cvi 6 index mul 3 2 roll
+
+ % Stack now: width height string y1 y2 x1 x2
+ % Below is a doubly-nested for loop to iterate across this area
+ % in units of the stipple pattern size, going up columns then
+ % across rows, blasting out a stipple-pattern-sized rectangle at
+ % each position
+
+ 6 index exch {
+ 2 index 5 index 3 index {
+ % Stack now: width height string y1 y2 x y
+
+ gsave
+ 1 index exch translate
+ 5 index 5 index true matrix tmpstip imagemask
+ grestore
+ } for
+ pop
+ } for
+ pop pop pop pop pop
+} bind def
+
+
+/LS { % Stack: x1 y1 x2 y2
+ newpath 4 2 roll moveto lineto stroke
+} def
+
+/EndText {
+ %Stack :
+ grestore
+} def
+
+/BeginText {
+ %Stack : w h theta centerX centerY
+ gsave
+ % Translate the origin to the center of bounding box and rotate
+ translate neg rotate
+ % Translate back to the origin of the text region
+ -0.5 mul exch -0.5 mul exch translate
+} def
+
+/DrawAdjText {
+ %Stack : str strWidth x y
+ moveto % Go to the text position
+ exch dup dup 4 2 roll
+
+ % Adjust character widths to get desired overall string width
+ % adjust X = (desired width - real width)/#chars
+
+ stringwidth pop sub exch
+ length div
+ 0 3 -1 roll
+
+ % Flip back the scale so that the string is not drawn in reverse
+
+ gsave
+ 1 -1 scale
+ ashow
+ grestore
+} def
+
+/DrawBitmap {
+ % Stack: ?bgColorProc? boolean centerX centerY width height theta imageStr
+ gsave
+ 6 -2 roll translate % Translate to center of bounding box
+ 4 1 roll neg rotate % Rotate by theta
+
+ % Find upperleft corner of bounding box
+
+ 2 copy -.5 mul exch -.5 mul exch translate
+ 2 copy scale % Make pixel unit scale
+ newpath
+ 0 0 moveto 0 1 lineto 1 1 lineto 1 0 lineto
+ closepath
+
+ % Fill rectangle with background color
+
+ 4 -1 roll {
+ gsave
+ 4 -1 roll exec fill
+ grestore
+ } if
+
+ % Paint the image string into the unit rectangle
+
+ 2 copy true 3 -1 roll 0 0 5 -1 roll 0 0 6 array astore 5 -1 roll
+ imagemask
+ grestore
+}def
+
+% Symbols:
+
+% Skinny-cross
+/Sc {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate 45 rotate
+ 0 0 3 -1 roll Sp
+ grestore
+} def
+
+% Skinny-plus
+/Sp {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ 2 idiv
+ dup 2 copy
+ newpath neg 0 moveto 0 lineto
+ DrawSymbolProc
+ newpath neg 0 exch moveto 0 exch lineto
+ DrawSymbolProc
+ grestore
+} def
+
+% Cross
+/Cr {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate 45 rotate
+ 0 0 3 -1 roll Pl
+ grestore
+} def
+
+% Plus
+/Pl {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ dup 2 idiv
+ exch 6 idiv
+
+ %
+ % 2 3 The plus/cross symbol is a
+ % closed polygon of 12 points.
+ % 0 1 4 5 The diagram to the left
+ % x,y represents the positions of
+ % 11 10 7 6 the points which are computed
+ % below.
+ % 9 8
+ %
+
+ newpath
+ 2 copy exch neg exch neg moveto dup neg dup lineto
+ 2 copy neg exch neg lineto 2 copy exch neg lineto
+ dup dup neg lineto 2 copy neg lineto 2 copy lineto
+ dup dup lineto 2 copy exch lineto 2 copy neg exch lineto
+ dup dup neg exch lineto exch neg exch lineto
+ closepath
+ DrawSymbolProc
+ grestore
+} def
+
+% Circle
+/Ci {
+ % Stack: x y symbolSize
+ 3 copy pop
+ moveto newpath
+ 2 div 0 360 arc
+ closepath DrawSymbolProc
+} def
+
+% Square
+/Sq {
+ % Stack: x y symbolSize
+ dup dup 2 div dup
+ 6 -1 roll exch sub exch
+ 5 -1 roll exch sub 4 -2 roll Box
+ DrawSymbolProc
+} def
+
+% Line
+/Li {
+ % Stack: x y symbolSize
+ 3 1 roll exch 3 -1 roll 2 div 3 copy
+ newpath
+ sub exch moveto add exch lineto
+ stroke
+} def
+
+% Diamond
+/Di {
+ % Stack: x y symbolSize
+ gsave
+ 3 1 roll translate 45 rotate 0 0 3 -1 roll Sq
+ grestore
+} def
+
+% Triangle
+/Tr {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ BaseRatio mul 0.5 mul % Calculate 1/2 base
+ dup 0 exch 30 cos mul % h1 = height above center point
+ neg % b2 0 -h1
+ newpath moveto % point 1; b2
+ dup 30 sin 30 cos div mul % h2 = height below center point
+ 2 copy lineto % point 2; b2 h2
+ exch neg exch lineto %
+ closepath
+ DrawSymbolProc
+ grestore
+} def
+
+% Arrow
+/Ar {
+ % Stack: x y symbolSize
+ gsave
+ 3 -2 roll translate
+ BaseRatio mul 0.5 mul % Calculate 1/2 base
+ dup 0 exch 30 cos mul % h1 = height above center point
+ % b2 0 h1
+ newpath moveto % point 1; b2
+ dup 30 sin 30 cos div mul % h2 = height below center point
+ neg % -h2 b2
+ 2 copy lineto % point 2; b2 h2
+ exch neg exch lineto %
+ closepath
+ DrawSymbolProc
+ grestore
+} def
+
+% Bitmap
+/Bm {
+ % Stack: x y symbolSize
+ gsave
+ 3 1 roll translate pop DrawSymbolProc
+ grestore
+} def
+
+%%BeginSetup
+gsave % Save the graphics state
+
+% Default line/text style parameters
+
+1 setlinewidth % width
+1 setlinejoin % join
+0 setlinecap % cap
+[] 0 setdash % dashes
+
+/CL 0 def % Set color level mode
+0 0 0 setrgbcolor % color
+
diff --git a/blt/library/dd_protocols/dd-color.tcl b/blt/library/dd_protocols/dd-color.tcl
new file mode 100644
index 00000000000..02ba488439d
--- /dev/null
+++ b/blt/library/dd_protocols/dd-color.tcl
@@ -0,0 +1,51 @@
+# ----------------------------------------------------------------------
+# PURPOSE: drag&drop send routine for "color" data
+#
+# Widgets that are to participate in drag&drop operations for
+# "color" data should be registered as follows:
+#
+# drag&drop .win source handler color dd_send_color
+# drag&drop .win target handler color my_color_handler
+#
+# proc my_color_handler {} {
+# global DragDrop
+#
+# set data $DragDrop(color)
+# .
+# . do something with $data
+# .
+# }
+#
+# AUTHOR: Michael J. McLennan Phone: (215)770-2842
+# AT&T Bell Laboratories E-mail: aluxpo!mmc@att.com
+#
+# SCCS: %W% (%G%)
+# ----------------------------------------------------------------------
+# Copyright (c) 1993 AT&T All Rights Reserved
+# ======================================================================
+
+# ----------------------------------------------------------------------
+# COMMAND: dd_send_color <interp> <ddwin> <data>
+#
+# INPUTS
+# <interp> = interpreter for target application
+# <ddwin> = pathname for target drag&drop window
+# <data> = data returned from -tokencmd
+#
+# RETURNS
+# ""
+#
+# SIDE-EFFECTS
+# Sends data to remote application DragDrop(color), and then
+# invokes the "color" handler for the drag&drop target.
+# ----------------------------------------------------------------------
+proc dd_send_color {interp ddwin data} {
+ send $interp "
+ foreach color [list $data] {
+ winfo rgb . \$color
+ }
+ global DragDrop
+ set DragDrop(color) [list $data]
+ "
+ send $interp "drag&drop target $ddwin handle color"
+}
diff --git a/blt/library/dd_protocols/dd-file.tcl b/blt/library/dd_protocols/dd-file.tcl
new file mode 100644
index 00000000000..d49c6f9d085
--- /dev/null
+++ b/blt/library/dd_protocols/dd-file.tcl
@@ -0,0 +1,53 @@
+# ----------------------------------------------------------------------
+# PURPOSE: drag&drop send routine for "file" data
+#
+# Widgets that are to participate in drag&drop operations for
+# "file" data should be registered as follows:
+#
+# drag&drop .win source handler text dd_send_file
+# drag&drop .win target handler text my_file_handler
+#
+# proc my_file_handler {} {
+# global DragDrop
+#
+# set data $DragDrop(file)
+# .
+# . do something with $data
+# .
+# }
+#
+# AUTHOR: Michael J. McLennan Phone: (215)770-2842
+# AT&T Bell Laboratories E-mail: aluxpo!mmc@att.com
+#
+# SCCS: %W% (%G%)
+# ----------------------------------------------------------------------
+# Copyright (c) 1993 AT&T All Rights Reserved
+# ======================================================================
+
+# ----------------------------------------------------------------------
+# COMMAND: dd_send_file <interp> <ddwin> <data>
+#
+# INPUTS
+# <interp> = interpreter for target application
+# <ddwin> = pathname for target drag&drop window
+# <data> = data returned from -tokencmd
+#
+# RETURNS
+# ""
+#
+# SIDE-EFFECTS
+# Sends data to remote application DragDrop(file), and then
+# invokes the "file" handler for the drag&drop target.
+# ----------------------------------------------------------------------
+proc dd_send_file {interp ddwin data} {
+ send $interp "
+ foreach file [list $data] {
+ if {!\[file exists \$file\]} {
+ error \"not a file: \$file\"
+ }
+ }
+ global DragDrop
+ set DragDrop(file) [list $data]
+ "
+ send $interp "drag&drop target $ddwin handle file"
+}
diff --git a/blt/library/dd_protocols/dd-number.tcl b/blt/library/dd_protocols/dd-number.tcl
new file mode 100644
index 00000000000..0cee53722fa
--- /dev/null
+++ b/blt/library/dd_protocols/dd-number.tcl
@@ -0,0 +1,51 @@
+# ----------------------------------------------------------------------
+# PURPOSE: drag&drop send routine for "number" data
+#
+# Widgets that are to participate in drag&drop operations for
+# "number" data should be registered as follows:
+#
+# drag&drop .win source handler number dd_send_number
+# drag&drop .win target handler number my_number_handler
+#
+# proc my_number_handler {} {
+# global DragDrop
+#
+# set data $DragDrop(number)
+# .
+# . do something with $data
+# .
+# }
+#
+# AUTHOR: Michael J. McLennan Phone: (215)770-2842
+# AT&T Bell Laboratories E-mail: aluxpo!mmc@att.com
+#
+# SCCS: %W% (%G%)
+# ----------------------------------------------------------------------
+# Copyright (c) 1993 AT&T All Rights Reserved
+# ======================================================================
+
+# ----------------------------------------------------------------------
+# COMMAND: dd_send_number <interp> <ddwin> <data>
+#
+# INPUTS
+# <interp> = interpreter for target application
+# <ddwin> = pathname for target drag&drop window
+# <data> = data returned from -tokencmd
+#
+# RETURNS
+# ""
+#
+# SIDE-EFFECTS
+# Sends data to remote application DragDrop(number), and then
+# invokes the "number" handler for the drag&drop target.
+# ----------------------------------------------------------------------
+proc dd_send_number {interp ddwin data} {
+ send $interp "
+ foreach num [list $data] {
+ expr \$num*1
+ }
+ global DragDrop
+ set DragDrop(number) [list $data]
+ "
+ send $interp "drag&drop target $ddwin handle number"
+}
diff --git a/blt/library/dd_protocols/dd-text.tcl b/blt/library/dd_protocols/dd-text.tcl
new file mode 100644
index 00000000000..eedbd487657
--- /dev/null
+++ b/blt/library/dd_protocols/dd-text.tcl
@@ -0,0 +1,48 @@
+# ----------------------------------------------------------------------
+# PURPOSE: drag&drop send routine for "text" data
+#
+# Widgets that are to participate in drag&drop operations for
+# "text" data should be registered as follows:
+#
+# drag&drop .win source handler text dd_send_text
+# drag&drop .win target handler text my_text_handler
+#
+# proc my_text_handler {} {
+# global DragDrop
+#
+# set data $DragDrop(text)
+# .
+# . do something with $data
+# .
+# }
+#
+# AUTHOR: Michael J. McLennan Phone: (215)770-2842
+# AT&T Bell Laboratories E-mail: aluxpo!mmc@att.com
+#
+# SCCS: %W% (%G%)
+# ----------------------------------------------------------------------
+# Copyright (c) 1993 AT&T All Rights Reserved
+# ======================================================================
+
+# ----------------------------------------------------------------------
+# COMMAND: dd_send_text <interp> <ddwin> <data>
+#
+# INPUTS
+# <interp> = interpreter for target application
+# <ddwin> = pathname for target drag&drop window
+# <data> = data returned from -tokencmd
+#
+# RETURNS
+# ""
+#
+# SIDE-EFFECTS
+# Sends data to remote application DragDrop(text), and then
+# invokes the "text" handler for the drag&drop target.
+# ----------------------------------------------------------------------
+proc dd_send_text {interp ddwin data} {
+ send $interp "
+ global DragDrop
+ set DragDrop(text) [list $data]
+ "
+ send $interp "drag&drop target $ddwin handle text"
+}
diff --git a/blt/library/dd_protocols/tclIndex b/blt/library/dd_protocols/tclIndex
new file mode 100644
index 00000000000..d3ca8316bfb
--- /dev/null
+++ b/blt/library/dd_protocols/tclIndex
@@ -0,0 +1,12 @@
+# Tcl autoload index file, version 2.0
+# This file is generated by the "auto_mkindex" command
+# and sourced to set up indexing information for one or
+# more commands. Typically each line is a command that
+# sets an element in the auto_index array, where the
+# element name is the name of a command and the value is
+# a script that loads the command.
+
+set auto_index(dd_send_file) "source $dir/dd-file.tcl"
+set auto_index(dd_send_text) "source $dir/dd-text.tcl"
+set auto_index(dd_send_number) "source $dir/dd-number.tcl"
+set auto_index(dd_send_color) "source $dir/dd-color.tcl"
diff --git a/blt/library/dnd.tcl b/blt/library/dnd.tcl
new file mode 100644
index 00000000000..c8fb68d64cc
--- /dev/null
+++ b/blt/library/dnd.tcl
@@ -0,0 +1,102 @@
+#
+# dnd.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT drag&drop command
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@bell-labs.com
+# http://www.tcltk.com/blt
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+if { $tcl_version >= 8.0 } {
+ set cmd blt::dnd
+} else {
+ set cmd dnd
+}
+for { set i 1 } { $i <= 5 } { incr i } {
+ bind BltDndButton$i <ButtonPress-$i> [list $cmd select %W %X %Y %t]
+ bind BltDndButton$i <B$i-Motion> [list $cmd drag %W %X %Y]
+ bind BltDndButton$i <ButtonRelease-$i> [list $cmd drop %W %X %Y]
+}
+
+# ----------------------------------------------------------------------
+#
+# DndInit --
+#
+# Invoked from C whenever a new drag&drop source is created.
+# Sets up the default bindings for the drag&drop source.
+#
+# <ButtonPress-?> Starts the drag operation.
+# <B?-Motion> Updates the drag.
+# <ButtonRelease-?> Drop the data on the target.
+#
+# Arguments:
+# widget source widget
+# button Mouse button used to activate drag.
+# cmd "dragdrop" or "blt::dragdrop"
+#
+# ----------------------------------------------------------------------
+
+proc blt::DndInit { widget button } {
+ set tagList {}
+ if { $button > 0 } {
+ lappend tagList BltDndButton$button
+ }
+ foreach tag [bindtags $widget] {
+ if { ![string match BltDndButton* $tag] } {
+ lappend tagList $tag
+ }
+ }
+ bindtags $widget $tagList
+}
+
+proc blt::DndStdDrop { widget args } {
+ array set info $args
+ set fmt [lindex $info(formats) 0]
+ dnd pull $widget $fmt
+ return 0
+}
+
+proc blt::PrintInfo { array } {
+ upvar $array state
+
+ parray state
+ if { $info(state) & 0x01 } {
+ puts "Shift-Drop"
+ }
+ if { $info(state) & 0x02 } {
+ puts "CapsLock-Drop"
+ }
+ if { $info(state) & 0x04 } {
+ puts "Control-Drop"
+ }
+ if { $info(state) & 0x08 } {
+ puts "Alt-Drop"
+ }
+ if { $info(state) & 0x10 } {
+ puts "NumLock-Drop"
+ }
+} \ No newline at end of file
diff --git a/blt/library/dragdrop.tcl b/blt/library/dragdrop.tcl
new file mode 100644
index 00000000000..04e19607c82
--- /dev/null
+++ b/blt/library/dragdrop.tcl
@@ -0,0 +1,75 @@
+#
+# dragdrop.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT drag&drop command
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@bell-labs.com
+# http://www.tcltk.com/blt
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+if { $tcl_version >= 8.0 } {
+ set cmd blt::drag&drop
+} else {
+ set cmd drag&drop
+}
+for { set i 1 } { $i <= 5 } { incr i } {
+ bind BltDrag&DropButton$i <ButtonPress-$i> [list $cmd drag %W %X %Y]
+ bind BltDrag&DropButton$i <B$i-Motion> [list $cmd drag %W %X %Y]
+ bind BltDrag&DropButton$i <ButtonRelease-$i> [list $cmd drop %W %X %Y]
+}
+
+# ----------------------------------------------------------------------
+#
+# Drag&DropInit --
+#
+# Invoked from C whenever a new drag&drop source is created.
+# Sets up the default bindings for the drag&drop source.
+#
+# <ButtonPress-?> Starts the drag operation.
+# <B?-Motion> Updates the drag.
+# <ButtonRelease-?> Drop the data on the target.
+#
+# Arguments:
+# widget source widget
+# button Mouse button used to activate drag.
+# cmd "dragdrop" or "blt::dragdrop"
+#
+# ----------------------------------------------------------------------
+
+proc blt::Drag&DropInit { widget button } {
+ set tagList {}
+ if { $button > 0 } {
+ lappend tagList BltDrag&DropButton$button
+ }
+ foreach tag [bindtags $widget] {
+ if { ![string match BltDrag&DropButton* $tag] } {
+ lappend tagList $tag
+ }
+ }
+ bindtags $widget $tagList
+}
+
diff --git a/blt/library/graph.tcl b/blt/library/graph.tcl
new file mode 100644
index 00000000000..1770005e636
--- /dev/null
+++ b/blt/library/graph.tcl
@@ -0,0 +1,492 @@
+
+proc Blt_ActiveLegend { graph } {
+ $graph legend bind all <Enter> [list blt::ActivateLegend $graph ]
+ $graph legend bind all <Leave> [list blt::DeactivateLegend $graph]
+ $graph legend bind all <ButtonPress-1> [list blt::HighlightLegend $graph]
+}
+
+proc Blt_Crosshairs { graph } {
+ blt::Crosshairs $graph
+}
+
+proc Blt_ZoomStack { graph } {
+ blt::ZoomStack $graph
+}
+
+proc Blt_PrintKey { graph } {
+ blt::PrintKey $graph
+}
+
+proc Blt_ClosestPoint { graph } {
+ blt::ClosestPoint $graph
+}
+
+#
+# The following procedures that reside in the "blt" namespace are
+# supposed to be private.
+#
+
+proc blt::ActivateLegend { graph } {
+ set elem [$graph legend get current]
+ $graph legend activate $elem
+}
+proc blt::DeactivateLegend { graph } {
+ set elem [$graph legend get current]
+ $graph legend deactivate $elem
+}
+
+proc blt::HighlightLegend { graph } {
+ set elem [$graph legend get current]
+ set relief [$graph element cget $elem -labelrelief]
+ if { $relief == "flat" } {
+ $graph element configure $elem -labelrelief raised
+ $graph element activate $elem
+ } else {
+ $graph element configure $elem -labelrelief flat
+ $graph element deactivate $elem
+ }
+}
+
+proc blt::Crosshairs { graph { event "Any-Motion" } } {
+ $graph crosshairs on
+ bind crosshairs-$graph <$event> {
+ %W crosshairs configure -position @%x,%y
+ }
+ bind crosshairs-$graph <Leave> {
+ %W crosshairs off
+ }
+ bind crosshairs-$graph <Enter> {
+ %W crosshairs on
+ }
+ $graph crosshairs configure -color red
+ blt::AddBindTag $graph crosshairs-$graph
+}
+
+proc blt::InitStack { graph } {
+ global zoomInfo
+ set zoomInfo($graph,interval) 100
+ set zoomInfo($graph,afterId) 0
+ set zoomInfo($graph,A,x) {}
+ set zoomInfo($graph,A,y) {}
+ set zoomInfo($graph,B,x) {}
+ set zoomInfo($graph,B,y) {}
+ set zoomInfo($graph,stack) {}
+ set zoomInfo($graph,corner) A
+}
+
+proc blt::ZoomStack { graph {start "ButtonPress-1"} {reset "ButtonPress-3"} } {
+ global zoomInfo zoomMod
+
+ blt::InitStack $graph
+
+ if { [info exists zoomMod] } {
+ set modifier $zoomMod
+ } else {
+ set modifier ""
+ }
+ bind zoom-$graph <${modifier}${start}> { blt::SetZoomPoint %W %x %y }
+ bind zoom-$graph <${modifier}${reset}> {
+ if { [%W inside %x %y] } {
+ blt::ResetZoom %W
+ }
+ }
+ blt::AddBindTag $graph zoom-$graph
+}
+
+proc blt::PrintKey { graph {event "Shift-ButtonRelease-3"} } {
+ bind print-$graph <$event> { Blt_PostScriptDialog %W }
+ blt::AddBindTag $graph print-$graph
+}
+
+proc blt::ClosestPoint { graph {event "Control-ButtonPress-2"} } {
+ bind closest-point-$graph <$event> {
+ blt::FindElement %W %x %y
+ }
+ blt::AddBindTag $graph closest-point-$graph
+}
+
+proc blt::AddBindTag { widget tag } {
+ set oldTagList [bindtags $widget]
+ if { [lsearch $oldTagList $tag] < 0 } {
+ bindtags $widget [linsert $oldTagList 0 $tag]
+ }
+}
+
+proc blt::RemoveBindTag { widget tag } {
+ set oldTagList [bindtags $widget]
+ set index [lsearch $oldTagList $tag]
+ if { $index >= 0 } {
+ bindtags $widget [lreplace $oldTagList $index $index]
+ }
+}
+
+proc blt::FindElement { graph x y } {
+ if ![$graph element closest $x $y info -interpolate yes] {
+ beep
+ return
+ }
+ # --------------------------------------------------------------
+ # find(name) - element Id
+ # find(index) - index of closest point
+ # find(x) find(y) - coordinates of closest point
+ # or closest point on line segment.
+ # find(dist) - distance from sample coordinate
+ # --------------------------------------------------------------
+ set markerName "bltClosest_$info(name)"
+ catch { $graph marker delete $markerName }
+ $graph marker create text -coords { $info(x) $info(y) } \
+ -name $markerName \
+ -text "$info(name): $info(dist)\nindex $info(index)" \
+ -font *lucida*-r-*-10-* \
+ -anchor center -justify left \
+ -yoffset 0 -bg {}
+
+ set coords [$graph invtransform $x $y]
+ set nx [lindex $coords 0]
+ set ny [lindex $coords 1]
+
+ $graph marker create line -coords "$nx $ny $info(x) $info(y)" \
+ -name line.$markerName
+
+ blt::FlashPoint $graph $info(name) $info(index) 10
+ blt::FlashPoint $graph $info(name) [expr $info(index) + 1] 10
+}
+
+proc blt::FlashPoint { graph name index count } {
+ if { $count & 1 } {
+ $graph element deactivate $name
+ } else {
+ $graph element activate $name $index
+ }
+ incr count -1
+ if { $count > 0 } {
+ after 200 blt::FlashPoint $graph $name $index $count
+ update
+ } else {
+ eval $graph marker delete [$graph marker names "bltClosest_*"]
+ }
+}
+
+proc blt::GetCoords { graph x y index } {
+ global zoomInfo
+ if { [$graph cget -invertxy] } {
+ set zoomInfo($graph,$index,x) $y
+ set zoomInfo($graph,$index,y) $x
+ } else {
+ set zoomInfo($graph,$index,x) $x
+ set zoomInfo($graph,$index,y) $y
+ }
+}
+
+proc blt::MarkPoint { graph index } {
+ global zoomInfo
+ set x [$graph xaxis invtransform $zoomInfo($graph,$index,x)]
+ set y [$graph yaxis invtransform $zoomInfo($graph,$index,y)]
+ set marker "zoomText_$index"
+ set text [format "x=%.4g\ny=%.4g" $x $y]
+
+ if [$graph marker exists $marker] {
+ $graph marker configure $marker -coords { $x $y } -text $text
+ } else {
+ $graph marker create text -coords { $x $y } -name $marker \
+ -font *lucida*-r-*-10-* \
+ -text $text -anchor center -bg {} -justify left
+ }
+}
+
+proc blt::DestroyZoomTitle { graph } {
+ global zoomInfo
+
+ if { $zoomInfo($graph,corner) == "A" } {
+ catch { $graph marker delete "zoomTitle" }
+ }
+}
+
+proc blt::PopZoom { graph } {
+ global zoomInfo
+
+ set zoomStack $zoomInfo($graph,stack)
+ if { [llength $zoomStack] > 0 } {
+ set cmd [lindex $zoomStack 0]
+ set zoomInfo($graph,stack) [lrange $zoomStack 1 end]
+ eval $cmd
+ blt::ZoomTitleLast $graph
+ busy hold $graph
+ update
+ after 2000 "blt::DestroyZoomTitle $graph"
+ busy release $graph
+ } else {
+ catch { $graph marker delete "zoomTitle" }
+ }
+}
+
+# Push the old axis limits on the stack and set the new ones
+
+proc blt::PushZoom { graph } {
+ global zoomInfo
+ eval $graph marker delete [$graph marker names "zoom*"]
+ if { [info exists zoomInfo($graph,afterId)] } {
+ after cancel $zoomInfo($graph,afterId)
+ }
+ set x1 $zoomInfo($graph,A,x)
+ set y1 $zoomInfo($graph,A,y)
+ set x2 $zoomInfo($graph,B,x)
+ set y2 $zoomInfo($graph,B,y)
+
+ if { ($x1 == $x2) || ($y1 == $y2) } {
+ # No delta, revert to start
+ return
+ }
+ set cmd {}
+ foreach margin { xaxis yaxis x2axis y2axis } {
+ foreach axis [$graph $margin use] {
+ set min [$graph axis cget $axis -min]
+ set max [$graph axis cget $axis -max]
+ set c [list $graph axis configure $axis -min $min -max $max]
+ append cmd "$c\n"
+ }
+ }
+ set zoomInfo($graph,stack) [linsert $zoomInfo($graph,stack) 0 $cmd]
+
+ busy hold $graph
+ # This update lets the busy cursor take effect.
+ update
+
+ foreach margin { xaxis x2axis } {
+ foreach axis [$graph $margin use] {
+ set min [$graph axis invtransform $axis $x1]
+ set max [$graph axis invtransform $axis $x2]
+ if { $min > $max } {
+ $graph axis configure $axis -min $max -max $min
+ } else {
+ $graph axis configure $axis -min $min -max $max
+ }
+ }
+ }
+ foreach margin { yaxis y2axis } {
+ foreach axis [$graph $margin use] {
+ set min [$graph axis invtransform $axis $y1]
+ set max [$graph axis invtransform $axis $y2]
+ if { $min > $max } {
+ $graph axis configure $axis -min $max -max $min
+ } else {
+ $graph axis configure $axis -min $min -max $max
+ }
+ }
+ }
+ # This "update" forces the graph to be redrawn
+ update
+
+ busy release $graph
+}
+
+#
+# This routine terminates either an existing zoom, or pops back to
+# the previous zoom level (if no zoom is in progress).
+#
+
+proc blt::ResetZoom { graph } {
+ global zoomInfo
+
+ if { ![info exists zoomInfo($graph,corner)] } {
+ blt::InitStack $graph
+ }
+ eval $graph marker delete [$graph marker names "zoom*"]
+
+ if { $zoomInfo($graph,corner) == "A" } {
+ # Reset the whole axis
+ blt::PopZoom $graph
+ } else {
+ global zoomMod
+
+ if { [info exists zoomMod] } {
+ set modifier $zoomMod
+ } else {
+ set modifier "Any-"
+ }
+ set zoomInfo($graph,corner) A
+ blt::RemoveBindTag $graph select-region-$graph
+ }
+}
+
+option add *zoomTitle.font -*-helvetica-medium-R-*-*-18-*-*-*-*-*-*-*
+option add *zoomTitle.shadow yellow4
+option add *zoomTitle.foreground yellow1
+option add *zoomTitle.coords "-Inf Inf"
+
+proc blt::ZoomTitleNext { graph } {
+ global zoomInfo
+ set level [expr [llength $zoomInfo($graph,stack)] + 1]
+ if { [$graph cget -invertxy] } {
+ set coords "-Inf -Inf"
+ } else {
+ set coords "-Inf Inf"
+ }
+ $graph marker create text -name "zoomTitle" -text "Zoom #$level" \
+ -coords $coords -bindtags "" -anchor nw
+}
+
+proc blt::ZoomTitleLast { graph } {
+ global zoomInfo
+
+ set level [llength $zoomInfo($graph,stack)]
+ if { $level > 0 } {
+ $graph marker create text -name "zoomTitle" -anchor nw \
+ -text "Zoom #$level"
+ }
+}
+
+
+proc blt::SetZoomPoint { graph x y } {
+ global zoomInfo zoomMod
+ if { ![info exists zoomInfo($graph,corner)] } {
+ blt::InitStack $graph
+ }
+ blt::GetCoords $graph $x $y $zoomInfo($graph,corner)
+ if { [info exists zoomMod] } {
+ set modifier $zoomMod
+ } else {
+ set modifier "Any-"
+ }
+ bind select-region-$graph <${modifier}Motion> {
+ blt::GetCoords %W %x %y B
+ #blt::MarkPoint $graph B
+ blt::Box %W
+ }
+ if { $zoomInfo($graph,corner) == "A" } {
+ if { ![$graph inside $x $y] } {
+ return
+ }
+ # First corner selected, start watching motion events
+
+ #blt::MarkPoint $graph A
+ blt::ZoomTitleNext $graph
+
+ blt::AddBindTag $graph select-region-$graph
+ set zoomInfo($graph,corner) B
+ } else {
+ # Delete the modal binding
+ blt::RemoveBindTag $graph select-region-$graph
+ blt::PushZoom $graph
+ set zoomInfo($graph,corner) A
+ }
+}
+
+option add *zoomOutline.dashes 4
+option add *zoomTitle.anchor nw
+option add *zoomOutline.lineWidth 2
+option add *zoomOutline.xor yes
+
+proc blt::MarchingAnts { graph offset } {
+ global zoomInfo
+
+ incr offset
+ if { [$graph marker exists zoomOutline] } {
+ $graph marker configure zoomOutline -dashoffset $offset
+ set interval $zoomInfo($graph,interval)
+ set id [after $interval [list blt::MarchingAnts $graph $offset]]
+ set zoomInfo($graph,afterId) $id
+ }
+}
+
+proc blt::Box { graph } {
+ global zoomInfo
+
+ if { $zoomInfo($graph,A,x) > $zoomInfo($graph,B,x) } {
+ set x1 [$graph xaxis invtransform $zoomInfo($graph,B,x)]
+ set y1 [$graph yaxis invtransform $zoomInfo($graph,B,y)]
+ set x2 [$graph xaxis invtransform $zoomInfo($graph,A,x)]
+ set y2 [$graph yaxis invtransform $zoomInfo($graph,A,y)]
+ } else {
+ set x1 [$graph xaxis invtransform $zoomInfo($graph,A,x)]
+ set y1 [$graph yaxis invtransform $zoomInfo($graph,A,y)]
+ set x2 [$graph xaxis invtransform $zoomInfo($graph,B,x)]
+ set y2 [$graph yaxis invtransform $zoomInfo($graph,B,y)]
+ }
+ set coords { $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1 }
+ if { [$graph marker exists "zoomOutline"] } {
+ $graph marker configure "zoomOutline" -coords $coords
+ } else {
+ set X [lindex [$graph xaxis use] 0]
+ set Y [lindex [$graph yaxis use] 0]
+ $graph marker create line -coords $coords -name "zoomOutline" \
+ -mapx $X -mapy $Y
+ set interval $zoomInfo($graph,interval)
+ set id [after $interval [list blt::MarchingAnts $graph 0]]
+ set zoomInfo($graph,afterId) $id
+ }
+}
+
+
+proc Blt_PostScriptDialog { graph } {
+ set top $graph.top
+ toplevel $top
+
+ foreach var { center landscape maxpect preview decorations padx
+ pady paperwidth paperheight width height colormode } {
+ global $graph.$var
+ set $graph.$var [$graph postscript cget -$var]
+ }
+ set row 1
+ set col 0
+ label $top.title -text "PostScript Options"
+ table $top $top.title -cspan 7
+ foreach bool { center landscape maxpect preview decorations } {
+ set w $top.$bool-label
+ label $w -text "-$bool" -font *courier*-r-*12*
+ table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 }
+ set w $top.$bool-yes
+ global $graph.$bool
+ radiobutton $w -text "yes" -variable $graph.$bool -value 1
+ table $top $row,$col+1 $w -anchor w
+ set w $top.$bool-no
+ radiobutton $w -text "no" -variable $graph.$bool -value 0
+ table $top $row,$col+2 $w -anchor w
+ incr row
+ }
+ label $top.modes -text "-colormode" -font *courier*-r-*12*
+ table $top $row,0 $top.modes -anchor e -pady { 2 0 } -padx { 0 4 }
+ set col 1
+ foreach m { color greyscale } {
+ set w $top.$m
+ radiobutton $w -text $m -variable $graph.colormode -value $m
+ table $top $row,$col $w -anchor w
+ incr col
+ }
+ set row 1
+ frame $top.sep -width 2 -bd 1 -relief sunken
+ table $top $row,3 $top.sep -fill y -rspan 6
+ set col 4
+ foreach value { padx pady paperwidth paperheight width height } {
+ set w $top.$value-label
+ label $w -text "-$value" -font *courier*-r-*12*
+ table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 }
+ set w $top.$value-entry
+ global $graph.$value
+ entry $w -textvariable $graph.$value -width 8
+ table $top $row,$col+1 $w -cspan 2 -anchor w -padx 8
+ incr row
+ }
+ table configure $top c3 -width .125i
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ table $top $row,0 $top.cancel -width 1i -pady 2 -cspan 3
+ button $top.reset -text "Reset" -command "destroy $top"
+ #table $top $row,1 $top.reset -width 1i
+ button $top.print -text "Print" -command "blt::ResetPostScript $graph"
+ table $top $row,4 $top.print -width 1i -pady 2 -cspan 2
+}
+
+proc blt::ResetPostScript { graph } {
+ foreach var { center landscape maxpect preview decorations padx
+ pady paperwidth paperheight width height colormode } {
+ global $graph.$var
+ set old [$graph postscript cget -$var]
+ if { [catch {$graph postscript configure -$var [set $graph.$var]}] != 0 } {
+ $graph postscript configure -$var $old
+ set $graph.$var $old
+ }
+ }
+ $graph postscript output "out.ps"
+ puts stdout "wrote file \"out.ps\"."
+ flush stdout
+}
diff --git a/blt/library/hierbox.tcl b/blt/library/hierbox.tcl
new file mode 100644
index 00000000000..53eaa91e120
--- /dev/null
+++ b/blt/library/hierbox.tcl
@@ -0,0 +1,522 @@
+#
+# hierbox.tcl
+# ----------------------------------------------------------------------
+# Bindings for the BLT hierbox widget
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@lucent.com
+# http://www.tcltk.com/blt
+#
+# RCS: $Id$
+#
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+array set bltHierbox {
+ afterId ""
+ scroll 0
+ space off
+ x 0
+ y 0
+}
+
+catch {
+ namespace eval blt::Hierbox {}
+}
+
+#
+# ButtonPress assignments
+#
+# B1-Enter start auto-scrolling
+# B1-Leave stop auto-scrolling
+# ButtonPress-2 start scan
+# B2-Motion adjust scan
+# ButtonRelease-2 stop scan
+#
+
+bind Hierbox <ButtonPress-2> {
+ set bltHierbox(cursor) [%W cget -cursor]
+ %W configure -cursor hand1
+ %W scan mark %x %y
+}
+
+bind Hierbox <B2-Motion> {
+ %W scan dragto %x %y
+}
+
+bind Hierbox <ButtonRelease-2> {
+ %W configure -cursor $bltHierbox(cursor)
+}
+
+bind Hierbox <B1-Leave> {
+ if { $bltHierbox(scroll) } {
+ blt::Hierbox::AutoScroll %W
+ }
+}
+
+bind Hierbox <B1-Enter> {
+ after cancel $bltHierbox(afterId)
+}
+
+
+#
+# KeyPress assignments
+#
+# Up
+# Down
+# Shift-Up
+# Shift-Down
+# Prior (PageUp)
+# Next (PageDn)
+# Left
+# Right
+# space Start selection toggle of entry currently with focus.
+# Return Start selection toggle of entry currently with focus.
+# Home
+# End
+# F1
+# F2
+# ASCII char Go to next open entry starting with character.
+#
+# KeyRelease
+#
+# space Stop selection toggle of entry currently with focus.
+# Return Stop selection toggle of entry currently with focus.
+
+
+bind Hierbox <KeyPress-Up> {
+ blt::Hierbox::MoveFocus %W up
+ if { $bltHierbox(space) } {
+ %W selection toggle focus
+ }
+}
+
+bind Hierbox <KeyPress-Down> {
+ blt::Hierbox::MoveFocus %W down
+ if { $bltHierbox(space) } {
+ %W selection toggle focus
+ }
+}
+
+bind Hierbox <Shift-KeyPress-Up> {
+ blt::Hierbox::MoveFocus %W prevsibling
+}
+
+bind Hierbox <Shift-KeyPress-Down> {
+ blt::Hierbox::MoveFocus %W nextsibling
+}
+
+bind Hierbox <KeyPress-Prior> {
+ blt::Hierbox::MovePage %W top
+}
+
+bind Hierbox <KeyPress-Next> {
+ blt::Hierbox::MovePage %W bottom
+}
+
+bind Hierbox <KeyPress-Left> {
+ %W close focus
+}
+bind Hierbox <KeyPress-Right> {
+ %W open focus
+ %W see focus -anchor w
+}
+
+bind Hierbox <KeyPress-space> {
+ blt::HierboxToggle %W focus
+ set bltHierbox(space) on
+}
+
+bind Hierbox <KeyRelease-space> {
+ set bltHierbox(space) off
+}
+
+bind Hierbox <KeyPress-Return> {
+ blt::HierboxToggle %W focus
+ set bltHierbox(space) on
+}
+
+bind Hierbox <KeyRelease-Return> {
+ set bltHierbox(space) off
+}
+
+bind Hierbox <KeyPress> {
+ blt::Hierbox::NextMatchingEntry %W %A
+}
+
+bind Hierbox <KeyPress-Home> {
+ blt::Hierbox::MoveFocus %W root
+}
+
+bind Hierbox <KeyPress-End> {
+ blt::Hierbox::MoveFocus %W end
+}
+
+bind Hierbox <KeyPress-F1> {
+ %W open -r root
+}
+
+bind Hierbox <KeyPress-F2> {
+ eval %W close -r [%W entry children root 0 end]
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::HierboxToggle <hierbox> <index>
+# Arguments: hierbox hierarchy widget
+#
+# Invoked when the user presses the space bar. Toggles the selection
+# for the entry at <index>.
+# ----------------------------------------------------------------------
+proc blt::HierboxToggle { widget index } {
+ switch -- [$widget cget -selectmode] {
+ single {
+ if { [$widget selection includes $index] } {
+ $widget selection clearall
+ } else {
+ $widget selection set $index
+ }
+ }
+ multiple {
+ $widget selection toggle $index
+ }
+ }
+}
+
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hierbox::MovePage <hierbox> <where>
+# Arguments: hierbox hierarchy widget
+#
+# Invoked by KeyPress bindings. Pages the current view up or down.
+# The <where> argument should be either "top" or "bottom".
+# ----------------------------------------------------------------------
+
+proc blt::Hierbox::MovePage { widget where } {
+ # If the focus is already at the top/bottom of the window, we want
+ # to scroll a page. It's really one page minus an entry because we
+ # want to see the last entry on the next/last page.
+ if { [$widget index focus] == [$widget index view.$where] } {
+ if {$where == "top"} {
+ $widget yview scroll -1 pages
+ $widget yview scroll 1 units
+ } else {
+ $widget yview scroll 1 pages
+ $widget yview scroll -1 units
+ }
+ }
+ update
+
+ # Adjust the entry focus and the view. Also activate the entry.
+ # just in case the mouse point is not in the widget.
+ $widget entry highlight view.$where
+ $widget focus view.$where
+ $widget see view.$where
+ if { [$widget cget -selectmode] == "single" } {
+ $widget selection clearall
+ $widget selection set focus
+ }
+}
+
+#
+# Edit mode assignments
+#
+# ButtonPress-3 Enables/disables edit mode on entry. Sets focus to
+# entry.
+#
+# KeyPress
+#
+# Left Move insertion position to previous.
+# Right Move insertion position to next.
+# Up Move insertion position up one line.
+# Down Move insertion position down one line.
+# Return End edit mode.
+# Shift-Return Line feed.
+# Home Move to first position.
+# End Move to last position.
+# ASCII char Insert character left of insertion point.
+# Del Delete character right of insertion point.
+# Delete Delete character left of insertion point.
+# Ctrl-X Cut
+# Ctrl-V Copy
+# Ctrl-P Paste
+#
+# KeyRelease
+#
+# ButtonPress-1 Start selection if in entry, otherwise clear selection.
+# B1-Motion Extend/reduce selection.
+# ButtonRelease-1 End selection if in entry, otherwise use last selection.
+# B1-Enter Disabled.
+# B1-Leave Disabled.
+# ButtonPress-2 Same as above.
+# B2-Motion Same as above.
+# ButtonRelease-2 Same as above.
+#
+# All bindings in editting mode will "break" to override other bindings.
+#
+#
+
+bind Hierbox <ButtonPress-3> {
+ set node [%W nearest %x %y]
+ %W entry insert $node @%x,%y ""
+# %W entry insert $node 2 ""
+}
+
+
+proc blt::Hierbox::Init { widget } {
+ #
+ # Active entry bindings
+ #
+ $widget bind Entry <Enter> {
+ %W entry highlight current
+ }
+ $widget bind Entry <Leave> {
+ %W entry highlight ""
+ }
+
+ #
+ # Button bindings
+ #
+ $widget button bind all <ButtonRelease-1> {
+ %W see -anchor nw current
+ %W toggle current
+ }
+ $widget button bind all <Enter> {
+ %W button highlight current
+ }
+ $widget button bind all <Leave> {
+ %W button highlight ""
+ }
+
+ #
+ # ButtonPress-1
+ #
+ # Performs the following operations:
+ #
+ # 1. Clears the previous selection.
+ # 2. Selects the current entry.
+ # 3. Sets the focus to this entry.
+ # 4. Scrolls the entry into view.
+ # 5. Sets the selection anchor to this entry, just in case
+ # this is "multiple" mode.
+ #
+
+ $widget bind Entry <ButtonPress-1> {
+ blt::Hierbox::SetSelectionAnchor %W current
+ set bltHierbox(scroll) 1
+ }
+
+ $widget bin Entry <Double-ButtonPress-1> {
+ %W toggle current
+ }
+
+ #
+ # B1-Motion
+ #
+ # For "multiple" mode only. Saves the current location of the
+ # pointer for auto-scrolling.
+ #
+ $widget bind Entry <B1-Motion> {
+ set bltHierbox(x) %x
+ set bltHierbox(y) %y
+ set index [%W nearest %x %y]
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection mark $index
+ } else {
+ blt::Hierbox::SetSelectionAnchor %W $index
+ }
+ }
+
+ #
+ # ButtonRelease-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <ButtonRelease-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection anchor current
+ }
+ after cancel $bltHierbox(afterId)
+ set bltHierbox(scroll) 0
+ }
+
+ #
+ # Shift-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ set index [%W index anchor]
+ %W selection clearall
+ %W selection set $index current
+ } else {
+ blt::Hierbox::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Shift-B1-Motion> {
+ # do nothing
+ }
+ $widget bind Entry <Shift-ButtonRelease-1> {
+ after cancel $bltHierbox(afterId)
+ set bltHierbox(scroll) 0
+ }
+
+ #
+ # Control-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <Control-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ set index [%W index current]
+ %W selection toggle $index
+ %W selection anchor $index
+ } else {
+ blt::Hierbox::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Control-B1-Motion> {
+ # do nothing
+ }
+ $widget bind Entry <Control-ButtonRelease-1> {
+ after cancel $bltHierbox(afterId)
+ set bltHierbox(scroll) 0
+ }
+ #
+ # Control-Shift-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <Control-Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ if { [%W selection includes anchor] } {
+ %W selection set anchor current
+ } else {
+ %W selection clear anchor current
+ %W selection set current
+ }
+ } else {
+ blt::Hierbox::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Control-Shift-B1-Motion> {
+ # do nothing
+ }
+}
+
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hierbox::AutoScroll <hierbox>
+#
+# Invoked when the user is selecting elements in a hierbox widget
+# and drags the mouse pointer outside of the widget. Scrolls the
+# view in the direction of the pointer.
+#
+# Arguments: hierbox hierarchy widget
+#
+# ----------------------------------------------------------------------
+proc blt::Hierbox::AutoScroll { widget } {
+ global bltHierbox
+ if { ![winfo exists $widget] } {
+ return
+ }
+ set x $bltHierbox(x)
+ set y $bltHierbox(y)
+ set index [$widget nearest $x $y]
+ if { $y >= [winfo height $widget] } {
+ $widget yview scroll 1 units
+ set neighbor down
+ } elseif { $y < 0 } {
+ $widget yview scroll -1 units
+ set neighbor up
+ } else {
+ set neighbor $index
+ }
+ if { [$widget cget -selectmode] == "single" } {
+ blt::Hierbox::SetSelectionAnchor $widget $neighbor
+ } else {
+ $widget selection mark $index
+ }
+ set bltHierbox(afterId) [after 10 blt::Hierbox::AutoScroll $widget]
+}
+
+proc blt::Hierbox::SetSelectionAnchor { widget index } {
+ set index [$widget index $index]
+ $widget selection clearall
+ $widget see $index
+ $widget focus $index
+ $widget selection set $index
+ $widget selection anchor $index
+}
+
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hierbox::NextMatchingEntry <hierbox> <char>
+# Arguments: hierbox hierarchy widget
+#
+# Invoked by KeyPress bindings. Searches for an entry that starts
+# with the letter <char> and makes that entry active.
+# ----------------------------------------------------------------------
+
+proc blt::Hierbox::NextMatchingEntry { widget key } {
+ if {[string match {[ -~]} $key]} {
+ set last [$widget index focus]
+ set next [$widget index next]
+ while { $next != $last } {
+ set label [$widget entry cget $next -label]
+ if { [string index $label 0] == $key } {
+ break
+ }
+ set next [$widget index -at $next next]
+ }
+ $widget focus $next
+ if {[$widget cget -selectmode] == "single"} {
+ $widget selection clearall
+ $widget selection set focus
+ }
+ $widget see focus
+ }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hierbox::MoveFocus <hierbox> <where>
+#
+# Invoked by KeyPress bindings. Moves the active selection to the
+# entry <where>, which is an index such as "up", "down", "prevsibling",
+# "nextsibling", etc.
+# ----------------------------------------------------------------------
+proc blt::Hierbox::MoveFocus { widget where } {
+ catch {$widget focus $where}
+ if { [$widget cget -selectmode] == "single" } {
+ $widget selection clearall
+ $widget selection set focus
+ }
+ $widget see focus
+}
diff --git a/blt/library/hiertable.tcl b/blt/library/hiertable.tcl
new file mode 100644
index 00000000000..241c9044699
--- /dev/null
+++ b/blt/library/hiertable.tcl
@@ -0,0 +1,943 @@
+# hiertable.tcl
+# ----------------------------------------------------------------------
+# Bindings for the BLT hiertable widget
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@lucent.com
+# http://www.tcltk.com/blt
+#
+# RCS: $Id$
+#
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+namespace eval blt::Hiertable {
+ set afterId ""
+ set scroll 0
+ set column ""
+ set space off
+ set x 0
+ set y 0
+}
+
+#
+# ButtonPress assignments
+#
+# B1-Enter start auto-scrolling
+# B1-Leave stop auto-scrolling
+# ButtonPress-2 start scan
+# B2-Motion adjust scan
+# ButtonRelease-2 stop scan
+#
+
+bind Hiertable <ButtonPress-2> {
+ set blt::Hiertable::cursor [%W cget -cursor]
+ %W configure -cursor hand1
+ %W scan mark %x %y
+}
+
+bind Hiertable <B2-Motion> {
+ %W scan dragto %x %y
+}
+
+bind Hiertable <ButtonRelease-2> {
+ %W configure -cursor $blt::Hiertable::cursor
+}
+
+bind Hiertable <B1-Leave> {
+ if { $blt::Hiertable::scroll } {
+ blt::Hiertable::AutoScroll %W
+ }
+}
+
+bind Hiertable <B1-Enter> {
+ after cancel $blt::Hiertable::afterId
+}
+
+#
+# KeyPress assignments
+#
+# Up
+# Down
+# Shift-Up
+# Shift-Down
+# Prior (PageUp)
+# Next (PageDn)
+# Left
+# Right
+# space Start selection toggle of entry currently with focus.
+# Return Start selection toggle of entry currently with focus.
+# Home
+# End
+# F1
+# F2
+# ASCII char Go to next open entry starting with character.
+#
+# KeyRelease
+#
+# space Stop selection toggle of entry currently with focus.
+# Return Stop selection toggle of entry currently with focus.
+
+
+bind Hiertable <KeyPress-Up> {
+ blt::Hiertable::MoveFocus %W up
+ if { $blt::Hiertable::space } {
+ %W selection toggle focus
+ }
+}
+
+bind Hiertable <KeyPress-Down> {
+ blt::Hiertable::MoveFocus %W down
+ if { $blt::Hiertable::space } {
+ %W selection toggle focus
+ }
+}
+
+bind Hiertable <Shift-KeyPress-Up> {
+ blt::Hiertable::MoveFocus %W prevsibling
+}
+
+bind Hiertable <Shift-KeyPress-Down> {
+ blt::Hiertable::MoveFocus %W nextsibling
+}
+
+bind Hiertable <KeyPress-Prior> {
+ blt::Hiertable::MovePage %W top
+}
+
+bind Hiertable <KeyPress-Next> {
+ blt::Hiertable::MovePage %W bottom
+}
+
+bind Hiertable <KeyPress-Left> {
+ %W close focus
+}
+bind Hiertable <KeyPress-Right> {
+ %W open focus
+ %W see focus -anchor w
+}
+
+bind Hiertable <KeyPress-space> {
+ if { [%W cget -selectmode] == "single" } {
+ if { [%W selection includes focus] } {
+ %W selection clearall
+ } else {
+ %W selection clearall
+ %W selection set focus
+ }
+ } else {
+ %W selection toggle focus
+ }
+ set blt::Hiertable::space on
+}
+
+bind Hiertable <KeyRelease-space> {
+ set blt::Hiertable::space off
+}
+
+bind Hiertable <KeyPress-Return> {
+ blt::Hiertable::MoveFocus %W focus
+ set blt::Hiertable::space on
+}
+
+bind Hiertable <KeyRelease-Return> {
+ set blt::Hiertable::space off
+}
+
+bind Hiertable <KeyPress> {
+ blt::Hiertable::NextMatchingEntry %W %A
+}
+
+bind Hiertable <KeyPress-Home> {
+ blt::Hiertable::MoveFocus %W top
+}
+
+bind Hiertable <KeyPress-End> {
+ blt::Hiertable::MoveFocus %W bottom
+}
+
+bind Hiertable <KeyPress-F1> {
+ %W open -r root
+}
+
+bind Hiertable <KeyPress-F2> {
+ eval %W close -r [%W entry children root]
+}
+
+#
+# Differences between "current" and nearest.
+#
+# set index [$widget index current]
+# set index [$widget nearest $x $y]
+#
+# o Nearest gives you the closest entry.
+# o current is "" if
+# 1) the pointer isn't over an entry.
+# 2) the pointer is over a open/close button.
+# 3)
+#
+#
+# ----------------------------------------------------------------------
+#
+# USAGE: blt::Hiertable::Init <hiertable>
+#
+# Invoked by internally by Hiertable_Init routine. Initializes the
+# default bindings for the hiertable widget entries. These are local
+# to the widget, so they can't be set through the widget's class
+# bind tags.
+#
+# Arguments: hiertable hierarchy widget
+#
+# ----------------------------------------------------------------------
+proc blt::Hiertable::Init { widget } {
+ #
+ # Active entry bindings
+ #
+ $widget bind Entry <Enter> {
+ %W entry highlight current
+ }
+ $widget bind Entry <Leave> {
+ %W entry highlight ""
+ }
+
+ #
+ # Button bindings
+ #
+ $widget button bind all <ButtonRelease-1> {
+ %W see -anchor nw current
+ %W toggle current
+ }
+ $widget button bind all <Enter> {
+ %W button highlight current
+ }
+ $widget button bind all <Leave> {
+ %W button highlight ""
+ }
+
+ #
+ # ButtonPress-1
+ #
+ # Performs the following operations:
+ #
+ # 1. Clears the previous selection.
+ # 2. Selects the current entry.
+ # 3. Sets the focus to this entry.
+ # 4. Scrolls the entry into view.
+ # 5. Sets the selection anchor to this entry, just in case
+ # this is "multiple" mode.
+ #
+
+ $widget bind Entry <ButtonPress-1> {
+ blt::Hiertable::SetSelectionAnchor %W current
+ set blt::Hiertable::scroll 1
+ }
+
+ $widget bind Entry <Double-ButtonPress-1> {
+ %W toggle current
+ }
+
+ #
+ # B1-Motion
+ #
+ # For "multiple" mode only. Saves the current location of the
+ # pointer for auto-scrolling. Resets the selection mark.
+ #
+ $widget bind Entry <B1-Motion> {
+ set blt::Hiertable::x %x
+ set blt::Hiertable::y %y
+ set index [%W nearest %x %y]
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection mark $index
+ } else {
+ blt::Hiertable::SetSelectionAnchor %W $index
+ }
+ }
+
+ #
+ # ButtonRelease-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <ButtonRelease-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection anchor current
+ }
+ after cancel $blt::Hiertable::afterId
+ set blt::Hiertable::scroll 0
+ }
+
+ #
+ # Shift-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+
+ $widget bind Entry <Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ set index [%W index anchor]
+ %W selection clearall
+ %W selection set $index current
+ } else {
+ blt::Hiertable::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Shift-Double-ButtonPress-1> {
+ puts <Shift-Double-ButtonPress-1>
+ # do nothing
+ }
+ $widget bind Entry <Shift-B1-Motion> {
+ # do nothing
+ }
+ $widget bind Entry <Shift-ButtonRelease-1> {
+ after cancel $blt::Hiertable::afterId
+ set blt::Hiertable::scroll 0
+ }
+
+ #
+ # Control-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $widget bind Entry <Control-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ set index [%W index current]
+ %W selection toggle $index
+ %W selection anchor $index
+ } else {
+ blt::Hiertable::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Control-Double-ButtonPress-1> {
+ puts <Control-Double-ButtonPress-1>
+ # do nothing
+ }
+ $widget bind Entry <Control-B1-Motion> {
+ # do nothing
+ }
+ $widget bind Entry <Control-ButtonRelease-1> {
+ after cancel $blt::Hiertable::afterId
+ set blt::Hiertable::scroll 0
+ }
+
+ $widget bind Entry <Control-Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ if { [%W selection includes anchor] } {
+ %W selection set anchor current
+ } else {
+ %W selection clear anchor current
+ %W selection set current
+ }
+ } else {
+ blt::Hiertable::SetSelectionAnchor %W current
+ }
+ }
+ $widget bind Entry <Control-Shift-Double-ButtonPress-1> {
+ puts <Control-Shift-Double-ButtonPress-1>
+ # do nothing
+ }
+ $widget bind Entry <Control-Shift-B1-Motion> {
+ # do nothing
+ }
+ $widget column bind all <Enter> {
+ %W column highlight [%W column current]
+ }
+ $widget column bind all <Leave> {
+ %W column highlight ""
+ }
+ $widget column bind Rule <Enter> {
+ %W column highlight [%W column current]
+ %W column resize activate [%W column current]
+ }
+ $widget column bind Rule <Leave> {
+ %W column highlight ""
+ %W column resize activate ""
+ }
+ $widget column bind Rule <ButtonPress-1> {
+ %W column resize anchor %x
+ }
+ $widget column bind Rule <B1-Motion> {
+ %W column resize mark %x
+ }
+ $widget column bind Rule <ButtonRelease-1> {
+ %W column configure [%W column current] -width [%W column resize set]
+ }
+ $widget column bind all <ButtonRelease-1> {
+ set column [%W column nearest %x %y]
+ if { $column != "" } {
+ %W column invoke $column
+ }
+ }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hiertable::AutoScroll <hiertable>
+#
+# Invoked when the user is selecting elements in a hiertable widget
+# and drags the mouse pointer outside of the widget. Scrolls the
+# view in the direction of the pointer.
+#
+# Arguments: hiertable hierarchy widget
+#
+# ----------------------------------------------------------------------
+proc blt::Hiertable::AutoScroll { widget } {
+ if { ![winfo exists $widget] } {
+ return
+ }
+ set x $blt::Hiertable::x
+ set y $blt::Hiertable::y
+
+ set index [$widget nearest $x $y]
+
+ if {$y >= [winfo height $widget]} {
+ $widget yview scroll 1 units
+ set neighbor down
+ } elseif {$y < 0} {
+ $widget yview scroll -1 units
+ set neighbor up
+ } else {
+ set neighbor $index
+ }
+ if { [$widget cget -selectmode] == "single" } {
+ blt::Hiertable::SetSelectionAnchor $widget $neighbor
+ } else {
+ $widget selection mark $index
+ }
+ set ::blt::Hiertable::afterId [after 10 blt::Hiertable::AutoScroll $widget]
+}
+
+proc blt::Hiertable::SetSelectionAnchor { widget index } {
+ set index [$widget index $index]
+ # If the anchor hasn't changed, don't do anything
+ if { $index != [$widget index anchor] } {
+ $widget selection clearall
+ $widget see $index
+ $widget focus $index
+ $widget selection set $index
+ $widget selection anchor $index
+ }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hiertable::MoveFocus <hiertable> <where>
+#
+# Invoked by KeyPress bindings. Moves the active selection to the
+# entry <where>, which is an index such as "up", "down", "prevsibling",
+# "nextsibling", etc.
+# ----------------------------------------------------------------------
+proc blt::Hiertable::MoveFocus { widget where } {
+ catch {$widget focus $where}
+ if { [$widget cget -selectmode] == "single" } {
+ $widget selection clearall
+ $widget selection set focus
+ }
+ $widget see focus
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hiertable::MovePage <hiertable> <where>
+# Arguments: hiertable hierarchy widget
+#
+# Invoked by KeyPress bindings. Pages the current view up or down.
+# The <where> argument should be either "top" or "bottom".
+# ----------------------------------------------------------------------
+
+proc blt::Hiertable::MovePage { widget where } {
+
+ # If the focus is already at the top/bottom of the window, we want
+ # to scroll a page. It's really one page minus an entry because we
+ # want to see the last entry on the next/last page.
+ if { [$widget index focus] == [$widget index view.$where] } {
+ if {$where == "top"} {
+ $widget yview scroll -1 pages
+ $widget yview scroll 1 units
+ } else {
+ $widget yview scroll 1 pages
+ $widget yview scroll -1 units
+ }
+ }
+ update
+
+ # Adjust the entry focus and the view. Also activate the entry.
+ # just in case the mouse point is not in the widget.
+ $widget entry highlight view.$where
+ $widget focus view.$where
+ $widget see view.$where
+ if { [$widget cget -selectmode] == "single" } {
+ $widget selection clearall
+ $widget selection set focus
+ }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: blt::Hiertable::NextMatchingEntry <hiertable> <char>
+# Arguments: hiertable hierarchy widget
+#
+# Invoked by KeyPress bindings. Searches for an entry that starts
+# with the letter <char> and makes that entry active.
+# ----------------------------------------------------------------------
+
+proc blt::Hiertable::NextMatchingEntry { widget key } {
+ if {[string match {[ -~]} $key]} {
+ set last [$widget index focus]
+ set next [$widget index next]
+ while { $next != $last } {
+ set label [$widget entry cget $next -label]
+ if { [string index $label 0] == $key } {
+ break
+ }
+ set next [$widget index -at $next next]
+ }
+ $widget focus $next
+ if {[$widget cget -selectmode] == "single"} {
+ $widget selection clearall
+ $widget selection set focus
+ }
+ $widget see focus
+ }
+}
+
+#
+# Edit mode assignments
+#
+# ButtonPress-3 Enables/disables edit mode on entry. Sets focus to
+# entry.
+#
+# KeyPress
+#
+# Left Move insertion position to previous.
+# Right Move insertion position to next.
+# Up Move insertion position up one line.
+# Down Move insertion position down one line.
+# Return End edit mode.
+# Shift-Return Line feed.
+# Home Move to first position.
+# End Move to last position.
+# ASCII char Insert character left of insertion point.
+# Del Delete character right of insertion point.
+# Delete Delete character left of insertion point.
+# Ctrl-X Cut
+# Ctrl-V Copy
+# Ctrl-P Paste
+#
+# KeyRelease
+#
+# ButtonPress-1 Start selection if in entry, otherwise clear selection.
+# B1-Motion Extend/reduce selection.
+# ButtonRelease-1 End selection if in entry, otherwise use last selection.
+# B1-Enter Disabled.
+# B1-Leave Disabled.
+# ButtonPress-2 Same as above.
+# B2-Motion Same as above.
+# ButtonRelease-2 Same as above.
+#
+# All bindings in editting mode will "break" to override other bindings.
+#
+#
+
+bind xEditor <ButtonPress-3> {
+ set node [%W nearest %x %y]
+ %W entry insert $node @%x,%y ""
+# %W entry insert $node 2 ""
+}
+
+image create photo blt::Hiertable::CloseNormalFolder -format gif -data {
+R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+ZTKAsiCtWq0JADs=
+}
+image create photo blt::Hiertable::OpenNormalFolder -format gif -data {
+R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+nQkAOw==
+}
+image create photo blt::Hiertable::CloseActiveFolder -format gif -data {
+R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+ZTKAsiCtWq0JADs=
+}
+image create photo blt::Hiertable::OpenActiveFolder -format gif -data {
+R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+nQkAOw==
+}
+
+
+if { $tcl_platform(platform) == "windows" } {
+ if { $tk_version >= 8.3 } {
+ set cursor "@[file join $blt_library treeview.cur]"
+ } else {
+ set cursor "size_we"
+ }
+ option add *Hiertable.ResizeCursor [list $cursor]
+} else {
+ option add *Hiertable.ResizeCursor \
+ "@$blt_library/treeview.xbm $blt_library/treeview_m.xbm black white"
+}
+
+# Standard Motif bindings:
+
+bind HiertableEditor <ButtonPress-1> {
+ %W text icursor @%x,%y
+}
+
+bind HiertableEditor <Left> {
+ %W text icursor last
+}
+bind HiertableEditor <Right> {
+ %W text icursor next
+}
+bind HiertableEditor <Shift-Left> {
+ set new [expr {[%W text index insert] - 1}]
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+bind HiertableEditor <Shift-Right> {
+ set new [expr {[%W text index insert] + 1}]
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+
+bind HiertableEditor <Home> {
+ %W text icursor 0
+}
+bind HiertableEditor <Shift-Home> {
+ set new 0
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+bind HiertableEditor <End> {
+ %W text icursor end
+}
+bind HiertableEditor <Shift-End> {
+ set new end
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+
+bind HiertableEditor <Delete> {
+ if { [%W text selection present]} {
+ %W text delete sel.first sel.last
+ } else {
+ %W text delete insert
+ }
+}
+
+bind HiertableEditor <BackSpace> {
+ if { [%W text selection present] } {
+ %W text delete sel.first sel.last
+ } else {
+ set index [expr [%W text index insert] - 1]
+ if { $index >= 0 } {
+ %W text delete $index $index
+ }
+ }
+}
+
+bind HiertableEditor <Control-space> {
+ %W text selection from insert
+}
+
+bind HiertableEditor <Select> {
+ %W text selection from insert
+}
+
+bind HiertableEditor <Control-Shift-space> {
+ %W text selection adjust insert
+}
+
+bind HiertableEditor <Shift-Select> {
+ %W text selection adjust insert
+}
+
+bind HiertableEditor <Control-slash> {
+ %W text selection range 0 end
+}
+
+bind HiertableEditor <Control-backslash> {
+ %W text selection clear
+}
+
+bind HiertableEditor <KeyPress> {
+ blt::Hiertable::Insert %W %A
+}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong. Ditto for Escape, Return, and Tab.
+
+bind HiertableEditor <Alt-KeyPress> {
+ # nothing
+}
+
+bind HiertableEditor <Meta-KeyPress> {
+ # nothing
+}
+
+bind HiertableEditor <Control-KeyPress> {
+ # nothing
+}
+
+bind HiertableEditor <Escape> {
+ %W text cancel
+ grab release %W
+}
+
+bind HiertableEditor <Return> {
+ %W text apply
+ grab release %W
+}
+
+bind HiertableEditor <Shift-Return> {
+ blt::Hiertable::Insert %W "\n"
+}
+
+bind HiertableEditor <KP_Enter> {
+ # nothing
+}
+
+bind HiertableEditor <Tab> {
+ # nothing
+}
+
+if {![string compare $tcl_platform(platform) "macintosh"]} {
+ bind HiertableEditor <Command-KeyPress> {
+ # nothing
+ }
+}
+
+# On Windows, paste is done using Shift-Insert. Shift-Insert already
+# generates the <<Paste>> event, so we don't need to do anything here.
+if { [string compare $tcl_platform(platform) "windows"] != 0 } {
+ bind HiertableEditor <Insert> {
+ catch {blt::Hiertable::Insert %W [selection get -displayof %W]}
+ }
+}
+
+# Additional emacs-like bindings:
+bind HiertableEditor <ButtonPress-3> {
+ if { [winfo viewable %W] } {
+ grab release %W
+ %W text cancel
+ }
+}
+
+bind HiertableEditor <Control-a> {
+ %W text icursor 0
+ %W text selection clear
+}
+
+bind HiertableEditor <Control-b> {
+ %W text icursor [expr {[%W index insert] - 1}]
+ %W text selection clear
+}
+
+bind HiertableEditor <Control-d> {
+ %W text delete insert
+}
+
+bind HiertableEditor <Control-e> {
+ %W text icursor end
+ %W text selection clear
+}
+
+bind HiertableEditor <Control-f> {
+ %W text icursor [expr {[%W index insert] + 1}]
+ %W text selection clear
+}
+
+bind HiertableEditor <Control-h> {
+ if {[%W text selection present]} {
+ %W text delete sel.first sel.last
+ } else {
+ set index [expr [%W text index insert] - 1]
+ if { $index >= 0 } {
+ %W text delete $index $index
+ }
+ }
+}
+
+bind HiertableEditor <Control-k> {
+ %W text delete insert end
+}
+
+
+
+# blt::Hiertable::Insert --
+# Insert a string into an entry at the point of the insertion cursor.
+# If there is a selection in the entry, and it covers the point of the
+# insertion cursor, then delete the selection before inserting.
+#
+# Arguments:
+# w - The entry window in which to insert the string
+# s - The string to insert (usually just a single character)
+
+proc blt::Hiertable::Insert {w s} {
+ if {![string compare $s ""]} {
+ return
+ }
+ catch {
+ set insert [$w text index insert]
+ if {([$w text index sel.first] <= $insert)
+ && ([$w text index sel.last] >= $insert)} {
+ $w delete sel.first sel.last
+ }
+ }
+ $w text insert insert $s
+}
+
+# tkEntryTranspose -
+# This procedure implements the "transpose" function for entry widgets.
+# It tranposes the characters on either side of the insertion cursor,
+# unless the cursor is at the end of the line. In this case it
+# transposes the two characters to the left of the cursor. In either
+# case, the cursor ends up to the right of the transposed characters.
+#
+# Arguments:
+# w - The entry window.
+
+proc HiertableTranspose w {
+ set i [$w text index insert]
+ if {$i < [$w text index end]} {
+ incr i
+ }
+ set first [expr {$i-2}]
+ if {$first < 0} {
+ return
+ }
+ set new [string index [$w get] [expr {$i-1}]][string index [$w get] $first]
+ $w delete $first $i
+ $w insert insert $new
+}
+
+# Hiertable::GetSelection --
+#
+# Returns the selected text of the entry with respect to the -show option.
+#
+# Arguments:
+# w - The entry window from which the text to get
+
+proc blt::Hiertable::GetSelection {w} {
+ set entryString [string range [$w get] [$w index sel.first] \
+ [expr [$w index sel.last] - 1]]
+ if {[$w cget -show] != ""} {
+ regsub -all . $entryString [string index [$w cget -show] 0] entryString
+ }
+ return $entryString
+}
+
+if 0 {
+ bind HiertableEditor <Control-t> {
+ tkEntryTranspose %W
+ }
+ bind HiertableEditor <Meta-b> {
+ %W text icursor [blt::Hiertable::PreviousWord %W insert]
+ %W text selection clear
+ }
+ bind HiertableEditor <Meta-d> {
+ %W delete insert [blt::Hiertable::NextWord %W insert]
+ }
+ bind HiertableEditor <Meta-f> {
+ %W text icursor [blt::Hiertable::NextWord %W insert]
+ %W text selection clear
+ }
+ bind HiertableEditor <Meta-BackSpace> {
+ %W delete [blt::Hiertable::PreviousWord %W insert] insert
+ }
+ bind HiertableEditor <Meta-Delete> {
+ %W delete [blt::Hiertable::PreviousWord %W insert] insert
+ }
+ # tkEntryNextWord -- Returns the index of the next word position
+ # after a given position in the entry. The next word is platform
+ # dependent and may be either the next end-of-word position or the
+ # next start-of-word position after the next end-of-word position.
+ #
+ # Arguments:
+ # w - The entry window in which the cursor is to move.
+ # start - Position at which to start search.
+
+ if {![string compare $tcl_platform(platform) "windows"]} {
+ proc blt::Hiertable::NextWord {w start} {
+ set pos [tcl_endOfWord [$w get] [$w index $start]]
+ if {$pos >= 0} {
+ set pos [tcl_startOfNextWord [$w get] $pos]
+ }
+ if {$pos < 0} {
+ return end
+ }
+ return $pos
+ }
+ } else {
+ proc blt::Hiertable::NextWord {w start} {
+ set pos [tcl_endOfWord [$w get] [$w index $start]]
+ if {$pos < 0} {
+ return end
+ }
+ return $pos
+ }
+ }
+
+ # blt::Hiertable::PreviousWord --
+ #
+ # Returns the index of the previous word position before a given
+ # position in the entry.
+ #
+ # Arguments:
+ # w - The entry window in which the cursor is to move.
+ # start - Position at which to start search.
+
+ proc blt::Hiertable::PreviousWord {w start} {
+ set pos [tcl_startOfPreviousWord [$w get] [$w index $start]]
+ if {$pos < 0} {
+ return 0
+ }
+ return $pos
+ }
+}
diff --git a/blt/library/pkgIndex.tcl.in b/blt/library/pkgIndex.tcl.in
new file mode 100644
index 00000000000..ee93e9dc2cd
--- /dev/null
+++ b/blt/library/pkgIndex.tcl.in
@@ -0,0 +1,29 @@
+# Tcl package index file, version 1.0
+
+proc LoadBLT { version dir } {
+
+ set suffix [info sharedlibextension]
+ regsub {\.} $version {} version_no_dots
+
+ # Determine whether to load the normal BLT library or
+ # the "lite" tcl-only version.
+
+ if { [info commands tk] == "tk" } {
+ set library BLT${version_no_dots}${suffix}
+ } else {
+ set library BLTlite${version_no_dots}${suffix}
+ }
+
+ global tcl_platform
+ if { $tcl_platform(platform) == "unix" } {
+ set library [file join $dir lib${library}]
+ }
+ load $library BLT
+}
+
+set version "%VERSION%"
+set libdir "%LIB_DIR%"
+
+package ifneeded BLT $version [list LoadBLT $version $libdir]
+
+# End of package index file
diff --git a/blt/library/tabnotebook.tcl b/blt/library/tabnotebook.tcl
new file mode 100644
index 00000000000..3b4fb19289e
--- /dev/null
+++ b/blt/library/tabnotebook.tcl
@@ -0,0 +1,318 @@
+#
+# tabnotebook.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT tabnotebook widget
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@bell-labs.com
+# http://www.tcltk.com/blt
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+#
+# Indicates whether to activate (highlight) tabs when the mouse passes
+# over them. This is turned off during scan operations.
+#
+set bltTabnotebook(activate) yes
+
+# ----------------------------------------------------------------------
+#
+# ButtonPress assignments
+#
+# <ButtonPress-2> Starts scan mechanism (pushes the tabs)
+# <B2-Motion> Adjust scan
+# <ButtonRelease-2> Stops scan
+#
+# ----------------------------------------------------------------------
+bind Tabnotebook <B2-Motion> {
+ %W scan dragto %x %y
+}
+
+bind Tabnotebook <ButtonPress-2> {
+ set bltTabnotebook(cursor) [%W cget -cursor]
+ set bltTabnotebook(activate) no
+ %W configure -cursor hand1
+ %W scan mark %x %y
+}
+
+bind Tabnotebook <ButtonRelease-2> {
+ %W configure -cursor $bltTabnotebook(cursor)
+ set bltTabnotebook(activate) yes
+ %W highlight @%x,%y
+}
+
+# ----------------------------------------------------------------------
+#
+# KeyPress assignments
+#
+# <KeyPress-Up> Moves focus to the tab immediately above the
+# current.
+# <KeyPress-Down> Moves focus to the tab immediately below the
+# current.
+# <KeyPress-Left> Moves focus to the tab immediately left of the
+# currently focused tab.
+# <KeyPress-Right> Moves focus to the tab immediately right of the
+# currently focused tab.
+# <KeyPress-space> Invokes the commands associated with the current
+# tab.
+# <KeyPress-Return> Same as above.
+# <KeyPress> Go to next tab starting with the ASCII character.
+#
+# ----------------------------------------------------------------------
+bind Tabnotebook <KeyPress-Up> { blt::SelectTab %W "up" }
+bind Tabnotebook <KeyPress-Down> { blt::SelectTab %W "down" }
+bind Tabnotebook <KeyPress-Right> { blt::SelectTab %W "right" }
+bind Tabnotebook <KeyPress-Left> { blt::SelectTab %W "left" }
+bind Tabnotebook <KeyPress-space> { %W invoke focus }
+bind Tabnotebook <KeyPress-Return> { %W invoke focus }
+
+bind Tabnotebook <KeyPress> {
+ if { [string match {[A-Za-z0-9]*} "%A"] } {
+ blt::FindMatchingTab %W %A
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# FirstMatchingTab --
+#
+# Find the first tab (from the tab that currently has focus)
+# starting with the same first letter as the tab. It searches
+# in order of the tab positions and wraps around. If no tab
+# matches, it stops back at the current tab.
+#
+# Arguments:
+# widget Tabnotebook widget.
+# key ASCII character of key pressed
+#
+# ----------------------------------------------------------------------
+proc blt::FindMatchingTab { widget key } {
+ set key [string tolower $key]
+ set itab [$widget index focus]
+ set numTabs [$widget size]
+ for { set i 0 } { $i < $numTabs } { incr i } {
+ if { [incr itab] >= $numTabs } {
+ set itab 0
+ }
+ set label [string tolower [$widget tab cget $itab -text]]
+ if { [string index $label 0] == $key } {
+ break
+ }
+ }
+ $widget focus $itab
+ $widget see focus
+}
+
+# ----------------------------------------------------------------------
+#
+# SelectTab --
+#
+# Invokes the command for the tab. If the widget associated tab
+# is currently torn off, the tearoff is raised.
+#
+# Arguments:
+# widget Tabnotebook widget.
+# x y Unused.
+#
+# ----------------------------------------------------------------------
+proc blt::SelectTab { widget tab } {
+ set index [$widget index $tab]
+ if { $index != "" } {
+ $widget select $index
+ $widget focus $index
+ $widget see $index
+ set w [$widget tab tearoff $index]
+ if { ($w != "") && ($w != "$widget") } {
+ raise [winfo toplevel $w]
+ }
+ $widget invoke $index
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# DestroyTearoff --
+#
+# Destroys the toplevel window and the container tearoff
+# window holding the embedded widget. The widget is placed
+# back inside the tab.
+#
+# Arguments:
+# widget Tabnotebook widget.
+# tab Tab selected.
+#
+# ----------------------------------------------------------------------
+proc blt::DestroyTearoff { widget tab } {
+ set id [$widget id $tab]
+ set top "$widget.toplevel-$id"
+ if { [winfo exists $top] } {
+ wm withdraw $top
+ update
+ $widget tab tearoff $tab $widget
+ destroy $top
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# CreateTearoff --
+#
+# Creates a new toplevel window and moves the embedded widget
+# into it. The toplevel is placed just below the tab. The
+# DELETE WINDOW property is set so that if the toplevel window
+# is requested to be deleted by the window manager, the embedded
+# widget is placed back inside of the tab. Note also that
+# if the tabnotebook container is ever destroyed, the toplevel is
+# also destroyed.
+#
+# Arguments:
+# widget Tabnotebook widget.
+# tab Tab selected.
+# x y The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::CreateTearoff { widget tab rootX rootY } {
+
+ # ------------------------------------------------------------------
+ # When reparenting the window contained in the tab, check if the
+ # window or any window in its hierarchy currently has focus.
+ # Since we're reparenting windows behind its back, Tk can
+ # mistakenly activate the keyboard focus when the mouse enters the
+ # old toplevel. The simplest way to deal with this problem is to
+ # take the focus off the window and set it to the tabnotebook widget
+ # itself.
+ # ------------------------------------------------------------------
+
+ set focus [focus]
+ set window [$widget tab cget $tab -window]
+ set index [$widget index $tab]
+ if { ($focus == $window) || ([string match $window.* $focus]) } {
+ focus -force $widget
+ }
+ set id [$widget id $index]
+ set top "$widget.toplevel-$id"
+ toplevel $top
+ $widget tab tearoff $tab $top.container
+ table $top $top.container -fill both
+
+ incr rootX 10 ; incr rootY 10
+ wm geometry $top +$rootX+$rootY
+
+ set parent [winfo toplevel $widget]
+ wm title $top "[wm title $parent]: [$widget tab cget $index -text]"
+ wm transient $top $parent
+
+ # If the user tries to delete the toplevel, put the window back
+ # into the tab folder.
+
+ wm protocol $top WM_DELETE_WINDOW [list blt::DestroyTearoff $widget $tab]
+
+ # If the container is ever destroyed, automatically destroy the
+ # toplevel too.
+
+ bind $top.container <Destroy> [list destroy $top]
+}
+
+# ----------------------------------------------------------------------
+#
+# ToggleTearoff --
+#
+# Toggles the tab tearoff. If the tab contains a embedded widget,
+# it is placed inside of a toplevel window. If the widget has
+# already been torn off, the widget is replaced back in the tab.
+#
+# Arguments:
+# widget tabnotebook widget.
+# x y The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::ToggleTearoff { widget x y index } {
+ set tab [$widget index $index]
+ if { $tab == "" } {
+ return
+ }
+ $widget invoke $tab
+
+ set container [$widget tab tearoff $index]
+ if { $container == "$widget" } {
+ blt::CreateTearoff $widget $tab $x $y
+ } elseif { $container != "" } {
+ blt::DestroyTearoff $widget $tab
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# TabnotebookInit
+#
+# Invoked from C whenever a new tabnotebook widget is created.
+# Sets up the default bindings for the all tab entries.
+# These bindings are local to the widget, so they can't be
+# set through the usual widget class bind tags mechanism.
+#
+# <Enter> Activates the tab.
+# <Leave> Deactivates all tabs.
+# <ButtonPress-1> Selects the tab and invokes its command.
+# <Control-ButtonPress-1>
+# Toggles the tab tearoff. If the tab contains
+# a embedded widget, it is placed inside of a
+# toplevel window. If the widget has already
+# been torn off, the widget is replaced back
+# in the tab.
+#
+# Arguments:
+# widget tabnotebook widget
+#
+# ----------------------------------------------------------------------
+proc blt::TabnotebookInit { widget } {
+ $widget bind all <Enter> {
+ if { $bltTabnotebook(activate) } {
+ %W highlight current
+ }
+ }
+ $widget bind all <Leave> {
+ %W highlight ""
+ }
+ $widget bind all <ButtonPress-1> {
+ blt::SelectTab %W "current"
+ }
+ $widget bind all <Control-ButtonPress-1> {
+ blt::ToggleTearoff %W %X %Y active
+ }
+ $widget configure -perforationcommand {
+ blt::ToggleTearoff %W $bltTabnotebook(x) $bltTabnotebook(y) select
+ }
+ $widget bind Perforation <Enter> {
+ %W perforation highlight on
+ }
+ $widget bind Perforation <Leave> {
+ %W perforation highlight off
+ }
+ $widget bind Perforation <ButtonPress-1> {
+ set bltTabnotebook(x) %X
+ set bltTabnotebook(y) %Y
+ %W perforation invoke
+ }
+}
diff --git a/blt/library/tabset.tcl b/blt/library/tabset.tcl
new file mode 100644
index 00000000000..ab3f0427033
--- /dev/null
+++ b/blt/library/tabset.tcl
@@ -0,0 +1,325 @@
+#
+# tabset.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT tabset widget
+# ----------------------------------------------------------------------
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@bell-labs.com
+# http://www.tcltk.com/blt
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ======================================================================
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+#
+# Indicates whether to activate (highlight) tabs when the mouse passes
+# over them. This is turned off during scan operations.
+#
+set bltTabset(activate) yes
+
+# ----------------------------------------------------------------------
+#
+# ButtonPress assignments
+#
+# <ButtonPress-2> Starts scan mechanism (pushes the tabs)
+# <B2-Motion> Adjust scan
+# <ButtonRelease-2> Stops scan
+#
+# ----------------------------------------------------------------------
+bind Tabset <B2-Motion> {
+ %W scan dragto %x %y
+}
+
+bind Tabset <ButtonPress-2> {
+ set bltTabset(cursor) [%W cget -cursor]
+ set bltTabset(activate) no
+ %W configure -cursor hand1
+ %W scan mark %x %y
+}
+
+bind Tabset <ButtonRelease-2> {
+ %W configure -cursor $bltTabset(cursor)
+ set bltTabset(activate) yes
+ %W highlight @%x,%y
+}
+
+# ----------------------------------------------------------------------
+#
+# KeyPress assignments
+#
+# <KeyPress-Up> Moves focus to the tab immediately above the
+# current.
+# <KeyPress-Down> Moves focus to the tab immediately below the
+# current.
+# <KeyPress-Left> Moves focus to the tab immediately left of the
+# currently focused tab.
+# <KeyPress-Right> Moves focus to the tab immediately right of the
+# currently focused tab.
+# <KeyPress-space> Invokes the commands associated with the current
+# tab.
+# <KeyPress-Return> Same as above.
+# <KeyPress> Go to next tab starting with the ASCII character.
+#
+# ----------------------------------------------------------------------
+bind Tabset <KeyPress-Up> { blt::SelectTab %W "up" }
+bind Tabset <KeyPress-Down> { blt::SelectTab %W "down" }
+bind Tabset <KeyPress-Right> { blt::SelectTab %W "right" }
+bind Tabset <KeyPress-Left> { blt::SelectTab %W "left" }
+bind Tabset <KeyPress-space> { %W invoke focus }
+bind Tabset <KeyPress-Return> { %W invoke focus }
+
+bind Tabset <KeyPress> {
+ if { [string match {[A-Za-z0-9]*} "%A"] } {
+ blt::FindMatchingTab %W %A
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# FirstMatchingTab --
+#
+# Find the first tab (from the tab that currently has focus)
+# starting with the same first letter as the tab. It searches
+# in order of the tab positions and wraps around. If no tab
+# matches, it stops back at the current tab.
+#
+# Arguments:
+# widget Tabset widget.
+# key ASCII character of key pressed
+#
+# ----------------------------------------------------------------------
+proc blt::FindMatchingTab { widget key } {
+ set key [string tolower $key]
+ set itab [$widget index focus]
+ set numTabs [$widget size]
+ for { set i 0 } { $i < $numTabs } { incr i } {
+ if { [incr itab] >= $numTabs } {
+ set itab 0
+ }
+ set name [$widget get $itab]
+ set label [string tolower [$widget tab cget $name -text]]
+ if { [string index $label 0] == $key } {
+ break
+ }
+ }
+ $widget focus $itab
+ $widget see focus
+}
+
+# ----------------------------------------------------------------------
+#
+# SelectTab --
+#
+# Invokes the command for the tab. If the widget associated tab
+# is currently torn off, the tearoff is raised.
+#
+# Arguments:
+# widget Tabset widget.
+# x y Unused.
+#
+# ----------------------------------------------------------------------
+proc blt::SelectTab { widget tab } {
+ set index [$widget index $tab]
+ if { $index != "" } {
+ $widget select $index
+ $widget focus $index
+ $widget see $index
+ set w [$widget tab tearoff $index]
+ if { ($w != "") && ($w != "$widget") } {
+ raise [winfo toplevel $w]
+ }
+ $widget invoke $index
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# DestroyTearoff --
+#
+# Destroys the toplevel window and the container tearoff
+# window holding the embedded widget. The widget is placed
+# back inside the tab.
+#
+# Arguments:
+# widget Tabset widget.
+# tab Tab selected.
+#
+# ----------------------------------------------------------------------
+proc blt::DestroyTearoff { widget tab } {
+ regsub -all {\.} [$widget get $tab] {_} name
+ set top "$widget.toplevel-$name"
+ if { [winfo exists $top] } {
+ wm withdraw $top
+ update
+ $widget tab tearoff $tab $widget
+ destroy $top
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# CreateTearoff --
+#
+# Creates a new toplevel window and moves the embedded widget
+# into it. The toplevel is placed just below the tab. The
+# DELETE WINDOW property is set so that if the toplevel window
+# is requested to be deleted by the window manager, the embedded
+# widget is placed back inside of the tab. Note also that
+# if the tabset container is ever destroyed, the toplevel is
+# also destroyed.
+#
+# Arguments:
+# widget Tabset widget.
+# tab Tab selected.
+# x y The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::CreateTearoff { widget tab rootX rootY } {
+
+ # ------------------------------------------------------------------
+ # When reparenting the window contained in the tab, check if the
+ # window or any window in its hierarchy currently has focus.
+ # Since we're reparenting windows behind its back, Tk can
+ # mistakenly activate the keyboard focus when the mouse enters the
+ # old toplevel. The simplest way to deal with this problem is to
+ # take the focus off the window and set it to the tabset widget
+ # itself.
+ # ------------------------------------------------------------------
+
+ set focus [focus]
+ set name [$widget get $tab]
+ set window [$widget tab cget $name -window]
+ if { ($focus == $window) || ([string match $window.* $focus]) } {
+ focus -force $widget
+ }
+ regsub -all {\.} [$widget get $tab] {_} name
+ set top "$widget.toplevel-$name"
+ toplevel $top
+ $widget tab tearoff $tab $top.container
+ table $top $top.container -fill both
+
+ incr rootX 10 ; incr rootY 10
+ wm geometry $top +$rootX+$rootY
+ set name [$widget get $tab]
+
+ set parent [winfo toplevel $widget]
+ wm title $top "[wm title $parent]: [$widget tab cget $name -text]"
+ wm transient $top $parent
+
+ #blt::winop changes $top
+
+ #
+ # If the user tries to delete the toplevel, put the window back
+ # into the tab folder.
+ #
+ wm protocol $top WM_DELETE_WINDOW [list blt::DestroyTearoff $widget $tab]
+ #
+ # If the container is ever destroyed, automatically destroy the
+ # toplevel too.
+ #
+ bind $top.container <Destroy> [list destroy $top]
+}
+
+# ----------------------------------------------------------------------
+#
+# Tearoff --
+#
+# Toggles the tab tearoff. If the tab contains a embedded widget,
+# it is placed inside of a toplevel window. If the widget has
+# already been torn off, the widget is replaced back in the tab.
+#
+# Arguments:
+# widget tabset widget.
+# x y The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::Tearoff { widget x y index } {
+ set tab [$widget index -index $index]
+ if { $tab == "" } {
+ return
+ }
+ $widget invoke $tab
+
+ set container [$widget tab tearoff $index]
+ if { $container == "$widget" } {
+ blt::CreateTearoff $widget $tab $x $y
+ } elseif { $container != "" } {
+ blt::DestroyTearoff $widget $tab
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# TabsetInit
+#
+# Invoked from C whenever a new tabset widget is created.
+# Sets up the default bindings for the all tab entries.
+# These bindings are local to the widget, so they can't be
+# set through the usual widget class bind tags mechanism.
+#
+# <Enter> Activates the tab.
+# <Leave> Deactivates all tabs.
+# <ButtonPress-1> Selects the tab and invokes its command.
+# <Control-ButtonPress-1>
+# Toggles the tab tearoff. If the tab contains
+# a embedded widget, it is placed inside of a
+# toplevel window. If the widget has already
+# been torn off, the widget is replaced back
+# in the tab.
+#
+# Arguments:
+# widget tabset widget
+#
+# ----------------------------------------------------------------------
+proc blt::TabsetInit { widget } {
+ $widget bind all <Enter> {
+ if { $bltTabset(activate) } {
+ %W highlight current
+ }
+ }
+ $widget bind all <Leave> {
+ %W highlight ""
+ }
+ $widget bind all <ButtonPress-1> {
+ blt::SelectTab %W "current"
+ }
+ $widget bind all <Control-ButtonPress-1> {
+ if { [%W cget -tearoff] } {
+ blt::Tearoff %W %X %Y active
+ }
+ }
+ $widget configure -perforationcommand {
+ blt::Tearoff %W $bltTabset(x) $bltTabset(y) select
+ }
+ $widget bind Perforation <Enter> {
+ %W perforation highlight on
+ }
+ $widget bind Perforation <Leave> {
+ %W perforation highlight off
+ }
+ $widget bind Perforation <ButtonPress-1> {
+ set bltTabset(x) %X
+ set bltTabset(y) %Y
+ %W perforation invoke
+ }
+}
diff --git a/blt/library/tclIndex b/blt/library/tclIndex
new file mode 100644
index 00000000000..0b72d797afc
--- /dev/null
+++ b/blt/library/tclIndex
@@ -0,0 +1,13 @@
+# Tcl autoload index file, version 2.0
+# This file is generated by the "auto_mkindex" command
+# and sourced to set up indexing information for one or
+# more commands. Typically each line is a command that
+# sets an element in the auto_index array, where the
+# element name is the name of a command and the value is
+# a script that loads the command.
+
+set auto_index(Blt_ActiveLegend) [list source $dir/graph.tcl]
+set auto_index(Blt_Crosshairs) [list source $dir/graph.tcl]
+set auto_index(Blt_ZoomStack) [list source $dir/graph.tcl]
+set auto_index(Blt_PrintKey) [list source $dir/graph.tcl]
+set auto_index(Blt_ClosestPoint) [list source $dir/graph.tcl]
diff --git a/blt/library/treeview.cur b/blt/library/treeview.cur
new file mode 100644
index 00000000000..040cf4340f4
--- /dev/null
+++ b/blt/library/treeview.cur
Binary files differ
diff --git a/blt/library/treeview.tcl b/blt/library/treeview.tcl
new file mode 100644
index 00000000000..9c602d9eb47
--- /dev/null
+++ b/blt/library/treeview.tcl
@@ -0,0 +1,972 @@
+# ======================================================================
+#
+# treeview.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT treeview widget
+# ----------------------------------------------------------------------
+#
+# AUTHOR: George Howlett
+# Bell Labs Innovations for Lucent Technologies
+# gah@lucent.com
+# http://www.tcltk.com/blt
+#
+# RCS: $Id$
+#
+# ----------------------------------------------------------------------
+# Copyright (c) 1998 Lucent Technologies, Inc.
+# ----------------------------------------------------------------------
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness. In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+namespace eval blt::tv {
+ set afterId ""
+ set scroll 0
+ set column ""
+ set space off
+ set x 0
+ set y 0
+}
+
+image create photo blt::tv::normalCloseFolder -format gif -data {
+ R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+ AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+ ZTKAsiCtWq0JADs=
+}
+image create photo blt::tv::normalOpenFolder -format gif -data {
+ R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+ AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+ nQkAOw==
+}
+image create photo blt::tv::activeCloseFolder -format gif -data {
+ R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+ AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+ ZTKAsiCtWq0JADs=
+}
+image create photo blt::tv::activeOpenFolder -format gif -data {
+ R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+ AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+ nQkAOw==
+}
+
+if { $tcl_platform(platform) == "windows" } {
+ if { $tk_version >= 8.3 } {
+ set cursor "@[file join $blt_library treeview.cur]"
+ } else {
+ set cursor "size_we"
+ }
+ option add *${className}.ResizeCursor [list $cursor]
+} else {
+ option add *${className}.ResizeCursor \
+ "@$blt_library/treeview.xbm $blt_library/treeview_m.xbm black white"
+}
+
+# ----------------------------------------------------------------------
+#
+# Initialize --
+#
+# Invoked by internally by Treeview_Init routine. Initializes
+# the default bindings for the treeview widget entries. These
+# are local to the widget, so they can't be set through the
+# widget's class bind tags.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::Initialize { w } {
+ #
+ # Active entry bindings
+ #
+ $w bind Entry <Enter> {
+ %W entry highlight current
+ }
+ $w bind Entry <Leave> {
+ %W entry highlight ""
+ }
+
+ #
+ # Button bindings
+ #
+ $w button bind all <ButtonRelease-1> {
+ %W see -anchor nw current
+ %W toggle current
+ }
+ $w button bind all <Enter> {
+ %W button highlight current
+ }
+ $w button bind all <Leave> {
+ %W button highlight ""
+ }
+
+ #
+ # ButtonPress-1
+ #
+ # Performs the following operations:
+ #
+ # 1. Clears the previous selection.
+ # 2. Selects the current entry.
+ # 3. Sets the focus to this entry.
+ # 4. Scrolls the entry into view.
+ # 5. Sets the selection anchor to this entry, just in case
+ # this is "multiple" mode.
+ #
+
+ $w bind Entry <ButtonPress-1> {
+ blt::tv::SetSelectionAnchor %W current
+ set blt::tv::scroll 1
+ }
+
+ $w bind Entry <Double-ButtonPress-1> {
+ %W toggle current
+ }
+
+ #
+ # B1-Motion
+ #
+ # For "multiple" mode only. Saves the current location of the
+ # pointer for auto-scrolling. Resets the selection mark.
+ #
+ $w bind Entry <B1-Motion> {
+ set blt::tv::x %x
+ set blt::tv::y %y
+ set index [%W nearest %x %y]
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection mark $index
+ } else {
+ blt::tv::SetSelectionAnchor %W $index
+ }
+ }
+
+ #
+ # ButtonRelease-1
+ #
+ # For "multiple" mode only.
+ #
+ $w bind Entry <ButtonRelease-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ %W selection anchor current
+ }
+ after cancel $blt::tv::afterId
+ set blt::tv::scroll 0
+ }
+
+ #
+ # Shift-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+
+ $w bind Entry <Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ set index [%W index anchor]
+ %W selection clearall
+ %W selection set $index current
+ } else {
+ blt::tv::SetSelectionAnchor %W current
+ }
+ }
+ $w bind Entry <Shift-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w bind Entry <Shift-B1-Motion> {
+ # do nothing
+ }
+ $w bind Entry <Shift-ButtonRelease-1> {
+ after cancel $blt::tv::afterId
+ set blt::tv::scroll 0
+ }
+
+ #
+ # Control-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $w bind Entry <Control-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" } {
+ set index [%W index current]
+ %W selection toggle $index
+ %W selection anchor $index
+ } else {
+ blt::tv::SetSelectionAnchor %W current
+ }
+ }
+ $w bind Entry <Control-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w bind Entry <Control-B1-Motion> {
+ # do nothing
+ }
+ $w bind Entry <Control-ButtonRelease-1> {
+ after cancel $blt::tv::afterId
+ set blt::tv::scroll 0
+ }
+
+ $w bind Entry <Control-Shift-ButtonPress-1> {
+ if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+ if { [%W index anchor] == "" } {
+ %W selection anchor current
+ }
+ if { [%W selection includes anchor] } {
+ %W selection set anchor current
+ } else {
+ %W selection clear anchor current
+ %W selection set current
+ }
+ } else {
+ blt::tv::SetSelectionAnchor %W current
+ }
+ }
+ $w bind Entry <Control-Shift-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w bind Entry <Control-Shift-B1-Motion> {
+ # do nothing
+ }
+ $w column bind all <Enter> {
+ %W column highlight [%W column current]
+ }
+ $w column bind all <Leave> {
+ %W column highlight ""
+ }
+ $w column bind Rule <Enter> {
+ %W column highlight [%W column current]
+ %W column resize activate [%W column current]
+ }
+ $w column bind Rule <Leave> {
+ %W column highlight ""
+ %W column resize activate ""
+ }
+ $w column bind Rule <ButtonPress-1> {
+ %W column resize anchor %x
+ }
+ $w column bind Rule <B1-Motion> {
+ %W column resize mark %x
+ }
+ $w column bind Rule <ButtonRelease-1> {
+ %W column configure [%W column current] -width [%W column resize set]
+ }
+ $w column bind all <ButtonRelease-1> {
+ set column [%W column nearest %x %y]
+ if { $column != "" } {
+ %W column invoke $column
+ }
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# AutoScroll --
+#
+# Invoked when the user is selecting elements in a treeview
+# widget and drags the mouse pointer outside of the widget.
+# Scrolls the view in the direction of the pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::AutoScroll { w } {
+ if { ![winfo exists $w] } {
+ return
+ }
+ set x $blt::tv::x
+ set y $blt::tv::y
+
+ set index [$w nearest $x $y]
+
+ if {$y >= [winfo height $w]} {
+ $w yview scroll 1 units
+ set neighbor down
+ } elseif {$y < 0} {
+ $w yview scroll -1 units
+ set neighbor up
+ } else {
+ set neighbor $index
+ }
+ if { [$w cget -selectmode] == "single" } {
+ blt::tv::SetSelectionAnchor $w $neighbor
+ } else {
+ $w selection mark $index
+ }
+ set ::blt::tv::afterId [after 50 blt::tv::AutoScroll $w]
+}
+
+proc blt::tv::SetSelectionAnchor { w tagOrId } {
+ set index [$w index $tagOrId]
+ # If the anchor hasn't changed, don't do anything
+ if { $index != [$w index anchor] } {
+ $w selection clearall
+ $w see $index
+ $w focus $index
+ $w selection set $index
+ $w selection anchor $index
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# MoveFocus --
+#
+# Invoked by KeyPress bindings. Moves the active selection to
+# the entry <where>, which is an index such as "up", "down",
+# "prevsibling", "nextsibling", etc.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::MoveFocus { w tagOrId } {
+ catch {$w focus $tagOrId}
+ if { [$w cget -selectmode] == "single" } {
+ $w selection clearall
+ $w selection set focus
+ $w selection anchor focus
+ }
+ $w see focus
+}
+
+# ----------------------------------------------------------------------
+#
+# MovePage --
+#
+# Invoked by KeyPress bindings. Pages the current view up or
+# down. The <where> argument should be either "top" or
+# "bottom".
+#
+# ----------------------------------------------------------------------
+proc blt::tv::MovePage { w where } {
+
+ # If the focus is already at the top/bottom of the window, we want
+ # to scroll a page. It's really one page minus an entry because we
+ # want to see the last entry on the next/last page.
+ if { [$w index focus] == [$w index view.$where] } {
+ if {$where == "top"} {
+ $w yview scroll -1 pages
+ $w yview scroll 1 units
+ } else {
+ $w yview scroll 1 pages
+ $w yview scroll -1 units
+ }
+ }
+ update
+
+ # Adjust the entry focus and the view. Also activate the entry.
+ # just in case the mouse point is not in the widget.
+ $w entry highlight view.$where
+ $w focus view.$where
+ $w see view.$where
+ if { [$w cget -selectmode] == "single" } {
+ $w selection clearall
+ $w selection set focus
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# NextMatch --
+#
+# Invoked by KeyPress bindings. Searches for an entry that
+# starts with the letter <char> and makes that entry active.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::NextMatch { w key } {
+ if {[string match {[ -~]} $key]} {
+ set last [$w index focus]
+ set next [$w index next]
+ while { $next != $last } {
+ set label [$w entry cget $next -label]
+ set label [string index $label 0]
+ if { [string tolower $label] == [string tolower $key] } {
+ break
+ }
+ set next [$w index -at $next next]
+ }
+ $w focus $next
+ if {[$w cget -selectmode] == "single"} {
+ $w selection clearall
+ $w selection set focus
+ }
+ $w see focus
+ }
+}
+
+#------------------------------------------------------------------------
+#
+# InsertText --
+#
+# Inserts a text string into an entry at the insertion cursor.
+# If there is a selection in the entry, and it covers the point
+# of the insertion cursor, then delete the selection before
+# inserting.
+#
+# Arguments:
+# w Widget where to insert the text.
+# text Text string to insert (usually just a single character)
+#
+#------------------------------------------------------------------------
+proc blt::tv::InsertText { w text } {
+ if { [string length $text] > 0 } {
+ set index [$w text index insert]
+ if { ($index >= [$w text index sel.first]) &&
+ ($index <= [$w text index sel.last]) } {
+ $w text delete sel.first sel.last
+ }
+ $w text insert $index $text
+ }
+}
+
+#------------------------------------------------------------------------
+#
+# Transpose -
+#
+# This procedure implements the "transpose" function for entry
+# widgets. It tranposes the characters on either side of the
+# insertion cursor, unless the cursor is at the end of the line.
+# In this case it transposes the two characters to the left of
+# the cursor. In either case, the cursor ends up to the right
+# of the transposed characters.
+#
+# Arguments:
+# w The entry window.
+#
+#------------------------------------------------------------------------
+proc blt::tv::Transpose { w } {
+ set i [$w text index insert]
+ if {$i < [$w text index end]} {
+ incr i
+ }
+ set first [expr {$i-2}]
+ if {$first < 0} {
+ return
+ }
+ set new [string index [$w get] [expr {$i-1}]][string index [$w get] $first]
+ $w delete $first $i
+ $w insert insert $new
+}
+
+#------------------------------------------------------------------------
+#
+# GetSelection --
+#
+# Returns the selected text of the entry with respect to the
+# -show option.
+#
+# Arguments:
+# w Entry window from which the text to get
+#
+#------------------------------------------------------------------------
+
+proc blt::tv::GetSelection { w } {
+ set text [string range [$w get] [$w index sel.first] \
+ [expr [$w index sel.last] - 1]]
+ if {[$w cget -show] != ""} {
+ regsub -all . $text [string index [$w cget -show] 0] text
+ }
+ return $text
+}
+
+#
+# ButtonPress assignments
+#
+# B1-Enter start auto-scrolling
+# B1-Leave stop auto-scrolling
+# ButtonPress-2 start scan
+# B2-Motion adjust scan
+# ButtonRelease-2 stop scan
+#
+
+bind ${className} <ButtonPress-2> {
+ set blt::tv::cursor [%W cget -cursor]
+ %W configure -cursor hand1
+ %W scan mark %x %y
+}
+
+bind ${className} <B2-Motion> {
+ %W scan dragto %x %y
+}
+
+bind ${className} <ButtonRelease-2> {
+ %W configure -cursor $blt::tv::cursor
+}
+
+bind ${className} <B1-Leave> {
+ if { $blt::tv::scroll } {
+ blt::tv::AutoScroll %W
+ }
+}
+
+bind ${className} <B1-Enter> {
+ after cancel $blt::tv::afterId
+}
+
+#
+# KeyPress assignments
+#
+# Up
+# Down
+# Shift-Up
+# Shift-Down
+# Prior (PageUp)
+# Next (PageDn)
+# Left
+# Right
+# space Start selection toggle of entry currently with focus.
+# Return Start selection toggle of entry currently with focus.
+# Home
+# End
+# F1
+# F2
+# ASCII char Go to next open entry starting with character.
+#
+# KeyRelease
+#
+# space Stop selection toggle of entry currently with focus.
+# Return Stop selection toggle of entry currently with focus.
+
+
+bind ${className} <KeyPress-Up> {
+ blt::tv::MoveFocus %W up
+ if { $blt::tv::space } {
+ %W selection toggle focus
+ }
+}
+
+bind ${className} <KeyPress-Down> {
+ blt::tv::MoveFocus %W down
+ if { $blt::tv::space } {
+ %W selection toggle focus
+ }
+}
+
+bind ${className} <Shift-KeyPress-Up> {
+ blt::tv::MoveFocus %W prevsibling
+}
+
+bind ${className} <Shift-KeyPress-Down> {
+ blt::tv::MoveFocus %W nextsibling
+}
+
+bind ${className} <KeyPress-Prior> {
+ blt::tv::MovePage %W top
+}
+
+bind ${className} <KeyPress-Next> {
+ blt::tv::MovePage %W bottom
+}
+
+bind ${className} <KeyPress-Left> {
+ %W close focus
+}
+bind ${className} <KeyPress-Right> {
+ %W open focus
+ %W see focus -anchor w
+}
+
+bind ${className} <KeyPress-space> {
+ if { [%W cget -selectmode] == "single" } {
+ if { [%W selection includes focus] } {
+ %W selection clearall
+ } else {
+ %W selection clearall
+ %W selection set focus
+ }
+ } else {
+ %W selection toggle focus
+ }
+ set blt::tv::space on
+}
+
+bind ${className} <KeyRelease-space> {
+ set blt::tv::space off
+}
+
+bind ${className} <KeyPress-Return> {
+ blt::tv::MoveFocus %W focus
+ set blt::tv::space on
+}
+
+bind ${className} <KeyRelease-Return> {
+ set blt::tv::space off
+}
+
+bind ${className} <KeyPress> {
+ blt::tv::NextMatch %W %A
+}
+
+bind ${className} <KeyPress-Home> {
+ blt::tv::MoveFocus %W top
+}
+
+bind ${className} <KeyPress-End> {
+ blt::tv::MoveFocus %W bottom
+}
+
+bind ${className} <KeyPress-F1> {
+ %W open -r root
+}
+
+bind ${className} <KeyPress-F2> {
+ eval %W close -r [%W entry children root]
+}
+
+#
+# Differences between "current" and nearest.
+#
+# set index [$w index current]
+# set index [$w nearest $x $y]
+#
+# o Nearest gives you the closest entry.
+# o current is "" if
+# 1) the pointer isn't over an entry.
+# 2) the pointer is over a open/close button.
+# 3)
+#
+
+#
+# Edit mode assignments
+#
+# ButtonPress-3 Enables/disables edit mode on entry. Sets focus to
+# entry.
+#
+# KeyPress
+#
+# Left Move insertion position to previous.
+# Right Move insertion position to next.
+# Up Move insertion position up one line.
+# Down Move insertion position down one line.
+# Return End edit mode.
+# Shift-Return Line feed.
+# Home Move to first position.
+# End Move to last position.
+# ASCII char Insert character left of insertion point.
+# Del Delete character right of insertion point.
+# Delete Delete character left of insertion point.
+# Ctrl-X Cut
+# Ctrl-V Copy
+# Ctrl-P Paste
+#
+# KeyRelease
+#
+# ButtonPress-1 Start selection if in entry, otherwise clear selection.
+# B1-Motion Extend/reduce selection.
+# ButtonRelease-1 End selection if in entry, otherwise use last
+# selection.
+# B1-Enter Disabled.
+# B1-Leave Disabled.
+# ButtonPress-2 Same as above.
+# B2-Motion Same as above.
+# ButtonRelease-2 Same as above.
+#
+#
+
+bind ${className} <ButtonPress-3> {
+ %W see current
+ set id [%W text get %x %y]
+ if { $id != -1 } {
+ focus %W.edit
+ grab set %W.edit
+ %W text selection range 0 end
+ }
+}
+
+
+# Standard Motif bindings:
+
+bind ${className}Editor <ButtonPress-1> {
+ %W text icursor @%x,%y
+ %W text selection clear
+}
+
+bind ${className}Editor <Left> {
+ %W text icursor last
+ %W text selection clear
+}
+
+bind ${className}Editor <Right> {
+ %W text icursor next
+ %W text selection clear
+}
+
+bind ${className}Editor <Shift-Left> {
+ set new [expr {[%W text index insert] - 1}]
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+
+bind ${className}Editor <Shift-Right> {
+ set new [expr {[%W text index insert] + 1}]
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+
+bind ${className}Editor <Home> {
+ %W text icursor 0
+ %W selection clear
+}
+bind ${className}Editor <Shift-Home> {
+ set new 0
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+bind ${className}Editor <End> {
+ %W text icursor end
+ %W selection clear
+}
+bind ${className}Editor <Shift-End> {
+ set new end
+ if {![%W text selection present]} {
+ %W text selection from insert
+ %W text selection to $new
+ } else {
+ %W text selection adjust $new
+ }
+ %W text icursor $new
+}
+
+bind ${className}Editor <Delete> {
+ if { [%W text selection present]} {
+ %W text delete sel.first sel.last
+ } else {
+ %W text delete insert
+ }
+}
+
+bind ${className}Editor <BackSpace> {
+ if { [%W text selection present] } {
+ %W text delete sel.first sel.last
+ } else {
+ set index [expr [%W text index insert] - 1]
+ if { $index >= 0 } {
+ %W text delete $index $index
+ }
+ }
+}
+
+bind ${className}Editor <Control-space> {
+ %W text selection from insert
+}
+
+bind ${className}Editor <Select> {
+ %W text selection from insert
+}
+
+bind ${className}Editor <Control-Shift-space> {
+ %W text selection adjust insert
+}
+
+bind ${className}Editor <Shift-Select> {
+ %W text selection adjust insert
+}
+
+bind ${className}Editor <Control-slash> {
+ %W text selection range 0 end
+}
+
+bind ${className}Editor <Control-backslash> {
+ %W text selection clear
+}
+
+bind ${className}Editor <KeyPress> {
+ blt::tv::InsertText %W %A
+}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong. Ditto for Escape, Return, and Tab.
+
+bind ${className}Editor <Alt-KeyPress> {
+ # nothing
+}
+
+bind ${className}Editor <Meta-KeyPress> {
+ # nothing
+}
+
+bind ${className}Editor <Control-KeyPress> {
+ # nothing
+}
+
+bind ${className}Editor <Escape> {
+ %W text cancel
+ grab release %W
+}
+
+bind ${className}Editor <Return> {
+ %W text apply
+ grab release %W
+}
+
+bind ${className}Editor <Shift-Return> {
+ blt::tv::InsertText %W "\n"
+}
+
+bind ${className}Editor <KP_Enter> {
+ # nothing
+}
+
+bind ${className}Editor <Tab> {
+ # nothing
+}
+
+if {![string compare $tcl_platform(platform) "macintosh"]} {
+ bind ${className}Editor <Command-KeyPress> {
+ # nothing
+ }
+}
+
+# On Windows, paste is done using Shift-Insert. Shift-Insert already
+# generates the <<Paste>> event, so we don't need to do anything here.
+if { [string compare $tcl_platform(platform) "windows"] != 0 } {
+ bind ${className}Editor <Insert> {
+ catch {blt::tv::InsertText %W [selection get -displayof %W]}
+ }
+}
+
+# Additional emacs-like bindings:
+bind ${className}Editor <ButtonPress-3> {
+ grab release %W
+ %W text cancel
+ update
+ set id [%W text get -root %X %Y]
+ if { $id != -1 } {
+ focus %W
+ grab set %W
+ %W text selection range 0 end
+ }
+}
+
+bind ${className}Editor <Control-a> {
+ %W text icursor 0
+ %W text selection clear
+}
+
+bind ${className}Editor <Control-b> {
+ %W text icursor [expr {[%W index insert] - 1}]
+ %W text selection clear
+}
+
+bind ${className}Editor <Control-d> {
+ %W text delete insert
+}
+
+bind ${className}Editor <Control-e> {
+ %W text icursor end
+ %W text selection clear
+}
+
+bind ${className}Editor <Control-f> {
+ %W text icursor [expr {[%W index insert] + 1}]
+ %W text selection clear
+}
+
+bind ${className}Editor <Control-h> {
+ if {[%W text selection present]} {
+ %W text delete sel.first sel.last
+ } else {
+ set index [expr [%W text index insert] - 1]
+ if { $index >= 0 } {
+ %W text delete $index $index
+ }
+ }
+}
+
+bind ${className}Editor <Control-k> {
+ %W text delete insert end
+}
+
+if 0 {
+ bind ${className}Editor <Control-t> {
+ blt::tv::Transpose %W
+ }
+ bind ${className}Editor <Meta-b> {
+ %W text icursor [blt::tv::PreviousWord %W insert]
+ %W text selection clear
+ }
+ bind ${className}Editor <Meta-d> {
+ %W delete insert [blt::tv::NextWord %W insert]
+ }
+ bind ${className}Editor <Meta-f> {
+ %W text icursor [blt::tv::NextWord %W insert]
+ %W text selection clear
+ }
+ bind ${className}Editor <Meta-BackSpace> {
+ %W delete [blt::tv::PreviousWord %W insert] insert
+ }
+ bind ${className}Editor <Meta-Delete> {
+ %W delete [blt::tv::PreviousWord %W insert] insert
+ }
+ # tkEntryNextWord -- Returns the index of the next word position
+ # after a given position in the entry. The next word is platform
+ # dependent and may be either the next end-of-word position or the
+ # next start-of-word position after the next end-of-word position.
+ #
+ # Arguments:
+ # w - The entry window in which the cursor is to move.
+ # start - Position at which to start search.
+
+ if {![string compare $tcl_platform(platform) "windows"]} {
+ proc blt::tv::NextWord {w start} {
+ set pos [tcl_endOfWord [$w get] [$w index $start]]
+ if {$pos >= 0} {
+ set pos [tcl_startOfNextWord [$w get] $pos]
+ }
+ if {$pos < 0} {
+ return end
+ }
+ return $pos
+ }
+ } else {
+ proc blt::tv::NextWord {w start} {
+ set pos [tcl_endOfWord [$w get] [$w index $start]]
+ if {$pos < 0} {
+ return end
+ }
+ return $pos
+ }
+ }
+
+ # PreviousWord --
+ #
+ # Returns the index of the previous word position before a given
+ # position in the entry.
+ #
+ # Arguments:
+ # w - The entry window in which the cursor is to move.
+ # start - Position at which to start search.
+
+ proc blt::tv::PreviousWord {w start} {
+ set pos [tcl_startOfPreviousWord [$w get] [$w index $start]]
+ if {$pos < 0} {
+ return 0
+ }
+ return $pos
+ }
+}
diff --git a/blt/library/treeview.xbm b/blt/library/treeview.xbm
new file mode 100644
index 00000000000..9bc935ad35c
--- /dev/null
+++ b/blt/library/treeview.xbm
@@ -0,0 +1,8 @@
+#define test_width 16
+#define test_height 16
+#define test_x_hot 7
+#define test_y_hot 7
+static unsigned char test_bits[] = {
+ 0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x64, 0x26,
+ 0x66, 0x66, 0x7f, 0xfe, 0x66, 0x66, 0x64, 0x26, 0x60, 0x06, 0x60, 0x06,
+ 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00};
diff --git a/blt/library/treeview_m.xbm b/blt/library/treeview_m.xbm
new file mode 100644
index 00000000000..9ba8767b12d
--- /dev/null
+++ b/blt/library/treeview_m.xbm
@@ -0,0 +1,8 @@
+#define testm_width 16
+#define testm_height 16
+#define testm_x_hot 7
+#define testm_y_hot 7
+static unsigned char testm_bits[] = {
+ 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xfc, 0x3f, 0xfe, 0x7f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x0f,
+ 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f};
diff --git a/blt/man/BLT.mann b/blt/man/BLT.mann
new file mode 100644
index 00000000000..b151f3b1d4f
--- /dev/null
+++ b/blt/man/BLT.mann
@@ -0,0 +1,153 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+.so man.macros
+.TH intro n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+BLT \- Introduction to the BLT library
+.BE
+.SH DESCRIPTION
+BLT is a library of extensions to the Tk library. It adds new
+commands and variables to the application's interpreter.
+.LP
+.SH COMMANDS
+The following commands are added to the interpreter from the BLT library:
+.TP 15
+\fBtable\fR
+A table geometry manager for Tk. You specify window placements as table
+row,column positions and windows can also span multiple rows or columns.
+It also has many options for setting and/or bounding window sizes.
+.TP 15
+\fBgraph\fR
+A 2D plotting widget. Plots two variable data in a window with an optional
+legend and annotations. It has of several components; coordinate axes,
+crosshairs, a legend, and a collection of elements and tags.
+.TP 15
+\fBbarchart\fR
+A barchart widget. Plots two-variable data as rectangular bars in a
+window. The x-coordinate values designate the position of the bar along
+the x-axis, while the y-coordinate values designate the magnitude.
+The \fBbarchart\fR widget has of several components; coordinate axes,
+crosshairs, a legend, and a collection of elements and tags.
+.TP 15
+\fBvector\fR
+Creates a vector of floating point values. The vector's components
+can be manipulated in three ways: through a Tcl array variable, a Tcl
+command, or the C API.
+.TP
+\fBspline\fR
+Computes a spline fitting a set of data points (x and y vectors) and
+produces a vector of the interpolated images (y-coordinates) at a
+given set of x-coordinates.
+.TP 15
+\fBbgexec\fR
+Like Tcl's \fBexec\fR command, \fBbgexec\fR runs a pipeline of Unix
+commands in the background. Unlike \fBexec\fR, the output of the last
+process is collected and a global Tcl variable is set upon its completion.
+\fBbgexec\fR can be used with \fBtkwait\fR to wait for Unix commands
+to finish while still handling expose events. Intermediate output is
+also available while the pipeline is active.
+.TP 15
+\fBbusy\fR
+Creates a "busy window" which prevents user-interaction when an
+application is busy. The busy window also provides an easy way
+to have temporary busy cursors (such as a watch or hourglass).
+.TP 15
+\fBbitmap\fR
+Reads and writes bitmaps from Tcl. New X bitmaps can be defined
+on-the-fly from Tcl, obviating the need to copy around bitmap files.
+Other options query loaded X bitmap's dimensions and data.
+.TP 15
+\fBdrag&drop\fR
+Provides a drag-and-drop facility for Tk. Information (represented
+by a token window) can be dragged to and from any Tk window, including
+those of another Tk application. \fBdrag&drop\fR acts as a
+coordinator, directing Tk \fBsend\fR commands between (or within) TCL/Tk
+applications.
+.TP 15
+\fBhtext\fR
+A simple hypertext widget. Combines text and Tk widgets into a single
+scroll-able window. Tcl commands can be embedded into text, which are
+invoked as the text is parsed. In addition, Tk widgets can be
+appended to the window at the current point in the text. \fBHtext\fR
+can be also used to create scrolled windows of Tk widgets.
+.TP 15
+\fBwinop\fR
+Raise, lower, map, or, unmap any window. The raise and lower functions
+are useful for stacking windows above or below "busy windows".
+.TP 15
+\fBwatch\fR
+Arranges for Tcl procedures to be called before and/or after the execution
+of every Tcl command. This command
+may be used in the logging, profiling, or tracing of Tcl code.
+.TP 15
+\fBbltdebug\fR
+A simple Tcl command tracing facility useful for debugging Tcl code.
+Displays each Tcl command before and after substitution along its level
+in the interpreter on standard error.
+.SH VARIABLES
+.PP
+The following Tcl variables are either set or used by BLT at various times
+in its execution:
+.TP 15
+\fBblt_library\fR
+This variable contains the name of a directory containing a library
+of Tcl scripts and other files related to BLT. Currently, this
+directory contains the \fBdrag&drop\fR protocol scripts and the
+PostScript prolog
+used by \fBgraph\fR and \fBbarchart\fR.
+The value of this variable is taken from the BLT_LIBRARY environment
+variable, if one exists, or else from a default value compiled into
+the \fBBLT\fR library.
+.TP 15
+\fBblt_versions\fR
+This variable is set in the interpreter for each application. It is an
+array of the current version numbers for each
+of the BLT commands in the form \fImajor\fR.\fIminor\fR. \fIMajor\fR and
+\fIminor\fR are integers. The major version number increases in
+any command that includes changes that are not backward compatible
+(i.e. whenever existing applications and scripts may have to change to
+work with the new release). The minor version number increases with
+each new release of a command, except that it resets to zero whenever the
+major version number changes. The array is indexed by the individual
+command name.
+.SH ADDING BLT TO YOUR APPLICATIONS
+It's easy to add BLT to an existing Tk application. BLT requires no
+patches or edits to the Tcl or Tk libraries. To add BLT, simply add the
+following code snippet to your application's tkAppInit.c file.
+.CS
+if (Blt_Init(interp) != TCL_OK) {
+ return TCL_ERROR;
+}
+.CE
+Recompile and link with the BLT library (libBLT.a) and that's it.
+.PP
+Alternately, you can dynamically load BLT, simply by invoking the
+command
+.CS
+package require BLT
+.CE
+from your Tcl script.
+.SH BUGS
+Send bug reports, requests, suggestions, etc. to
+gah@siliconmetrics.com or gah@myfirstlink.net
+.SH KEYWORDS
+BLT
diff --git a/blt/man/Blt_Tree.man3 b/blt/man/Blt_Tree.man3
new file mode 100644
index 00000000000..7ef583bae35
--- /dev/null
+++ b/blt/man/Blt_Tree.man3
@@ -0,0 +1,232 @@
+'\"
+'\" 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.
+'\"
+'\" RCS: @(#) $Id$
+'\"
+.so man.macros
+.TH Blt_Tree 3 BLT_VERSION BLT "Blt Library Procedures"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+Blt_Tree \- Tree data object.
+.SH SYNOPSIS
+.nf
+#include <bltTree.h>
+.sp
+struct Blt_Tree {
+\fBTcl_Alloc\fR(\fIsize\fR)
+.sp
+\fBTcl_Free\fR(\fIptr\fR)
+.sp
+char *
+\fBTcl_Realloc\fR(\fIptr, size\fR)
+.SH ARGUMENTS
+.AS char *size
+.AP int size in
+Size in bytes of the memory block to allocate.
+.AP char *ptr in
+Pointer to memory block to free or realloc.
+.BE
+
+.SH DESCRIPTION
+.PP
+These procedures provide a platform and compiler independent interface
+for memory allocation. Programs that need to transfer ownership of
+memory blocks between Tcl and other modules should use these routines
+rather than the native \fBmalloc()\fR and \fBfree()\fR routines
+provided by the C run-time library.
+.PP
+\fBTcl_Alloc\fR returns a pointer to a block of at least \fIsize\fR
+bytes suitably aligned for any use.
+.PP
+\fBTcl_Free\fR makes the space referred to by \fIptr\fR available for
+further allocation.
+.PP
+\fBTcl_Realloc\fR changes the size of the block pointed to by
+\fIptr\fR to \fIsize\fR bytes and returns a pointer to the new block.
+The contents will be unchanged up to the lesser of the new and old
+sizes. The returned location may be different from \fIptr\fR.
+.SH TREE OBJECT ROUTINES
+The following library routines allow you to create and destroy tree
+objects. Each tree object has a name that uniquely identifies it.
+Tree objects can also be shared. For example, the \fBtree\fR
+and \fBhiertable\fR commands may access the same tree data object.
+Each client grabs a token associated with the tree. When all tokens
+are released the tree data object is automatically destroyed.
+.TP 2.0i
+\fBBlt_TreeCreate\fR
+Create a tree data object and optionally obtains a token associated
+with it.
+.TP
+\fBBlt_TreeExists\fR
+Indicates if a tree by a given name exists.
+.TP
+\fBBlt_TreeGetToken\fR
+Obtains a token for an existing tree data object.
+.TP
+\fBBlt_TreeReleaseToken\fR
+Releases a token for a tree data object. The tree object is deleted
+when all outstanding tokens have been released.
+.TP
+\fBBlt_TreeName\fR
+Returns the name of the tree object.
+.TP
+\fBBlt_TreeChangeRoot\fR
+Specifies a node as the new root to a tree.
+.SH TREENODE ROUTINES
+Tree objects initially contain only a root node. You can add or
+delete nodes with the following routines.
+.TP 2i
+\fBBlt_TreeCreateNode\fR
+Creates a new child node for a given parent in the tree.
+.TP
+\fBBlt_TreeDeleteNode\fR
+Deletes a node and its children.
+.TP
+\fBBlt_TreeNodeId\fR
+Returns the unique node identifier for a node.
+.TP
+\fBBlt_TreeGetNode\fR
+Gets a node based upon its identifier.
+.TP
+\fBBlt_TreeFindChild\fR
+Searches for a child node given by its label in a parent node.
+.TP
+\fBBlt_TreeNodeLabel\fR
+Returns the current label for a node.
+.TP
+\fBBlt_TreeRelabelNode\fR
+Resets a node's label.
+.TP
+\fBBlt_TreeNodePath\fR
+Returns the fullpath to a node.
+.TP
+\fBBlt_TreeNodeDepth\fR
+Returns the depth of the node.
+.TP
+\fBBlt_TreeNodeDegree\fR
+Returns the number of children for a node.
+.TP
+\fBBlt_TreeIsLeaf\fR
+Indicates if a node has no children.
+.TP
+\fBBlt_TreeIsBefore\fR
+Indicates if a node is before another node in depth-first search order.
+.TP
+\fBBlt_TreeIsAncestor\fR
+Indicates if a node is an ancestor or another.
+.TP
+\fBBlt_TreeSortNode\fR
+Sorts the children of a node.
+.TP
+\fBBlt_TreeSize\fR
+Returns the number of nodes in a node and its descendants.
+.TP
+\fBBlt_TreeMoveNode\fR
+.SH NODE NAVIGATION
+Each node can have zero or more children nodes. These routines
+let you navigate the tree hierarchy.
+.TP 2i
+\fBBlt_TreeNodeParent\fR
+Returns the parent node.
+.TP
+\fBBlt_TreeFirstChild\fR
+Returns the first child of a parent node.
+.TP
+\fBBlt_TreeLastChild\fR
+Returns the last child of a parent node.
+.TP
+\fBBlt_TreeNextSibling\fR
+Returns the next sibling node in the parent's list of children.
+.TP
+\fBBlt_TreePrevSibling\fR
+Returns the previous sibling node in the parent's list of children.
+.TP
+\fBBlt_TreeRootNode\fR
+Returns the root node of the tree.
+.TP
+\fBBlt_TreeNextNode\fR
+Returns the next node in depth-first order.
+.TP
+\fBBlt_TreePrevNode\fR
+Returns the previous node in depth-first order.
+.TP
+\fBBlt_TreeEndNode\fR
+Returns the last node in the tree as determined by depth-first order.
+.TP
+\fBBlt_TreeApply\fR
+Walks through a node and all it descendants, applying a given
+callback procedure.
+.TP
+\fBBlt_TreeApplyDFS\fR
+Walks through a node and all it descendants in depth-first search
+order, applying a given callback procedure.
+.TP
+\fBBlt_TreeApplyBFS\fR
+Walks through a node and all it descendants in breadth-first search
+order, applying a given callback procedure.
+.SH NODE DATA VALUES
+Data values can be stored at any node. Values have by both a string
+key and a Tcl_Obj value. Data value keys do not have to be homogenous
+across all nodes (i.e. nodes do not have to contain the same keys).
+There is also a special node array data type.
+.TP 2i
+\fBBlt_TreeGetValue\fR
+Gets the node data value given by a key.
+.TP
+\fBBlt_TreeValueExists\fR
+Indicates if a node data value given by a key exists.
+.TP
+\fBBlt_TreeSetValue\fR
+Sets a node's value of a key.
+.TP
+\fBBlt_TreeUnsetValue\fR
+Remove the node data value and key.
+.TP
+\fBBlt_TreeGetArrayValue\fR
+Gets the node data array value given by a key and an array index.
+.TP
+\fBBlt_TreeSetArrayValue\fR
+Sets the node data array value given by a key and an array index.
+.TP
+\fBBlt_TreeUnsetArrayValue\fR
+Remove the node data array value.
+.TP
+\fBBlt_TreeArrayValueExists\fR
+Determines if an array element by a given index exists.
+.TP
+\fBBlt_TreeFirstKey\fR
+Returns the key of the first value in the node.
+.TP
+\fBBlt_TreeNextKey\fR
+Returns the key of the next value in the node.
+.TP
+\fBBlt_TreePrivateValue\fR
+Lock the value to current client, making it private.
+.TP
+\fBBlt_TreePublicValue\fR
+Unlock the value so that all clients can access it.
+.TP
+\fBBlt_TreeGetKey\fR
+.SH NODE TRACES
+.TP 2i
+\fBBlt_TreeCreateTrace\fR
+Sets up a trace callback to be invoked when the node value is
+read, set, or unset.
+.TP
+\fBBlt_TreeDeleteTrace\fR
+Deletes an existing trace.
+.SH NODE EVENTS
+.TP 2i
+\fBBlt_TreeCreateEventHandler\fR
+Sets up a callback to be invoked when events (create, delete,
+relabel, etc) take place on a node.
+.TP
+\fBBlt_TreeDeleteEventHandler\fR
+Deletes an existing node callback.
+.SH KEYWORDS
+alloc, allocation, free, malloc, memory, realloc
+
diff --git a/blt/man/Blt_TreeCreate.man3 b/blt/man/Blt_TreeCreate.man3
new file mode 100644
index 00000000000..fd45537adec
--- /dev/null
+++ b/blt/man/Blt_TreeCreate.man3
@@ -0,0 +1,100 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeCreate 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeCreate \- Create tree data object.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+int
+\fBBlt_TreeCreate\fR(\fIinterp\fR, \fIname\fR, \fItokenPtr\fR)
+.SH ARGUMENTS
+.AS Tcl_Interp *interp
+.AP Tcl_Interp *interp in
+Interpreter to report results back to.
+.AP "const char" *name in
+Name of the new tree. Can be qualified by a namespace.
+.AP Blt_Tree *tokenPtr out
+If not NULL, points to location to store the client tree token.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure creates a C-based tree data object and optionally
+returns a token to it. The arguments are as follows:
+.TP 1i
+\fIinterp\fR
+Interpreter to report results back to. If an error occurs, then
+interp->result will contain an error message.
+.TP 1i
+\fIname\fR
+Name of the new tree object. You can think of \fIname\fR as
+the memory address of the object. It's a unique name that identifies
+the tree object. No tree object \fIname\fR
+can already exist. \fIName\fR can be qualified by a namespace such
+as \f(CWfred::myTree\fR. If no namespace qualifier is used, the tree
+will be created in the current namespace, not the global namespace.
+If a qualifier is present, the namespace must already exist.
+.TP 1i
+\fItokenPtr\fR
+Holds the returned token. \fITokenPtr\fR points to a location
+where it is stored. Tree tokens are used to work with the tree object.
+If NULL, no token is allocated. You can later use
+\fBTcl_TreeGetToken\fR to obtain a token.
+.PP
+The new tree data object created will initially contain only a root
+node. You can add new nodes with \fBBlt_TreeCreateNode\fR.
+.PP
+Optionally a token for the tree data object is returned. Tree data
+objects can be shared. For example, the \fBtree\fR and
+\fBhiertable\fR commands may be accessing the same tree data object.
+Each client grabs a token that is associated with the tree. When all
+tokens are released (see \fBBlt_TreeReleaseToken\fR) the tree data
+object is automatically destroyed.
+.PP
+.SH RETURNS
+A standard Tcl result is returned. If TCL_ERROR is returned, then
+\fIinterp->result\fR will contain an error message. The following
+errors may occur:
+.IP \(bu 3
+There already exists a tree by the same name as \fIname\fR. You can
+use \fBTcl_TreeExists\fR to determine if a tree exists beforehand.
+.IP \(bu
+The tree name is prefixed by a namespace that doesn't exist. If you
+qualified the tree name with a namespace, the namespace must exist.
+Unlike Tcl procs and variables, the namespace is not automatically
+created for you.
+.IP \(bu
+Memory can't be allocated for the tree or token.
+.SH EXAMPLE
+The following example creates a new
+.CS
+Blt_Tree token;
+
+if (Blt_TreeCreate(interp, "myTree", &token) != TCL_OK) {
+ return TCL_ERROR;
+}
+printf("tree is %s\\n", Blt_TreeName(token));
+.CE
+.SH KEYWORDS
+Tcl_TreeGetToken, Tcl_TreeExists, Tcl_TreeReleaseToken
diff --git a/blt/man/Blt_TreeCreateNode.man3 b/blt/man/Blt_TreeCreateNode.man3
new file mode 100644
index 00000000000..2e8b6bb966d
--- /dev/null
+++ b/blt/man/Blt_TreeCreateNode.man3
@@ -0,0 +1,95 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeCreateNode 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeCreateNode \- Creates a node in a tree data object.
+.SH SYNOPSIS
+.nf
+#include <bltTree.h>
+.sp
+Blt_TreeNode
+\fBBlt_TreeCreateNode\fR(\fItree\fR, \fIparent\fR, \fIname\fR, \fIposition\fR)
+.SH ARGUMENTS
+.AS Blt_TreeNode parent
+.AP Blt_Tree tree in
+Tree containing the parent node.
+.AP Blt_TreeNode parent in
+Node in which to insert the new child.
+.AP "const char" *name in
+Node label. If NULL, a label will automatically be generated.
+.AP int position in
+Position in the parent's list of children to insert the new node.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure creates a new node is a tree data object. The node
+is initially empty, but data values can be added with
+\fBBlt_TreeSetValue\fR. Each node has a serial number that identifies it
+within the tree. No two nodes in the same tree will ever have the
+same ID. You can find a node's ID with \fBBlt_TreeNodeId\fR.
+.PP
+The arguments are as follows:
+.TP 1i
+\fItree\fR
+The tree containing the parent node.
+.TP
+\fIparent\fR
+Node in which the new child will be inserted.
+.TP
+\fIname\fR
+Label of the new node. If \fIname\fR is NULL, a label in the
+form "\f(CWnode0\fR", "\f(CWnode1\fR", etc. will automatically be
+generated. \fIName\fR can be any string. Labels are non-unique. A
+parent can contain two nodes with the same label. Nodes can be
+relabeled using \fBBlt_TreeRelabelNode\fR.
+.TP
+\fIposition\fR
+Position the parent's list of children to insert the new node. For
+example, if \fIposition\fR is 0, then the new node is prepended to the
+beginning of the list. If \fIposition\fR is -1, then the node is
+appended onto the end of the parent's list.
+.PP
+.SH RETURNS
+The new node returned is of type \fBBlt_TreeNode\fR. It's a token
+that can be used with other routines to add/delete data values or
+children nodes.
+.SH EXAMPLE
+The following example creates a new node from the root node.
+.CS
+Blt_Tree token;
+Blt_TreeNode root, node;
+
+if (Blt_TreeGetToken(interp, "myTree", &token) != TCL_OK) {
+ return TCL_ERROR;
+}
+root = Blt_TreeRootNode(token);
+node = Blt_TreeCreateNode(token, root, "myNode", -1);
+.CE
+.SH NOTIFICATIONS
+\fBBlt_TreeCreateNode\fR can trigger tree notify events.
+You can be notified whenever a node is created by using the
+\fBBlt_TreeCreateNotifyHandler\fR. A callback routine is registered
+that will be automatically invoked whenever a new node is added
+via \fBBlt_TreeCreateNode\fR to the tree.
+.SH KEYWORDS
+tree, token
diff --git a/blt/man/Blt_TreeDeleteNode.man3 b/blt/man/Blt_TreeDeleteNode.man3
new file mode 100644
index 00000000000..565c5ec33ae
--- /dev/null
+++ b/blt/man/Blt_TreeDeleteNode.man3
@@ -0,0 +1,75 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeDeleteNode 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeDeleteNode \- Deletes a node and its descendants.
+.SH SYNOPSIS
+.nf
+#include <bltTree.h>
+.sp
+Blt_TreeNode
+\fBBlt_TreeDeleteNode\fR(\fItree\fR, \fInode\fR)
+.SH ARGUMENTS
+.AS Blt_TreeNode node
+.AP Blt_Tree tree in
+Tree containing the node.
+.AP Blt_TreeNode node in
+Node to be deleted.
+.BE
+.SH DESCRIPTION
+This procedure deletes a given node and all it descendants from a tree
+data object.
+.PP
+The arguments are as follows:
+.TP 1i
+\fItree\fR
+The tree containing the parent node.
+.TP
+\fInode\fR
+Node to be deleted. The node and its descendant nodes are deleted.
+Each node's data values are deleted also. The reference count of
+the Tcl_Obj is decremented.
+.PP
+Since all tree objects must contain at least a root node, the root
+node itself can't be deleted unless the tree is released and
+destroyed. Therefore you can clear a tree by deleting its root, but
+the root node will remain until the tree is destroyed.
+.SH RETURNS
+Always returns TCL_OK. Errors generated in a notification callbacks
+are backgrounded (see \fBTcl_TreeCreateNotifyHandler\fR).
+.SH EXAMPLE
+The following example deletes the root node.
+.CS
+Blt_TreeNode root;
+
+root = Blt_TreeRootNode(token);
+Blt_TreeDeleteNode(token, root);
+.CE
+.SH NOTIFICATIONS
+\fBBlt_TreeDeleteNode\fR can trigger tree notify events.
+You can be notified whenever a node is deleted by using the
+\fBBlt_TreeCreateNotifyHandler\fR. A callback routine is registered
+that will be automatically invoked whenever a node is deleted
+via \fBBlt_TreeDeleteNode\fR to the tree.
+.SH KEYWORDS
+tree, token
diff --git a/blt/man/Blt_TreeExists.man3 b/blt/man/Blt_TreeExists.man3
new file mode 100644
index 00000000000..70b257a97b1
--- /dev/null
+++ b/blt/man/Blt_TreeExists.man3
@@ -0,0 +1,66 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeExists 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeExists \- Indicates if a tree exists.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+int
+\fBBlt_TreeExists\fR(\fIinterp\fR, \fIname\fR)
+.SH ARGUMENTS
+.AS Tcl_Interp *interp
+.AP Tcl_Interp *interp in
+Interpreter to determine current namespace context.
+.AP "const char" *name in
+Name of an existing tree data object. Can be qualified by a namespace.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure determines if a C-based tree data object exists by
+a given name. The arguments are as follows:
+.TP 1i
+interp
+Used the determine the current namespace context.
+.TP 1i
+name
+Name of an existing tree data object. \fIName\fR can be qualified by
+a namespace such as \f(CWfred::myTree\fR. If no namespace qualifier
+is used, the current namespace is searched, then the global namespace.
+.PP
+.SH RETURNS
+A boolean result is returned. If the tree exists 1 is returned,
+0 otherwise.
+.SH EXAMPLE
+The following example checks if a tree "myTree" exists.
+.CS
+.ft CW
+if (!Blt_TreeExists(interp, "myTree")) {
+ fprintf(stderr, "can't find tree \\"myTree\\\\n");
+}
+.ft R
+.CE
+.SH KEYWORDS
+tree, token
+Tcl_TreeCreate, Tcl_TreeGetToken, Tcl_TreeReleaseToken \ No newline at end of file
diff --git a/blt/man/Blt_TreeGetNode.man3 b/blt/man/Blt_TreeGetNode.man3
new file mode 100644
index 00000000000..16be9c38d2c
--- /dev/null
+++ b/blt/man/Blt_TreeGetNode.man3
@@ -0,0 +1,69 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeGetNode 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeGetNode \- Finds the node from the ID.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+Blt_TreeNode
+\fBBlt_TreeGetNode\fR(\fItree\fR, \fInumber\fR)
+.SH ARGUMENTS
+.AS "unsigned int" number
+.AP Blt_Tree tree in
+Tree containing the requested node.
+.AP "unsigned int" number in
+Serial number of the requested node.
+.BE
+.SH DESCRIPTION
+This procedure returns a node in a tree object
+based upon a give serial number.
+The node is searched using the serial number.
+.PP
+The arguments are as follows:
+.TP 1i
+\fItree\fR
+The tree containing the requested node.
+.TP 1i
+\fInumber\fR
+The serial number of the requested node.
+.SH RETURNS
+The node represented by the given serial number is returned. If no
+node with that ID exists in \fItree\fR then NULL is returned.
+.SH EXAMPLE
+The following example gets the node from a serial number.
+.CS
+unsigned int number;
+Blt_TreeNode node;
+Blt_TreeToken token;
+...
+node = Blt_TreeGetNode(token, number);
+if (node == NULL) {
+ printf("no node with ID %d exists\\n", number);
+} else {
+ printf("node found: label is %s\\n", Blt_TreeNodeLabel(node));
+}
+.CE
+.SH KEYWORDS
+Tcl_TreeCreateNode, Tcl_TreeDeleteNode
diff --git a/blt/man/Blt_TreeGetToken.man3 b/blt/man/Blt_TreeGetToken.man3
new file mode 100644
index 00000000000..e685db98250
--- /dev/null
+++ b/blt/man/Blt_TreeGetToken.man3
@@ -0,0 +1,88 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeGetToken 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeGetToken \- Grabs a token associated with existing tree data object.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+int
+\fBBlt_TreeGetToken\fR(\fIinterp\fR, \fIname\fR, \fItokenPtr\fR)
+.SH ARGUMENTS
+.AS Tcl_Interp *interp
+.AP Tcl_Interp *interp in
+Interpreter to report results back to.
+.AP "const char" *name in
+Name of an existing tree data object. Can be qualified by a namespace.
+.AP Blt_Tree *tokenPtr out
+Points to location to store the client tree token.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure obtains a token to a C-based tree data object. The
+arguments are as follows:
+.TP 1i
+\fIinterp\fR
+Interpreter to report results back to. If an error occurs, then
+interp->result will contain an error message.
+.TP 1i
+\fIname\fR
+Name of an existing tree data object. It's an error if a tree
+\fIname\fR doesn't already exist. \fIName\fR can be qualified by
+a namespace such as \f(CWfred::myTree\fR. If no namespace qualifier
+is used, the tree the current namespace is searched, then the global
+namespace.
+.TP 1i
+\fItokenPtr\fR
+Points to the location where the returned token is stored. A tree
+token is used to work with the tree object.
+.PP
+A token for the tree data object is returned. Tree data objects can
+be shared. For example, the \fBtree\fR and \fBhiertable\fR commands
+may be accessing the same tree data object. Each client grabs a token
+that is associated with the tree. When all tokens are released (see
+\fBBlt_TreeReleaseToken\fR) the tree data object is automatically
+destroyed.
+.PP
+.SH RETURNS
+A standard Tcl result is returned. If TCL_ERROR is returned, then
+\fIinterp->result\fR will contain an error message. The following errors
+may occur:
+.IP \(bu 3
+No tree exists as \fIname\fR. You can use \fBTcl_TreeExists\fR to
+determine if a tree exists beforehand.
+.IP \(bu
+Memory can't be allocated for the token.
+.SH EXAMPLE
+The following example allocated a token for an existing tree.
+.CS
+Blt_Tree token;
+
+if (Blt_TreeGetToken(interp, "myTree", &token) != TCL_OK) {
+ return TCL_ERROR;
+}
+printf("tree is %s\\n", Blt_TreeName(token));
+.CE
+.SH SEE ALSO
+Tcl_TreeCreate, Tcl_TreeExists, Tcl_TreeReleaseToken
diff --git a/blt/man/Blt_TreeName.man3 b/blt/man/Blt_TreeName.man3
new file mode 100644
index 00000000000..04dca8dff0b
--- /dev/null
+++ b/blt/man/Blt_TreeName.man3
@@ -0,0 +1,59 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeName 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeName \- Returns the name of the tree data object.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+char *
+\fBBlt_TreeName\fR(\fItree\fR)
+.SH ARGUMENTS
+.AS Blt_Tree tree
+.AP Blt_Tree tree in
+Token for the tree object.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure returns the name of the C-based tree data object.
+The arguments are as follows:
+.TP 1i
+\fItree\fR
+Token for the tree object. The token must have been previously
+obtained via \fBBlt_TreeGetToken\fR or \fBBlt_TreeCreate\fR.
+.SH RETURNS
+The name of the tree object is returned. The name will be fully
+qualified with a namespace context.
+.SH EXAMPLE
+The following example prints the name of the new tree.
+.CS
+Blt_Tree token;
+
+if (Blt_TreeCreate(interp, NULL, &token) != TCL_OK) {
+ return TCL_ERROR;
+}
+printf("tree is %s\\n", Blt_TreeName(token));
+.CE
+.SH KEYWORDS
+Tcl_TreeGetToken, Tcl_TreeExists, Tcl_TreeReleaseToken
diff --git a/blt/man/Blt_TreeNodeId.man3 b/blt/man/Blt_TreeNodeId.man3
new file mode 100644
index 00000000000..44fc4ef4fd8
--- /dev/null
+++ b/blt/man/Blt_TreeNodeId.man3
@@ -0,0 +1,58 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeNodeId 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeNodeId \- Returns the node serial number.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+unsigned int
+\fBBlt_TreeNodeId\fR(\fInode\fR)
+.SH ARGUMENTS
+.AS Blt_TreeNode node
+.AP Blt_TreeNode node in
+Node whose ID is to be returned.
+.BE
+.SH DESCRIPTION
+This procedure returns the node serial number. The node serial number
+is useful for programs that export the tree data object to the Tcl
+programming level. Since node labels (and therefore pathnames) are
+not unique, the ID can be used to uniquely identify a node.
+.PP
+The arguments are as follows:
+.TP 1i
+\fInode\fR
+The node whose serial number is returned. The serial number of
+the root node for example is always 0.
+.SH RETURNS
+The serial number of the node. Nodes are given a unique serial number
+when they are created. You can use the ID to later retrieve the node
+using \fBBlt_TreeGetNode\fR.
+.SH EXAMPLE
+The following example prints the ID of a node.
+.CS
+printf("root ID is %s\\n", Blt_TreeNodeId(node));
+.CE
+.SH KEYWORDS
+Tcl_TreeCreateNode, Tcl_TreeDeleteNode
diff --git a/blt/man/Blt_TreeReleaseToken.man3 b/blt/man/Blt_TreeReleaseToken.man3
new file mode 100644
index 00000000000..e0bd7c38496
--- /dev/null
+++ b/blt/man/Blt_TreeReleaseToken.man3
@@ -0,0 +1,64 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH Blt_TreeReleaseToken 3 BLT_VERSION BLT "BLT Library Procedures"
+.BS
+.SH NAME
+Blt_TreeReleaseToken \- Releases token associated with tree object.
+.SH SYNOPSIS
+.nf
+\fB#include <bltTree.h>\fR
+.sp
+int
+\fBBlt_TreeReleaseToken\fR(\fItoken\fR)
+.SH ARGUMENTS
+.AS Blt_Tree token
+.AP Blt_Tree *token in
+Token of tree to be released.
+.BE
+.SH DESCRIPTION
+.PP
+This procedure releases the token associated with a C-based tree data
+object. When all outstanding tokens for a tree data object have been
+released, then the data object itself will be freed. The arguments
+are as follows:
+.TP 1i
+token
+Token of the tree data object to be released. This token was
+initialized either by \fBTcl_TreeGetToken\fI or \fIBlt_TreeCreate\fR
+earlier.
+.SH RETURNS
+Nothing.
+.SH EXAMPLE
+The following example creates and then releases a new token.
+.CS
+Blt_Tree token;
+
+if (Blt_TreeCreate(interp, "myTree", &token) != TCL_OK) {
+ return TCL_ERROR;
+}
+printf("tree is %s\\n", Blt_TreeName(token));
+
+/* Tree will be destroyed when the token is released. */
+Blt_TreeReleaseToken(token);
+.CE
+.SH KEYWORDS
+tree, token
diff --git a/blt/man/Makefile.in b/blt/man/Makefile.in
new file mode 100644
index 00000000000..39ed98cf2b8
--- /dev/null
+++ b/blt/man/Makefile.in
@@ -0,0 +1,74 @@
+# ------------------------------------------------------------------------
+# Makefile for manual page files
+# ------------------------------------------------------------------------
+
+prefix = @prefix@
+mandir = @mandir@
+sectiondir = $(mandir)/mann
+srcdir = @srcdir@
+version = @BLT_VERSION@
+
+instdirs = $(mandir) $(mandir)/mann $(mandir)/man3
+
+MAN_N = BLT.n barchart.n beep.n bgexec.n bitmap.n \
+ bltdebug.n busy.n container.n cutbuffer.n \
+ dragdrop.n eps.n graph.n hierbox.n \
+ hiertable.n htext.n spline.n stripchart.n \
+ table.n tabset.n tile.n tree.n treeview.n vector.n \
+ watch.n winop.n
+
+MAN_3 = Blt_Tree.3 Blt_TreeGetNode.3 \
+ Blt_TreeCreate.3 Blt_TreeGetToken.3 \
+ Blt_TreeCreateNode.3 Blt_TreeName.3 \
+ Blt_TreeDeleteNode.3 Blt_TreeNodeId.3 \
+ Blt_TreeExists.3 Blt_TreeReleaseToken.3
+
+MANPAGES = $(MAN_N) $(MAN_3)
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+SHELL = /bin/sh
+RM = rm -rf
+VPATH = $(srcdir)
+
+all: man.macros $(MANPAGES)
+
+install: mkdirs install-mann install-man3
+
+install-mann: $(MAN_N)
+ for i in *.n ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(mandir)/mann; \
+ done
+
+install-man3: $(MAN_3)
+ for i in *.3 ; do \
+ $(INSTALL_DATA) $$i $(INSTALL_ROOT)$(mandir)/man3; \
+ done
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)$$i ; then \
+ : ; \
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)$$i ; \
+ fi ; \
+ done
+
+.SUFFIXES: .n .mann .3 .man3
+
+.man3.3: $(srcdir)/man.macros
+ $(RM) $@
+ sed -e "/man\.macros/r $(srcdir)/man.macros" -e '/man\.macros/d' -e 's/BLT_VERSION/$(version)/' $< > $@
+
+.mann.n: $(srcdir)/man.macros
+ $(RM) $@
+ sed -e "/man\.macros/r $(srcdir)/man.macros" -e '/man\.macros/d' -e 's/BLT_VERSION/$(version)/' $< > $@
+
+clean:
+ $(RM) *.3 *.n
+
+distclean: clean
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"* Makefile
+
diff --git a/blt/man/barchart.mann b/blt/man/barchart.mann
new file mode 100644
index 00000000000..26bbad22099
--- /dev/null
+++ b/blt/man/barchart.mann
@@ -0,0 +1,2236 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Barchart widget created by Sani Nassif and George Howlett.
+'\"
+.so man.macros
+.TH barchart n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+barchart \- Bar chart for plotting X-Y coordinate data.
+.SH SYNOPSIS
+\fBbarchart\fI \fIpathName \fR?\fIoption value\fR?...
+.BE
+.SH DESCRIPTION
+The \fBbarchart\fR command creates a bar chart for plotting
+two-dimensional data (X-Y coordinates). A bar chart is a graphic means
+of comparing numbers by displaying bars of lengths proportional to the
+y-coordinates of the points they represented. The bar chart has many
+configurable components: coordinate axes, elements, legend, grid
+lines, cross hairs, etc. They allow you to customize the look and
+feel of the graph.
+.SH INTRODUCTION
+The \fBbarchart\fR command creates a new window for plotting
+two-dimensional data (X-Y coordinates), using bars of
+various lengths to represent the data points. The bars are drawn in a
+rectangular area displayed in the center of the new window. This is the
+\fIplotting area\fR. The coordinate axes are drawn in
+the margins surrounding the plotting area. By default, the legend is
+drawn in the right margin. The title is displayed in top margin.
+.PP
+A \fBbarchart\fR widget has several configurable components:
+coordinate axes, data elements, legend, grid, cross hairs, pens,
+postscript, and annotation markers. Each component can be queried or
+modified.
+.TP 1i
+\f(CWaxis\fR
+
+Up to four coordinate axes (two X\-coordinate and two Y\-coordinate
+axes) can be displayed, but you can create and use any number of
+axes. Axes control what region of data is displayed and how the data
+is scaled. Each axis consists of the axis line, title, major and minor
+ticks, and tick labels. Tick labels display the value at each major
+tick.
+.TP 1i
+\f(CWcrosshairs\fR
+Cross hairs are used to position the mouse pointer relative to the X
+and Y coordinate axes. Two perpendicular lines, intersecting at the
+current location of the mouse, extend across the plotting area to the
+coordinate axes.
+.TP 1i
+\f(CWelement\fR
+An element represents a set of data to be plotted. It contains an x
+and y vector of values representing the data points. Each
+data point is displayed as a bar where the length of the bar is
+proportional to the ordinate (Y-coordinate) of the data point.
+The appearance of the bar, such as its color, stipple, or relief
+is configurable.
+.sp
+A special case exists when two or more data points have the same
+abscissa (X-coordinate). By default, the bars are overlayed, one on
+top of the other. The bars are drawn in the order of the element
+display list. But you can also configure the bars to be displayed in
+two other ways. They may be displayed as a stack, where each bar
+(with the same abscissa) is stacked on the previous. Or they can be
+drawn side-by-side as thin bars. The width of each bar is a function
+of the number of data points with the same abscissa.
+.TP 1i
+\f(CWgrid\fR
+Extends the major and minor ticks of the X\-axis and/or Y\-axis across the
+plotting area.
+.TP 1i
+\f(CWlegend\fR
+The legend displays the name and symbol of each data element.
+The legend can be drawn in any margin or in the plotting area.
+.TP 1i
+\f(CWmarker\fR
+Markers are used annotate or highlight areas of the graph. For
+example, you could use a text marker to label a particular data
+point. Markers come in various forms: text strings, bitmaps, connected
+line segments, images, polygons, or embedded widgets.
+.TP 1i
+\f(CWpen\fR
+Pens define attributes for elements. Data elements use pens to
+specify how they should be drawn. A data element may use many pens at
+once. Here the particular pen used for a data point is determined
+from each element's weight vector (see the element's \fB\-weight\fR
+and \fB\-style\fR options).
+.TP 1i
+\f(CWpostscript\fR
+The widget can generate encapsulated PostScript output. This component
+has several options to configure how the PostScript is generated.
+.SH SYNTAX
+.DS
+\fBbarchart \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBbarchart\fR command creates a new window \fIpathName\fR and makes
+it into a \fBbarchart\fR widget. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist. Additional options may be specified on the
+command line or in the option database to configure aspects of the
+graph such as its colors and font. See the \fBconfigure\fR operation
+below for the exact details about what \fIoption\fR and \fIvalue\fR
+pairs are valid.
+.PP
+If successful, \fBbarchart\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to invoke various operations that query or modify the graph.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for the graph are described in
+the
+.SB "BARCHART OPERATIONS"
+section.
+.PP
+The command can also be used to access components of the graph.
+.DS
+\fIpathName component operation\fR ?\fIarg\fR?...
+.DE
+The operation, now located after the name of the component, is the
+function to be performed on that component. Each component has its own
+set of operations that manipulate that component. They will be
+described below in their own sections.
+.SH EXAMPLE
+The \fBbarchart\fR command creates a new bar chart.
+.CS
+# Create a new bar chart. Plotting area is black.
+barchart .b -plotbackground black
+.CE
+A new Tcl command \f(CW.b\fR is created. This command can be used
+to query and modify the bar chart. For
+example, to change the title of the graph to "My Plot", you use the
+new command and the \fBconfigure\fR operation.
+.CS
+# Change the title.
+\&.b configure -title "My Plot"
+.CE
+To add data elements, you use the command and the \fBelement\fR component.
+.CS
+# Create a new element named "e1"
+\&.b element create e1 \\
+ -xdata { 1 2 3 4 5 6 7 8 9 10 } \\
+ -ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14
+ 155.85 166.60 175.38 }
+.CE
+The element's X-Y coordinates are specified using lists of
+numbers. Alternately, BLT vectors could be used to hold the X-Y
+coordinates.
+.CS
+# Create two vectors and add them to the barchart.
+vector xVector yVector
+xVector set { 1 2 3 4 5 6 7 8 9 10 }
+yVector set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85
+ 166.60 175.38 }
+\&n.b element create e1 -xdata xVector -ydata yVector
+.CE
+The advantage of using vectors is that when you modify one, the graph
+is automatically redrawn to reflect the new values.
+.CS
+# Change the y coordinate of the first point.
+set yVector(0) 25.18
+.CE
+An element named \f(CWe1\fR is now created in \f(CW.b\fR. It
+is automatically added to the display list of elements. You can
+use this list to control in what order elements are displayed.
+To query or reset the element display list, you use the element's
+\fBshow\fR operation.
+.CS
+# Get the current display list
+set elemList [.b element show]
+# Remove the first element so it won't be displayed.
+\&.b element show [lrange $elemList 0 end]
+.CE
+The element will be displayed by as many bars as there are data points
+(in this case there are ten). The bars will be drawn centered at the
+x-coordinate of the data point. All the bars will have the same
+attributes (colors, stipple, etc). The width of each bar is by
+default one unit. You can change this with using the \fB\-barwidth\fR
+option.
+.CS
+# Change the scale of the x-coordinate data
+xVector set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }
+# Make sure we change the bar width too.
+\&.b configure -barwidth 0.2
+.CE
+The height of each bar is proportional to the ordinate (Y-coordinate)
+of the data point.
+.PP
+If two or more data points have the same abscissa (X-coordinate
+value), the bars representing those data points may be drawn in
+various ways.
+The default is to overlay the bars, one on top of the other.
+The ordering is determined from the of element display list. If
+the stacked mode is selected (using the \fB\-barmode\fR configuration
+option), the bars are stacked, each bar above the previous.
+.CS
+# Display the elements as stacked.
+\&.b configure -barmode stacked
+.CE
+If the aligned mode is selected, the bars having the same
+x-coordinates are displayed side by side. The width of each bar is a
+fraction of its normal width, based upon the number of bars with the
+same x-coordinate.
+.CS
+# Display the elements side-by-side.
+\&.b configure -barmode aligned
+.CE
+By default, the element's label in the legend will be also
+\f(CWe1\fR. You can change the label, or specify no legend entry,
+again using the element's \fBconfigure\fR operation.
+.CS
+# Don't display "e1" in the legend.
+\&.b element configure e1 -label ""
+.CE
+You can configure more than just the element's label. An element has
+many attributes such as stipple, foreground and background colors,
+relief, etc.
+.CS
+\&.b element configure e1 -fg red -bg pink \\
+ -stipple gray50
+.CE
+Four coordinate axes are automatically created: \f(CWx\fR, \f(CWx2\fR,
+\f(CWy\fR, and \f(CWy2\fR. And by default, elements are mapped onto the
+axes \f(CWx\fR and \f(CWy\fR. This can be changed with the \fB\-mapx\fR
+and \fB\-mapy\fR options.
+.CS
+# Map "e1" on the alternate y axis "y2".
+\&.b element configure e1 -mapy y2
+.CE
+Axes can be configured in many ways too. For example, you change the
+scale of the Y\-axis from linear to log using the \fBaxis\fR component.
+.CS
+# Y-axis is log scale.
+\&.b axis configure y -logscale yes
+.CE
+One important way axes are used is to zoom in on a particular data
+region. Zooming is done by simply specifying new axis limits using
+the \fB\-min\fR and \fB\-max\fR configuration options.
+.CS
+\&.b axis configure x \-min 1.0 \-max 1.5
+\&.b axis configure y \-min 12.0 \-max 55.15
+.CE
+To zoom interactively, you link the\fBaxis configure\fR operations with
+some user interaction (such as pressing the mouse button), using the
+\fBbind\fR command. To convert between screen and graph coordinates,
+use the \fBinvtransform\fR operation.
+.CS
+# Click the button to set a new minimum
+bind .b <ButtonPress-1> {
+ %W axis configure x \-min [%W axis invtransform x %x]
+ %W axis configure x \-min [%W axis invtransform x %y]
+}
+.CE
+By default, the limits of the axis are determined from data values.
+To reset back to the default limits, set the \fB\-min\fR and
+\fB\-max\fR options to the empty value.
+.CS
+# Reset the axes to autoscale again.
+\&.b axis configure x \-min {} \-max {}
+\&.b axis configure y \-min {} \-max {}
+.CE
+By default, the legend is drawn in the right margin. You can
+change this or any legend configuration options using the
+\fBlegend\fR component.
+.CS
+# Configure the legend font, color, and relief
+\&.b legend configure -position left -relief raised \\
+ -font fixed -fg blue
+.CE
+To prevent the legend from being displayed, turn on the \fB\-hide\fR
+option.
+.CS
+# Don't display the legend.
+\&.b legend configure \-hide yes\fR
+.CE
+The \fBbarchart\fR has simple drawing procedures called markers. They can be
+used to highlight or annotate data in the graph. The types of markers
+available are bitmaps, polygons, lines, or windows. Markers can be
+used, for example, to mark or brush points. For example there may be
+a line marker which indicates some low-water value. Markers are created
+using the \fBmarker\fR operation.
+.CS
+# Create a line represent the low water mark at 10.0
+\&.b marker create line -name "low_water" \\
+ -coords { -Inf 10.0 Inf 10.0 } \\
+ -dashes { 2 4 2 } -fg red -bg blue
+.CE
+This creates a line marker named \f(CWlow_water\fR. It will display a
+horizontal line stretching across the plotting area at the
+y-coordinate 10.0. The coordinates "-Inf" and "Inf" indicate the
+relative minimum and maximum of the axis (in this case the x-axis). By
+default, markers are drawn last, on top of the bars. You can change this
+with the \fB\-under\fR option.
+.CS
+# Draw the marker before elements are drawn.
+\&.b marker configure low_water -under yes
+.CE
+You can add cross hairs or grid lines using the \fBcrosshairs\fR and
+\fBgrid\fR components.
+.CS
+# Display both cross hairs and grid lines.
+\&.b crosshairs configure -hide no -color red
+\&.b grid configure -hide no -dashes { 2 2 }
+.CE
+Finally, to get hardcopy of the graph, use the \fBpostscript\fR
+component.
+.CS
+# Print the bar chart into file "file.ps"
+\&.b postscript output file.ps -maxpect yes -decorations no
+.CE
+This generates a file \f(CWfile.ps\fR containing the encapsulated
+PostScript of the graph. The option \fB\-maxpect\fR says to scale the
+plot to the size of the page. Turning off the \fB\-decorations\fR
+option denotes that no borders or color backgrounds should be
+drawn (i.e. the background of the margins, legend, and plotting
+area will be white).
+.SH SYNTAX
+.DS
+\fBbarchart \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBbarchart\fR command creates a new window \fIpathName\fR and makes
+it into a barchart widget. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist. Additional options may may be specified on the
+command line or in the option database to configure aspects of the
+bar chart such as its colors and font. See the \fBconfigure\fR operation
+below for the exact details as to what \fIoption\fR and \fIvalue\fR
+pairs are valid.
+.PP
+If successful, \fBbarchart\fR returns \fIpathName\fR. It also creates a
+new Tcl command \fIpathName\fR. This command may be used to invoke
+various operations to query or modify the bar chart. It has the general
+form:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for the bar chart are described in
+the following section.
+.SH "BARCHART OPERATIONS"
+.TP
+\fIpathName \fBbar \fIelemName \fR?\fIoption value\fR?...
+Creates a new barchart element \fIelemName\fR. It's an
+error if an element \fIelemName\fR already exists.
+See the manual for \fBbarchart\fR for details about
+what \fIoption\fR and \fIvalue\fR pairs are valid.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBconfigure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the graph. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the option \fIoption\fR is set to \fIvalue\fR.
+The following options are valid.
+.RS
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color. This includes the margins and
+legend, but not the plotting area.
+.TP
+\fB\-barmode \fImode\fR
+Indicates how related bar elements will be drawn. Related elements
+have data points with the same abscissas (X-coordinates). \fIMode\fR
+indicates how those segments should be drawn. \fIMode\fR can be
+\f(CWinfront\fR, \f(CWaligned\fR, \f(CWoverlap\fR, or \f(CWstacked\fR.
+The default mode is \f(CWinfront\fR.
+.RS
+.TP 1i
+\f(CWinfront\fR
+Each successive segment is drawn in front of the previous.
+.TP 1i
+\f(CWstacked\fR
+Each successive segment is stacked vertically on top of the previous.
+.TP 1i
+\f(CWaligned\fR
+Segments is displayed aligned from right-to-left.
+.TP 1i
+\f(CWoverlap\fR
+Like \f(CWaligned\fR but segments slightly overlap each other.
+.RE
+.TP
+\fB\-barwidth \fIvalue\fR
+Specifies the width of the bars. This value can be overrided by the
+individual elements using their \fB\-barwidth\fR configuration option.
+\fIValue\fR is the width in terms of graph coordinates. The
+default width is \f(CW1.0\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-bottommargin \fIpixels\fR
+Specifies the size of the margin below the X\-coordinate axis. If
+\fIpixels\fR is \f(CW0\fR, the size of the margin is selected automatically.
+The default is \f(CW0\fR.
+.TP
+\fB\-bufferelements \fIboolean\fR
+Indicates whether an internal pixmap to buffer the display of data
+elements should be used. If \fIboolean\fR is true, data elements are
+drawn to an internal pixmap. This option is especially useful when
+the graph is redrawn frequently while the remains data unchanged (for
+example, moving a marker across the plot). See the
+.SB "SPEED TIPS"
+section.
+The default is \f(CW1\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CWcrosshair\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the graph title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-18-180-*\fR.
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a maximum distance to consider when searching for the
+closest data point (see the element's \fBclosest\fR operation below).
+Data points further than \fIpixels\fR away are ignored. The default is
+\f(CW0.5i\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW4i\fR.
+.TP
+\fB\-invertxy \fIboolean\fR
+Indicates whether the placement X\-axis and Y\-axis should be inverted. If
+\fIboolean\fR is true, the X and Y axes are swapped. The default is
+\f(CW0\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the title should be justified. This matters only when
+the title contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-leftmargin \fIpixels\fR
+Sets the size of the margin from the left edge of the window to
+the Y\-coordinate axis. If \fIpixels\fR is \f(CW0\fR, the size is
+calculated automatically. The default is \f(CW0\fR.
+.TP
+\fB\-plotbackground \fIcolor\fR
+Specifies the background color of the plotting area. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-plotborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the plotting area. The
+\fB\-plotrelief\fR option determines if a border is drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-plotpadx \fIpad\fR
+Sets the amount of padding to be added to the left and right sides of
+the plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the left side of the
+plotting area entry is padded by the first distance and the right side
+by the second. If \fIpad\fR is just one distance, both the left and
+right sides are padded evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotpady \fIpad\fR
+Sets the amount of padding to be added to the top and bottom of the
+plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the top of the plotting
+area is padded by the first distance and the bottom by the second. If
+\fIpad\fR is just one distance, both the top and bottom are padded
+evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotrelief \fIrelief\fR
+Specifies the 3-D effect for the plotting area. \fIRelief\fR
+specifies how the interior of the plotting area should appear relative
+to rest of the graph; for example, \f(CWraised\fR means the plot should
+appear to protrude from the graph, relative to the surface of the
+graph. The default is \f(CWsunken\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the barchart widget. \fIRelief\fR
+specifies how the graph should appear relative to widget it is packed
+into; for example, \f(CWraised\fR means the graph should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-rightmargin \fIpixels\fR
+Sets the size of margin from the plotting area to the right edge of
+the window. By default, the legend is drawn in this margin. If
+\fIpixels\fR is than 1, the margin size is selected automatically.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW""\fR.
+.TP
+\fB\-tile \fIimage\fR
+Specifies a tiled background for the widget. If \fIimage\fR isn't
+\f(CW""\fR, the background is tiled using \fIimage\fR.
+Otherwise, the normal background color is drawn (see the
+\fB\-background\fR option). \fIImage\fR must be an image created
+using the Tk \fBimage\fR command. The default is \f(CW""\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title to \fItext\fR. If \fItext\fR is \f(CW""\fR,
+no title will be displayed.
+.TP
+\fB\-topmargin \fIpixels\fR
+Specifies the size of the margin above the x2 axis. If \fIpixels\fR
+is \f(CW0\fR, the margin size is calculated automatically.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the requested width of the widget. The default is
+\f(CW5i\fR.
+.RE
+.TP
+\fIpathName \fBcrosshairs \fIoperation \fR?\fIarg\fR?
+See the
+.SB "CROSSHAIRS COMPONENT"
+section.
+.TP
+\fIpathName \fBelement \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "ELEMENT COMPONENTS"
+section.
+.TP
+\fIpathName \fBextents \fIitem\fR
+Returns the size of a particular item in the graph. \fIItem\fR must
+be either \f(CWleftmargin\fR, \f(CWrightmargin\fR, \f(CWtopmargin\fR,
+\f(CWbottommargin\fR, \f(CWplotwidth\fR, or \f(CWplotheight\fR.
+.TP
+\fIpathName \fBgrid \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "GRID COMPONENT"
+section.
+.TP
+\fIpathName \fBinvtransform \fIwinX winY\fR
+Performs an inverse coordinate transformation, mapping window
+coordinates back to graph coordinates, using the standard X\-axis and Y\-axis.
+Returns a list of containing the X-Y graph coordinates.
+.TP
+\fIpathName \fBinside \fIx y\fR
+Returns \f(CW1\fR is the designated screen coordinate (\fIx\fR and \fIy\fR)
+is inside the plotting area and \f(CW0\fR otherwise.
+.TP
+\fIpathName \fBlegend \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "LEGEND COMPONENT"
+section.
+.TP
+\fIpathName \fBline\fB operation arg\fR...
+The operation is the same as \fBelement\fR.
+.TP
+\fIpathName \fBmarker \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "MARKER COMPONENTS"
+section.
+.TP
+\fIpathName\fR \fBmetafile\fR ?\fIfileName\fR?
+\fIThis operation is for Window platforms only\fR.
+Creates a Windows enhanced metafile of the barchart.
+If present, \fIfileName\fR is the file name of the new metafile.
+Otherwise, the metafile is automatically added to the clipboard.
+.TP
+\fIpathName \fBpostscript \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "POSTSCRIPT COMPONENT"
+section.
+.TP
+\fIpathName \fBsnap \fIphotoName\fR
+Takes a snapshot of the graph and stores the contents in the photo
+image \fIphotoName\fR. \fIPhotoName\fR is the name of a Tk photo
+image that must already exist.
+.TP
+\fIpathName \fBtransform \fIx y\fR
+Performs a coordinate transformation, mapping graph coordinates to
+window coordinates, using the standard X\-axis and Y\-axis.
+Returns a list containing the X\-Y screen coordinates.
+.TP
+\fIpathName \fBxaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBx2axis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fByaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBy2axis \fIoperation\fR ?\fIarg\fR?...
+See the
+.SB "AXIS COMPONENTS"
+section.
+.SH "BARCHART COMPONENTS"
+A graph is composed of several components: coordinate axes, data
+elements, legend, grid, cross hairs, postscript, and annotation
+markers. Instead of one big set of configuration options and
+operations, the graph is partitioned, where each component has its own
+configuration options and operations that specifically control that
+aspect or part of the graph.
+.SS "AXIS COMPONENTS"
+Four coordinate axes are automatically created: two X\-coordinate axes
+(\f(CWx\fR and \f(CWx2\fR) and two Y\-coordinate axes (\f(CWy\fR, and
+\f(CWy2\fR). By default, the axis \f(CWx\fR is located in the bottom
+margin, \f(CWy\fR in the left margin, \f(CWx2\fR in the top margin, and
+\f(CWy2\fR in the right margin.
+.PP
+An axis consists of the axis line, title, major and minor ticks, and
+tick labels. Major ticks are drawn at uniform intervals along the
+axis. Each tick is labeled with its coordinate value. Minor ticks
+are drawn at uniform intervals within major ticks.
+.PP
+The range of the axis controls what region of data is plotted.
+Data points outside the minimum and maximum limits of the axis are
+not plotted. By default, the minimum and maximum limits are
+determined from the data, but you can reset either limit.
+.PP
+You can create and use several axes. To create an axis, invoke
+the axis component and its create operation.
+.CS
+# Create a new axis called "temperature"
+\&.b axis create temperature
+.CE
+You map data elements to an axis using the element's \-mapy and \-mapx
+configuration options. They specify the coordinate axes an element
+is mapped onto.
+.CS
+# Now map the temperature data to this axis.
+\&.b element create "temp" \-xdata $x \-ydata $tempData \\
+ \-mapy temperature
+.CE
+While you can have many axes, only four axes can be displayed
+simultaneously. They are drawn in each of the margins surrounding
+the plotting area. The axes \f(CWx\fR and \f(CWy\fR are drawn in the
+bottom and left margins. The axes \f(CWx2\fR and \f(CWy2\fR are drawn in
+top and right margins. Only \f(CWx\fR and \f(CWy\fR are shown by
+default. Note that the axes can have different scales.
+.PP
+To display a different axis, you invoke one of the following
+components: \fBxaxis\fR, \fByaxis\fR, \fBx2axis\fR, and \fBy2axis\fR.
+The \fBuse\fR operation designates the axis to be drawn in the
+corresponding margin: \fBxaxis\fR in the bottom, \fByaxis\fR in the left,
+\fBx2axis\fR in the top, and \fBy2axis\fR in the right.
+.CS
+# Display the axis temperature in the left margin.
+\&.b yaxis use temperature
+.CE
+.PP
+You can configure axes in many ways. The axis scale can be linear or
+logarithmic. The values along the axis can either monotonically
+increase or decrease. If you need custom tick labels, you can specify
+a Tcl procedure to format the label any way you wish. You can
+control how ticks are drawn, by changing the major tick interval
+or the number of minor ticks. You can define non-uniform tick intervals,
+such as for time-series plots.
+.PP
+.TP
+\fIpathName \fBaxis \fBcget \fIaxisName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIaxisName\fR. \fIOption\fR may be any option described below
+for the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBconfigure \fIaxisName \fR?\fIaxisName\fR?... ?\fIoption value\fR?...
+Queries or modifies the configuration options of \fIaxisName\fR.
+Several axes can be changed. If \fIoption\fR isn't specified, a list
+describing all the current options for \fIaxisName\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+\fIoption\fR is returned. If one or more \fIoption\fR and \fIvalue\fR
+pairs are specified, then for each pair, the axis option \fIoption\fR
+is set to \fIvalue\fR. The following options are valid for axes.
+.RS
+.TP
+\fB\-autorange \fIrange\fR
+Sets the range of values for the axis to \fIrange\fR. The axis limits
+are automatically reset to display the most recent data points in this range.
+If \fIrange\fR is 0.0, the range is
+determined from the limits of the data. If \fB\-min\fR or \fB-max\fR
+are specified, they override this option. The default is \f(CW0.0\fR.
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the axis and tick labels.
+The default is \f(CWblack\fR.
+.TP
+\fB\-command \fIprefix\fR
+Specifies a Tcl command to be invoked when formatting the axis tick
+labels. \fIPrefix\fR is a string containing the name of a Tcl proc and
+any extra arguments for the procedure. This command is invoked for each
+major tick on the axis. Two additional arguments are passed to the
+procedure: the pathname of the widget and the current the numeric
+value of the tick. The procedure returns the formatted tick label. If
+\f(CW""\fR is returned, no label will appear next to the tick. You can
+get the standard tick labels again by setting \fIprefix\fR to
+\f(CW""\fR. The default is \f(CW""\fR.
+.sp 1
+Please note that this procedure is invoked while the bar chart is redrawn.
+You may query the widget's configuration options. But do not reset
+options, because this can have unexpected results.
+.TP
+\fB\-descending \fIboolean\fR
+Indicates whether the values along the axis are monotonically increasing or
+decreasing. If \fIboolean\fR is true, the axis values will be
+decreasing. The default is \f(CW0\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the axis is displayed.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the axis title should be justified. This matters only
+when the axis title contains more than one line of text. \fIJustify\fR
+must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-limits \fIformatStr\fR
+Specifies a printf-like description to format the minimum and maximum
+limits of the axis. The limits are displayed at the top/bottom or
+left/right sides of the plotting area. \fIFormatStr\fR is a list of
+one or two format descriptions. If one description is supplied, both
+the minimum and maximum limits are formatted in the same way. If two,
+the first designates the format for the minimum limit, the second for
+the maximum. If \f(CW""\fR is given as either description, then
+the that limit will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the axis and tick lines. The default is \f(CW1\fR
+pixel.
+.TP
+\fB\-logscale \fIboolean\fR
+Indicates whether the scale of the axis is logarithmic or linear. If
+\fIboolean\fR is true, the axis is logarithmic. The default scale is
+linear.
+.TP
+\fB\-loose \fIboolean\fR
+Indicates whether the limits of the axis should fit the data points tightly,
+at the outermost data points, or loosely, at the outer tick intervals.
+This is relevant only when the axis limit is automatically calculated.
+If \fIboolean\fR is true, the axis range is "loose".
+The default is \f(CW0\fR.
+.TP
+\fB\-majorticks \fImajorList\fR
+Specifies where to display major axis ticks. You can use this option
+to display ticks at non-uniform intervals. \fIMajorList\fR is a list
+of axis coordinates designating the location of major ticks. No
+minor ticks are drawn. If \fImajorList\fR is \f(CW""\fR,
+major ticks will be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-max \fIvalue\fR
+Sets the maximum limit of \fIaxisName\fR. Any data point greater
+than \fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the maximum limit is calculated using the largest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-min \fIvalue\fR
+Sets the minimum limit of \fIaxisName\fR. Any data point less than
+\fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the minimum limit is calculated using the smallest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-minorticks \fIminorList\fR
+Specifies where to display minor axis ticks. You can use this option
+to display minor ticks at non-uniform intervals. \fIMinorList\fR is a
+list of real values, ranging from 0.0 to 1.0, designating the placement of
+a minor tick. No minor ticks are drawn if the \fB\-majortick\fR
+option is also set. If \fIminorList\fR is \f(CW""\fR, minor ticks will
+be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the how many degrees to rotate the axis tick labels.
+\fITheta\fR is a real value representing the number of degrees
+to rotate the tick labels. The default is \f(CW0.0\fR degrees.
+.TP
+\fB\-shiftby \fIvalue\fR
+Specifies how much to automatically shift the range of the axis.
+When the new data exceeds the current axis maximum, the maximum
+is increased in increments of \fIvalue\fR. You can use this
+option to prevent the axis limits from being recomputed
+at each new time point. If \fIvalue\fR is 0.0, then no automatic
+shifting is down. The default is \f(CW0.0\fR.
+.TP
+\fB\-showticks \fIboolean\fR
+Indicates whether axis ticks should be drawn. If \fIboolean\fR is
+true, ticks are drawn. If false, only the
+axis line is drawn. The default is \f(CW1\fR.
+.TP
+\fB\-stepsize \fIvalue\fR
+Specifies the interval between major axis ticks. If \fIvalue\fR isn't
+a valid interval (must be less than the axis range),
+the request is ignored and the step size is automatically calculated.
+.TP
+\fB\-subdivisions \fInumber\fR
+Indicates how many minor axis ticks are
+to be drawn. For example, if \fInumber\fR is two, only one minor
+tick is drawn. If \fInumber\fR is one, no minor ticks are
+displayed. The default is \f(CW2\fR.
+.TP
+\fB\-tickfont \fIfontName\fR
+Specifies the font for axis tick labels. The default is
+\f(CW*-Courier-Bold-R-Normal-*-100-*\fR.
+.TP
+\fB\-ticklength \fIpixels\fR
+Sets the length of major and minor ticks (minor ticks are half the
+length of major ticks). If \fIpixels\fR is less than zero, the axis
+will be inverted with ticks drawn pointing towards the plot. The
+default is \f(CW0.1i\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title of the axis. If \fItext\fR is
+\f(CW""\fR, no axis title will be displayed.
+.TP
+\fB\-titlecolor \fIcolor\fR
+Sets the color of the axis title. The default is \f(CWblack\fR.
+.TP
+\fB\-titlefont \fIfontName\fR
+Specifies the font for axis title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-14-140-*\fR.
+.PP
+Axis configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWAxis\fR. The resource names
+are the names of the axes (such as \f(CWx\fR or \f(CWx2\fR).
+.CS
+option add *Barchart.Axis.Color blue
+option add *Barchart.x.LogScale true
+option add *Barchart.x2.LogScale false
+.CE
+.RE
+.TP
+\fIpathName \fBaxis \fBcreate \fIaxisName \fR?\fIoption value\fR?...
+Creates a new axis by the name \fIaxisName\fR. No axis by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBdelete \fR?\fIaxisName\fR?...
+Deletes the named axes. An axis is not really
+deleted until it is not longer in use, so it's safe to delete
+axes mapped to elements.
+.TP
+\fIpathName \fBaxis invtransform \fIaxisName value\fR
+Performs the inverse transformation, changing the screen coordinate
+\fIvalue\fR to a graph coordinate, mapping the value mapped to
+\fIaxisName\fR. Returns the graph coordinate.
+.TP
+\fIpathName \fBaxis limits \fIaxisName\fR
+Returns a list of the minimum and maximum limits for \fIaxisName\fR. The order
+of the list is \f(CWmin max\fR.
+.TP
+\fIpathName \fBaxis names \fR?\fIpattern\fR?...
+Returns a list of axes matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all axes are returned.
+.TP
+\fIpathName \fBaxis transform \fIaxisName value\fR
+Transforms the coordinate \fIvalue\fR to a screen coordinate by mapping
+the it to \fIaxisName\fR. Returns the transformed screen coordinate.
+.PP
+Only four axes can be displayed simultaneously. By default, they are
+\f(CWx\fR, \f(CWy\fR, \f(CWx2\fR, and \f(CWy2\fR. You can swap in a different
+axis with \fBuse\fR operation of the special axis components:
+\fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR.
+.CS
+\&.g create axis temp
+\&.g create axis time
+\&...
+\&.g xaxis use temp
+\&.g yaxis use time
+.CE
+Only the axes specified for use are displayed on the screen.
+.PP
+The \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR
+components operate on an axis location rather than a specific axis
+like the more general \fBaxis\fR component does. The \fBxaxis\fR
+component manages the X-axis located in the bottom margin (whatever
+axis that happens to be). Likewise, \fByaxis\fR uses the Y-axis in
+the left margin, \fBx2axis\fR the top X-axis, and \fBy2axis\fR the
+right Y-axis.
+.PP
+They implicitly control the axis that is currently using to that
+location. By default, \fBxaxis\fR uses the \f(CWx\fR axis, \fByaxis\fR
+uses \f(CWy\fR, \fBx2axis\fR uses \f(CWx2\fR, and \fBy2axis\fR uses
+\f(CWy2\fR. These components can be more convenient to use than always
+determining what axes are current being displayed by the graph.
+.PP
+The following operations are available for axes. They mirror exactly
+the operations of the \fBaxis\fR component. The \fIaxis\fR argument
+must be \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, or \fBy2axis\fR.
+.TP
+\fIpathName \fIaxis \fBcget \fIoption\fR
+.TP
+\fIpathName \fIaxis \fBconfigure \fR?\fIoption value\fR?...
+.TP
+\fIpathName \fIaxis\fB invtransform \fIvalue\fR
+.TP
+\fIpathName \fIaxis \fBlimits\fR
+.TP
+\fIpathName \fIaxis\fB transform \fIvalue\fR
+.TP
+\fIpathName \fIaxis\fB use \fR?\fIaxisName\fR?
+Designates the axis \fIaxisName\fR is to be displayed at this
+location. \fIAxisName\fR can not be already in use at another location.
+This command returns the name of the axis currently using this location.
+.SS "CROSSHAIRS COMPONENT"
+Cross hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position
+the mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives.
+This means that they can be quickly drawn and erased without redrawing
+the entire widget.
+.PP
+The following operations are available for cross hairs:
+.TP
+\fIpathName \fBcrosshairs cget \fIoption\fR
+Returns the current value of the cross hairs configuration option
+given by \fIoption\fR. \fIOption\fR may be any option
+described below for the cross hairs \fBconfigure\fR operation.
+.TP
+\fIpathName \fBcrosshairs configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the cross hairs. If
+\fIoption\fR isn't specified, a list describing all the current
+options for the cross hairs is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the cross hairs option \fIoption\fR is set to
+\fIvalue\fR.
+The following options are available for cross hairs.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the cross hairs. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the cross hairs. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the cross hair lines. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the cross hairs will be solid
+lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether cross hairs are drawn. If \fIboolean\fR is true,
+cross hairs are not drawn. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the cross hair lines. The default is \f(CW1\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies the screen position where the cross hairs intersect.
+\fIPos\fR must be in the form "\fI@x,y\fR", where \fIx\fR and \fIy\fR
+are the window coordinates of the intersection.
+.PP
+Cross hairs configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWcrosshairs\fR and \f(CWCrosshairs\fR respectively.
+.CS
+option add *Barchart.Crosshairs.LineWidth 2
+option add *Barchart.Crosshairs.Color red
+.CE
+.RE
+.TP
+\fIpathName \fBcrosshairs off\fR
+Turns off the cross hairs.
+.TP
+\fIpathName \fBcrosshairs on\fR
+Turns on the display of the cross hairs.
+.TP
+\fIpathName \fBcrosshairs toggle\fR
+Toggles the current state of the cross hairs, alternately mapping and
+unmapping the cross hairs.
+.SH "ELEMENTS"
+A data element represents a set of data. It contains x and y vectors
+which are the coordinates of the data points. Elements are displayed
+as bars where the length of the bar is proportional to the ordinate of
+the data point. Elements also control the appearance of the data,
+such as the color, stipple, relief, etc.
+.PP
+When new data elements are created, they are automatically added to a
+list of displayed elements. The display list controls what elements
+are drawn and in what order.
+.PP
+The following operations are available for elements.
+.TP
+\fIpathName \fBelement activate \fIelemName \fR?\fIindex\fR?...
+Specifies the data points of element \fIelemName\fR to be drawn
+using active foreground and background colors. \fIElemName\fR is the
+name of the element and \fIindex\fR is a number representing the index
+of the data point. If no indices are present then all data points
+become active.
+.TP
+\fIpathName \fBelement bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an element with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on graph elements, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBelement cget \fIelemName \fIoption\fR
+Returns the current value of the element configuration option given by
+\fIoption\fR. \fIOption\fR may be any of the options described below
+for the element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement closest \fIx y\fR ?\fIoption value\fR?... ?\fIelemName\fR?...
+Finds the data point representing the bar closest to the window
+coordinates \fIx\fR and \fIy\fR in the element \fIelemName\fR.
+\fIElemName\fR is the name of an element, which must be displayed. If no
+elements are specified, then all displayed elements are searched. It
+returns a list containing the name of the closest element, the index
+of its closest point, and the graph coordinates of the
+point. If no data point within the threshold distance can be found,
+\f(CW""\fR is returned. The following \fIoption\fR-\fIvalue\fR pairs
+are available.
+.RS
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a threshold distance where selected data points are ignored.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+If this option isn't specified, then it defaults to the value of the
+\fBbarchart\fR's \fB\-halo\fR option.
+.RE
+.TP
+\fIpathName \fBelement configure \fIelemName \fR?\fIelemName\fR... ?\fIoption value\fR?...
+Queries or modifies the configuration options for elements. Several
+elements can be modified at the same time. If \fIoption\fR isn't
+specified, a list describing all the current options for
+\fIelemName\fR is returned. If \fIoption\fR is specified, but not
+\fIvalue\fR, then a list describing the option \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the element option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for elements.
+.RS
+.TP
+\fB\-activepen \fIpenName\fR
+Specifies pen to use to draw active element. If \fIpenName\fR is
+\f(CW""\fR, no active elements will be drawn. The default is
+\f(CWactiveLine\fR.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for the element. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events for elements. Each tag in the list matching the current event
+sequence will have its Tcl command executed. Implicitly the name of
+the element is always the first tag in the list. The default value is
+\f(CWall\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the the color of the border around each bar. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-barwidth \fIvalue\fR
+Specifies the width the bars drawn for the element. \fIValue\fR is
+the width in X-coordinates. If this option isn't
+specified, the width of each bar is the value of the widget's
+\fB\-barwidth\fR option.
+.TP
+\fB\-baseline \fIvalue\fR
+Specifies the baseline of the bar segments. This affects how bars are
+drawn since bars are drawn from their respective y-coordinate the
+baseline. By default the baseline is \f(CW0.0\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the border width of the 3-D border drawn around the outside of
+each bar. The \fB\-relief\fR option determines if such a border is
+drawn. \fIPixels\fR must be a valid screen distance like \f(CW2\fR or
+\f(CW0.25i\fR. The default is \f(CW2\fR.
+.TP
+\fB\-data \fIcoordList\fR
+Specifies the X\-Y coordinates of the data. \fICoordList\fR is a
+list of numeric expressions representing the X\-Y coordinate pairs
+of each data point.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the color of the interior of the bars.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the element is displayed. The default is \f(CWno\fR.
+.TP
+\fB\-label \fItext\fR
+Sets the element's label in the legend. If \fItext\fR
+is \f(CW""\fR, the element will have no entry in the legend.
+The default label is the element's name.
+.TP
+\fB\-mapx \fIxAxis\fR
+Selects the X\-axis to map the element's X\-coordinates onto.
+\fIXAxis\fR must be the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Selects the Y\-axis to map the element's Y\-coordinates onto.
+\fIYAxis\fR must be the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-relief \fIstring\fR
+Specifies the 3-D effect desired for bars. \fIRelief\fR indicates how
+the interior of the bar should appear relative to the surface of the
+chart; for example, \f(CWraised\fR means the bar should appear to
+protrude from the surface of the plotting area. The default is
+\f(CWraised\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern with which to draw the bars. If
+\fIbitmap\fR is \f(CW""\fR, then the bar is drawn in a solid fashion.
+.TP
+\fB\-xdata \fIxVector\fR
+Specifies the x-coordinate vector of the data.
+\fIXVector\fR is the name of a BLT vector or a
+list of numeric expressions.
+.TP
+\fB\-ydata \fIyVector\fR
+Specifies the y-coordinate vector of the data.
+\fIYVector\fR is the name of a BLT vector or a list of
+numeric expressions.
+.PP
+Element configuration options may also be set by the
+\fBoption\fR command. The resource names in the option database
+are prefixed by \f(CWelem\fR.
+.CS
+option add *Barchart.Element.background blue
+.CE
+.RE
+.TP
+\fIpathName \fBelement create \fIelemName\fR ?\fIoption value\fR?...
+Creates a new element \fIelemName\fR. Element
+names must be unique, so an element \fIelemName\fR may not already
+exist. If additional arguments are present, they specify any of the
+element options valid for element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement deactivate \fIpattern\fR...
+Deactivates all the elements matching \fIpattern\fR for the graph.
+Elements whose names match any of the patterns given are redrawn
+using their normal colors.
+.TP
+\fIpathName \fBelement delete\fR ?\fIpattern\fR?...
+Deletes all the elements matching \fIpattern\fR for the graph.
+Elements whose names match any of the patterns given are deleted.
+The graph will be redrawn without the deleted elements.
+.TP
+\fIpathName \fBelement exists \fIelemName\fR
+Returns \f(CW1\fR if an element \fIelemName\fR currently exists and
+\f(CW0\fR otherwise.
+.TP
+\fIpathName \fBelement names \fR?\fIpattern\fR?...
+Returns the elements matching one or more pattern. If no
+\fIpattern\fR is given, the names of all elements is returned.
+.TP
+\fIpathName \fBelement show\fR ?\fInameList\fR?
+Queries or modifies the element display list. The element display
+list designates the elements drawn and in what
+order. \fINameList\fR is a list of elements to be displayed in the
+order they are named. If there is no \fInameList\fR argument,
+the current display list is returned.
+.TP
+\fIpathName \fBelement type\fR \fIelemName\fR
+Returns the type of \fIelemName\fR.
+If the element is a bar element, the commands returns the string
+\f(CW"bar"\fR, otherwise it returns \f(CW"line"\fR.
+.CE
+.SS "GRID COMPONENT"
+Grid lines extend from the major and minor ticks of each axis
+horizontally or vertically across the plotting area. The following
+operations are available for grid lines.
+.TP
+\fIpathName \fBgrid cget \fIoption\fR
+Returns the current value of the grid line configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described below
+for the grid \fBconfigure\fR operation.
+.TP
+\fIpathName \fBgrid configure\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for grid lines. If
+\fIoption\fR isn't specified, a list describing all the current
+grid options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the grid line option \fIoption\fR is set to
+\fIvalue\fR. The following options are valid for grid lines.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the grid lines. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the grid lines. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the grid lines. Each number must be between 1 and 255.
+If \fIdashList\fR is \f(CW""\fR, the grid will be solid lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the grid should be drawn. If \fIboolean\fR
+is true, grid lines are not shown. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of grid lines. The default width is \f(CW1\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to display grid lines. \fIXAxis\fR
+must be the name of an axis or \f(CW""\fR for no grid lines.
+The default is \f(CW""\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to display grid lines. \fIYAxis\fR
+must be the name of an axis or \f(CW""\fR for no grid lines.
+The default is \f(CWy\fR.
+.TP
+\fB\-minor \fIboolean\fR
+Indicates whether the grid lines should be drawn for minor ticks.
+If \fIboolean\fR is true, the lines will appear at
+minor tick intervals. The default is \f(CW1\fR.
+.PP
+Grid configuration options may also be set by the
+\fBoption\fR command. The resource name and class are \f(CWgrid\fR and
+\f(CWGrid\fR respectively.
+.CS
+option add *Barchart.grid.LineWidth 2
+option add *Barchart.Grid.Color black
+.CE
+.RE
+.TP
+\fIpathName \fBgrid off\fR
+Turns off the display the grid lines.
+.TP
+\fIpathName \fBgrid on\fR
+Turns on the display the grid lines.
+.TP
+\fIpathName \fBgrid toggle\fR
+Toggles the display of the grid.
+.SS "LEGEND COMPONENT"
+The legend displays a list of the data elements. Each entry consists
+of the element's symbol and label. The legend can appear in any
+margin (the default location is in the right margin). It
+can also be positioned anywhere within the plotting area.
+.PP
+The following operations are valid for the legend.
+.TP
+\fIpathName \fBlegend activate \fIpattern\fR...
+Selects legend entries to be drawn using the active legend colors and relief.
+All entries whose element names match \fIpattern\fR are selected. To
+be selected, the element name must match only one \fIpattern\fR.
+.TP
+\fIpathName \fBlegend bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a legend entry with this
+tag, \fIcommand\fR will be invoked. Implicitly the element names
+in the entry are tags. The syntax is similar to the
+\fBbind\fR command except that it operates on legend entries, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBlegend cget \fIoption\fR
+Returns the current value of a legend configuration option.
+\fIOption\fR may be any option described below in the
+legend \fBconfigure\fR operation.
+.TP
+\fIpathName \fBlegend configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for the legend. If
+\fIoption\fR isn't specified, a list describing the current
+legend options for \fIpathName\fR is returned. If \fIoption\fR is
+specified, but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the legend option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for the legend.
+.RS
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active legend entries. All legend
+entries marked active (see the legend \fBactivate\fR operation) are
+drawn using this background color.
+.TP
+\fB\-activeborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the active legend
+entries. The default is \f(CW2\fR.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color for active legend entries. All legend
+entries marked as active (see the legend \fBactivate\fR operation) are
+drawn using this foreground color.
+.TP
+\fB\-activerelief \fIrelief\fR
+Specifies the 3-D effect desired for active legend entries.
+\fIRelief\fR denotes how the interior of the entry should appear
+relative to the legend; for example, \f(CWraised\fR means the entry
+should appear to protrude from the legend, relative to the surface of
+the legend. The default is \f(CWflat\fR.
+.TP
+\fB\-anchor \fIanchor\fR
+Tells how to position the legend relative to the positioning point for
+the legend. This is dependent on the value of the \fB\-position\fR
+option. The default is \f(CWcenter\fR.
+.RS
+.TP 1.25i
+\f(CWleft\fR or \f(CWright\fR
+The anchor describes how to position the legend vertically.
+.TP
+\f(CWtop\fR or \f(CWbottom\fR
+The anchor describes how to position the legend horizontally.
+.TP
+\f(CW@x,y\fR
+The anchor specifies how to position the legend relative to the
+positioning point. For example, if \fIanchor\fR is \f(CWcenter\fR then
+the legend is centered on the point; if \fIanchor\fR is \f(CWn\fR then
+the legend will be drawn such that the top center point of the
+rectangular region occupied by the legend will be at the positioning
+point.
+.TP
+\f(CWplotarea\fR
+The anchor specifies how to position the legend relative to the
+plotting area. For example, if \fIanchor\fR is \f(CWcenter\fR then the
+legend is centered in the plotting area; if \fIanchor\fR is \f(CWne\fR
+then the legend will be drawn such that occupies the upper right
+corner of the plotting area.
+.RE
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the legend. If \fIcolor\fR is \f(CW""\fR,
+the legend background with be transparent.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for legend entries. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events for legend entries. Each tag in the list matching the current
+event sequence will have its Tcl command executed. The default value
+is \f(CWall\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the legend (if
+such border is being drawn; the \fBrelief\fR option determines this).
+The default is \f(CW2\fR pixels.
+.TP
+\fB\-font \fIfontName\fR
+\fIFontName\fR specifies a font to use when drawing the labels of each
+element into the legend. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of the text drawn for the element's label.
+The default is \f(CWblack\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the legend should be displayed. If \fIboolean\fR is
+true, the legend will not be draw. The default is \f(CWno\fR.
+.TP
+\fB\-ipadx \fIpad\fR
+Sets the amount of internal padding to be added to the width of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the left side of the legend entry is
+padded by the first distance and the right side by the second. If
+\fIpad\fR is just one distance, both the left and right sides are padded
+evenly. The default is \f(CW2\fR.
+.TP
+\fB\-ipady \fIpad\fR
+Sets an amount of internal padding to be added to the height of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the top of the entry is padded by the
+first distance and the bottom by the second. If \fIpad\fR is just
+one distance, both the top and bottom of the entry are padded evenly.
+The default is \f(CW2\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the legend.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the legend is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the legend. \fIPad\fR can be a list
+of one or two screen distances. If \fIpad\fR has two elements, the area above
+the legend is padded by the first distance and the area below by the
+second. If \fIpad\fR is just one distance, both the top and
+bottom areas are padded evenly. The default is \f(CW0\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies where the legend is drawn. The
+\fB\-anchor\fR option also affects where the legend is positioned. If
+\fIpos\fR is \f(CWleft\fR, \f(CWleft\fR, \f(CWtop\fR, or \f(CWbottom\fR, the
+legend is drawn in the specified margin. If \fIpos\fR is
+\f(CWplotarea\fR, then the legend is drawn inside the plotting area at a
+particular anchor. If \fIpos\fR is in the form "\fI@x,y\fR", where
+\fIx\fR and \fIy\fR are the window coordinates, the legend is drawn in
+the plotting area at the specified coordinates. The default is
+\f(CWright\fR.
+.TP
+\fB\-raised \fIboolean\fR
+Indicates whether the legend is above or below the data elements. This
+matters only if the legend is in the plotting area. If \fIboolean\fR
+is true, the legend will be drawn on top of any elements that may
+overlap it. The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the border around the legend.
+\fIRelief\fR specifies how the interior of the legend should appear
+relative to the bar chart; for example, \f(CWraised\fR means the legend
+should appear to protrude from the bar chart, relative to the surface of
+the bar chart. The default is \f(CWsunken\fR.
+.PP
+Legend configuration options may also be set by the \fBoption\fR
+command. The resource name and class are \f(CWlegend\fR and
+\f(CWLegend\fR respectively.
+.CS
+option add *Barchart.legend.Foreground blue
+option add *Barchart.Legend.Relief raised
+.CE
+.RE
+.TP
+\fIpathName \fBlegend deactivate \fIpattern\fR...
+Selects legend entries to be drawn using the normal legend colors and
+relief. All entries whose element names match \fIpattern\fR are
+selected. To be selected, the element name must match only one
+\fIpattern\fR.
+.TP
+\fIpathName \fBlegend get \fIpos\fR
+Returns the name of the element whose entry is at the screen position
+\fIpos\fR in the legend. \fIPos\fR must be in the form "\fI@x,y\fR",
+where \fIx\fR and \fIy\fR are window coordinates. If the given
+coordinates do not lie over a legend entry, \f(CW""\fR is returned.
+.SS "PEN COMPONENTS"
+Pens define attributes for elements.
+Pens mirror the configuration options of data elements that pertain to
+how symbols and lines are drawn. Data elements use pens to determine
+how they are drawn. A data element may use several pens at once. In
+this case, the pen used for a particular data point is determined from
+each element's weight vector (see the element's \fB\-weight\fR and
+\fB\-style\fR options).
+.PP
+One pen, called \f(CWactiveBar\fR, is automatically created.
+It's used as the default active pen for elements. So you can change
+the active attributes for all elements by simply reconfiguring this
+pen.
+.CS
+\&.g pen configure "activeBar" -fg green -bg green4
+.CE
+You can create and use several pens. To create a pen, invoke
+the pen component and its create operation.
+.CS
+\&.g pen create myPen
+.CE
+You map pens to a data element using either the element's
+\fB\-pen\fR or \fB\-activepen\fR options.
+.CS
+\&.g element create "e1" -xdata $x -ydata $tempData \\
+ -pen myPen
+.CE
+An element can use several pens at once. This is done by specifying
+the name of the pen in the element's style list (see the
+\fB\-styles\fR option).
+.CS
+\&.g element configure "e1" -styles { myPen 2.0 3.0 }
+.CE
+This says that any data point with a weight between 2.0 and 3.0
+is to be drawn using the pen \f(CWmyPen\fR. All other points
+are drawn with the element's default attributes.
+.PP
+The following operations are available for pen components.
+.PP
+.TP
+\fIpathName \fBpen \fBcget \fIpenName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIpenName\fR. \fIOption\fR may be any option described below
+for the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBconfigure \fIpenName \fR?\fIpenName\fR... ?\fIoption value\fR?...
+Queries or modifies the configuration options of
+\fIpenName\fR. Several pens can be modified at once. If \fIoption\fR
+isn't specified, a list describing the current options for
+\fIpenName\fR is returned. If \fIoption\fR is specified, but not
+\fIvalue\fR, then a list describing \fIoption\fR is returned. If one
+or more \fIoption\fR and \fIvalue\fR pairs are specified, then for
+each pair, the pen option \fIoption\fR is set to \fIvalue\fR. The
+following options are valid for pens.
+.RS
+.TP
+\fB\-background \fIcolor\fR
+Sets the the color of the border around each bar. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the border width of the 3-D border drawn around the outside of
+each bar. The \fB\-relief\fR option determines if such a border is
+drawn. \fIPixels\fR must be a valid screen distance like \f(CW2\fR or
+\f(CW0.25i\fR. The default is \f(CW2\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the color of the interior of the bars.
+.TP
+\fB\-relief \fIstring\fR
+Specifies the 3-D effect desired for bars. \fIRelief\fR indicates how
+the interior of the bar should appear relative to the surface of the
+chart; for example, \f(CWraised\fR means the bar should appear to
+protrude from the bar chart, relative to the surface of the plotting
+area. The default is \f(CWraised\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern with which to draw the bars. If
+\fIbitmap\fR is \f(CW""\fR, then the bar is drawn in a solid fashion.
+.TP
+\fB\-type \fIelemType\fR
+Specifies the type of element the pen is to be used with.
+This option should only be employed when creating the pen. This
+is for those that wish to mix different types of elements (bars and
+lines) on the same graph. The default type is "bar".
+.PP
+Pen configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWPen\fR. The resource names
+are the names of the pens.
+.CS
+option add *Barchart.Pen.Foreground blue
+option add *Barchart.activeBar.foreground green
+.CE
+.RE
+.TP
+\fIpathName \fBpen \fBcreate \fIpenName \fR?\fIoption value\fR?...
+Creates a new pen by the name \fIpenName\fR. No pen by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBdelete \fR?\fIpenName\fR?...
+Deletes the named pens. A pen is not really
+deleted until it is not longer in use, so it's safe to delete
+pens mapped to elements.
+.TP
+\fIpathName \fBpen names \fR?\fIpattern\fR?...
+Returns a list of pens matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all pens are returned.
+.SS "POSTSCRIPT COMPONENT"
+The barchart can generate encapsulated PostScript output. There
+are several configuration options you can specify to control how the
+plot will be generated. You can change the page dimensions and
+borders. The plot itself can be scaled, centered, or rotated to
+landscape. The PostScript output can be written directly to a file or
+returned through the interpreter.
+.PP
+The following postscript operations are available.
+.TP
+\fIpathName \fBpostscript cget \fIoption\fR
+Returns the current value of the postscript option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the postscript \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpostscript configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for PostScript
+generation. If \fIoption\fR isn't specified, a list describing
+the current postscript options for \fIpathName\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+\fIoption\fR is returned. If one or more \fIoption\fR and \fIvalue\fR
+pairs are specified, then for each pair, the postscript option
+\fIoption\fR is set to \fIvalue\fR. The following postscript options
+are available.
+.RS
+.TP
+\fB\-center \fIboolean\fR
+Indicates whether the plot should be centered on the PostScript page. If
+\fIboolean\fR is false, the plot will be placed in the upper left
+corner of the page. The default is \f(CW1\fR.
+.TP
+\fB\-colormap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a color mapping from the X color name to PostScript. Each
+element of \fIvarName\fR must consist of PostScript code to set a
+particular color value (e.g. ``\f(CW1.0 1.0 0.0 setrgbcolor\fR''). When
+generating color information in PostScript, the array variable \fIvarName\fR
+is checked if an element of the name as the color exists. If so, it uses
+its value as the PostScript
+command to set the color. If this option hasn't been specified, or if
+there isn't an entry in \fIvarName\fR for a given color, then it uses
+the red, green, and blue intensities from the X color.
+.TP
+\fB\-colormode \fImode\fR
+Specifies how to output color information. \fIMode\fR must be either
+\f(CWcolor\fR (for full color output), \f(CWgray\fR (convert all colors to
+their gray-scale equivalents) or \f(CWmono\fR (convert foreground colors
+to black and background colors to white). The default mode is
+\f(CWcolor\fR.
+.TP
+\fB\-fontmap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a font mapping from the X font name to PostScript. Each
+element of \fIvarName\fR must consist of a Tcl list with one or two
+elements; the name and point size of a PostScript font.
+When outputting PostScript commands for a particular font, the array
+variable \fIvarName\fR is checked to see if an element by the
+specified font exists. If there is such an element, then the font
+information contained in that element is used in the PostScript
+output. (If the point size is omitted from the list, the point size
+of the X font is used). Otherwise the X font is examined in an
+attempt to guess what PostScript font to use. This works only for
+fonts whose foundry property is \fIAdobe\fR (such as Times, Helvetica,
+Courier, etc.). If all of this fails then the font defaults to
+\f(CWHelvetica-Bold\fR.
+.TP
+\fB\-decorations \fIboolean\fR
+Indicates whether PostScript commands to generate color backgrounds and 3-D
+borders will be output. If \fIboolean\fR is false, the graph will
+background will be white and no 3-D borders will be generated. The
+default is \f(CW1\fR.
+.TP
+\fB\-height \fIpixels\fR
+Sets the height of the plot. This lets you print the bar chart with a
+height different from the one drawn on the screen. If
+\fIpixels\fR is 0, the height is the same as the widget's height.
+The default is \f(CW0\fR.
+.TP
+\fB\-landscape \fIboolean\fR
+If \fIboolean\fR is true, this specifies the printed area is to be
+rotated 90 degrees. In non-rotated output the X\-axis of the printed
+area runs along the short dimension of the page (``portrait''
+orientation); in rotated output the X\-axis runs along the long
+dimension of the page (``landscape'' orientation). Defaults to
+\f(CW0\fR.
+.TP
+\fB\-maxpect \fIboolean\fR
+Indicates to scale the plot so that it fills the PostScript page.
+The aspect ratio of the barchart is still retained. The default is
+\f(CW0\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the horizontal padding for the left and right page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the left border is padded
+by the first distance and the right border by the second. If
+\fIpad\fR has just one distance, both the left and right borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the vertical padding for the top and bottom page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the top border is padded
+by the first distance and the bottom border by the second. If
+\fIpad\fR has just one distance, both the top and bottom borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-paperheight \fIpixels\fR
+Sets the height of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default height is
+\f(CW11.0i\fR.
+.TP
+\fB\-paperwidth \fIpixels\fR
+Sets the width of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default width is
+\f(CW8.5i\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the width of the plot. This lets you generate a plot
+of a width different from that of the widget. If \fIpixels\fR
+is 0, the width is the same as the widget's width. The default is
+\f(CW0\fR.
+.PP
+Postscript configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWpostscript\fR and \f(CWPostscript\fR respectively.
+.CS
+option add *Barchart.postscript.Decorations false
+option add *Barchart.Postscript.Landscape true
+.CE
+.RE
+.TP
+\fIpathName \fBpostscript output \fR?\fIfileName\fR? ?\fIoption value\fR?...
+Outputs a file of encapsulated PostScript. If a
+\fIfileName\fR argument isn't present, the command returns the
+PostScript. If any \fIoption-value\fR pairs are present, they set
+configuration options controlling how the PostScript is generated.
+\fIOption\fR and \fIvalue\fR can be anything accepted by the
+postscript \fBconfigure\fR operation above.
+.SS "MARKER COMPONENTS"
+Markers are simple drawing procedures used to annotate or highlight
+areas of the graph. Markers have various types: text strings,
+bitmaps, images, connected lines, windows, or polygons. They can be
+associated with a particular element, so that when the element is
+hidden or un-hidden, so is the marker. By default, markers are the
+last items drawn, so that data elements will appear in
+behind them. You can change this by configuring the \fB\-under\fR
+option.
+.PP
+Markers, in contrast to elements, don't affect the scaling of the
+coordinate axes. They can also have \fIelastic\fR coordinates
+(specified by \f(CW-Inf\fR and \f(CWInf\fR respectively) that translate
+into the minimum or maximum limit of the axis. For example, you can
+place a marker so it always remains in the lower left corner of the
+plotting area, by using the coordinates \f(CW-Inf\fR,\f(CW-Inf\fR.
+.PP
+The following operations are available for markers.
+.TP
+\fIpathName \fBmarker after \fImarkerId\fR ?\fIafterId\fR?
+Changes the order of the markers, drawing the first
+marker after the second. If no second \fIafterId\fR argument is
+specified, the marker is placed at the end of the display list. This
+command can be used to control how markers are displayed since markers
+are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker before \fImarkerId\fR ?\fIbeforeId\fR?
+Changes the order of the markers, drawing the first
+marker before the second. If no second \fIbeforeId\fR argument is
+specified, the marker is placed at the beginning of the display list.
+This command can be used to control how markers are displayed since
+markers are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a marker with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on graph markers, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBmarker cget \fIoption\fR
+Returns the current value of the marker configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below in the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBmarker configure \fImarkerId\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for markers. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fImarkerId\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the marker option \fIoption\fR is set to \fIvalue\fR.
+.sp
+The following options are valid for all markers.
+Each type of marker also has its own type-specific options.
+They are described in the sections below.
+.RS
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for the marker. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events for markers are handled. Each tag in the list matching the
+current event sequence will have its Tcl command executed. Implicitly
+the name of the marker is always the first tag in the list.
+The default value is \f(CWall\fR.
+.TP
+\fB\-coords \fIcoordList\fR
+Specifies the coordinates of the marker. \fICoordList\fR is
+a list of graph coordinates. The number of coordinates required
+is dependent on the type of marker. Text, image, and window markers
+need only two coordinates (an X\-Y coordinate). Bitmap markers
+can take either two or four coordinates (if four, they represent the
+corners of the bitmap). Line markers
+need at least four coordinates, polygons at least six.
+If \fIcoordList\fR is \f(CW""\fR, the marker will not be displayed.
+The default is \f(CW""\fR.
+.TP
+\fB\-element \fIelemName\fR
+Links the marker with the element \fIelemName\fR. The marker is
+drawn only if the element is also currently displayed (see the
+element's \fBshow\fR operation). If \fIelemName\fR is \f(CW""\fR, the
+marker is always drawn. The default is \f(CW""\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the marker is drawn. If \fIboolean\fR is true,
+the marker is not drawn. The default is \f(CWno\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to map the marker's X\-coordinates onto.
+\fIXAxis\fR must the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to map the marker's Y\-coordinates onto.
+\fIYAxis\fR must the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-name \fImarkerId\fR
+Changes the identifier for the marker. The identifier \fImarkerId\fR
+can not already be used by another marker. If this option
+isn't specified, the marker's name is uniquely generated.
+.TP
+\fB\-under \fIboolean\fR
+Indicates whether the marker is drawn below/above data
+elements. If \fIboolean\fR is true, the marker is be drawn
+underneath the data elements. Otherwise, the marker is
+drawn on top of the element. The default is \f(CW0\fR.
+.TP
+\fB\-xoffset \fIpixels\fR
+Specifies a screen distance to offset the marker horizontally.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.TP
+\fB\-yoffset \fIpixels\fR
+Specifies a screen distance to offset the markers vertically.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.PP
+Marker configuration options may also be set by the \fBoption\fR command.
+The resource class is either \f(CWBitmapMarker\fR, \f(CWImageMarker\fR,
+\f(CWLineMarker\fR, \f(CWPolygonMarker\fR, \f(CWTextMarker\fR, or \f(CWWindowMarker\fR,
+depending on the type of marker. The resource name is the name of the
+marker.
+.CS
+option add *Barchart.TextMarker.Foreground white
+option add *Barchart.BitmapMarker.Foreground white
+option add *Barchart.m1.Background blue
+.CE
+.RE
+.TP
+\fIpathName \fBmarker create \fItype\fR ?\fIoption value\fR?...
+Creates a marker of the selected type. \fIType\fR may be either
+\f(CWtext\fR, \f(CWline\fR, \f(CWbitmap\fR, \f(CWimage\fR, \f(CWpolygon\fR, or
+\f(CWwindow\fR. This command returns the marker identifier,
+used as the \fImarkerId\fR argument in the other marker-related
+commands. If the \fB\-name\fR option is used, this overrides the
+normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old.
+.TP
+\fIpathName \fBmarker delete\fR ?\fIname\fR?...
+Removes one of more markers. The graph will automatically be redrawn
+without the marker.\fR.
+.TP
+\fIpathName \fBmarker exists \fImarkerId\fR
+Returns \f(CW1\fR if the marker \fImarkerId\fR exists and \f(CW0\fR
+otherwise.
+.TP
+\fIpathName \fBmarker names\fR ?\fIpattern\fR?
+Returns the names of all the markers that currently exist. If
+\fIpattern\fR is supplied, only those markers whose names match it
+will be returned.
+.TP
+\fIpathName \fBmarker type \fImarkerId\fR
+Returns the type of the marker given by \fImarkerId\fR, such as
+\f(CWline\fR or \f(CWtext\fR. If \fImarkerId\fR is not a valid a marker
+identifier, \f(CW""\fR is returned.
+.SS "BITMAP MARKERS"
+A bitmap marker displays a bitmap. The size of the
+bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the
+bitmap. The bitmap retains its normal width and height. If four
+coordinates, the first and second pairs of coordinates represent the
+corners of the bitmap. The bitmap will be stretched or reduced as
+necessary to fit into the bounding rectangle.
+.PP
+Bitmap markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create bitmap \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs, each
+sets a configuration options for the marker. These
+same \fIoption\fR\-\fIvalue\fR pairs may be used with the marker's
+\fBconfigure\fR operation.
+.PP
+The following options are specific to bitmap markers:
+.TP
+\fB\-background \fIcolor\fR
+Same as the \fB\-fill\fR option.
+.TP
+\fB\-bitmap \fIbitmap\fR
+Specifies the bitmap to be displayed. If \fIbitmap\fR is \f(CW""\fR,
+the marker will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the bitmap. If \fIcolor\fR is the empty
+string, no background will be transparent. The default background color is
+\f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Same as the \fB\-outline\fR option.
+.TP
+\fB\-mask \fImask\fR
+Specifies a mask for the bitmap to be displayed. This mask is a bitmap
+itself, denoting the pixels that are transparent. If \fImask\fR is
+\f(CW""\fR, all pixels of the bitmap will be drawn. The default is
+\f(CW""\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the foreground color of the bitmap. The default value is \f(CWblack\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Sets the rotation of the bitmap. \fITheta\fR is a real number
+representing the angle of rotation in degrees. The marker is first
+rotated and then placed according to its anchor position. The default
+rotation is \f(CW0.0\fR.
+.SS "IMAGE MARKERS"
+A image marker displays an image. Image markers are
+created with the marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create image \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to image markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the image relative to the
+positioning point for the image. For example, if \fIanchor\fR
+is \f(CWcenter\fR then the image is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the image will be drawn such that
+the top center point of the rectangular region occupied by the
+image will be at the positioning point.
+This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-image \fIimage\fR
+Specifies the image to be drawn.
+If \fIimage\fR is \f(CW""\fR, the marker will not be
+drawn. The default is \f(CW""\fR.
+.SS "LINE MARKERS"
+A line marker displays one or more connected line segments.
+Line markers are created with marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create line \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to line markers:
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the line. \fIDashList\fR is a list of up to 11
+numbers that alternately represent the lengths of the dashes and gaps
+on the line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the marker line will be solid.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the line. This color is used with
+striped lines (see the \fB\-fdashes\R option). If \fIcolor\fR is
+the empty string, no background color is drawn (the line will be
+dashed, not striped). The default background color is \f(CW""\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the lines.
+The default width is \f(CW0\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the foreground color of the line. The default value is \f(CWblack\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern used to draw the line, rather than
+a solid line.
+\fIBitmap\fR specifies a bitmap to use as the stipple
+pattern. If \fIbitmap\fR is \f(CW""\fR, then the
+line is drawn in a solid fashion. The default is \f(CW""\fR.
+.SS "POLYGON MARKERS"
+A polygon marker displays a closed region described as two or more
+connected line segments. It is assumed the first and
+last points are connected. Polygon markers are created using the
+marker \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create polygon \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the \fBmarker configure\fR command to change the marker's
+configuration.
+The following options are supported for polygon markers:
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the outline of the polygon. \fIDashList\fR is a
+list of up to 11 numbers that alternately represent the lengths of
+the dashes and gaps on the outline. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the outline will be a solid line.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the fill color of the polygon. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the polygon is transparent.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the outline of the polygon. If \fIpixels\fR is zero,
+no outline is drawn. The default is \f(CW0\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color of the outline of the polygon. If the polygon is
+stippled (see the \fB\-stipple\fR option), then this represents the
+foreground color of the stipple. The default is \f(CWblack\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies that the polygon should be drawn with a stippled pattern
+rather than a solid color. \fIBitmap\fR specifies a bitmap to use as
+the stipple pattern. If \fIbitmap\fR is \f(CW""\fR, then the polygon is
+filled with a solid color (if the \fB\-fill\fR option is set). The
+default is \f(CW""\fR.
+.SS "TEXT MARKERS"
+A text marker displays a string of characters on one or more lines of
+text. Embedded newlines cause line breaks. They may be used to
+annotate regions of the graph. Text markers are created with the
+\fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create text \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs,
+each sets a configuration option for the text marker.
+These same \fIoption\fR\-\fIvalue\fR pairs may be used with the
+marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to text markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the text relative to the
+positioning point for the text. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the text is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the text will be drawn such that the
+top center point of the rectangular region occupied by the text will
+be at the positioning point. This default is \f(CWcenter\fR.
+.TP
+\fB\-background \fIcolor\fR
+Same as the \fB\-fill\fR option.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the text. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-120-*\fR.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the text. If \fIcolor\fR is the empty
+string, no background will be transparent. The default background color is
+\f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Same as the \fB\-outline\fR option.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the text should be justified. This matters only when
+the marker contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color of the text. The default value is \f(CWblack\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the text.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the text is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the text. \fIPad\fR can be a list of
+one or two screen distances. If \fIpad\fR has two elements, the area above the
+text is padded by the first distance and the area below by the second.
+If \fIpad\fR is just one distance, both the top and bottom areas
+are padded evenly. The default is \f(CW4\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the number of degrees to rotate the text. \fITheta\fR is a
+real number representing the angle of rotation. The marker is first
+rotated along its center and is then drawn according to its anchor
+position. The default is \f(CW0.0\fR.
+.TP
+\fB\-text \fItext\fR
+Specifies the text of the marker. The exact way the text is
+displayed may be affected by other options such as \fB\-anchor\fR or
+\fB\-rotate\fR.
+.SS "WINDOW MARKERS"
+A window marker displays a widget at a given position.
+Window markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create window \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR command.
+.PP
+The following options are specific to window markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the widget relative to the
+positioning point for the widget. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the widget is centered on the point; if \fIanchor\fR
+is \f(CWn\fR then the widget will be displayed such that the top center
+point of the rectangular region occupied by the widget will be at the
+positioning point. This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the height to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever height the widget requests internally.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the width to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever width the widget requests internally.
+.TP
+\fB\-window \fIpathName\fR
+Specifies the widget to be managed by the barchart. \fIPathName\fR must
+be a child of the \fBbarchart\fR widget.
+.SH "GRAPH COMPONENT BINDINGS"
+Specific barchart components, such as elements, markers and legend
+entries, can have a command trigger when event occurs in them, much
+like canvas items in Tk's canvas widget. Not all event sequences are
+valid. The only binding events that may be specified are those
+related to the mouse and keyboard (such as \fBEnter\fR, \fBLeave\fR,
+\fBButtonPress\fR, \fBMotion\fR, and \fBKeyPress\fR).
+.PP
+Only one element or marker can be picked during an event. This means,
+that if the mouse is directly over both an element and a marker, only
+the uppermost component is selected. This isn't true for legend entries.
+Both a legend entry and an element (or marker) binding commands
+will be invoked if both items are picked.
+.PP
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the
+element name and another is associated with one of the element's tags
+(see the \fB\-bindtags\fR option). When this occurs, all of the
+matching bindings are invoked. A binding associated with the element
+name is invoked first, followed by one binding for each of the element's
+bindtags. If there are multiple matching bindings for a single tag,
+then only the most specific binding is invoked. A continue command
+in a binding script terminates that script, and a break command
+terminates that script and skips any remaining scripts for the event,
+just as for the bind command.
+.PP
+The \fB\-bindtags\fR option for these components controls addition
+tag names which can be matched. Implicitly elements and markers
+always have tags matching their names. Setting the value of
+the \fB\-bindtags\fR option doesn't change this.
+.SH "C LANGUAGE API"
+You can manipulate data elements from the C language. There
+may be situations where it is too expensive to translate the data
+values from ASCII strings. Or you might want to read data in a
+special file format.
+.PP
+Data can manipulated from the C language using BLT vectors.
+You specify the X-Y data coordinates of an element as vectors and
+manipulate the vector from C. The barchart will be redrawn automatically
+after the vectors are updated.
+.PP
+From Tcl, create the vectors and configure the element to use them.
+.CS
+vector X Y
+\&.g element configure line1 -xdata X -ydata Y
+.CE
+To set data points from C, you pass the values as arrays of doubles
+using the \fBBlt_ResetVector\fR call. The vector is reset with the
+new data and at the next idle point (when Tk re-enters its event
+loop), the graph will be redrawn automatically.
+.CS
+#include <tcl.h>
+#include <blt.h>
+
+register int i;
+Blt_Vector *xVec, *yVec;
+double x[50], y[50];
+
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */
+if ((Blt_GetVector(interp, "X", 50, &xVec) != TCL_OK) ||
+ (Blt_GetVector(interp, "Y", 50, &yVec) != TCL_OK)) {
+ return TCL_ERROR;
+}
+
+for (i = 0; i < 50; i++) {
+ x[i] = i * 0.02;
+ y[i] = sin(x[i]);
+}
+
+/* Put the data into BLT vectors */
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {
+ return TCL_ERROR;
+}
+.CE
+See the \fBvector\fR manual page for more details.
+.SH SPEED TIPS
+There may be cases where the bar chart needs to be drawn and updated as
+quickly as possible. If drawing speed becomes a big
+problem, here are a few tips to speed up displays.
+.TP 2
+\(bu
+Try to minimize the number of data points. The more data points
+looked at, the more work the bar chart must do.
+.TP 2
+\(bu
+If your data is generated as floating point values, the time required
+to convert the data values to and from ASCII strings can be
+significant, especially when there any many data points. You can
+avoid the redundant string-to-decimal conversions using the C API to
+BLT vectors.
+.TP 2
+\(bu
+Don't stipple or dash the element. Solid bars are much faster.
+.TP 2
+\(bu
+If you update data elements frequently, try turning off the
+widget's \fB\-bufferelements\fR option. When the bar chart is first
+displayed, it draws data elements into an internal pixmap. The pixmap
+acts as a cache, so that when the bar chart needs to be redrawn again, and
+the data elements or coordinate axes haven't changed, the pixmap is
+simply copied to the screen. This is especially useful when you are
+using markers to highlight points and regions on the bar chart. But if
+the bar chart is updated frequently, changing either the element data or
+coordinate axes, the buffering becomes redundant.
+.SH LIMITATIONS
+Auto-scale routines do not use requested min/max limits
+as boundaries when the axis is logarithmically scaled.
+.PP
+The PostScript output generated for polygons with more than 1500
+points may exceed the limits of some printers (See PostScript Language
+Reference Manual, page 568). The work-around is to break the polygon
+into separate pieces.
+.SH KEYWORDS
+bar chart, widget
diff --git a/blt/man/beep.mann b/blt/man/beep.mann
new file mode 100644
index 00000000000..ced245c6f55
--- /dev/null
+++ b/blt/man/beep.mann
@@ -0,0 +1,48 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+.so man.macros
+.TH beep n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+beep \- ring the bell
+.SH SYNOPSIS
+\fBbeep\fR ?\fIpercent\fR?
+.BE
+.SH DESCRIPTION
+The \fBbeep\fR command rings the keyboard bell. \fIPercent\fR is
+relative to the base volume of the keyboard bell and can range from
+-100 to 100 inclusive.
+.PP
+If \fIpercent\fR is nonnegative then the bell volume is:
+.CS
+base - [(base * \fIpercent\fR) / 100] + \fIpercent\fR
+.CE
+If \fIpercent\fR is negative then the bell volume is:
+.CS C
+base + [(base * \fIpercent\fR) / 100]
+.CE
+The default \fIpercent\fR is 50.
+.SH EXAMPLE
+.CS
+beep
+.CE
+.SH KEYWORDS
+bell, beep
diff --git a/blt/man/bgexec.mann b/blt/man/bgexec.mann
new file mode 100644
index 00000000000..2731065de6f
--- /dev/null
+++ b/blt/man/bgexec.mann
@@ -0,0 +1,309 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Bgexec command created by George Howlett.
+'\"
+.so man.macros
+.TH bgexec n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bgexec \- Run programs in the background while handling Tk events.
+.SH SYNOPSIS
+\fBblt::bgexec \fIvarName\fR ?\fIoption value\fR?... \fIprogram\fR ?\fIarg\fR?...
+.BE
+.SH DESCRIPTION
+.PP
+The \fBbgexec\fR command executes programs in the background,
+allowing Tk to handle events. A global Tcl variable \fIvarName\fR is
+set when the program has completed.
+.SH INTRODUCTION
+Tcl's \fBexec\fR command is very useful for gathering information from
+the operating system. It runs a program and returns the output as its
+result. This works well for Tcl-only applications. But
+for Tk applications, a problem occurs when the program takes time to
+process. Let's say we want the get the disk usage of a
+directory. We'll use the Unix program \f(CWdu\fR to get the summary.
+.CS
+set out [exec du -s $dir]
+puts "Disk usage for $dir is $out"
+.CE
+While \f(CWdu\fR is running, scrollbars won't respond. None of the Tk
+widgets will be redrawn properly. The \fBsend\fR command won't work.
+And the worst part is that the application appears hung up or dead.
+The problem is that while \fBexec\fR is waiting for \fIdu\fR to
+finish, Tk is not able to handle X events.
+.PP
+The \fBbgexec\fR command performs the same functions as \fBexec\fR,
+but also allows Tk to handle events. You can execute a long-running
+program and the Tk widgets will behave normally. When the
+program finishes, its output and the exit status are written to Tcl
+variables. This makes it easy to monitor and save the output of a
+program.
+.SH EXAMPLE
+Here is the disk usage example again, this time using \fBbgexec\fR.
+The syntax to invoke "du" is exactly the same as the previous
+example, when we used \fBexec\fR.
+.CS
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir
+puts "Disk usage for $dir is $myOutput"
+.CE
+Two global variables, \f(CWmyStatus\fR and \f(CWmyOutput\fR, will be set by
+\fBbgexec\fR when \f(CWdu\fR has completed. \f(CWMyStatus\fR
+will contain the program's exit status. \f(CWMyOutput\fR, specified by the
+\fB\-output\fR option, will store the output of the program.
+.PP
+You can also terminate the program by setting the variable
+\f(CWmyStatus\fR. If \f(CWmyStatus\fR is set before \f(CWdu\fR has
+completed, the process is killed. Under Unix, this is done sending by
+a configurable signal (by default it's SIGKILL). Under Win32, this
+is done by calling \fBTerminateProcess\fR. It makes no
+difference what \f(CWmyStatus\fR is set to.
+.CS
+set myStatus {}
+.CE
+There are several \fBbgexec\fR options to collect different types of
+information.
+.CS
+global myStatus myOutput myErrs
+blt::bgexec myStatus -output myOutput -error myErrs du -s $dir
+.CE
+The \fB\-error\fR option is similar to \fB\-output\fR. It sets a global
+variable when the program completes. The variable will contain
+any data written to stderr by the program.
+.PP
+The \fB\-output\fR and \fB\-error\fR variables are set only
+after the program completes. But if the program takes a long time, to
+run you may want to receive its partial output. You can gather data
+as it becomes available using the \fB\-onoutput\fR option. It
+specifies a Tcl command prefix. Whenever new data is available, this
+command is executed, with the data appended as an argument to the
+command.
+.CS
+proc GetInfo { data } {
+ puts $data
+}
+blt::bgexec myStatus -onoutput GetInfo du -s $dir
+.CE
+When output is available, the procedure \f(CWGetInfo\fR is called.
+The \fB\-onerror\fR option performs a similar function for the stderr
+data stream.
+.PP
+Like \fBexec\fR, \fBbgexec\fR returns an error if the exit code of the
+program is not zero. If you think you may get a non-zero exit
+code, you might want to invoke \fBbgexec\fR from within a \fBcatch\fR.
+.CS
+catch { blt::bgexec myStatus -output myOutput du -s $dir }
+.CE
+By default, \fBbgexec\fR will wait for the program to finish.
+But you can detach the program making ampersand (&) the last
+argument on the command line.
+.CS
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir &
+.CE
+\fBBgexec\fR will return immediately and its result will be a list of
+the spawned process ids. If at some point you need to wait for the
+program to finish up, you can use \fBtkwait\fR. When the program
+finishes, the variable \f(CWmyStatus\fR will be written to, breaking
+out the \fBtkwait\fR command.
+.CS
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir &
+ ...
+tkwait variable myStatus
+.CE
+.SH SYNTAX
+The \fBbgexec\fR command takes the following form:
+.sp
+\fB blt::bgexec \fIvarName\fR ?\fIoption value\fR?... \fIprogram\fR ?\fIarg\fR?...
+.sp
+\fIVarName\fR is the name of a global variable which is set when
+\fIprogram\fR has finished executing. The exit status of
+will be stored in \fIvarName\fR. The exit status is a
+list of a status token, the process-id of the program, the exit code,
+and a status message. You can also prematurely terminate the program
+by setting \fIvarName\fR. Under Unix, the program will be sent a signal to
+terminate it (by default the signal is a SIGKILL; see the
+\fB\-killsignal\fR option).
+.PP
+\fIProgram\fR is the name of the program to be executed and \fIargs\fR
+are any extra arguments for \fIprogram\fR. The syntax of
+\fIprogram\fR and \fIargs\fR is the same as the \fBexec\fR command. So
+you can redirect I/O, execute pipelines, etc. (see the \fBexec\fR
+manual for further information) just like \fBexec\fR. If the last
+argument is an ampersand (&), the program will be run detached, and
+\fBbgexec\fR will return immediately. \fIVarName\fR will still be set
+with the return status when \fIprogram\fR completes.
+.SH OPTIONS
+\fIOption\fR refers to the switch name always beginning with a dash (\-).
+\fIValue\fR is the value of the option. Option-value pairs are
+terminated either by the program name, or double dashes (\-\-).
+The following options are available for \fBbgexec\fR:
+.TP
+\fB\-decodeerror \fIencodingName\fR
+.br
+Specifies the encoding of the stderr channel.
+This affects only data returned to the Tcl interpreter. No translation
+is done on file redirection.
+.br
+For example if data is to be converted from Unicode for use in Tcl,
+you would use the "unicode" encoding. The default is that no
+tranlation is performed.
+.TP
+\fB\-decodeoutput \fIencodingName\fR
+.br
+Specifies the encoding of the stdout channels.
+This affects only data returned to the Tcl interpreter. No translation
+is done on file redirection.
+.br
+For example if data is to be converted from Unicode for use in Tcl,
+you would use the "unicode" encoding. The default is that no
+tranlation is performed.
+.TP
+\fB\-error \fIvarName\fR
+.br
+Specifies that a global variable \fIvarName\fR is to be set with the
+contents of stderr after the program has completed.
+.TP
+\fB\-keepnewline \fIboolean\fR
+Specifies that a trailing newline should be retained in the
+output. If \fIboolean\fR is true, the trailing newline is truncated
+from the output of the \fB\-onoutput\fR and \fB\-output\fR variables.
+The default value is \f(CWtrue\fR.
+.TP
+\fB\-killsignal \fIsignal\fR
+Specifies the signal to be sent to the program when
+terminating. This is available only under Unix.
+\fISignal\fR can either be a number (typically 1-32) or
+a mnemonic (such as SIGINT). If \fIsignal\fR is the empty string,
+then no signal is sent. The default signal is \f(CW9\fR (SIGKILL).
+.TP
+\fB\-lasterror \fIvarName\fR
+Specifies a variable \fIvarName\fR that is updated whenever data
+becomes available from standard error of the program.
+\fIVarName\fR is a global variable. Unlike the \fB\-error\fR option,
+data is available as soon as it arrives.
+.TP
+\fB\-lastoutput \fIvarName\fR
+Specifies a variable \fIvarName\fR that is updated whenever data
+becomes available from standard output of the program.
+\fIVarName\fR is a global variable. Unlike the \fB\-output\fR option,
+data is available as soon as it arrives.
+.TP
+\fB\-linebuffered \fIboolean\fR
+Specifies that updates should be made on a line-by-line basis.
+Normally when new data is available \fBbgexec\fR will set the variable
+(\fB\-lastoutput\fR and \fB\-lasterror\fR options) or invoke the
+command (\fB\-onoutput\fR and \fB\-onerror\fR options) delivering all
+the new data currently available. If \fIboolean\fR is true, only one
+line at a time will be delivered. This can be useful when you want to
+process the output on a line-by-line basis.
+The default value is
+\f(CWfalse\fR.
+.TP
+\fB\-output \fIvarName\fR
+.br
+Specifies that a global variable \fIvarName\fR is to be set with the
+output of the program, once it has completed. If this option
+is not set, no output will be accumulated.
+.TP
+\fB\-onerror \fIcommand\fR
+Specifies the start of a Tcl command that will be executed
+whenever new data is available from standard error. The data
+is appended to the command as an extra argument before it is
+executed.
+.TP
+\fB\-onoutput \fIcommand\fR
+Specifies the start of a Tcl command that will be executed
+whenever new data is available from standard output. The data
+is appended to the command as an extra argument before it is
+executed.
+.TP
+\fB\-update \fIvarName\fR
+Deprecated. This option is replaced by \fB\-lasterror\fR.
+.TP
+\fB\-\|\-\fR
+This marks the end of the options. The following argument will
+be considered the name of a program even if it starts with
+a dash (\fB\-\fR).
+.SH PREEMPTION
+Because \fBbgexec\fR allows Tk to handle events while a program is
+running, it's possible for an application to preempt itself with
+further user-interactions. Let's say your application has a button
+that runs the disk usage example. And while the \f(CWdu\fR program is
+running, the user accidently presses the button again. A second
+\fBbgexec\fR program will preempt the first. What this means is that
+the first program can not finish until the second program has
+completed.
+.PP
+Care must be taken to prevent an application from preempting itself by
+blocking further user-interactions (such as button clicks). The BLT
+\fBbusy\fR command is very useful for just these situations.
+See the \fBbusy\fR manual for details.
+.SH DIFFERENCES WITH FILEEVENT
+Since Tk 4.0, a subset of \fBbgexec\fR can be also achieved using the
+\fBfileevent\fR command. The steps for running a program in the
+background are:
+.PP
+Execute the program with the \fBopen\fR command (using the "|"
+syntax) and save the file handle.
+.CS
+global fileId
+set fileId [open "|du -s $dir" r]
+.CE
+Next register a Tcl code snippet with \fBfileevent\fR to be run
+whenever output is available on the file handle. The code snippet
+will read from the file handle and save the output in a variable.
+.CS
+fileevent fileId readable {
+ if { [gets $fileId line] < 0 } {
+ close $fileId
+ set output $temp
+ unset fileId temp
+ } else {
+ append temp $line
+ }
+}
+.CE
+.PP
+The biggest advantage of \fBbgexec\fR is that, unlike \fBfileevent\fR,
+it requires no additional Tcl code to run a program. It's simpler and
+less error prone. You don't have to worry about non-blocking I/O.
+It's handled tranparently for you.
+.PP
+\fBBgexec\fR runs programs that \fBfileevent\fR can not.
+\fBFileevent\fR assumes that the when stdout is closed the program has
+completed. But some programs, like the Unix \f(CWcompress\fR program,
+reopen stdout, fooling \fBfileevent\fR into thinking the program has
+terminated. In the example above, we assume that the program will
+write and flush its output line-by-line. However running another
+program, your application may block in the \fBgets\fR command reading
+a partial line.
+.PP
+\fBBgexec\fR lets you get back the exit status of the program. It also
+allows you to collect data from both stdout and stderr simultaneously.
+Finally, since data collection is handled in C code, \fBbgexec\fR is
+faster. You get back to the Tk event loop more quickly, making your
+application seem more responsive.
+.SH SEE ALSO
+busy, exec, tkwait
+.SH KEYWORDS
+exec, background, busy
diff --git a/blt/man/bitmap.mann b/blt/man/bitmap.mann
new file mode 100644
index 00000000000..56695382dcc
--- /dev/null
+++ b/blt/man/bitmap.mann
@@ -0,0 +1,220 @@
+'\"
+'\" Copyright 1991-2001 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Bitmap command created by George Howlett.
+'\"
+.so man.macros
+.TH bitmap n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bitmap \- Define a new bitmap from a Tcl script
+.SH SYNOPSIS
+\fBbitmap define \fIbitmapName data\fR ?\fIoption value\fR?...
+.sp
+\fBbitmap compose \fIbitmapName text\fR ?\fIoption value\fR?...
+.sp
+\fBbitmap exists \fIbitmapName\fR
+.sp
+\fBbitmap source \fIbitmapName\fR
+.sp
+\fBbitmap data \fIbitmapName\fR
+.sp
+\fBbitmap height \fIbitmapName\fR
+.sp
+\fBbitmap width \fIbitmapName\fR
+.BE
+.SH DESCRIPTION
+The \fBbitmap\fR command lets you create new bitmaps directly from your
+Tcl script. The bitmap can be specified as a list of data or a text string
+which is converted into a bitmap. You can arbitrarily scale
+or rotate the bitmap too.
+.SH INTRODUCTION
+Bitmaps are commonly used within Tk. In label and button widgets, you
+display bitmaps them instead of text strings and in the canvas and
+text widgets, they're used for stippling. But Tk let's you can create
+new bitmaps only by reading the bitmap data from a file. This makes
+bitmaps cumbersome to manage, especially in packaging the program as a
+\fBwish\fR script, since each bitmap must be its own file. It would
+be nicer if you could create new bitmaps directly from your Tcl script.
+.PP
+The \fBbitmap\fR command lets you do just that. You can specify the
+bitmap as in various formats (such as the X11 bitmap format). You can
+also compose a bitmap from a text string. The \fBbitmap\fR command
+also lets you and arbitrarily rotate or scale the bitmap. For example, you
+could use this to create button widgets with the text label rotated 90
+degrees.
+.SH EXAMPLE
+<<<<<<< bitmap.mann
+You can define a new bitmap with the \fBdefine\fR operation. For
+example, let's say you are using the X11 bitmap "gray1". Normally to
+use it, you would specify the location of the file.
+.CS
+label .l -bitmap @/usr/X11R6/include/X11/bitmaps/gray1
+.CE
+But you can simply cut and paste the contents of "gray1" into the
+\fBbitmap\fR command.
+.CS
+bitmap define gray1 {
+ #define gray1_width 2
+ #define gray1_height 2
+ static char gray1_bits[] = {
+ 0x01, 0x02};
+}
+label .l -bitmap gray1
+.CE
+Tk will recognize "gray1" as a bitmap which can now be used with any
+widget that accepts bitmaps.
+.CS
+.barchart element configure elem1 -stipple gray1
+.CE
+The bitmap data can be specified in a mulitude of forms.
+The following commands are all equivalent.
+.CS
+bitmap define gray1 {
+ #define gray1_width 2
+ #define gray1_height 2
+ static char gray1_bits[] = {
+ 0x01, 0x02};
+}
+bitmap define gray1 { { 2 2 } { 0x01, 0x02 } }
+bitmap define gray1 { { 2 2 } { 0x01 0x02 } }
+bitmap define gray1 { { 2 2 } { 1 2 } }
+.CE
+Either the data is in the standard X11 bitmap form, or it's a list of
+two lists. The first list contains the height and width of the bitmap.
+The second list is the bitmap source data. Each element of that list
+is an hexadecimal number specifying which pixels are foreground (1)
+and which are background (0) of the bitmap. Note that the format of
+the source data is exactly that of the XBM format.
+.P
+You can scale or rotate the bitmap as you create it, by using the
+\fB-scale\fR or\fB-rotate\fR options.
+.CS
+bitmap define gray1 {
+ #define gray1_width 2
+ #define gray1_height 2
+ static char gray1_bits[] = {
+ 0x01, 0x02};
+} -scale 2.0 -rotate 90.0
+.CE
+In addition, you can compose bitmaps from text strings. This makes it
+easy to create rotated buttons or labels. The text string can have
+multi-line.
+.CS
+bitmap compose rot_text "This is rotated\\ntext" \\
+ -rotate 90.0 -font fixed
+.CE
+There are also a number of ways to query bitmaps. This isn't limited
+to bitmaps that you create, but any bitmap.
+.CS
+bitmap exists rot_text
+bitmap width rot_text
+bitmap height rot_text
+bitmap data rot_text
+bitmap source rot_text
+.CE
+The \fBexists\fR operation indicates if a bitmap by that name is
+defined. You can query the dimensions of the bitmap using the
+\fBwidth\fR and \fBheight\fR operations. The \fBdata\fR operation
+returns the list of the data used to create the bitmap.
+For example, you could query the data of a bitmap and \fBsend\fR
+it across the network to another Tk application.
+.CS
+set data [bitmap data @/usr/X11R6/include/X11/bitmaps/ghost.xbm]
+send {wish #2} bitmap define ghost $data
+.CE
+.SH OPERATIONS
+The following operations are available for \fBbitmap\fR:
+.TP
+\fBbitmap compose \fIbitmapName text \fR?\fIoption value\fR?...
+Creates a bitmap \fIbitmapName\fR from the text string \fItext\fR.
+A bitmap \fIbitmapName\fR can not already exist.
+The following options are available.
+.RS
+.TP
+\fB\-font \fIfontName\fR
+Specifies a font to use when drawing text into the bitmap.
+If this option isn't specified then \fIfontName\fR defaults to
+\f(CW*-Helvetica-Bold-R-Normal-*-140-*\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the angle of rotation of the text in the bitmap.
+\fITheta\fR is a real number representing the angle in degrees.
+It defaults to \f(CW0.0\fR degrees.
+.TP
+\fB\-scale \fIvalue\fR
+Specifies the scale of the bitmap.
+\fIValue\fR is a real number representing the scale. A scale
+of 1.0 indicates no scaling is necessary, while 2.0 would
+double the size of the bitmap. There is no way to specify
+differents scales for the width and height of the bitmap.
+The default scale is \f(CW1.0\fR.
+.RE
+.TP
+\fBbitmap data \fIbitmapName\fR
+Returns a list of both the
+dimensions of the bitmap \fIbitmapName\fR and its source data.
+.TP
+\fBbitmap define \fIbitmapName data\fR \fR?\fIoption value\fR?...
+Associates \fIbitmapName\fR with in-memory bitmap data so that
+\fIbitmapName\fR can be used in later calls to \fBTk_GetBitmap\fR.
+The \fIbitmapName\fR argument is the name of the bitmap; it must not
+previously have been defined in either a call to Tk_DefineBitmap or
+\fBbitmap\fR. The argument \fIdata\fP describes the bitmap to
+be created. It is either the X11 bitmap format (a C structure) or
+a list of two lists: the dimensions and source data. The dimensions
+are a list of two numbers which are the width
+and height of the bitmap. The source data is a list of hexadecimal
+values in a format similar to the X11 or X10 bitmap format. The
+values may be optionally separated by commas and do not need to be
+prefixed with "0x". The following options are available.
+.RS
+.TP
+\fB\-rotate \fItheta\fR
+Specifies how many degrees to rotate the bitmap.
+\fITheta\fR is a real number representing the angle.
+The default is \f(CW0.0\fR degrees.
+.TP
+\fB\-scale \fIvalue\fR
+Specifies how to scale the bitmap.
+\fIValue\fR is a real number representing the scale. A scale
+of 1.0 indicates no scaling is necessary, while 2.0 would
+double the size of the bitmap. There is no way to specify
+differents scales for the width and height of the bitmap.
+The default scale is \f(CW1.0\fR.
+.RE
+.TP
+\fBbitmap exists \fIbitmapName\fR
+Returns \f(CW1\fR if a bitmap \fIbitmapName\fR exists, otherwise \f(CW0\fR.
+.TP
+\fBbitmap height \fIbitmapName\fR
+Returns the height in pixels of the bitmap \fIbitmapName\fR.
+.TP
+\fBbitmap source \fIbitmapName\fR
+Returns the source data of the bitmap \fIbitmapName\fR. The source data is a
+list of the hexadecimal values.
+.TP
+\fBbitmap width \fIbitmapName\fR
+Returns the width in pixels of the bitmap \fIbitmapName\fR.
+.SH LIMITATIONS
+Tk currently offers no way of destroying bitmaps. Once a bitmap is
+created, it exists until the application terminates.
+.SH KEYWORDS
+bitmap
diff --git a/blt/man/bltdebug.mann b/blt/man/bltdebug.mann
new file mode 100644
index 00000000000..ecb3f86f739
--- /dev/null
+++ b/blt/man/bltdebug.mann
@@ -0,0 +1,38 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+.so man.macros
+.TH bltdebug n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bltdebug \- print Tcl commands before execution
+.SH SYNOPSIS
+\fBbltdebug\fR ?\fIlevel\fR?
+.BE
+.SH DESCRIPTION
+The \fBbltdebug\fR command is a simple tracing facility for Tcl commands.
+Each command line is printed before it is executed on standard error.
+The output consists of the command line both before and after
+substitutions have occurred. \fILevel\fR indicates at what level to
+stop tracing commands. If \fIlevel\fR is \f(CW0\fR, no tracing is
+performed. This is the default. If no \fIlevel\fR argument is given,
+the current level is printed.
+.SH KEYWORDS
+debug
diff --git a/blt/man/busy.mann b/blt/man/busy.mann
new file mode 100644
index 00000000000..24ee7d84eac
--- /dev/null
+++ b/blt/man/busy.mann
@@ -0,0 +1,241 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Busy command created by George Howlett.
+'\"
+.so man.macros
+.TH busy n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+busy \- Make Tk widgets busy, temporarily blocking user interactions.
+.SH SYNOPSIS
+\fBbusy hold \fIwindow\fR ?\fIoption value\fR?...
+.sp
+\fBbusy release \fIwindow\fR ?\fIwindow\fR?...
+.sp
+\fBbusy configure \fIwindow\fR ?\fIoption value\fR?...
+.sp
+\fBbusy forget \fIwindow\fR ?\fIwindow\fR?...
+.sp
+\fBbusy isbusy \fR?\fIpattern\fR?
+.sp
+\fBbusy names \fR?\fIpattern\fR?
+.sp
+\fBbusy status \fIwindow\fR
+.BE
+.SH DESCRIPTION
+.PP
+The \fBbusy\fR command provides a simple means to block
+keyboard, button, and pointer events from Tk widgets, while overriding
+the widget's cursor with a configurable busy cursor.
+.SH INTRODUCTION
+.PP
+There are many times in applications where you want to temporarily
+restrict what actions the user can take. For example, an application
+could have a "run" button that when pressed causes some processing to
+occur. But while the application is busy processing, you probably don't
+want the the user to be able to click the "run" button again. You
+may also want restrict the user from other tasks such as clicking a
+"print" button.
+.PP
+The \fBbusy\fR command lets you make Tk widgets busy. This means
+that user interactions such as button clicks, moving the mouse, typing
+at the keyboard, etc. are ignored by the widget. You can set a
+special cursor (like a watch) that overrides the widget's normal
+cursor, providing feedback that the application (widget) is
+temporarily busy.
+.PP
+When a widget is made busy, the widget and all of its descendents will
+ignore events. It's easy to make an entire panel of widgets busy. You
+can simply make the toplevel widget (such as ".") busy. This is
+easier and far much more efficient than recursively traversing the
+widget hierarchy, disabling each widget and re-configuring its cursor.
+.PP
+Often, the busy command can be used instead of Tk's \fBgrab\fR
+command. Unlike \fBgrab\fR which restricts all user interactions to
+one widget, with the busy command you can have more than one widget
+active (for example, a "cancel" dialog and a "help" button).
+.SH EXAMPLE
+You can make several widgets busy by simply making its ancestor widget
+busy using the \fBhold\fR operation.
+.CS
+frame .top
+button .top.button; canvas .top.canvas
+pack .top.button .top.canvas
+pack .top
+ . . .
+busy hold .top
+update
+.CE
+All the widgets within \f(CW.top\fR (including \f(CW.top\fR) are now busy.
+Using \fBupdate\fR insures that \fBbusy\fR command will take effect before
+any other user events can occur.
+.PP
+When the application is no longer busy processing, you can allow user
+interactions again by the \fBrelease\fR operation.
+.nf
+
+\f(CW busy release .top \fR
+
+.fi
+The busy window has a configurable cursor. You can change the busy
+cursor using the \fBconfigure\fR operation.
+.nf
+
+\f(CW busy configure .top -cursor "watch"\fR
+
+.fi
+Finally, when you no longer need to the busy window,
+invoke the \fBforget\fR operation to free any resources it allocated.
+.nf
+
+\f(CW busy forget .top \fR
+
+.fi
+Destroying the widget will also clean up any resources allocated by
+the busy command.
+.PP
+.SH OPERATIONS
+The following operations are available for the \fBbusy\fR command:
+.TP
+\fBbusy hold \fIwindow\fR ?\fIoption value\fR?...
+Makes the widget \fIwindow\fR (and its descendants in the Tk window
+hierarchy) busy. \fIWindow\fR must be a valid path name of a Tk
+widget. The busy window is mapped the next time idle tasks are
+processed, and the widget and its descendants will be blocked from
+user interactions. All events in the widget window and its
+descendants are ignored. Normally \fBupdate\fR should be called
+immediately afterward to insure that the \fBhold\fR operation is in
+effect \fIbefore\fR the application starts its processing. The
+following configuration options are valid:
+.RS
+.TP
+\fB\-cursor \fIcursorName\fR
+Specifies the cursor to be displayed when the widget is made busy.
+\fICursorName\fR can be in any form accepted by \fBTk_GetCursor\fR.
+The default cursor is \f(CWwatch\fR.
+.RE
+.TP
+\fBbusy configure \fIwindow\fR ?\fIoption value\fR?...
+Queries or modifies the \fBbusy\fR command configuration options for
+\fIwindow\fR. \fIWindow\fR must be the path name of a widget previously
+made busy by the \fBhold\fR operation. If no options are
+specified, a list describing all of the available options for
+\fIwindow\fR (see \fBTk_ConfigureInfo\fR for information on the format
+of this list) is returned. If \fIoption\fR is specified with no
+\fIvalue\fR, then the command returns a list describing the one named
+option (this list will be identical to the corresponding sublist of
+the value returned if no \fIoption\fR is specified). If one or more
+\fIoption\-value\fR pairs are specified, then the command modifies the
+given widget option(s) to have the given value(s); in this case the
+command returns the empty string. \fIOption\fR may have any of the
+values accepted by the \fBhold\fR operation.
+.sp
+Please note that the
+option database is referenced through \fIwindow\fR. For example, if
+the widget \f(CW.frame\fR is to be made busy, the busy
+cursor can be specified for it by either \fBoption\fR command:
+.nf
+
+ \f(CWoption add *frame.busyCursor gumby\fR
+ \f(CWoption add *Frame.BusyCursor gumby\fR
+
+.fi
+.TP
+\fBbusy forget \fIwindow\fR ?\fIwindow\fR?...
+Releases resources allocated by the busy command for \fIwindow\fR,
+including the busy window. User events will again be received again
+by \fIwindow\fR. Resources are also released when \fIwindow\fR
+is destroyed. \fIWindow\fR must be the name of a widget specified
+in the \fBhold\fR operation, otherwise an error is reported.
+.TP
+\fBbusy isbusy \fR?\fIpattern\fR?
+Returns the pathnames of all widgets that are currently busy.
+If a \fIpattern\fR is given, the path names of busy widgets
+matching \fIpattern\fR are returned.
+.TP
+\fBbusy names \fR?\fIpattern\fR?
+Returns the pathnames of all widgets that have previously been
+made busy (i.e. a busy window is allocated and associated with the
+widget). It makes no difference if the window is currently busy or
+not. If a \fIpattern\fR is given, the path names of busy widgets
+matching \fIpattern\fR are returned.
+.TP
+\fBbusy release \fIwindow\fR ?\fIwindow\fR?...
+Restores user interactions to the widget \fIwindow\fR again.
+This differs from the \fBforget\fR operation in that the busy window
+is not destroyed, but simply unmapped.
+\fIWindow\fR must be the name of a widget specified
+in a \fBhold\fR operation, otherwise an error is reported.
+.TP
+\fBbusy status \fIwindow\fR
+Returns the status of a widget \fIwindow\fR previously made busy.
+An error is reported if \fIwindow\fR was never made busy, or
+the \fBforget\fR operation was invoked (i.e. does not currently have a
+busy window associated with it). If \fIwindow\fR is presently can
+not receive user interactions, \f(CW1\fR is returned, otherwise \f(CW0\fR.
+.sp 1
+.SH BINDINGS
+The event blocking feature is implemented by creating and mapping a
+transparent window that completely covers the widget. When the busy
+window is mapped, it invisibly shields the widget and its hierarchy
+from all events that may be sent. Like Tk widgets, busy windows have
+widget names in the Tk window hierarchy. This means that you can use
+the \fBbind\fR command, to handle events in the busy window.
+.CS
+busy hold .frame.canvas
+bind .frame.canvas_Busy <Enter> { ... }
+.CE
+.PP
+Normally the busy window is a sibling of the widget. The
+name of the busy window is "\fIwidget\f(CW_Busy\fR" where \fIwidget\fR
+is the name of the widget to be made busy. In the previous example, the
+pathname of the busy window is "\f(CW.frame.canvas_Busy\fR" The
+exception is when the widget is a toplevel widget (such as ".") where
+the busy window can't be made a sibling. The busy window is then a
+child of the widget named "\fIwidget\f(CW._Busy\fR" where \fIwidget\fR
+is the name of the toplevel widget. In the following example, the
+pathname of the busy window is "\f(CW._Busy\fR"
+.CS
+busy hold .
+bind ._Busy <Enter> { ... }
+.CE
+.SH ENTER/LEAVE EVENTS
+Mapping and unmapping busy windows generates Enter/Leave events for
+all widgets they cover. Please note this if you are tracking
+Enter/Leave events in widgets.
+.SH KEYBOARD EVENTS
+When a widget is made busy, the widget is prevented from gaining the
+keyboard focus by the busy window. But if the widget already had
+focus, it still may received keyboard events. To prevent this, you
+must move focus to another window.
+.CS
+busy hold .frame
+label .dummy
+focus .dummy
+update
+.CE
+The above example moves the focus from .frame immediately after
+invoking the \fBhold\fR so that no keyboard events will be sent to
+\f(CW.frame\fR or any of its descendants.
+.SH KEYWORDS
+busy, keyboard events, pointer events, window, cursor
+
+
diff --git a/blt/man/container.mann b/blt/man/container.mann
new file mode 100644
index 00000000000..682fff36dcc
--- /dev/null
+++ b/blt/man/container.mann
@@ -0,0 +1,303 @@
+'\"
+'\" Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Container widget created by George Howlett.
+'\"
+.so man.macros.in
+.TH container n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+container \- Widget to contain a foreign window.
+.BE
+.SH SYNOPSIS
+\fBcontainer\fR \fIpathName \fR?\fIoptions\fR?
+.SH DESCRIPTION
+The \fBcontainer\fR widget lets you embed an X11 window from a foreign
+application into your Tk application. The foreign window is
+reparented inside of the widget. You can then place and arrange the
+container just as you would any Tk widget.
+.SH INTRODUCTION
+Notebooks are a popular graphical paradigm. They allow you to organize
+many windows in a single widget. For example, you might have an
+application the displays several X-Y graphs at the same time.
+Typically, you can't pack the graphs into the same \fBframe\fR because
+they are too large. The other alternative is to pack the graphs into
+several \fBtoplevel\fR widgets, allowing them to overlap on the
+screen. The problem is that all the different toplevel windows
+clutter the screen and are difficult to manage.
+.PP
+The \fBcontainer\fR widget lets organize your application by displaying
+each graph as a page in a folder of a notebook. Only one page is
+visible at a time. When you click on a tab, the folder (graph)
+corresponding to the tab is displayed in the \fBcontainer\fR widget. The
+container also lets you temporarily tear pages out of the notebook into a
+separate toplevel widget, and put them back in the container later. For
+example, you could compare two graphs side-by-side by tearing them
+out, and then replace them when you are finished.
+.PP
+A container may contain an unlimited number of folders. If there are too
+many tabs to view, you can arrange them as multiple tiers or scroll
+the tabs. The container uses the conventional Tk scrollbar syntax, so you
+can attach a scrollbar too.
+.SH EXAMPLE
+You create a container widget with the \fBcontainer\fR command.
+.CS
+# Create a new container
+container .c
+.CE
+A new Tcl command \f(CW.c\fR is also created. This command can be
+used to query and modify the container. For example, to change the
+default borderwidth, you use the new command and
+the container's \fBconfigure\fR operation.
+.CS
+# Change the default font.
+\&.c configure \-borderwidth 2
+.CE
+You can then add folders using the \fBinsert\fR operation.
+.CS
+# Create a new folder "f1"
+\&.c coinsert 0 "f1"
+.CE
+This inserts the new tab named "f1" into the container. The index
+\f(CW0\fR indicates location to insert the new tab. You can also use
+the index \f(CWend\fR to append a tab to the end of the container. By
+default, the text of the tab is the name of the tab. You can change
+this by configuring the \fB\-text\fR option.
+.CS
+# Change the label of "f1"
+\&.ts tab configure "f1" -label "Tab #1"
+.CE
+The \fBinsert\fR operation lets you add one or more folders at a time.
+.CS
+\&.ts insert end "f2" -label "Tab #2" "f3" "f4"
+.CE
+The tab on each folder contains a label. A label may display both
+an image and a text string. You can reconfigure the tab's attributes
+(foreground/background colors, font, rotation, etc) using the \fBtab
+configure\fR operation.
+.CS
+# Add an image to the label of "f1"
+set image [image create photo -file stopsign.gif]
+\&.ts tab configure "f1" -image $image
+\&.ts tab configure "f2" -rotate 90
+.CE
+Each folder may contain an embedded widget to represent its contents.
+The widget to be embedded must be a child of the container widget. Using
+the \fB\-window\fR option, you specify the name of widget to be
+embedded. But don't pack the widget, the container takes care of placing
+and arranging the widget for you.
+.CS
+graph .ts.graph
+\&.ts tab configure "f1" -window ".ts.graph" \\
+ -fill both -padx 0.25i -pady 0.25i
+.CE
+The size of the folder is determined the sizes of the Tk widgets
+embedded inside each folder. The folder will be as wide as the widest
+widget in any folder. The tallest determines the height. You can use
+the tab's \fB\-pagewidth\fR and \fB\-pageheight\fR options override this.
+.PP
+Other options control how the widget appears in the folder. The
+\fB\-fill\fR option says that you wish to have the widget stretch to
+fill the available space in the folder.
+.CS
+\&.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i
+.CE
+.PP
+Now when you click the left mouse button on "f1", the
+graph will be displayed in the folder. It will be automatically
+hidden when another folder is selected. If you click on the right
+mouse button, the embedded widget will be moved into a toplevel widget
+of its own. Clicking again on the right mouse button puts it back into
+the folder.
+.PP
+If you want to share a page between two different folders, the
+\fB\-command\fR option lets you specify a Tcl command to be invoked
+whenever the folder is selected. You can reset the \fB\-window\fR
+option for the tab whenever it's clicked.
+.CS
+\&.ts tab configure "f2" -command {
+ \&.ts tab configure "f2" -window ".ts.graph"
+}
+\&.ts tab configure "f1" -command {
+ \&.ts tab configure "f1" -window ".ts.graph"
+}
+.CE
+If you have many folders, you may wish to stack tabs in multiple
+tiers. The container's \fB\-tiers\fR option requests a maximum
+number of tiers. The default is one tier.
+.CS
+\&.ts configure -tiers 2
+.CE
+If the tabs can fit in less tiers, the widget will use that many.
+Whenever there are more tabs than can be displayed in the maximum number
+of tiers, the container will automatically let you scroll the tabs. You
+can even attach a scrollbar to the container.
+.CS
+\&.ts configure -scrollcommand { .sbar set } -scrollincrement 20
+\&.sbar configure -orient horizontal -command { .ts view }
+.CE
+By default tabs are along the top of the container from left to right.
+But tabs can be placed on any side of the container using the \fB\-side\fR
+option.
+.CS
+# Arrange tabs along the right side of the container.
+\&.ts configure -side right -rotate 270
+.CE
+.SH SYNTAX
+The \fBcontainer\fR command creates a new window using the \fIpathName\fR
+argument and makes it into a container widget.
+.CS
+\fBcontainer \fIpathName \fR?\fIoption value\fR?...
+.CE
+Additional options may be specified on the command line or in the
+option database to configure aspects of the container such as its colors,
+font, text, and relief. The \fBcontainer\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist.
+.PP
+When first created, a new container contains no tabs. Tabs are added or
+deleted using widget operations described below. It is not necessary
+for all the tabs to be displayed in the container window at once;
+commands described below may be used to change the view in the window.
+Containers allow scrolling of tabs using the \fB\-scrollcommand\fR
+option. They also support scanning (see the \fBscan\fR operation).
+Tabs may be arranged along any side of the container window using the
+\fB\-side\fR option.
+.PP
+The size of the container window is determined the number of tiers of
+tabs and the sizes of the Tk widgets embedded inside each folder.
+The widest widget determines the width of the folder. The tallest
+determines the height. If no folders contain an embedded widget, the
+size is detemined solely by the size of the tabs.
+.PP
+You can override either dimension with the container's \fB\-width\fR
+and \fB\-height\fR options.
+.SH "CONTAINER OPERATIONS"
+All \fBcontainer\fR operations are invoked by specifying the widget's
+pathname, the operation, and any arguments that pertain to that
+operation. The general form is:
+.sp
+.CS
+ \fIpathName operation \fR?\fIarg arg ...\fR?
+.CE
+.sp
+\fIOperation\fR and the \fIarg\fRs determine the exact behavior of the
+command. The following operations are available for container widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-background \fIcolor\fR
+Sets the border color of the container.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines how the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-command \fIpattern\fR
+Specifies to search for a window whose \f(CWWM_COMMAND\fR property matches
+the given pattern. If no windows, or more than one window, matches
+the pattern, an error is generated. If \fIpattern\fR is the empty
+string, then no command search is performed.
+The default is \f(CW""\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. If \fIpixels\fR is
+0, then the height is height the embedded window plus the specified
+borderwidth. The default is \f(CW0\fR.
+.TP
+\fB\-highlightbackground \fIcolor\fR
+Sets the color to display in the traversal highlight region when
+the container does not have the input focus.
+.TP
+\fB\-highlightcolor \fIcolor\fR
+Sets the color to use for the traversal highlight rectangle that is
+drawn around the widget when it has the input focus.
+The default is \f(CWblack\fR.
+.TP
+\fB\-highlightthickness \fIpixels\fR
+Sets the width of the highlight rectangle to draw around the outside of
+the widget when it has the input focus. \fIPixels\fR is a non-negative
+value and may have any of the forms acceptable to \fBTk_GetPixels\fR.
+If the value is zero, no focus highlight is drawn around the widget.
+The default is \f(CW2\fR.
+.TP
+\fB\-name \fIpattern\fR
+Specifies to search for a window whose \f(CWWM_NAME\fR property matches
+the given pattern. If no windows, or more than one window, matches
+the pattern, an error is generated. If \fIpattern\fR is the empty
+string, then no name search is performed.
+The default is \f(CW""\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the container widget. \fIRelief\fR
+specifies how the container should appear relative to widget that
+it is packed into; for example, \f(CWraised\fR means the container should
+appear to protrude. The default is \f(CWsunken\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts decide whether to focus on the window.
+The default is \f(CW1\fR.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the requested width of the widget. If \fIpixels\fR is 0,
+then the width is the width the embedded window and the specified
+borderwidth. The default is \f(CW0\fR.
+.TP
+\fB\-window \fIid\fR
+Specifies the foreign embedded using its X window id.
+.RE
+.TP
+\fIpathName \fBfind \fB\-command\fR|\fB\-name\fR \fIpattern\fR
+Searches for all windows that match the given pattern. If the
+\fB\-command\fR switch is given, all windows whose \fCWWM_COMMAND\fR
+property match \fIpattern\fR are returned in a list. If the
+\fB\-name\fR switch is given, all windows whose \fCWWM_NAME\fR
+property match \fIpattern\fR are returned in a list. The list
+returned will contains pairs of the window id and the matching property.
+.SH KEYWORDS
+container, widget
diff --git a/blt/man/cutbuffer.mann b/blt/man/cutbuffer.mann
new file mode 100644
index 00000000000..ce061062ae6
--- /dev/null
+++ b/blt/man/cutbuffer.mann
@@ -0,0 +1,54 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+.so man.macros
+.TH cutbuffer n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+cutbuffer \- Manipulate X cut buffer properties
+.SH SYNOPSIS
+\fBcutbuffer\fI get ?number?\fR
+.br
+\fBcutbuffer\fI rotate ?count?\fR
+.br
+\fBcutbuffer\fI set value ?number?\fR
+.BE
+.SH DESCRIPTION
+.PP
+The \fBcutbuffer\fR command allows you to read or modify the eight X cut
+buffer properties. You can also rotate the buffers properties.
+.SH OPERATIONS
+The following operations are available for the \fBcutbuffer\fR command:
+.TP
+\fBcutbuffer get \fI?number?\fR
+Returns the value of a cutbuffer \fInumber\fR. \fINumber\fR must be a
+number between 0 and 7. The default is 0. The cutbuffer is returned
+exactly, except that NUL bytes are converted to '@' characters. If a
+cut buffer \fInumber\fR does not exist, then \f(CW""\fR is returned.
+.TP
+\fBcutbuffer rotate \fI?count?\fR
+Rotates the cut buffers by \fIcount\fR. \fICount\fR must be a number
+between -7 and 7. The default is 1.
+.TP
+\fBcutbuffer set \fIvalue\fR ?\fInumber\fR?
+Sets the cutbuffer \fInumber\fR to \fIvalue\fR. \fINumber\fR must be a
+number between 0 and 7. The default is 0.
+.SH KEYWORDS
+cut buffer, property
diff --git a/blt/man/dragdrop.mann b/blt/man/dragdrop.mann
new file mode 100644
index 00000000000..3d632891494
--- /dev/null
+++ b/blt/man/dragdrop.mann
@@ -0,0 +1,456 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH drag&drop n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+drag&drop \- facilities for handling drag&drop data transfers
+.SH SYNOPSIS
+\fBdrag&drop source
+.br
+\fBdrag&drop source \fIwindow \fR?\fIoptions\fR?
+.br
+\fBdrag&drop source \fIwindow \fBhandler \fR?\fIdataType\fR? ?\fIcommand arg arg...\fR?
+.sp
+\fBdrag&drop target
+.br
+\fBdrag&drop target \fIwindow \fBhandler \fR?\fIdataType command arg arg...\fR?
+.sp
+\fBdrag&drop target \fIwindow \fBhandle \fIdataType\fR ?\fIvalue\fR?
+.sp
+\fBdrag&drop token \fIwindow
+.sp
+\fBdrag&drop drag \fIwindow x y
+.br
+\fBdrag&drop drop \fIwindow x y
+.br
+\fBdrag&drop active
+.br
+\fBdrag&drop errors \fR?\fIproc\fR?
+.br
+\fBdrag&drop location \fR?\fIx y\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBdrag&drop\fR command provides access to a set of facilities
+for managing drag-and-drop data transfers. Any of the usual Tk widgets can
+be registered to participate in the drag-and-drop process. Widgets
+registered as a drag&drop \fIsource\fP can export data to other widgets
+registered as a drag&drop \fItarget\fP. Note that a particular widget
+can be registered as a source, as a target, or as both.
+.PP
+The drag-and-drop process begins when the user clicks and holds a mouse
+button in a source window; a token window appears with an icon or message
+to represent the data being transferred. As the user moves the mouse pointer,
+the token window follows along, acting as a movable packet of data.
+Whenever the mouse pointer falls on a valid target window, the border of the
+token window is changed to a raised (active) state. When the mouse button is
+released over the target window, a Tcl routine is invoked to send the data
+to the desired application, and the target window is asked to "handle"
+the data. If this communication process fails, a rejection symbol (a
+circle with a line through it) is displayed on the token window to
+indicate failure.
+.PP
+The details of the communication process are fully configurable by the
+application developer. In the simplest case, the value that is sent
+to the target window is a simple string. The target window is simply
+asked to "handle" that string value. In general, the source window
+can have a special "handler" procedure to transfer a particular data
+type by issuing a series of "send" commands. After this, the target
+window is again asked to "handle" the result.
+.PP
+Both sources and targets can have a list of "handlers" for different
+data types. As a token window is dragged from its source to various
+targets, each target is checked to see if it recognizes a handler
+offered by the source. If it does, it is treated as a valid target.
+Otherwise, it is ignored. This scheme allows the same source to
+interact with many different kinds of targets. For example, a source
+for RGB color samples might have "color" and "string" handlers. This
+would allow it to communicate with "color" targets (sending RGB data)
+as well as entry widgets (sending strings of the form "#rrggbb").
+.PP
+This introduction was presented as a brief overview of the communication
+process; further details are presented below:
+.TP
+\fBdrag&drop source\fR
+Returns a list of path names for widgets registered as drag&drop
+sources. Returns an empty string if no widgets have been registered.
+.TP
+\fBdrag&drop source \fIwindow \fR?\fIoptions\fR?
+Registers a new drag&drop source window with the given options, or
+modifies the options for an existing window:
+.RS
+.LP
+.nf
+Name: \fBbuttonBinding\fR
+Class: \fBButtonBinding\fR
+Switch: \fB\-button\fR \fIn\fR
+.fi
+.IP
+Specifies the mouse button (integer 1-5) that will invoke the drag&drop
+operation on the source window. This causes the following bindings to
+be added to the widget:
+.sp
+.nf
+.RS
+\f(CWbind \fIwin\fP <ButtonPress-\fIn\fP> {drag&drop drag %W %X %Y}
+\f(CWbind \fIwin\fP <B\fIn\fP-Motion> {drag&drop drag %W %X %Y}
+\f(CWbind \fIwin\fP <ButtonRelease-\fIn\fP> {drag&drop drop %W %X %Y}\fR
+.RE
+.fi
+.sp
+The default value is button 3. If the value "0" is specified, then no
+bindings are added; this enables the user to establish bindings
+manually.
+.LP
+.nf
+Name: \fBpackageCommand\fR
+Class: \fBCommand\fR
+Switch: \fB\-packagecmd \fIcommand\fR
+.fi
+.IP
+Specifies a Tcl command used to establish the appearance of the token
+window at the start of each drag&drop operation. This command is
+automatically invoked by the \fBdrag&drop drag\fP command whenever the
+token window is about to be mapped for a drag operation. It should
+update the appearance of the token window to represent the data that
+is being moved.
+.PP
+The following substitutions are made in the \fIcommand\fR string
+before it is executed:
+.RS
+.TP
+\fB%t\fR
+Replaced with the window path name for the token which represents
+the data being dragged.
+.TP
+\fB%W\fR
+Replaced with the window path name for the drag&drop source.
+.RE
+.LP
+The return value from the package command represents the data being
+transferred. If the package command returns an empty string, the
+drag operation is quietly aborted. This can be used to disallow
+drag&drop operations from certain parts of a widget, if the drag
+position is inappropriate.
+.LP
+For example, the following package routine will select an item
+from a listbox and configure the token window to display the selected
+string. It uses the \fBdrag&drop location\fR command to
+determine the entry in the listbox that the user has selected
+and it returns this as the data value:
+.sp
+.nf
+.RS
+\f(CWproc package_list_item {lbox token} {
+ set xy [drag&drop location]
+ set y [expr [lindex $xy 1]-[winfo rooty $lbox]]
+
+ set str [$lbox get [$lbox nearest $y]]
+ $token.value configure -text $str
+ return $str
+}\fR
+.RE
+.fi
+.sp
+The return value is available later when the source and target
+communicate. If the source has a command associated with its
+data handler, then this value is substituted in place of "%v"
+in the source handler. Otherwise, it is substituted in place
+of "%v" in the target handler.
+.LP
+.nf
+Name: \fBrejectBackground\fR
+Class: \fBBackground\fR
+Switch: \fB\-rejectbg \fIcolor\fR
+.fi
+.IP
+Specifies the color used to draw the background of the rejection symbol
+on the token window. The rejection symbol (a circle with a line through
+it--the international "no") appears whenever communication fails.
+.LP
+.nf
+Name: \fBrejectForeground\fR
+Class: \fBForeground\fR
+Switch: \fB\-rejectfg \fIcolor\fR
+.fi
+.IP
+Specifies the color used to draw the foreground of the rejection symbol
+on the token window.
+.LP
+.nf
+Name: \fBrejectStipple\fR
+Class: \fBStipple\fR
+Switch: \fB\-rejectstipple \fIpattern\fR
+.fi
+.IP
+Specifies a stipple pattern used to draw the foreground of the rejection
+symbol on the token window. Any of the forms acceptable to Tk_GetBitmap
+can be used.
+.LP
+.nf
+Name: \fBselfTarget\fR
+Class: \fBSelfTarget\fR
+Switch: \fB\-selftarget \fIboolean\fR
+.fi
+.IP
+If the \fIboolean\fR value is true, and if a source widget is also
+registered as a compatible target, then the source will be able to transmit
+to itself during drag&drop operations. This is primarily useful for
+complex sources such as a canvas widget, where items may be moved from
+place to place within the same widget. By default, this option is disabled.
+.LP
+.nf
+Name: \fBsend\fR
+Class: \fBSend\fR
+Switch: \fB\-send \fIlist\fR
+.fi
+.IP
+Specifies a \fIlist\fR of \fIdataTypes\fR enabled for communication. Only
+\fIdataTypes\fR defined by commands of the form "\fBdrag&drop source
+\fIwindow \fBhandler \fR?\fIdataType\fR ?\fIcommand arg arg...\fR?" are
+allowed. This list also determines the priority of the various
+\fIdataTypes\fR.
+When a token window is over a potential drag&drop target, this list is
+searched from start to finish for a \fIdataType\fR that is also recognized
+by the target. The first matching \fIdataType\fR found determines the
+value that will be sent if the token is dropped. If no matching \fIdataType\fR
+is found, then the target is incompatible, and is ignored. By default,
+this option has the value "all", indicating that all \fIdataTypes\fR should
+be considered in the order that they were defined for the source.
+.LP
+Note that this option makes it easy to control a drag&drop source. Setting
+the value to an empty string disables the source; setting the value back
+to "all" restores communication.
+.LP
+.nf
+Name: \fBsiteCommand\fR
+Class: \fBCommand\fR
+Switch: \fB\-sitecmd \fIcommand\fR
+.fi
+.IP
+Specifies a Tcl command used to update the appearance of the token window.
+If specified, this command is automatically invoked by the
+\fBdrag&drop drag\fP command whenever the token window is over a
+compatible drag&drop target.
+.PP
+The following substitutions are made in the \fIcommand\fR string
+before it is executed:
+.RS
+.TP
+\fB%s\fR
+Replaced with "1" if the token window is over a compatible target,
+and "0" otherwise.
+.TP
+\fB%t\fR
+Replaced with the window path name for the token which represents
+the data being dragged.
+.RE
+.LP
+Regardless of this command, border of the token window will become
+raised whenever the token is over a valid target. This command
+can be used to display other visual cues.
+.LP
+.nf
+Name: \fBtokenAnchor\fR
+Class: \fBAnchor\fR
+Switch: \fB\-tokenanchor \fIanchor\fR
+.fi
+.IP
+Specifies how the token window is positioned relative to the mouse
+pointer coordinates passed to the \fBdrag&drop drag\fP command.
+Must be one of the values n, s, e, w, center, nw, ne, sw or se.
+For example, "nw" means to position the token such that its upper-left
+corner is at the mouse pointer. The default value is "center".
+.LP
+.nf
+Name: \fBtokenBackground\fR
+Class: \fBBackground\fR
+Switch: \fB\-tokenbg \fIcolor\fR
+.fi
+.IP
+Specifies the color used to draw the background of the token window.
+.LP
+.nf
+Name: \fBtokenBorderWidth\fR
+Class: \fBBorderWidth\fR
+Switch: \fB\-tokenborderwidth \fIsize\fR
+.fi
+.IP
+Specifies the width in pixels of the border around the token window.
+This border becomes raised to indicate when the token is over a compatible
+drag&drop target site. The value may have any of the forms acceptable
+to Tk_GetPixels. The default value is "3".
+.LP
+.nf
+Name: \fBtokenCursor\fR
+Class: \fBCursor\fR
+Switch: \fB\-tokencursor \fIcursor\fR
+.fi
+.IP
+Specifies the cursor used when a token window is active. The value
+may have any of the forms acceptable to Tk_GetCursor. The default
+value is "center_ptr".
+.RE
+.TP
+\fBdrag&drop source \fIwindow \fBhandler \fR?\fIdataType\fR? ?\fIcommand arg arg...\fR?
+With no extra arguments, this command returns a list of all \fIdataType\fR
+names that have been registered for the source \fIwindow\fR. If only the
+\fIdataType\fR is specified, then the \fIdataType\fR is created if
+necessary, and the command associated with the \fIdataType\fR is returned.
+Otherwise, it concatenates the \fIcommand\fR and any extra \fIarg\fR strings,
+and registers a new \fIdataType\fR with this command.
+.PP
+The following substitutions are made in the \fIcommand\fR string
+before it is executed:
+.RS
+.TP
+\fB%i\fR
+Replaced with the name of the interpreter for the target application.
+.TP
+\fB%v\fR
+Replaced with the value returned from the "-packagecmd" command.
+.TP
+\fB%w\fR
+Replaced with the window path name for the target window.
+.RE
+.LP
+A typical source handler contains one or more "send" commands which
+transfer data to the remote application. The target window is then
+asked to handle the new data. Whatever value is returned by the
+source \fIcommand\fR handler is automatically substituted into the
+"%v" fields of the target handler.
+.LP
+This separation between the transfer and the handling of the data is
+important. It allows the same source handler to transfer data for
+many different targets, and it allows each of the targets to handle
+the incoming data differently. If an error is encountered during the
+communication process, the rejection symbol is posted on the token window
+to indicate failure.
+.RE
+.sp
+.TP
+\fBdrag&drop target\fR
+Returns a list of path names for widgets registered as drag&drop
+targets. Returns an empty string if no widgets have been registered.
+.TP
+\fBdrag&drop target \fIwindow \fBhandler \fR?\fIdataType command arg arg...\fR?
+Registers a new drag&drop target window with a given handler, or
+modifies the handlers for an existing window. If no \fIdataType\fR
+is specified, this command returns the current list of recognized
+\fIdataType\fR strings. Each \fIdataType\fR is a symbolic name
+representing a form of data, and the corresponding \fIcommand\fR is
+a Tcl command that specifies how the target will make use of the data.
+This command is invoked indirectly after a source has transferred data
+to a target application.
+.PP
+The following substitutions are made in the \fIcommand\fR string
+before it is executed:
+.RS
+.TP
+\fB%v\fR
+In the simplest case, the source window does not have a handler command
+for the selected \fIdataType\fR, and this field is replaced with the
+result from the "-packagecmd" command. When the source does have a
+handler command, the result from the "-packagecmd" command is substituted
+into its "%v" field, and the result from this command is substituted
+into this field in the target command.
+.TP
+\fB%W\fR
+Replaced with the window path name for the target window.
+.RE
+.TP
+\fBdrag&drop target \fIwindow \fRhandle \fIdataType\fR ?\fIvalue\fR?
+Searches for the given \fIdataType\fR name among the handlers registered
+for the target \fIwindow\fR, and invokes the appropriate \fIcommand\fR.
+If a \fIvalue\fR is specified, it is substituted into any "%v" fields
+in the handler command associated with the \fIdataType\fR. If the
+\fIdataType\fR name is not recognized, this command returns an error.
+This command is invoked automatically by the drag&drop facility when
+data is being transferred from a source to a target.
+.TP
+\fBdrag&drop token \fIwindow\fR
+Returns the token window associated with a drag&drop source \fIwindow\fR.
+The token window is used to represent data as it is being dragged from
+the source to a target. When a source is first established, its token
+window must be filled with widgets to display the source data. For
+example,
+.sp
+.nf
+.RS
+\f(CWdrag&drop source .foo
+
+set win [drag&drop token .foo]
+label $win.label -text "Data"
+pack $win.label\fR
+.RE
+.fi
+.sp
+.TP
+\fBdrag&drop drag \fIwindow x y\fR
+Marks the start of (or movement during) a drag&drop operation. If
+the token window is unmapped when this command is invoked, then the
+\fB\-packagecmd\fR for the source \fIwindow\fR is executed. If this
+command is successful and returns a non-null string, the token window
+is mapped. On subsequent calls, the token window is moved to the new
+\fIx y\fR location. Unless the "\fB\-button 0\fR" option is specified for
+the source, this command is automatically bound to <ButtonPress-\fIn\fR>
+and <B\fIn\fR-Motion> events for "\fB\-button \fIn\fR" of the source widget.
+.TP
+\fBdrag&drop drop \fIwindow x y\fR
+Marks the end of a drag&drop operation. If the mouse pointer is
+over a compatible target window, then the appropriate send handler for
+the first compatible \fIdataType\fR is invoked to handle the data transfer.
+If the data transfer is successful, then the token window is unmapped;
+otherwise, a rejection symbol is drawn on the token window, and the window
+is unmapped after a small delay. Unless the "\fB\-button 0\fR" option is
+specified for the source, this command is automatically bound to the
+<ButtonRelease-\fIn\fR> event for "\fB\-button \fIn\fR" of the source widget.
+.TP
+\fBdrag&drop active\fR
+Returns "1" if a drag&drop operation is in progress, and "0" otherwise.
+A drag&drop operation officially starts after the package command has
+been executed successfully, and ends after the send handler has been
+executed (successfully or otherwise).
+.TP
+\fBdrag&drop errors \fR?\fIproc\fR?
+Specifies a Tcl \fIproc\fR used to handle errors encountered during
+drag&drop operations. If a \fIproc\fR is not specified, this command
+returns the current error handler. By default, all errors are sent
+to the usual \fBtkerror\fR command, and therefore appear in a dialog
+box to the user. This behavior is quite useful when debugging
+communication protocols, but may not be desirable in a finished
+application. Errors can be suppressed entirely (leaving the rejection
+symbol as the only error indicator) by specifying a null string in
+place of the \fIproc\fR name.
+.TP
+\fBdrag&drop location \fR?\fIx y\fR?
+Used to set or query the pointer location during a drag&drop operation.
+The \fIx y\fR arguments specify the current location; if these arguments
+are missing, then the last reported (x,y) location is returned as a list
+with two elements. This command is issued automatically within the
+\fBdrag&drop drag\fR and \fBdrag&drop drop\fR commands, to
+keep track of pointer movement.
+
+.SH KEYWORDS
+drag&drop, send, bind, widget
diff --git a/blt/man/eps.mann b/blt/man/eps.mann
new file mode 100644
index 00000000000..3f31d80925c
--- /dev/null
+++ b/blt/man/eps.mann
@@ -0,0 +1,163 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Graph widget created by Sani Nassif and George Howlett.
+'\"
+.so man.macros
+.TH eps n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+eps \- Encapsulated PostScript canvas item.
+.SH SYNOPSIS
+\fIcanvas\fB create eps \fIx y \fR?\fIoption value\fR?...
+.BE
+.SH DESCRIPTION
+The \fBeps\fR canvas item lets you place encapulated PostScript (EPS)
+on a canvas, controlling its size and placement. The EPS item is
+displayed either as a solid rectangle or a preview image. The preview
+image is designated in one of two ways: 1) the EPS file
+contains an ASCII hexidecimal preview, or 2) a Tk photo image. When
+the canvas generates PostScript output, the EPS will be inserted with
+the proper translation and scaling to match that of the EPS item. So
+can use the canvas widget as a page layout tool.
+.SH EXAMPLE
+Let's say you have for PostScript files of four graphs which you
+want to tile two-by-two on a single page. Maybe you'd like
+to annotate the graphs by putting a caption at the bottom of
+each graph.
+.PP
+Normally, you would have to resort to an external tool or write your
+own PostScript program. The \fBeps\fR canvas item lets you do this
+through Tk's canvas widget. An \fBeps\fR item displays an
+image (or rectangle) representing the encapsulated PostScript file.
+It also scales and translates the EPS file when the canvas is printed.
+
+.SH SYNTAX
+.DS
+\fIcanvas \fBcreate eps \fIx y \fR?\fIoption value\fR?...
+.DE
+The \fBeps\fR item creates a new canvas item. \fICanvas\fR is the name
+of a \fBcanvas\fR widget. You must supply the X-Y coordinate of
+the new eps item. How the coordinate is exactly interpretered is
+controlled by the \fB\-anchor\fR option (see below).
+.PP
+Additional options may be specified on the command line to configure
+aspects of the eps item such as its color, stipple, and font. The
+following \fIoption\fR and \fIvalue\fR pairs are valid.
+.TP
+\fB\-anchor \fIanchor\fR
+Tells how to position the EPS item relative to its X-Y coordinate.
+The default is \f(CWcenter\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the EPS rectangle.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the item. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW0\fR.
+.TP
+\fB\-file \fIfileName\fR
+Specifies the name of the EPS file. The first line of an
+EPS file must start with "%!PS" and contain a "EPS" version
+specification. The other requirement is that there be a "%%BoundingBox:"
+entry which contains four integers representing the lower-left and
+upper-right coordinates of the area bounding the EPS.
+The default is \f(CW""\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-18-180-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Specifies the foreground color of the EPS rectangle. The option
+matters only when the \fB\-stipple\fR option is set.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the height EPS item. If \fIpixels\fR is \f(CW0\fR, then
+the height is determined from the PostScript "BoundingBox:" entry
+in the EPS file.
+The default is \f(CW0\fR.
+.TP
+\fB\-image \fIphoto\fR
+Specifies the name of a Tk photo image to be displayed as in
+the item as a preview image. This option overrides any preview
+specification found in the EPS file.
+The default is \f(CW""\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the title should be justified. This matters only when
+the title contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the EPS item. \fIRelief\fR
+specifies how the item should appear relative to canvas;
+for example, \f(CWraised\fR means the item should appear to
+protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-shadowcolor \fIcolor\fR
+Specifies the color of the drop shadow used for the title. The
+option with the \fB\-shadowoffset\fR option control how the
+title's drop shadow appears.
+The default is \f(CWgrey\fR.
+.TP
+\fB\-shadowoffset \fIpixels\fR
+Specifies the offset of the drop shadow from the title's text.
+If \fIpixels\fR is \f(CW0\fR, no shadow will be seen.
+The default is \f(CW0\fR.
+.TP
+\fB\-showimage \fIboolean\fR
+Indicates whether to display the image preview (if one exists),
+or a simple rectangle.
+The default is \f(CWyes\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a bitmap to used to stipple the rectangle representing
+the EPS item. The default is \f(CW""\fR.
+.TP
+\fB\-title \fIstring\fR
+Sets the title of the EPS item. If \fIstring\fR is \f(CW""\fR,
+then the title specified by the PostScript "Title:" entry
+is used. You can set the string a single space to display
+no title. The default is \f(CW""\fR.
+.TP
+\fB\-titleanchor \fIanchor\fR
+Tells how to position the title within EPS item.
+The default is \f(CWn\fR.
+.TP
+\fB\-titlecolor \fIcolor\fR
+Specifies the color of the title.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-titlerotate \fIdegrees\fR
+Sets the rotation of the title. \fIDegrees\fR is a real number
+representing the angle of rotation.
+The title is first rotated in space and then placed according to
+the \fB\-titleanchor\fR position. The default rotation is \f(CW0.0\fR.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the width EPS item. If \fIpixels\fR is \f(CW0\fR, then
+the width is determined from the PostScript "BoundingBox:" entry
+in the EPS file.
+The default is \f(CW0\fR.
+\f(CW5i\fR.
diff --git a/blt/man/graph.mann b/blt/man/graph.mann
new file mode 100644
index 00000000000..02a011a825b
--- /dev/null
+++ b/blt/man/graph.mann
@@ -0,0 +1,2329 @@
+'\"
+'\" Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Graph widget created by Sani Nassif and George Howlett.
+'\"
+.so man.macros
+.TH graph n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+graph \- 2D graph for plotting X\-Y coordinate data.
+.SH SYNOPSIS
+\fBgraph\fI \fIpathName \fR?\fIoption value\fR?...
+.BE
+.SH DESCRIPTION
+The \fBgraph\fR command creates a graph for plotting
+two-dimensional data (X\-Y coordinates). It has many configurable
+components: coordinate axes, elements, legend, grid lines, cross
+hairs, etc. They allow you to customize the look and feel of the
+graph.
+.SH INTRODUCTION
+The \fBgraph\fR command creates a new window for plotting
+two-dimensional data (X\-Y coordinates). Data points are plotted in a
+rectangular area displayed in the center of the new window. This is the
+\fIplotting area\fR. The coordinate axes are drawn in the
+margins around the plotting area. By default, the legend is displayed
+in the right margin. The title is displayed in top margin.
+.PP
+The \fBgraph\fR widget is composed of several components: coordinate
+axes, data elements, legend, grid, cross hairs, pens, postscript, and
+annotation markers.
+.TP 1i
+\f(CWaxis\fR
+The graph has four standard axes (\f(CWx\fR, \f(CWx2\fR,
+\f(CWy\fR, and \f(CWy2\fR), but you can create and display any number
+of axes. Axes control what region of data is
+displayed and how the data is scaled. Each axis consists of the axis
+line, title, major and minor ticks, and tick labels. Tick labels
+display the value at each major tick.
+.TP 1i
+\f(CWcrosshairs\fR
+Cross hairs are used to position the mouse pointer relative to the X
+and Y coordinate axes. Two perpendicular lines, intersecting at the
+current location of the mouse, extend across the plotting area to the
+coordinate axes.
+.TP 1i
+\f(CWelement\fR
+An element represents a set of data points. Elements can be plotted
+with a symbol at each data point and lines connecting the points.
+The appearance of the element, such as its symbol, line width, and
+color is configurable.
+.TP 1i
+\f(CWgrid\fR
+Extends the major and minor ticks of the X\-axis and/or Y\-axis across the
+plotting area.
+.TP 1i
+\f(CWlegend\fR
+The legend displays the name and symbol of each data element.
+The legend can be drawn in any margin or in the plotting area.
+.TP 1i
+\f(CWmarker\fR
+Markers are used annotate or highlight areas of the graph. For
+example, you could use a polygon marker to fill an area under a
+curve, or a text marker to label a particular data point. Markers
+come in various forms: text strings, bitmaps, connected line
+segments, images, polygons, or embedded widgets.
+.TP 1i
+\f(CWpen\fR
+Pens define attributes (both symbol and line style) for elements.
+Data elements use pens to specify how they should be drawn. A data
+element may use many pens at once. Here, the particular pen
+used for a data point is determined from each element's weight
+vector (see the element's \fB\-weight\fR and \fB\-style\fR options).
+.TP 1i
+\f(CWpostscript\fR
+The widget can generate encapsulated PostScript output. This component
+has several options to configure how the PostScript is generated.
+.SH SYNTAX
+.DS
+\fBgraph \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBgraph\fR command creates a new window \fIpathName\fR and makes
+it into a \fBgraph\fR widget. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist. Additional options may be specified on the
+command line or in the option database to configure aspects of the
+graph such as its colors and font. See the \fBconfigure\fR operation
+below for the exact details about what \fIoption\fR and \fIvalue\fR
+pairs are valid.
+.PP
+If successful, \fBgraph\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to invoke various operations that query or modify the graph.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for the graph are described in
+the
+.SB "GRAPH OPERATIONS"
+section.
+.PP
+The command can also be used to access components of the graph.
+.DS
+\fIpathName component operation\fR ?\fIarg\fR?...
+.DE
+The operation, now located after the name of the component, is the
+function to be performed on that component. Each component has its own
+set of operations that manipulate that component. They will be
+described below in their own sections.
+.SH EXAMPLE
+The \fBgraph\fR command creates a new graph.
+.CS
+# Create a new graph. Plotting area is black.
+graph .g \-plotbackground black
+.CE
+A new Tcl command \f(CW.g\fR is also created. This command can be used
+to query and modify the graph. For example, to change the title of
+the graph to "My Plot", you use the new command and the graph's
+\fBconfigure\fR operation.
+.CS
+# Change the title.
+\&.g configure \-title "My Plot"
+.CE
+A graph has several components. To access a particular component you
+use the component's name. For example, to add data elements, you use
+the new command and the \fBelement\fR component.
+.CS
+# Create a new element named "line1"
+\&.g element create line1 \\
+ \-xdata { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 } \\
+ \-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14
+ 155.85 166.60 175.38 }
+.CE
+The element's X-Y coordinates are specified using lists of
+numbers. Alternately, BLT vectors could be used to hold the X\-Y
+coordinates.
+.CS
+# Create two vectors and add them to the graph.
+vector xVec yVec
+xVec set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }
+yVec set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85
+ 166.60 175.38 }
+\&.g element create line1 \-xdata xVec \-ydata yVec
+.CE
+The advantage of using vectors is that when you modify one, the graph
+is automatically redrawn to reflect the new values.
+.CS
+# Change the y coordinate of the first point.
+set yVector(0) 25.18
+.CE
+An element named \f(CWe1\fR is now created in \f(CW.b\fR. It
+is automatically added to the display list of elements. You can
+use this list to control in what order elements are displayed.
+To query or reset the element display list, you use the element's
+\fBshow\fR operation.
+.CS
+# Get the current display list
+set elemList [.b element show]
+# Remove the first element so it won't be displayed.
+\&.b element show [lrange $elemList 0 end]
+.CE
+The element will be displayed by as many bars as there are data points
+(in this case there are ten). The bars will be drawn centered at the
+x-coordinate of the data point. All the bars will have the same
+attributes (colors, stipple, etc). The width of each bar is by
+default one unit. You can change this with using the \fB\-barwidth\fR
+option.
+.CS
+# Change the X\-Y coordinates of the first point.
+set xVec(0) 0.18
+set yVec(0) 25.18
+.CE
+An element named \f(CWline1\fR is now created in \f(CW.g\fR. By
+default, the element's label in the legend will be also \f(CWline1\fR.
+You can change the label, or specify no legend entry, again using the
+element's \fBconfigure\fR operation.
+.CS
+# Don't display "line1" in the legend.
+\&.g element configure line1 \-label ""
+.CE
+You can configure more than just the element's label. An element has
+many attributes such as symbol type and size, dashed or solid lines,
+colors, line width, etc.
+.CS
+\&.g element configure line1 \-symbol square \-color red \\
+ \-dashes { 2 4 2 } \-linewidth 2 \-pixels 2c
+.CE
+Four coordinate axes are automatically created: \f(CWx\fR, \f(CWx2\fR,
+\f(CWy\fR, and \f(CWy2\fR. And by default, elements are mapped onto the
+axes \f(CWx\fR and \f(CWy\fR. This can be changed with the \fB\-mapx\fR
+and \fB\-mapy\fR options.
+.CS
+# Map "line1" on the alternate Y\-axis "y2".
+\&.g element configure line1 \-mapy y2
+.CE
+Axes can be configured in many ways too. For example, you change the
+scale of the Y\-axis from linear to log using the \fBaxis\fR component.
+.CS
+# Y\-axis is log scale.
+\&.g axis configure y \-logscale yes
+.CE
+One important way axes are used is to zoom in on a particular data
+region. Zooming is done by simply specifying new axis limits using
+the \fB\-min\fR and \fB\-max\fR configuration options.
+.CS
+\&.g axis configure x \-min 1.0 \-max 1.5
+\&.g axis configure y \-min 12.0 \-max 55.15
+.CE
+To zoom interactively, you link the \fBaxis configure\fR operations with
+some user interaction (such as pressing the mouse button), using the
+\fBbind\fR command. To convert between screen and graph coordinates,
+use the \fBinvtransform\fR operation.
+.CS
+# Click the button to set a new minimum
+bind .g <ButtonPress-1> {
+ %W axis configure x \-min [%W axis invtransform x %x]
+ %W axis configure x \-min [%W axis invtransform x %y]
+}
+.CE
+By default, the limits of the axis are determined from data values.
+To reset back to the default limits, set the \fB\-min\fR and
+\fB\-max\fR options to the empty value.
+.CS
+# Reset the axes to autoscale again.
+\&.g axis configure x \-min {} \-max {}
+\&.g axis configure y \-min {} \-max {}
+.CE
+By default, the legend is drawn in the right margin. You can
+change this or any legend configuration options using the
+\fBlegend\fR component.
+.CS
+# Configure the legend font, color, and relief
+\&.g legend configure \-position left \-relief raised \\
+ \-font fixed \-fg blue
+.CE
+To prevent the legend from being displayed, turn on the \fB\-hide\fR
+option.
+.CS
+# Don't display the legend.
+\&.g legend configure \-hide yes\fR
+.CE
+The \fBgraph\fR widget has simple drawing procedures called markers.
+They can be used to highlight or annotate data in the graph. The types
+of markers available are bitmaps, images, polygons, lines, or windows.
+Markers can be used, for example, to mark or brush points. In this
+example, is a text marker that labels the data first point. Markers
+are created using the \fBmarker\fR component.
+.CS
+# Create a label for the first data point of "line1".
+\&.g marker create text \-name first_marker \-coords { 0.2 26.18 } \\
+ \-text "start" \-anchor se \-xoffset -10 \-yoffset -10
+.CE
+This creates a text marker named \f(CWfirst_marker\fR. It will display
+the text "start" near the coordinates of the first data point. The
+\fB\-anchor\fR, \fB\-xoffset\fR, and \fB\-yoffset\fR options are used
+to display the marker above and to the left of the data point, so that
+the data point isn't covered by the marker. By default,
+markers are drawn last, on top of data. You can change this with the
+\fB\-under\fR option.
+.CS
+# Draw the label before elements are drawn.
+\&.g marker configure first_marker \-under yes
+.CE
+You can add cross hairs or grid lines using the \fBcrosshairs\fR and
+\fBgrid\fR components.
+.CS
+# Display both cross hairs and grid lines.
+\&.g crosshairs configure \-hide no \-color red
+\&.g grid configure \-hide no \-dashes { 2 2 }
+# Set up a binding to reposition the crosshairs.
+bind .g <Motion> {
+ .g crosshairs configure -position @%x,%y
+}
+.CE
+The crosshairs are repositioned as the mouse pointer is moved
+in the graph. The pointer X-Y coordinates define the center
+of the crosshairs.
+.PP
+Finally, to get hardcopy of the graph, use the \fBpostscript\fR
+component.
+.CS
+# Print the graph into file "file.ps"
+\&.g postscript output file.ps \-maxpect yes \-decorations no
+.CE
+This generates a file \f(CWfile.ps\fR containing the encapsulated
+PostScript of the graph. The option \fB\-maxpect\fR says to scale the
+plot to the size of the page. Turning off the \fB\-decorations\fR
+option denotes that no borders or color backgrounds should be
+drawn (i.e. the background of the margins, legend, and plotting
+area will be white).
+.SH "GRAPH OPERATIONS"
+.TP
+\fIpathName \fBaxis \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "AXIS COMPONENTS"
+section.
+.TP
+\fIpathName \fBbar \fIelemName \fR?\fIoption value\fR?...
+Creates a new barchart element \fIelemName\fR. It's an
+error if an element \fIelemName\fR already exists.
+See the manual for \fBbarchart\fR for details about
+what \fIoption\fR and \fIvalue\fR pairs are valid.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBconfigure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the graph. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the option \fIoption\fR is set to \fIvalue\fR.
+The following options are valid.
+.RS
+.TP
+\fB\-aspect \fIwidth/height\fR
+Force a fixed aspect ratio of \fIwidth/height\fR, a floating point number.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color. This includes the margins and
+legend, but not the plotting area.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-bottommargin \fIpixels\fR
+If non-zero, overrides the computed size of the margin extending
+below the X\-coordinate axis.
+If \fIpixels\fR is \f(CW0\fR, the automatically computed size is used.
+The default is \f(CW0\fR.
+.TP
+\fB\-bufferelements \fIboolean\fR
+Indicates whether an internal pixmap to buffer the display of data
+elements should be used. If \fIboolean\fR is true, data elements are
+drawn to an internal pixmap. This option is especially useful when
+the graph is redrawn frequently while the remains data unchanged (for
+example, moving a marker across the plot). See the
+.SB "SPEED TIPS"
+section.
+The default is \f(CW1\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CWcrosshair\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the graph title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-18-180-*\fR.
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a maximum distance to consider when searching for the
+closest data point (see the element's \fBclosest\fR operation below).
+Data points further than \fIpixels\fR away are ignored. The default is
+\f(CW0.5i\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW4i\fR.
+.TP
+\fB\-invertxy \fIboolean\fR
+Indicates whether the placement X\-axis and Y\-axis should be inverted. If
+\fIboolean\fR is true, the X and Y axes are swapped. The default is
+\f(CW0\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the title should be justified. This matters only when
+the title contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-leftmargin \fIpixels\fR
+If non-zero, overrides the computed size of the margin extending
+from the left edge of the window to the Y\-coordinate axis.
+If \fIpixels\fR is \f(CW0\fR, the automatically computed size is used.
+The default is \f(CW0\fR.
+.TP
+\fB\-plotbackground \fIcolor\fR
+Specifies the background color of the plotting area. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-plotborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the plotting area. The
+\fB\-plotrelief\fR option determines if a border is drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-plotpadx \fIpad\fR
+Sets the amount of padding to be added to the left and right sides of
+the plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the left side of the
+plotting area entry is padded by the first distance and the right side
+by the second. If \fIpad\fR is just one distance, both the left and
+right sides are padded evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotpady \fIpad\fR
+Sets the amount of padding to be added to the top and bottom of the
+plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the top of the plotting
+area is padded by the first distance and the bottom by the second. If
+\fIpad\fR is just one distance, both the top and bottom are padded
+evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotrelief \fIrelief\fR
+Specifies the 3-D effect for the plotting area. \fIRelief\fR
+specifies how the interior of the plotting area should appear relative
+to rest of the graph; for example, \f(CWraised\fR means the plot should
+appear to protrude from the graph, relative to the surface of the
+graph. The default is \f(CWsunken\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the graph widget. \fIRelief\fR
+specifies how the graph should appear relative to widget it is packed
+into; for example, \f(CWraised\fR means the graph should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-rightmargin \fIpixels\fR
+If non-zero, overrides the computed size of the margin extending
+from the plotting area to the right edge of
+the window. By default, the legend is drawn in this margin.
+If \fIpixels\fR is \f(CW0\fR, the automatically computed size is used.
+The default is \f(CW0\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW""\fR.
+.TP
+\fB\-tile \fIimage\fR
+Specifies a tiled background for the widget. If \fIimage\fR isn't
+\f(CW""\fR, the background is tiled using \fIimage\fR.
+Otherwise, the normal background color is drawn (see the
+\fB\-background\fR option). \fIImage\fR must be an image created
+using the Tk \fBimage\fR command. The default is \f(CW""\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title to \fItext\fR. If \fItext\fR is \f(CW""\fR,
+no title will be displayed.
+.TP
+\fB\-topmargin \fIpixels\fR
+If non-zero, overrides the computed size of the margin above the x2
+axis. If \fIpixels\fR is \f(CW0\fR, the automatically computed size
+is used. The default is \f(CW0\fR.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the requested width of the widget. The default is
+\f(CW5i\fR.
+.RE
+.TP
+\fIpathName \fBcrosshairs \fIoperation \fR?\fIarg\fR?
+See the
+.SB "CROSSHAIRS COMPONENT"
+section.
+.TP
+\fIpathName \fBelement \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "ELEMENT COMPONENTS"
+section.
+.TP
+\fIpathName \fBextents \fIitem\fR
+Returns the size of a particular item in the graph. \fIItem\fR must
+be either \f(CWleftmargin\fR, \f(CWrightmargin\fR, \f(CWtopmargin\fR,
+\f(CWbottommargin\fR, \f(CWplotwidth\fR, or \f(CWplotheight\fR.
+.TP
+\fIpathName \fBgrid \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "GRID COMPONENT"
+section.
+.TP
+\fIpathName \fBinvtransform \fIwinX winY\fR
+Performs an inverse coordinate transformation, mapping window
+coordinates back to graph coordinates, using the standard X\-axis and Y\-axis.
+Returns a list of containing the X-Y graph coordinates.
+.TP
+\fIpathName \fBinside \fIx y\fR
+Returns \f(CW1\fR is the designated screen coordinate (\fIx\fR and \fIy\fR)
+is inside the plotting area and \f(CW0\fR otherwise.
+.TP
+\fIpathName \fBlegend \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "LEGEND COMPONENT"
+section.
+.TP
+\fIpathName \fBline\fB operation arg\fR...
+The operation is the same as \fBelement\fR.
+.TP
+\fIpathName \fBmarker \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "MARKER COMPONENTS"
+section.
+.TP
+\fIpathName\fR \fBmetafile\fR ?\fIfileName\fR?
+\fIThis operation is for Window platforms only\fR.
+Creates a Windows enhanced metafile of the graph.
+If present, \fIfileName\fR is the file name of the new metafile.
+Otherwise, the metafile is automatically added to the clipboard.
+.TP
+\fIpathName \fBpostscript \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "POSTSCRIPT COMPONENT"
+section.
+.TP
+\fIpathName \fBsnap \fIphotoName\fR
+Takes a snapshot of the graph and stores the contents in the photo
+image \fIphotoName\fR. \fIPhotoName\fR is the name of a Tk photo
+image that must already exist.
+.TP
+\fIpathName \fBtransform \fIx y\fR
+Performs a coordinate transformation, mapping graph coordinates to
+window coordinates, using the standard X\-axis and Y\-axis.
+Returns a list containing the X\-Y screen coordinates.
+.TP
+\fIpathName \fBxaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBx2axis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fByaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBy2axis \fIoperation\fR ?\fIarg\fR?...
+See the
+.SB "AXIS COMPONENTS"
+section.
+.SH "GRAPH COMPONENTS"
+A graph is composed of several components: coordinate axes, data
+elements, legend, grid, cross hairs, postscript, and annotation
+markers. Instead of one big set of configuration options and
+operations, the graph is partitioned, where each component has its own
+configuration options and operations that specifically control that
+aspect or part of the graph.
+.SS "AXIS COMPONENTS"
+Four coordinate axes are automatically created: two X\-coordinate axes
+(\f(CWx\fR and \f(CWx2\fR) and two Y\-coordinate axes (\f(CWy\fR, and
+\f(CWy2\fR). By default, the axis \f(CWx\fR is located in the bottom
+margin, \f(CWy\fR in the left margin, \f(CWx2\fR in the top margin, and
+\f(CWy2\fR in the right margin.
+.PP
+An axis consists of the axis line, title, major and minor ticks, and
+tick labels. Major ticks are drawn at uniform intervals along the
+axis. Each tick is labeled with its coordinate value. Minor ticks
+are drawn at uniform intervals within major ticks.
+.PP
+The range of the axis controls what region of data is plotted.
+Data points outside the minimum and maximum limits of the axis are
+not plotted. By default, the minimum and maximum limits are
+determined from the data, but you can reset either limit.
+.PP
+You can have several axes. To create an axis, invoke
+the axis component and its create operation.
+.CS
+# Create a new axis called "tempAxis"
+\&.g axis create tempAxis
+.CE
+You map data elements to an axis using the element's \-mapy and \-mapx
+configuration options. They specify the coordinate axes an element
+is mapped onto.
+.CS
+# Now map the tempAxis data to this axis.
+\&.g element create "e1" \-xdata $x \-ydata $y \-mapy tempAxis
+.CE
+Any number of axes can be displayed simultaneously. They are drawn in
+the margins surrounding the plotting area. The default axes \f(CWx\fR
+and \f(CWy\fR are drawn in the bottom and left margins. The axes
+\f(CWx2\fR and \f(CWy2\fR are drawn in top and right margins. By
+default, only \f(CWx\fR and \f(CWy\fR are shown. Note that the axes
+can have different scales.
+.PP
+To display a different axis or more than one axis, you invoke one of
+the following components: \fBxaxis\fR, \fByaxis\fR, \fBx2axis\fR, and
+\fBy2axis\fR. Each component has a \fBuse\fR operation that
+designates the axis (or axes) to be drawn in that corresponding
+margin: \fBxaxis\fR in the bottom, \fByaxis\fR in the left,
+\fBx2axis\fR in the top, and \fBy2axis\fR in the right.
+.CS
+# Display the axis tempAxis in the left margin.
+\&.g yaxis use tempAxis
+.CE
+The \fBuse\fR operation takes a list of axis names as its last
+argument. This is the list of axes to be drawn in this margin.
+.PP
+You can configure axes in many ways. The axis scale can be linear or
+logarithmic. The values along the axis can either monotonically
+increase or decrease. If you need custom tick labels, you can specify
+a Tcl procedure to format the label any way you wish. You can control
+how ticks are drawn, by changing the major tick interval or the number
+of minor ticks. You can define non-uniform tick intervals, such as
+for time-series plots.
+.PP
+.TP
+\fIpathName \fBaxis bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an axis with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on graph axes, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBaxis \fBcget \fIaxisName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIaxisName\fR. \fIOption\fR may be any option described below
+for the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBconfigure \fIaxisName \fR?\fIaxisName\fR?... ?\fIoption value\fR?...
+Queries or modifies the configuration options of \fIaxisName\fR.
+Several axes can be changed. If \fIoption\fR isn't specified, a list
+describing all the current options for \fIaxisName\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+\fIoption\fR is returned. If one or more \fIoption\fR and \fIvalue\fR
+pairs are specified, then for each pair, the axis option \fIoption\fR
+is set to \fIvalue\fR. The following options are valid for axes.
+.RS
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for the axis. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events for axes are handled. Each tag in the list matching the current event
+sequence will have its Tcl command executed. Implicitly the name of
+the element is always the first tag in the list. The default value is
+\f(CWall\fR.
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the axis and tick labels.
+The default is \f(CWblack\fR.
+.TP
+\fB\-command \fIprefix\fR
+Specifies a Tcl command to be invoked when formatting the axis tick
+labels. \fIPrefix\fR is a string containing the name of a Tcl proc and
+any extra arguments for the procedure. This command is invoked for each
+major tick on the axis. Two additional arguments are passed to the
+procedure: the pathname of the widget and the current the numeric
+value of the tick. The procedure returns the formatted tick label. If
+\f(CW""\fR is returned, no label will appear next to the tick. You can
+get the standard tick labels again by setting \fIprefix\fR to
+\f(CW""\fR. The default is \f(CW""\fR.
+.sp 1
+Please note that this procedure is invoked while the graph is redrawn.
+You may query configuration options. But do not them, because this
+can have unexpected results.
+.TP
+\fB\-descending \fIboolean\fR
+Indicates whether the values along the axis are monotonically increasing or
+decreasing. If \fIboolean\fR is true, the axis values will be
+decreasing. The default is \f(CW0\fR.
+.TP
+\fB\-hide \fIstring\fR
+Indicates if the axis and all the elements mapped to it will be
+displayed. The valid values for \fIstring\fR are shown below.
+The default value is \f(CW0\fR.
+.RS
+.TP
+\f(CWfalse\fR
+The axis and its data elements are displayed.
+.TP
+\f(CWtrue\fR
+The axis is hidden, but the data elements mapped to it are displayed.
+.TP
+\f(CWall\fR
+The axis and its data elements are hidden.
+.RE
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the axis title should be justified. This matters only
+when the axis title contains more than one line of text. \fIJustify\fR
+must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-limits \fIformatStr\fR
+Specifies a printf-like description to format the minimum and maximum
+limits of the axis. The limits are displayed at the top/bottom or
+left/right sides of the plotting area. \fIFormatStr\fR is a list of
+one or two format descriptions. If one description is supplied, both
+the minimum and maximum limits are formatted in the same way. If two,
+the first designates the format for the minimum limit, the second for
+the maximum. If \f(CW""\fR is given as either description, then
+the that limit will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the axis and tick lines. The default is \f(CW1\fR
+pixel.
+.TP
+\fB\-logscale \fIboolean\fR
+Indicates whether the scale of the axis is logarithmic or linear. If
+\fIboolean\fR is true, the axis is logarithmic. The default scale is
+linear.
+.TP
+\fB\-loose \fIboolean\fR
+Indicates whether the limits of the axis should fit the data points tightly,
+at the outermost data points, or loosely, at the outer tick intervals.
+If the axis limit is set with the -min or -max option, the axes are
+displayed tightly.
+If \fIboolean\fR is true, the axis range is "loose".
+The default is \f(CW0\fR.
+.TP
+\fB\-majorticks \fImajorList\fR
+Specifies where to display major axis ticks. You can use this option
+to display ticks at non-uniform intervals. \fIMajorList\fR is a list
+of axis coordinates designating the location of major ticks. No
+minor ticks are drawn. If \fImajorList\fR is \f(CW""\fR,
+major ticks will be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-max \fIvalue\fR
+Sets the maximum limit of \fIaxisName\fR. Any data point greater
+than \fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the maximum limit is calculated using the largest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-min \fIvalue\fR
+Sets the minimum limit of \fIaxisName\fR. Any data point less than
+\fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the minimum limit is calculated using the smallest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-minorticks \fIminorList\fR
+Specifies where to display minor axis ticks. You can use this option
+to display minor ticks at non-uniform intervals. \fIMinorList\fR is a
+list of real values, ranging from 0.0 to 1.0, designating the placement of
+a minor tick. No minor ticks are drawn if the \fB\-majortick\fR
+option is also set. If \fIminorList\fR is \f(CW""\fR, minor ticks will
+be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the how many degrees to rotate the axis tick labels.
+\fITheta\fR is a real value representing the number of degrees
+to rotate the tick labels. The default is \f(CW0.0\fR degrees.
+.TP
+\fB\-scrollcommand \fIcommand\fR
+Specify the prefix for a command used to communicate with scrollbars
+for this axis, such as \fI.sbar set\fP.
+.TP
+\fB\-showticks \fIboolean\fR
+Indicates whether axis ticks should be drawn. If \fIboolean\fR is
+true, ticks are drawn. If false, only the
+axis line is drawn. The default is \f(CW1\fR.
+.TP
+\fB\-stepsize \fIvalue\fR
+Specifies the interval between major axis ticks. If \fIvalue\fR isn't
+a valid interval (must be less than the axis range),
+the request is ignored and the step size is automatically calculated.
+.TP
+\fB\-subdivisions \fInumber\fR
+Indicates how many minor axis ticks are
+to be drawn. For example, if \fInumber\fR is two, only one minor
+tick is drawn. If \fInumber\fR is one, no minor ticks are
+displayed. The default is \f(CW2\fR.
+.TP
+\fB\-tickfont \fIfontName\fR
+Specifies the font for axis tick labels. The default is
+\f(CW*-Courier-Bold-R-Normal-*-100-*\fR.
+.TP
+\fB\-ticklength \fIpixels\fR
+Sets the length of major and minor ticks (minor ticks are half the
+length of major ticks). If \fIpixels\fR is less than zero, the axis
+will be inverted with ticks drawn pointing towards the plot. The
+default is \f(CW0.1i\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title of the axis. If \fItext\fR is
+\f(CW""\fR, no axis title will be displayed.
+.TP
+\fB\-titlecolor \fIcolor\fR
+Sets the color of the axis title. The default is \f(CWblack\fR.
+.TP
+\fB\-titlefont \fIfontName\fR
+Specifies the font for axis title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-14-140-*\fR.
+.PP
+Axis configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWAxis\fR. The resource names
+are the names of the axes (such as \f(CWx\fR or \f(CWx2\fR).
+.CS
+option add *Graph.Axis.Color blue
+option add *Graph.x.LogScale true
+option add *Graph.x2.LogScale false
+.CE
+.RE
+.TP
+\fIpathName \fBaxis \fBcreate \fIaxisName \fR?\fIoption value\fR?...
+Creates a new axis by the name \fIaxisName\fR. No axis by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBdelete \fR?\fIaxisName\fR?...
+Deletes the named axes. An axis is not really
+deleted until it is not longer in use, so it's safe to delete
+axes mapped to elements.
+.TP
+\fIpathName \fBaxis invtransform \fIaxisName value\fR
+Performs the inverse transformation, changing the screen coordinate
+\fIvalue\fR to a graph coordinate, mapping the value mapped to
+\fIaxisName\fR. Returns the graph coordinate.
+.TP
+\fIpathName \fBaxis limits \fIaxisName\fR
+Returns a list of the minimum and maximum limits for \fIaxisName\fR. The order
+of the list is \f(CWmin max\fR.
+.TP
+\fIpathName \fBaxis names \fR?\fIpattern\fR?...
+Returns a list of axes matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all axes are returned.
+.TP
+\fIpathName \fBaxis transform \fIaxisName value\fR
+Transforms the coordinate \fIvalue\fR to a screen coordinate by mapping
+the it to \fIaxisName\fR. Returns the transformed screen coordinate.
+.TP
+\fIpathName \fBaxis view \fIaxisName\fR
+Change the viewable area of this axis. Use as an argument to a scrollbar's "\fI\-command\fR".
+.PP
+The default axes are \f(CWx\fR, \f(CWy\fR, \f(CWx2\fR, and \f(CWy2\fR.
+But you can display more than four axes simultaneously. You can also
+swap in a different axis with \fBuse\fR operation of the special axis
+components: \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR.
+.CS
+\&.g create axis temp
+\&.g create axis time
+\&...
+\&.g xaxis use temp
+\&.g yaxis use time
+.CE
+Only the axes specified for use are displayed on the screen.
+.PP
+The \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR
+components operate on an axis location rather than a specific axis
+like the more general \fBaxis\fR component does. They implicitly
+control the axis that is currently using to that location. By
+default, \fBxaxis\fR uses the \f(CWx\fR axis, \fByaxis\fR uses
+\f(CWy\fR, \fBx2axis\fR uses \f(CWx2\fR, and \fBy2axis\fR uses
+\f(CWy2\fR. When more than one axis is displayed in a margin, it
+represents the first axis displayed.
+.PP
+The following operations are available for axes. They mirror exactly
+the operations of the \fBaxis\fR component. The \fIaxis\fR argument
+must be \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, or \fBy2axis\fR. This
+feature is deprecated since more than one axis can now be used a
+margin. You should only use the \fBxaxis\fR, \fBx2axis\fR,
+\fByaxis\fR, and \fBy2axis\fR components with the \fBuse\fR operation.
+For all other operations, use the general \fBaxis\fR component
+instead.
+.TP
+\fIpathName \fIaxis \fBcget \fIoption\fR
+.TP
+\fIpathName \fIaxis \fBconfigure \fR?\fIoption value\fR?...
+.TP
+\fIpathName \fIaxis\fB invtransform \fIvalue\fR
+.TP
+\fIpathName \fIaxis \fBlimits\fR
+.TP
+\fIpathName \fIaxis\fB transform \fIvalue\fR
+.TP
+\fIpathName \fIaxis\fB use \fR?\fIaxisName\fR?
+Designates the axis \fIaxisName\fR is to be displayed at this
+location. \fIAxisName\fR can not be already in use at another location.
+This command returns the name of the axis currently using this location.
+.SS "CROSSHAIRS COMPONENT"
+Cross hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position
+the mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives.
+This means that they can be quickly drawn and erased without redrawing
+the entire graph.
+.PP
+The following operations are available for cross hairs:
+.TP
+\fIpathName \fBcrosshairs cget \fIoption\fR
+Returns the current value of the cross hairs configuration option
+given by \fIoption\fR. \fIOption\fR may be any option
+described below for the cross hairs \fBconfigure\fR operation.
+.TP
+\fIpathName \fBcrosshairs configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the cross hairs. If
+\fIoption\fR isn't specified, a list describing all the current
+options for the cross hairs is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the cross hairs option \fIoption\fR is set to
+\fIvalue\fR.
+The following options are available for cross hairs.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the cross hairs. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the cross hairs. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the cross hair lines. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the cross hairs will be solid
+lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether cross hairs are drawn. If \fIboolean\fR is true,
+cross hairs are not drawn. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the cross hair lines. The default is \f(CW1\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies the screen position where the cross hairs intersect.
+\fIPos\fR must be in the form "\fI@x,y\fR", where \fIx\fR and \fIy\fR
+are the window coordinates of the intersection.
+.PP
+Cross hairs configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWcrosshairs\fR and \f(CWCrosshairs\fR respectively.
+.CS
+option add *Graph.Crosshairs.LineWidth 2
+option add *Graph.Crosshairs.Color red
+.CE
+.RE
+.TP
+\fIpathName \fBcrosshairs off\fR
+Turns off the cross hairs.
+.TP
+\fIpathName \fBcrosshairs on\fR
+Turns on the display of the cross hairs.
+.TP
+\fIpathName \fBcrosshairs toggle\fR
+Toggles the current state of the cross hairs, alternately mapping and
+unmapping the cross hairs.
+.SS "ELEMENT COMPONENTS"
+A data element represents a set of data. It contains x and y vectors
+containing the coordinates of the data points. Elements can be
+displayed with a symbol at each data point and lines connecting the
+points. Elements also control the appearance of the data, such as the
+symbol type, line width, color etc.
+.PP
+When new data elements are created, they are automatically added to a
+list of displayed elements. The display list controls what elements
+are drawn and in what order.
+.PP
+The following operations are available for elements.
+.TP
+\fIpathName \fBelement activate \fIelemName \fR?\fIindex\fR?...
+Specifies the data points of element \fIelemName\fR to be drawn
+using active foreground and background colors. \fIElemName\fR is the
+name of the element and \fIindex\fR is a number representing the index
+of the data point. If no indices are present then all data points
+become active.
+.TP
+\fIpathName \fBelement bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an element with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on graph elements, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBelement cget \fIelemName \fIoption\fR
+Returns the current value of the element configuration option given by
+\fIoption\fR. \fIOption\fR may be any of the options described below
+for the element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement closest \fIx y\fR \fIvarName\fR ?\fIoption value\fR?... ?\fIelemName\fR?...
+Finds the data point closest to the window coordinates \fIx\fR and
+\fIy\fR in the element \fIelemName\fR. \fIElemName\fR is the name of
+an element, that must not be hidden. If no elements are specified,
+then all visible elements are searched. It returns via the array
+variable \fIvarName\fR the name of the closest element, the index of
+its closest point, and the graph coordinates of the point. Returns
+\f(CW0\fR, if no data point within the threshold distance can be found,
+otherwise \f(CW1\fR is returned. The following
+\fIoption\fR\-\fIvalue\fR pairs are available.
+.RS
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a threshold distance where selected data points are ignored.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+If this option isn't specified, then it defaults to the value of the
+graph's \fB\-halo\fR option.
+.TP
+\fB\-interpolate \fIstring\fR
+Indicates whether to consider projections that lie along the line segments
+connecting data points when searching for the closest point.
+The default value is \f(CW0\fR. The values for \fIstring\fR are
+described below.
+.RS
+.TP 1.25i
+\f(CWno\fR
+Search only for the closest data point.
+.TP
+\f(CWyes\fR
+Search includes projections that lie along the
+line segments connecting the data points.
+.TP
+\f(CWx\fR
+Search includes vertical projections from the given X-coordinate.
+.TP
+\f(CWy\fR
+Search includes horizontal projections from the given Y-coordinate.
+.RE
+.RE
+.TP
+\fIpathName \fBelement configure \fIelemName \fR?\fIelemName\fR... ?\fIoption value\fR?...
+Queries or modifies the configuration options for elements. Several
+elements can be modified at the same time. If \fIoption\fR isn't
+specified, a list describing all the current options for
+\fIelemName\fR is returned. If \fIoption\fR is specified, but not
+\fIvalue\fR, then a list describing the option \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the element option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for elements.
+.RS
+.TP
+\fB\-activepen \fIpenName\fR
+Specifies pen to use to draw active element. If \fIpenName\fR is
+\f(CW""\fR, no active elements will be drawn. The default is
+\f(CWactiveLine\fR.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for the element. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events are handled for elements. Each tag in the list matching the
+current event
+sequence will have its Tcl command executed. Implicitly the name of
+the element is always the first tag in the list. The default value is
+\f(CWall\fR.
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the traces connecting the data points.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of element line. \fIDashList\fR is a list of up to
+11 numbers that alternately represent the lengths of the dashes and
+gaps on the element line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the lines will be solid.
+.TP
+\fB\-data \fIcoordList\fR
+Specifies the X\-Y coordinates of the data. \fICoordList\fR is a
+list of numeric expressions representing the X\-Y coordinate pairs
+of each data point.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the interior color of symbols. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the symbol is transparent. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the element is displayed.
+The default is \f(CWno\fR.
+.TP
+\fB\-label \fItext\fR
+Sets the element's label in the legend. If \fItext\fR
+is \f(CW""\fR, the element will have no entry in the legend.
+The default label is the element's name.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the connecting lines between data points. If
+\fIpixels\fR is \f(CW0\fR, no connecting lines will be drawn between
+symbols. The default is \f(CW0\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Selects the X\-axis to map the element's X\-coordinates onto.
+\fIXAxis\fR must be the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Selects the Y\-axis to map the element's Y\-coordinates onto.
+\fIYAxis\fR must be the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-offdash \fIcolor\fR
+Sets the color of the stripes when traces are dashed (see the
+\fB\-dashes\fR option). If \fIcolor\fR is \f(CW""\fR, then the "off"
+pixels will represent gaps instead of stripes. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color or the outline around each symbol. If \fIcolor\fR is
+\f(CW""\fR, then no outline is drawn. If \fIcolor\fR is \f(CWdefcolor\fR,
+then the color will be the same as the \fB\-color\fR option. The
+default is \f(CWdefcolor\fR.
+.TP
+\fB\-pen \fIpenname\fR
+Set the pen to use for this element.
+.TP
+\fB\-outlinewidth \fIpixels\fR
+Sets the width of the outline bordering each symbol. If \fIpixels\fR
+is \f(CW0\fR, no outline will be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-pixels \fIpixels\fR
+Sets the size of symbols. If \fIpixels\fR is \f(CW0\fR, no symbols will
+be drawn. The default is \f(CW0.125i\fR.
+.TP
+\fB\-scalesymbols \fIboolean\fR
+If \fIboolean\fR is true, the size of the symbols
+drawn for \fIelemName\fR will change with scale of the X\-axis and Y\-axis.
+At the time this option is set, the current ranges of the axes are
+saved as the normalized scales (i.e scale factor is 1.0) and the
+element is drawn at its designated size (see the \fB\-pixels\fR
+option). As the scale of the axes change, the symbol will be scaled
+according to the smaller of the X\-axis and Y\-axis scales. If \fIboolean\fR
+is false, the element's symbols are drawn at the designated size,
+regardless of axis scales. The default is \f(CW0\fR.
+.TP
+\fB\-smooth \fIsmooth\fR
+Specifies how connecting line segments are drawn between data points.
+\fISmooth\fR can be either \f(CWlinear\fR, \f(CWstep\fR, \f(CWnatural\fR, or
+\f(CWquadratic\fR. If \fIsmooth\fR is \f(CWlinear\fR, a single line
+segment is drawn, connecting both data points. When \fIsmooth\fR is
+\f(CWstep\fR, two line segments are drawn. The first is a horizontal
+line segment that steps the next X\-coordinate. The second is a
+vertical line, moving to the next Y\-coordinate. Both \fInatural\fR and
+\fIquadratic\fR generate multiple segments between data points. If
+\fInatural\fR, the segments are generated using a cubic spline. If
+\fIquadratic\fR, a quadratic spline is used. The default is
+\fIlinear\fR.
+.TP
+\fB\-styles \fIstyleList\fR
+Specifies what pen to use based on the range of weights given.
+\fIStyleList\fR is a list of style specifications. Each style
+specification, in turn, is a list consisting of a pen name, and
+optionally a minimum and maximum range. Data points whose weight (see
+the \fB\-weight\fR option) falls in this range, are drawn with this
+pen. If no range is specified it defaults to the index of the pen in
+the list. Note that this affects only symbol attributes. Line
+attributes, such as line width, dashes, etc. are ignored.
+.TP
+\fB\-symbol \fIsymbol\fR
+Specifies the symbol for data points. \fISymbol\fR can be either
+\f(CWsquare\fR, \f(CWcircle\fR, \f(CWdiamond\fR, \f(CWplus\fR, \f(CWcross\fR,
+\f(CWsplus\fR, \f(CWscross\fR, \f(CWtriangle\fR, \f(CW""\fR (where no symbol
+is drawn), or a bitmap. Bitmaps are specified as "\fIsource\fR
+?\fImask\fR?", where \fIsource\fR is the name of the bitmap, and
+\fImask\fR is the bitmap's optional mask. The default is
+\f(CWcircle\fR.
+.TP
+\fB\-trace \fIdirection\fR
+Indicates whether connecting lines between data points (whose
+X\-coordinate values are either increasing or decreasing) are drawn.
+\fIDirection\fR
+must be \f(CWincreasing\fR, \f(CWdecreasing\fR, or \f(CWboth\fR. For
+example, if \fIdirection\fR is \f(CWincreasing\fR, connecting lines will
+be drawn only between those data points where X\-coordinate values are
+monotonically increasing. If \fIdirection\fR is \f(CWboth\fR,
+connecting lines will be draw between all data points. The default is
+\f(CWboth\fR.
+.TP
+\fB\-weights \fIwVec\fR
+Specifies the weights of the individual data points. This,
+with the list pen styles (see the \fB\-styles\fR option),
+controls how data points are drawn. \fIWVec\fR is the name of a BLT
+vector or a list of numeric expressions representing the weights for
+each data point.
+.TP
+\fB\-xdata \fIxVec\fR
+Specifies the X\-coordinates of the data. \fIXVec\fR is the name of
+a BLT vector or a list of numeric expressions.
+.TP
+\fB\-ydata \fIyVec\fR
+Specifies the Y\-coordinates of the data. \fIYVec\fR is the name of
+a BLT vector or a list of numeric expressions.
+.PP
+Element configuration options may also be set by the \fBoption\fR
+command. The resource class is \f(CWElement\fR. The resource name is
+the name of the element.
+.CS
+option add *Graph.Element.symbol line
+option add *Graph.e1.symbol line
+.CE
+.RE
+.TP
+\fIpathName \fBelement create \fIelemName\fR ?\fIoption value\fR?...
+Creates a new element \fIelemName\fR. It's an error is
+an element \fIelemName\fR already exists. If
+additional arguments are present, they specify options valid for
+the element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement deactivate \fIelemName\fR ?\fIelemName\fR?...
+Deactivates all the elements matching \fIpattern\fR.
+Elements whose names match any of the patterns given are redrawn using
+their normal colors.
+.TP
+\fIpathName \fBelement delete\fR ?\fIelemName\fR?...
+Deletes all the named elements. The graph is automatically redrawn.
+.TP
+\fIpathName \fBelement exists \fIelemName\fR
+Returns \f(CW1\fR if an element \fIelemName\fR currently exists and
+\f(CW0\fR otherwise.
+.TP
+\fIpathName \fBelement names \fR?\fIpattern\fR?...
+Returns the elements matching one or more pattern. If no
+\fIpattern\fR is given, the names of all elements is returned.
+.TP
+\fIpathName \fBelement show\fR ?\fInameList\fR?
+Queries or modifies the element display list. The element display
+list designates the elements drawn and in what
+order. \fINameList\fR is a list of elements to be displayed in the
+order they are named. If there is no \fInameList\fR argument,
+the current display list is returned.
+.TP
+\fIpathName \fBelement type\fR \fIelemName\fR
+Returns the type of \fIelemName\fR.
+If the element is a bar element, the commands returns the string
+\f(CW"bar"\fR, otherwise it returns \f(CW"line"\fR.
+.CE
+.SS "GRID COMPONENT"
+Grid lines extend from the major and minor ticks of each axis
+horizontally or vertically across the plotting area. The following
+operations are available for grid lines.
+.TP
+\fIpathName \fBgrid cget \fIoption\fR
+Returns the current value of the grid line configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described below
+for the grid \fBconfigure\fR operation.
+.TP
+\fIpathName \fBgrid configure\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for grid lines. If
+\fIoption\fR isn't specified, a list describing all the current
+grid options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the grid line option \fIoption\fR is set to
+\fIvalue\fR. The following options are valid for grid lines.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the grid lines. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the grid lines. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the grid lines. Each number must be between 1 and 255.
+If \fIdashList\fR is \f(CW""\fR, the grid will be solid lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the grid should be drawn. If \fIboolean\fR
+is true, grid lines are not shown. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of grid lines. The default width is \f(CW1\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to display grid lines. \fIXAxis\fR
+must be the name of an axis or \f(CW""\fR for no grid lines.
+The default is \f(CW""\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to display grid lines. \fIYAxis\fR
+must be the name of an axis or \f(CW""\fR for no grid lines.
+The default is \f(CWy\fR.
+.TP
+\fB\-minor \fIboolean\fR
+Indicates whether the grid lines should be drawn for minor ticks.
+If \fIboolean\fR is true, the lines will appear at
+minor tick intervals. The default is \f(CW1\fR.
+.PP
+Grid configuration options may also be set by the
+\fBoption\fR command. The resource name and class are \f(CWgrid\fR and
+\f(CWGrid\fR respectively.
+.CS
+option add *Graph.grid.LineWidth 2
+option add *Graph.Grid.Color black
+.CE
+.RE
+.TP
+\fIpathName \fBgrid off\fR
+Turns off the display the grid lines.
+.TP
+\fIpathName \fBgrid on\fR
+Turns on the display the grid lines.
+.TP
+\fIpathName \fBgrid toggle\fR
+Toggles the display of the grid.
+.SS "LEGEND COMPONENT"
+The legend displays a list of the data elements. Each entry consists
+of the element's symbol and label. The legend can appear in any
+margin (the default location is in the right margin). It
+can also be positioned anywhere within the plotting area.
+.PP
+The following operations are valid for the legend.
+.TP
+\fIpathName \fBlegend activate \fIpattern\fR...
+Selects legend entries to be drawn using the active legend colors and relief.
+All entries whose element names match \fIpattern\fR are selected. To
+be selected, the element name must match only one \fIpattern\fR.
+.TP
+\fIpathName \fBlegend bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a legend entry with this
+tag, \fIcommand\fR will be invoked. Implicitly the element names
+in the entry are tags. The syntax is similar to the
+\fBbind\fR command except that it operates on legend entries, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBlegend cget \fIoption\fR
+Returns the current value of a legend configuration option.
+\fIOption\fR may be any option described below in the
+legend \fBconfigure\fR operation.
+.TP
+\fIpathName \fBlegend configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for the legend. If
+\fIoption\fR isn't specified, a list describing the current
+legend options for \fIpathName\fR is returned. If \fIoption\fR is
+specified, but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the legend option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for the legend.
+.RS
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active legend entries. All legend
+entries marked active (see the legend \fBactivate\fR operation) are
+drawn using this background color.
+.TP
+\fB\-activeborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the active legend
+entries. The default is \f(CW2\fR.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color for active legend entries. All legend
+entries marked as active (see the legend \fBactivate\fR operation) are
+drawn using this foreground color.
+.TP
+\fB\-activerelief \fIrelief\fR
+Specifies the 3-D effect desired for active legend entries.
+\fIRelief\fR denotes how the interior of the entry should appear
+relative to the legend; for example, \f(CWraised\fR means the entry
+should appear to protrude from the legend, relative to the surface of
+the legend. The default is \f(CWflat\fR.
+.TP
+\fB\-anchor \fIanchor\fR
+Tells how to position the legend relative to the positioning point for
+the legend. This is dependent on the value of the \fB\-position\fR
+option. The default is \f(CWcenter\fR.
+.RS
+.TP 1.25i
+\f(CWleft\fR or \f(CWright\fR
+The anchor describes how to position the legend vertically.
+.TP
+\f(CWtop\fR or \f(CWbottom\fR
+The anchor describes how to position the legend horizontally.
+.TP
+\f(CW@x,y\fR
+The anchor specifies how to position the legend relative to the
+positioning point. For example, if \fIanchor\fR is \f(CWcenter\fR then
+the legend is centered on the point; if \fIanchor\fR is \f(CWn\fR then
+the legend will be drawn such that the top center point of the
+rectangular region occupied by the legend will be at the positioning
+point.
+.TP
+\f(CWplotarea\fR
+The anchor specifies how to position the legend relative to the
+plotting area. For example, if \fIanchor\fR is \f(CWcenter\fR then the
+legend is centered in the plotting area; if \fIanchor\fR is \f(CWne\fR
+then the legend will be drawn such that occupies the upper right
+corner of the plotting area.
+.RE
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the legend. If \fIcolor\fR is \f(CW""\fR,
+the legend background with be transparent.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for legend entries. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events are handled for legend entries. Each tag in the list matching
+the current event sequence will have its Tcl command executed. The
+default value is \f(CWall\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the legend (if
+such border is being drawn; the \fBrelief\fR option determines this).
+The default is \f(CW2\fR pixels.
+.TP
+\fB\-font \fIfontName\fR
+\fIFontName\fR specifies a font to use when drawing the labels of each
+element into the legend. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of the text drawn for the element's label.
+The default is \f(CWblack\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the legend should be displayed. If \fIboolean\fR is
+true, the legend will not be draw. The default is \f(CWno\fR.
+.TP
+\fB\-ipadx \fIpad\fR
+Sets the amount of internal padding to be added to the width of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the left side of the legend entry is
+padded by the first distance and the right side by the second. If
+\fIpad\fR is just one distance, both the left and right sides are padded
+evenly. The default is \f(CW2\fR.
+.TP
+\fB\-ipady \fIpad\fR
+Sets an amount of internal padding to be added to the height of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the top of the entry is padded by the
+first distance and the bottom by the second. If \fIpad\fR is just
+one distance, both the top and bottom of the entry are padded evenly.
+The default is \f(CW2\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the legend.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the legend is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the legend. \fIPad\fR can be a list
+of one or two screen distances. If \fIpad\fR has two elements, the area above
+the legend is padded by the first distance and the area below by the
+second. If \fIpad\fR is just one distance, both the top and
+bottom areas are padded evenly. The default is \f(CW0\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies where the legend is drawn. The
+\fB\-anchor\fR option also affects where the legend is positioned. If
+\fIpos\fR is \f(CWleft\fR, \f(CWleft\fR, \f(CWtop\fR, or \f(CWbottom\fR, the
+legend is drawn in the specified margin. If \fIpos\fR is
+\f(CWplotarea\fR, then the legend is drawn inside the plotting area at a
+particular anchor. If \fIpos\fR is in the form "\fI@x,y\fR", where
+\fIx\fR and \fIy\fR are the window coordinates, the legend is drawn in
+the plotting area at the specified coordinates. The default is
+\f(CWright\fR.
+.TP
+\fB\-raised \fIboolean\fR
+Indicates whether the legend is above or below the data elements. This
+matters only if the legend is in the plotting area. If \fIboolean\fR
+is true, the legend will be drawn on top of any elements that may
+overlap it. The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the border around the legend.
+\fIRelief\fR specifies how the interior of the legend should appear
+relative to the graph; for example, \f(CWraised\fR means the legend
+should appear to protrude from the graph, relative to the surface of
+the graph. The default is \f(CWsunken\fR.
+.PP
+Legend configuration options may also be set by the \fBoption\fR
+command. The resource name and class are \f(CWlegend\fR and
+\f(CWLegend\fR respectively.
+.CS
+option add *Graph.legend.Foreground blue
+option add *Graph.Legend.Relief raised
+.CE
+.RE
+.TP
+\fIpathName \fBlegend deactivate \fIpattern\fR...
+Selects legend entries to be drawn using the normal legend colors and
+relief. All entries whose element names match \fIpattern\fR are
+selected. To be selected, the element name must match only one
+\fIpattern\fR.
+.TP
+\fIpathName \fBlegend get \fIpos\fR
+Returns the name of the element whose entry is at the screen position
+\fIpos\fR in the legend. \fIPos\fR must be in the form "\fI@x,y\fR",
+where \fIx\fR and \fIy\fR are window coordinates. If the given
+coordinates do not lie over a legend entry, \f(CW""\fR is returned.
+.SS "PEN COMPONENTS"
+Pens define attributes (both symbol and line style) for elements.
+Pens mirror the configuration options of data elements that pertain to
+how symbols and lines are drawn. Data elements use pens to determine
+how they are drawn. A data element may use several pens at once. In
+this case, the pen used for a particular data point is determined from
+each element's weight vector (see the element's \fB\-weight\fR and
+\fB\-style\fR options).
+.PP
+One pen, called \f(CWactiveLine\fR, is automatically created.
+It's used as the default active pen for elements. So you can change
+the active attributes for all elements by simply reconfiguring this
+pen.
+.CS
+\&.g pen configure "activeLine" -color green
+.CE
+You can create and use several pens. To create a pen, invoke
+the pen component and its create operation.
+.CS
+\&.g pen create myPen
+.CE
+You map pens to a data element using either the element's
+\fB\-pen\fR or \fB\-activepen\fR options.
+.CS
+\&.g element create "line1" -xdata $x -ydata $tempData \\
+ -pen myPen
+.CE
+An element can use several pens at once. This is done by specifying
+the name of the pen in the element's style list (see the
+\fB\-styles\fR option).
+.CS
+\&.g element configure "line1" -styles { myPen 2.0 3.0 }
+.CE
+This says that any data point with a weight between 2.0 and 3.0
+is to be drawn using the pen \f(CWmyPen\fR. All other points
+are drawn with the element's default attributes.
+.PP
+The following operations are available for pen components.
+.PP
+.TP
+\fIpathName \fBpen \fBcget \fIpenName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIpenName\fR. \fIOption\fR may be any option described below
+for the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBconfigure \fIpenName \fR?\fIpenName\fR... ?\fIoption value\fR?...
+Queries or modifies the configuration options of
+\fIpenName\fR. Several pens can be modified at once. If \fIoption\fR
+isn't specified, a list describing the current options for
+\fIpenName\fR is returned. If \fIoption\fR is specified, but not
+\fIvalue\fR, then a list describing \fIoption\fR is returned. If one
+or more \fIoption\fR and \fIvalue\fR pairs are specified, then for
+each pair, the pen option \fIoption\fR is set to \fIvalue\fR. The
+following options are valid for pens.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the traces connecting the data points.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of element line. \fIDashList\fR is a list of up to
+11 numbers that alternately represent the lengths of the dashes and
+gaps on the element line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the lines will be solid.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the interior color of symbols. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the symbol is transparent. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the connecting lines between data points. If
+\fIpixels\fR is \f(CW0\fR, no connecting lines will be drawn between
+symbols. The default is \f(CW0\fR.
+.TP
+\fB\-offdash \fIcolor\fR
+Sets the color of the stripes when traces are dashed (see the
+\fB\-dashes\fR option). If \fIcolor\fR is \f(CW""\fR, then the "off"
+pixels will represent gaps instead of stripes. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color or the outline around each symbol. If \fIcolor\fR is
+\f(CW""\fR, then no outline is drawn. If \fIcolor\fR is \f(CWdefcolor\fR,
+then the color will be the same as the \fB\-color\fR option. The
+default is \f(CWdefcolor\fR.
+.TP
+\fB\-outlinewidth \fIpixels\fR
+Sets the width of the outline bordering each symbol. If \fIpixels\fR
+is \f(CW0\fR, no outline will be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-pixels \fIpixels\fR
+Sets the size of symbols. If \fIpixels\fR is \f(CW0\fR, no symbols will
+be drawn. The default is \f(CW0.125i\fR.
+.TP
+\fB\-symbol \fIsymbol\fR
+Specifies the symbol for data points. \fISymbol\fR can be either
+\f(CWsquare\fR, \f(CWcircle\fR, \f(CWdiamond\fR, \f(CWplus\fR, \f(CWcross\fR,
+\f(CWsplus\fR, \f(CWscross\fR, \f(CWtriangle\fR, \f(CW""\fR (where no symbol
+is drawn), or a bitmap. Bitmaps are specified as "\fIsource\fR
+?\fImask\fR?", where \fIsource\fR is the name of the bitmap, and
+\fImask\fR is the bitmap's optional mask. The default is
+\f(CWcircle\fR.
+.TP
+\fB\-type \fIelemType\fR
+Specifies the type of element the pen is to be used with.
+This option should only be employed when creating the pen. This
+is for those that wish to mix different types of elements (bars and
+lines) on the same graph. The default type is "line".
+.PP
+Pen configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWPen\fR. The resource names
+are the names of the pens.
+.CS
+option add *Graph.Pen.Color blue
+option add *Graph.activeLine.color green
+.CE
+.RE
+.TP
+\fIpathName \fBpen \fBcreate \fIpenName \fR?\fIoption value\fR?...
+Creates a new pen by the name \fIpenName\fR. No pen by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBdelete \fR?\fIpenName\fR?...
+Deletes the named pens. A pen is not really
+deleted until it is not longer in use, so it's safe to delete
+pens mapped to elements.
+.TP
+\fIpathName \fBpen names \fR?\fIpattern\fR?...
+Returns a list of pens matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all pens are returned.
+.SS "POSTSCRIPT COMPONENT"
+The graph can generate encapsulated PostScript output. There
+are several configuration options you can specify to control how the
+plot will be generated. You can change the page dimensions and
+borders. The plot itself can be scaled, centered, or rotated to
+landscape. The PostScript output can be written directly to a file or
+returned through the interpreter.
+.PP
+The following postscript operations are available.
+.TP
+\fIpathName \fBpostscript cget \fIoption\fR
+Returns the current value of the postscript option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the postscript \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpostscript configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for PostScript
+generation. If \fIoption\fR isn't specified, a list describing
+the current postscript options for \fIpathName\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+\fIoption\fR is returned. If one or more \fIoption\fR and \fIvalue\fR
+pairs are specified, then for each pair, the postscript option
+\fIoption\fR is set to \fIvalue\fR. The following postscript options
+are available.
+.RS
+.TP
+\fB\-center \fIboolean\fR
+Indicates whether the plot should be centered on the PostScript page. If
+\fIboolean\fR is false, the plot will be placed in the upper left
+corner of the page. The default is \f(CW1\fR.
+.TP
+\fB\-colormap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a color mapping from the X color name to PostScript. Each
+element of \fIvarName\fR must consist of PostScript code to set a
+particular color value (e.g. ``\f(CW1.0 1.0 0.0 setrgbcolor\fR''). When
+generating color information in PostScript, the array variable \fIvarName\fR
+is checked if an element of the name as the color exists. If so, it uses
+its value as the PostScript
+command to set the color. If this option hasn't been specified, or if
+there isn't an entry in \fIvarName\fR for a given color, then it uses
+the red, green, and blue intensities from the X color.
+.TP
+\fB\-colormode \fImode\fR
+Specifies how to output color information. \fIMode\fR must be either
+\f(CWcolor\fR (for full color output), \f(CWgray\fR (convert all colors to
+their gray-scale equivalents) or \f(CWmono\fR (convert foreground colors
+to black and background colors to white). The default mode is
+\f(CWcolor\fR.
+.TP
+\fB\-fontmap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a font mapping from the X font name to PostScript. Each
+element of \fIvarName\fR must consist of a Tcl list with one or two
+elements; the name and point size of a PostScript font.
+When outputting PostScript commands for a particular font, the array
+variable \fIvarName\fR is checked to see if an element by the
+specified font exists. If there is such an element, then the font
+information contained in that element is used in the PostScript
+output. (If the point size is omitted from the list, the point size
+of the X font is used). Otherwise the X font is examined in an
+attempt to guess what PostScript font to use. This works only for
+fonts whose foundry property is \fIAdobe\fR (such as Times, Helvetica,
+Courier, etc.). If all of this fails then the font defaults to
+\f(CWHelvetica-Bold\fR.
+.TP
+\fB\-decorations \fIboolean\fR
+Indicates whether PostScript commands to generate color backgrounds and 3-D
+borders will be output. If \fIboolean\fR is false, the background will be
+white and no 3-D borders will be generated. The
+default is \f(CW1\fR.
+.TP
+\fB\-height \fIpixels\fR
+Sets the height of the plot. This lets you print the graph with a
+height different from the one drawn on the screen. If
+\fIpixels\fR is 0, the height is the same as the widget's height.
+The default is \f(CW0\fR.
+.TP
+\fB\-landscape \fIboolean\fR
+If \fIboolean\fR is true, this specifies the printed area is to be
+rotated 90 degrees. In non-rotated output the X\-axis of the printed
+area runs along the short dimension of the page (``portrait''
+orientation); in rotated output the X\-axis runs along the long
+dimension of the page (``landscape'' orientation). Defaults to
+\f(CW0\fR.
+.TP
+\fB\-maxpect \fIboolean\fR
+Indicates to scale the plot so that it fills the PostScript page.
+The aspect ratio of the graph is still retained. The default is
+\f(CW0\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the horizontal padding for the left and right page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the left border is padded
+by the first distance and the right border by the second. If
+\fIpad\fR has just one distance, both the left and right borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the vertical padding for the top and bottom page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the top border is padded
+by the first distance and the bottom border by the second. If
+\fIpad\fR has just one distance, both the top and bottom borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-paperheight \fIpixels\fR
+Sets the height of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default height is
+\f(CW11.0i\fR.
+.TP
+\fB\-paperwidth \fIpixels\fR
+Sets the width of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default width is
+\f(CW8.5i\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the width of the plot. This lets you generate a plot
+of a width different from that of the widget. If \fIpixels\fR
+is 0, the width is the same as the widget's width. The default is
+\f(CW0\fR.
+.PP
+Postscript configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWpostscript\fR and \f(CWPostscript\fR respectively.
+.CS
+option add *Graph.postscript.Decorations false
+option add *Graph.Postscript.Landscape true
+.CE
+.RE
+.TP
+\fIpathName \fBpostscript output \fR?\fIfileName\fR? ?\fIoption value\fR?...
+Outputs a file of encapsulated PostScript. If a
+\fIfileName\fR argument isn't present, the command returns the
+PostScript. If any \fIoption-value\fR pairs are present, they set
+configuration options controlling how the PostScript is generated.
+\fIOption\fR and \fIvalue\fR can be anything accepted by the
+postscript \fBconfigure\fR operation above.
+.SS "MARKER COMPONENTS"
+Markers are simple drawing procedures used to annotate or highlight
+areas of the graph. Markers have various types: text strings,
+bitmaps, images, connected lines, windows, or polygons. They can be
+associated with a particular element, so that when the element is
+hidden or un-hidden, so is the marker. By default, markers are the
+last items drawn, so that data elements will appear in
+behind them. You can change this by configuring the \fB\-under\fR
+option.
+.PP
+Markers, in contrast to elements, don't affect the scaling of the
+coordinate axes. They can also have \fIelastic\fR coordinates
+(specified by \f(CW-Inf\fR and \f(CWInf\fR respectively) that translate
+into the minimum or maximum limit of the axis. For example, you can
+place a marker so it always remains in the lower left corner of the
+plotting area, by using the coordinates \f(CW-Inf\fR,\f(CW-Inf\fR.
+.PP
+The following operations are available for markers.
+.TP
+\fIpathName \fBmarker after \fImarkerId\fR ?\fIafterId\fR?
+Changes the order of the markers, drawing the first
+marker after the second. If no second \fIafterId\fR argument is
+specified, the marker is placed at the end of the display list. This
+command can be used to control how markers are displayed since markers
+are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker before \fImarkerId\fR ?\fIbeforeId\fR?
+Changes the order of the markers, drawing the first
+marker before the second. If no second \fIbeforeId\fR argument is
+specified, the marker is placed at the beginning of the display list.
+This command can be used to control how markers are displayed since
+markers are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker bind \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a marker with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on graph markers, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBmarker cget \fIoption\fR
+Returns the current value of the marker configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below in the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBmarker configure \fImarkerId\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for markers. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fImarkerId\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the marker option \fIoption\fR is set to \fIvalue\fR.
+.sp
+The following options are valid for all markers.
+Each type of marker also has its own type-specific options.
+They are described in the sections below.
+.RS
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for the marker. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events for markers are handled. Each tag in the list matching the
+current event sequence will have its Tcl command executed. Implicitly
+the name of the marker is always the first tag in the list.
+The default value is \f(CWall\fR.
+.TP
+\fB\-coords \fIcoordList\fR
+Specifies the coordinates of the marker. \fICoordList\fR is
+a list of graph coordinates. The number of coordinates required
+is dependent on the type of marker. Text, image, and window markers
+need only two coordinates (an X\-Y coordinate). Bitmap markers
+can take either two or four coordinates (if four, they represent the
+corners of the bitmap). Line markers
+need at least four coordinates, polygons at least six.
+If \fIcoordList\fR is \f(CW""\fR, the marker will not be displayed.
+The default is \f(CW""\fR.
+.TP
+\fB\-element \fIelemName\fR
+Links the marker with the element \fIelemName\fR. The marker is
+drawn only if the element is also currently displayed (see the
+element's \fBshow\fR operation). If \fIelemName\fR is \f(CW""\fR, the
+marker is always drawn. The default is \f(CW""\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the marker is drawn. If \fIboolean\fR is true,
+the marker is not drawn. The default is \f(CWno\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to map the marker's X\-coordinates onto.
+\fIXAxis\fR must the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to map the marker's Y\-coordinates onto.
+\fIYAxis\fR must the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-name \fImarkerId\fR
+Changes the identifier for the marker. The identifier \fImarkerId\fR
+can not already be used by another marker. If this option
+isn't specified, the marker's name is uniquely generated.
+.TP
+\fB\-under \fIboolean\fR
+Indicates whether the marker is drawn below/above data
+elements. If \fIboolean\fR is true, the marker is be drawn
+underneath the data element symbols and lines. Otherwise, the marker is
+drawn on top of the element. The default is \f(CW0\fR.
+.TP
+\fB\-xoffset \fIpixels\fR
+Specifies a screen distance to offset the marker horizontally.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.TP
+\fB\-yoffset \fIpixels\fR
+Specifies a screen distance to offset the markers vertically.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.PP
+Marker configuration options may also be set by the \fBoption\fR command.
+The resource class is either \f(CWBitmapMarker\fR, \f(CWImageMarker\fR,
+\f(CWLineMarker\fR, \f(CWPolygonMarker\fR, \f(CWTextMarker\fR, or \f(CWWindowMarker\fR,
+depending on the type of marker. The resource name is the name of the
+marker.
+.CS
+option add *Graph.TextMarker.Foreground white
+option add *Graph.BitmapMarker.Foreground white
+option add *Graph.m1.Background blue
+.CE
+.RE
+.TP
+\fIpathName \fBmarker create \fItype\fR ?\fIoption value\fR?...
+Creates a marker of the selected type. \fIType\fR may be either
+\f(CWtext\fR, \f(CWline\fR, \f(CWbitmap\fR, \f(CWimage\fR, \f(CWpolygon\fR, or
+\f(CWwindow\fR. This command returns the marker identifier,
+used as the \fImarkerId\fR argument in the other marker-related
+commands. If the \fB\-name\fR option is used, this overrides the
+normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old.
+.TP
+\fIpathName \fBmarker delete\fR ?\fIname\fR?...
+Removes one of more markers. The graph will automatically be redrawn
+without the marker.\fR.
+.TP
+\fIpathName \fBmarker exists \fImarkerId\fR
+Returns \f(CW1\fR if the marker \fImarkerId\fR exists and \f(CW0\fR
+otherwise.
+.TP
+\fIpathName \fBmarker names\fR ?\fIpattern\fR?
+Returns the names of all the markers that currently exist. If
+\fIpattern\fR is supplied, only those markers whose names match it
+will be returned.
+.TP
+\fIpathName \fBmarker type \fImarkerId\fR
+Returns the type of the marker given by \fImarkerId\fR, such as
+\f(CWline\fR or \f(CWtext\fR. If \fImarkerId\fR is not a valid a marker
+identifier, \f(CW""\fR is returned.
+.SS "BITMAP MARKERS"
+A bitmap marker displays a bitmap. The size of the
+bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the
+bitmap. The bitmap retains its normal width and height. If four
+coordinates, the first and second pairs of coordinates represent the
+corners of the bitmap. The bitmap will be stretched or reduced as
+necessary to fit into the bounding rectangle.
+.PP
+Bitmap markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create bitmap \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs, each
+sets a configuration options for the marker. These
+same \fIoption\fR\-\fIvalue\fR pairs may be used with the marker's
+\fBconfigure\fR operation.
+.PP
+The following options are specific to bitmap markers:
+.TP
+\fB\-background \fIcolor\fR
+Same as the \fB\-fill\fR option.
+.TP
+\fB\-bitmap \fIbitmap\fR
+Specifies the bitmap to be displayed. If \fIbitmap\fR is \f(CW""\fR,
+the marker will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the bitmap. If \fIcolor\fR is the empty
+string, no background will be transparent. The default background color is
+\f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Same as the \fB\-outline\fR option.
+.TP
+\fB\-mask \fImask\fR
+Specifies a mask for the bitmap to be displayed. This mask is a bitmap
+itself, denoting the pixels that are transparent. If \fImask\fR is
+\f(CW""\fR, all pixels of the bitmap will be drawn. The default is
+\f(CW""\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the foreground color of the bitmap. The default value is \f(CWblack\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Sets the rotation of the bitmap. \fITheta\fR is a real number
+representing the angle of rotation in degrees. The marker is first
+rotated and then placed according to its anchor position. The default
+rotation is \f(CW0.0\fR.
+.SS "IMAGE MARKERS"
+A image marker displays an image. Image markers are
+created with the marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create image \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to image markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the image relative to the
+positioning point for the image. For example, if \fIanchor\fR
+is \f(CWcenter\fR then the image is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the image will be drawn such that
+the top center point of the rectangular region occupied by the
+image will be at the positioning point.
+This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-image \fIimage\fR
+Specifies the image to be drawn.
+If \fIimage\fR is \f(CW""\fR, the marker will not be
+drawn. The default is \f(CW""\fR.
+.SS "LINE MARKERS"
+A line marker displays one or more connected line segments.
+Line markers are created with marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create line \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to line markers:
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the line. \fIDashList\fR is a list of up to 11
+numbers that alternately represent the lengths of the dashes and gaps
+on the line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the marker line will be solid.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the line. This color is used with
+striped lines (see the \fB\-fdashes\fR option). If \fIcolor\fR is
+the empty string, no background color is drawn (the line will be
+dashed, not striped). The default background color is \f(CW""\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the lines.
+The default width is \f(CW0\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the foreground color of the line. The default value is \f(CWblack\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern used to draw the line, rather than
+a solid line.
+\fIBitmap\fR specifies a bitmap to use as the stipple
+pattern. If \fIbitmap\fR is \f(CW""\fR, then the
+line is drawn in a solid fashion. The default is \f(CW""\fR.
+.SS "POLYGON MARKERS"
+A polygon marker displays a closed region described as two or more
+connected line segments. It is assumed the first and
+last points are connected. Polygon markers are created using the
+marker \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create polygon \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the \fBmarker configure\fR command to change the marker's
+configuration.
+The following options are supported for polygon markers:
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the outline of the polygon. \fIDashList\fR is a
+list of up to 11 numbers that alternately represent the lengths of
+the dashes and gaps on the outline. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the outline will be a solid line.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the fill color of the polygon. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the polygon is transparent.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the outline of the polygon. If \fIpixels\fR is zero,
+no outline is drawn. The default is \f(CW0\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color of the outline of the polygon. If the polygon is
+stippled (see the \fB\-stipple\fR option), then this represents the
+foreground color of the stipple. The default is \f(CWblack\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies that the polygon should be drawn with a stippled pattern
+rather than a solid color. \fIBitmap\fR specifies a bitmap to use as
+the stipple pattern. If \fIbitmap\fR is \f(CW""\fR, then the polygon is
+filled with a solid color (if the \fB\-fill\fR option is set). The
+default is \f(CW""\fR.
+.SS "TEXT MARKERS"
+A text marker displays a string of characters on one or more lines of
+text. Embedded newlines cause line breaks. They may be used to
+annotate regions of the graph. Text markers are created with the
+\fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create text \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs,
+each sets a configuration option for the text marker.
+These same \fIoption\fR\-\fIvalue\fR pairs may be used with the
+marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to text markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the text relative to the
+positioning point for the text. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the text is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the text will be drawn such that the
+top center point of the rectangular region occupied by the text will
+be at the positioning point. This default is \f(CWcenter\fR.
+.TP
+\fB\-background \fIcolor\fR
+Same as the \fB\-fill\fR option.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the text. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-120-*\fR.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the background color of the text. If \fIcolor\fR is the empty
+string, no background will be transparent. The default background color is
+\f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Same as the \fB\-outline\fR option.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the text should be justified. This matters only when
+the marker contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color of the text. The default value is \f(CWblack\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the text.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the text is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the text. \fIPad\fR can be a list of
+one or two screen distances. If \fIpad\fR has two elements, the area above the
+text is padded by the first distance and the area below by the second.
+If \fIpad\fR is just one distance, both the top and bottom areas
+are padded evenly. The default is \f(CW4\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the number of degrees to rotate the text. \fITheta\fR is a
+real number representing the angle of rotation. The marker is first
+rotated along its center and is then drawn according to its anchor
+position. The default is \f(CW0.0\fR.
+.TP
+\fB\-text \fItext\fR
+Specifies the text of the marker. The exact way the text is
+displayed may be affected by other options such as \fB\-anchor\fR or
+\fB\-rotate\fR.
+.SS "WINDOW MARKERS"
+A window marker displays a widget at a given position.
+Window markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create window \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR command.
+.PP
+The following options are specific to window markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the widget relative to the
+positioning point for the widget. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the widget is centered on the point; if \fIanchor\fR
+is \f(CWn\fR then the widget will be displayed such that the top center
+point of the rectangular region occupied by the widget will be at the
+positioning point. This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the height to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever height the widget requests internally.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the width to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever width the widget requests internally.
+.TP
+\fB\-window \fIpathName\fR
+Specifies the widget to be managed by the graph. \fIPathName\fR must
+be a child of the \fBgraph\fR widget.
+.SH "GRAPH COMPONENT BINDINGS"
+Specific graph components, such as elements, markers and legend
+entries, can have a command trigger when event occurs in them, much
+like canvas items in Tk's canvas widget. Not all event sequences are
+valid. The only binding events that may be specified are those
+related to the mouse and keyboard (such as \fBEnter\fR, \fBLeave\fR,
+\fBButtonPress\fR, \fBMotion\fR, and \fBKeyPress\fR).
+.PP
+Only one element or marker can be picked during an event. This means,
+that if the mouse is directly over both an element and a marker, only
+the uppermost component is selected. This isn't true for legend entries.
+Both a legend entry and an element (or marker) binding commands
+will be invoked if both items are picked.
+.PP
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the
+element name and another is associated with one of the element's tags
+(see the \fB\-bindtags\fR option). When this occurs, all of the
+matching bindings are invoked. A binding associated with the element
+name is invoked first, followed by one binding for each of the element's
+bindtags. If there are multiple matching bindings for a single tag,
+then only the most specific binding is invoked. A continue command
+in a binding script terminates that script, and a break command
+terminates that script and skips any remaining scripts for the event,
+just as for the bind command.
+.PP
+The \fB\-bindtags\fR option for these components controls addition
+tag names which can be matched. Implicitly elements and markers
+always have tags matching their names. Setting the value of
+the \fB\-bindtags\fR option doesn't change this.
+.SH "C LANGUAGE API"
+You can manipulate data elements from the C language. There
+may be situations where it is too expensive to translate the data
+values from ASCII strings. Or you might want to read data in a
+special file format.
+.PP
+Data can manipulated from the C language using BLT vectors.
+You specify the X-Y data coordinates of an element as vectors and
+manipulate the vector from C. The graph will be redrawn automatically
+after the vectors are updated.
+.PP
+From Tcl, create the vectors and configure the element to use them.
+.CS
+vector X Y
+\&.g element configure line1 -xdata X -ydata Y
+.CE
+To set data points from C, you pass the values as arrays of doubles
+using the \fBBlt_ResetVector\fR call. The vector is reset with the
+new data and at the next idle point (when Tk re-enters its event
+loop), the graph will be redrawn automatically.
+.CS
+#include <tcl.h>
+#include <blt.h>
+
+register int i;
+Blt_Vector *xVec, *yVec;
+double x[50], y[50];
+
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */
+if ((Blt_GetVector(interp, "X", &xVec) != TCL_OK) ||
+ (Blt_GetVector(interp, "Y", &yVec) != TCL_OK)) {
+ return TCL_ERROR;
+}
+
+for (i = 0; i < 50; i++) {
+ x[i] = i * 0.02;
+ y[i] = sin(x[i]);
+}
+
+/* Put the data into BLT vectors */
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {
+ return TCL_ERROR;
+}
+.CE
+See the \fBvector\fR manual page for more details.
+.SH SPEED TIPS
+There may be cases where the graph needs to be drawn and updated as
+quickly as possible. If drawing speed becomes a big
+problem, here are a few tips to speed up displays.
+.TP 2
+\(bu
+Try to minimize the number of data points. The more data points
+the looked at, the more work the graph must do.
+.TP 2
+\(bu
+If your data is generated as floating point values, the time required
+to convert the data values to and from ASCII strings can be
+significant, especially when there any many data points. You can
+avoid the redundant string-to-decimal conversions using the C API to
+BLT vectors.
+.TP 2
+\(bu
+Data elements without symbols are drawn faster than with symbols.
+Set the data element's \fB\-symbol\fR option to \f(CWnone\fR. If you need to
+draw symbols, try using the simple symbols such as \f(CWsplus\fR and
+\f(CWscross\fR.
+.TP 2
+\(bu
+Don't stipple or dash the element. Solid lines are much faster.
+.TP 2
+\(bu
+If you update data elements frequently, try turning off the
+widget's \fB\-bufferelements\fR option. When the graph is first
+displayed, it draws data elements into an internal pixmap. The pixmap
+acts as a cache, so that when the graph needs to be redrawn again, and
+the data elements or coordinate axes haven't changed, the pixmap is
+simply copied to the screen. This is especially useful when you are
+using markers to highlight points and regions on the graph. But if
+the graph is updated frequently, changing either the element data or
+coordinate axes, the buffering becomes redundant.
+.SH LIMITATIONS
+Auto-scale routines do not use requested min/max limits as boundaries
+when the axis is logarithmically scaled.
+.PP
+The PostScript output generated for polygons with more than 1500
+points may exceed the limits of some printers (See PostScript Language
+Reference Manual, page 568). The work-around is to break the polygon
+into separate pieces.
+.SH KEYWORDS
+graph, widget
diff --git a/blt/man/hierbox.mann b/blt/man/hierbox.mann
new file mode 100644
index 00000000000..457bf1851f7
--- /dev/null
+++ b/blt/man/hierbox.mann
@@ -0,0 +1,2261 @@
+'\"
+'\" Copyright 2001-2 by Silicon Metrics Corporation.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Silicon Metrics or any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Silicon Metrics disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Silicon Metrics be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" The hierarchical table widget created by George Howlett.
+'\"
+.so man.macros
+.TH treeview n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+treeview \- Create and manipulate hierarchical table widgets
+.BE
+.SH SYNOPSIS
+\fBtreeview\fR \fIpathName \fR?\fIoptions\fR?
+.SH DESCRIPTION
+The \fBtreeview\fR widget displays a tree of data. It replaces both
+the \fBhiertable\fR and \fBhierbox\fR widgets. The \fBtreeview\fR is
+100% syntax compatible with the \fBhiertable\fR widget. The
+\fBhiertable\fR command is retained for sake of script-level
+compatibility. This widget obsoletes the \fBhierbox\fR widget. It
+does everything the old \fBhierbox\fR widget did, but also provides
+data sharing (via \fItree data objects\fR) and the ability to tag
+nodes.
+.SH INTRODUCTION
+The \fBtreeview\fR widget displays hierarchical data. Data is
+represented as nodes in a general-ordered tree. Each node may have
+sub-nodes and these nodes can in turn has their own children.
+.PP
+A node is displayed as a row entry in the widget. Each entry has a
+text label and icon. When a node has children, its entry is drawn
+with a small button to the left of the label. Clicking the mouse over
+this button opens or closes the node. When a node is \fIopen\fR, its
+children are exposed. When it is \fIclosed\fR, the children and their
+descedants are hidden. The button is normally a \f(CW+\fR or
+\f(CW\-\fR symbol (ala Windows Explorer), but can be replaced with a
+pair of Tk images (open and closed images).
+.PP
+If the node has data associated with it, they can be displayed in
+columns running vertically on either side the tree. You can control
+the color, font, etc of each entry. Any entry label or data field can
+be edited in-place.
+.SH "TREE DATA OBJECT"
+The tree is not stored inside the widget but in a tree data object
+(see the \fBtree\fR command for a further explanation). Tree data
+objects can be shared among different clients, such as a
+\fBtreeview\fR widget or the \fBtree\fR command. You can walk the
+tree and manage its data with the \fBtree\fR command tree, while
+displaying it with the \fBtreeview\fR widget. Whenever the tree is
+updated, the \fBtreeview\fR widget is automatically redrawn.
+.PP
+By default, the \fBtreeview\fR widget creates its own tree object.
+The tree initially contains just a root node. But you can also
+display trees created by the \fBtree\fR command using the \fB\-tree\fR
+configuration option. \fBTreeview\fR widgets can share the same tree
+object, possibly displaying different views of the same data.
+.PP
+A tree object has both a Tcl and C API. You can insert or delete
+nodes using \fBtreeview\fR widget or \fBtree\fR command operations,
+but also from C code. For example, you can load the tree from your C
+code while still managing and displaying the tree from Tcl. The widget
+is automatically notified whenever the tree is modified via C or Tcl.
+.SH SYNTAX
+.DS
+\fBtreeview \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBtreeview\fR command creates a new window \fIpathName\fR and
+makes it into a \fBtreeview\fR widget. At the time this command is
+invoked, there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist. Additional options may be
+specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the
+\fBconfigure\fR operation below for the exact details about what
+\fIoption\fR and \fIvalue\fR pairs are valid.
+.PP
+If successful, \fBtreeview\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to invoke various operations that query or modify the widget.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available are described in the
+.SB "TREEVIEW OPERATIONS"
+section.
+.SH "IDS AND TAGS"
+Nodes can be inserted into a tree using the \fBtreeview\fR widget
+.CS
+blt::treeview .t
+set node [.t insert end root "one"]
+.CE
+or \fBtree\fR command.
+.CS
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+.CE
+In both cases, a number identifying the node is returned (the value of
+\f(CW$node\fR). This serial number or \fIid\fR uniquely identifies
+the node. Please note that you can't infer a location or position of
+a node from its id. The only exception is that the root node is
+always id \f(CW0\fR. Since nodes may have the same labels or be moved
+within the tree, ids provide an convenient way to identify nodes. If
+a tree is shared, the ids will be the same regardless if you are using
+by the \fBtreeview\fR widget or the \fBtree\fR command. Ids are
+recycled when the node deleted.
+.PP
+A node may also have any number of \fItags\fR associated with it. A
+tag is just a string of characters, and it may take any form except
+that of an integer. For example, "\f(CWx123\fR" is valid, but
+"\f(CW123\fR" isn't. The same tag may be associated with many
+different nodes. This is typically done to associate a group of
+nodes. Many operations in the \fBtreeview\fR widget take either node
+ids or tag names as arguments. Using a tag says to apply the operation
+to all nodes with that tag.
+.PP
+The tag \fBall\fR is implicitly associated with every node in
+the tree. It may be used to invoke operations on all the nodes in the
+tree.
+.PP
+Tags may be shared, just like trees, between clients. For example,
+you can use the tags created by the \fBtree\fR command with
+\fBtreeview\fR widgets.
+.SH SPECIAL NODE IDS
+There are also several special non-numeric ids. Special ids differ
+from tags in that they are always translated to their numeric
+equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to
+the \fBtreeview\fR widget.
+.TP 15
+\fBactive\fR
+The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+The \fBactive\fR id is changed automatically by moving the mouse
+pointer over another node or by using the \fBentry activate\fR
+operation. Note that there can be only one active node at a time.
+.TP 15
+\fBanchor\fR
+The node representing the fixed end of the current selection.
+The anchor is set by the \fBselection anchor\fR operation.
+.TP 15
+\fBcurrent\fR
+The node where the mouse pointer is currently located.
+But unlike \fBactive\fR, this id changes while the
+selection is dragged. It is used to determine the
+current node during button drags.
+.TP 15
+\fBdown\fR
+The next open node from the current focus. The \fBdown\fR of
+the last open node is the same.
+.TP 15
+\fBend\fR
+The last open node (in depth-first order) on the tree.
+.TP 15
+\fBfocus\fR
+The node that currently has focus. When a node has focus,
+it receives key events. To indicate focus, the node
+is drawn with a dotted line around its label. You can change the
+focus using the \fBfocus\fR operation.
+.TP 15
+\fBlast\fR
+The last open node from the current focus. But unlike \fBup\fR,
+when the focus is at root, \fBlast\fR wraps around to the last
+open node in the tree.
+.TP 15
+\fBmark\fR
+The node representing the non-fixed end of the current selection.
+The mark is set by the \fBselection mark\fR operation.
+.TP 15
+\fBnext\fR
+The next open node from the current focus. But unlike \fBdown\fR,
+when the focus is on last open node, \fBnext\fR wraps around to the
+root node.
+.TP 15
+\fBnextsibling\fR
+The next sibling from the node with the current focus. If the node
+is already the last sibling then it is the \fBnextsibling\fB.
+.TP 15
+\fBparent\fR
+The parent of the node with the current focus. The \fBparent\fR
+of the root is also the root.
+.TP 15
+\fBprevsibling\fR
+The previous sibling from the node with the current focus. If the node
+is already the first sibling then it is the \fBprevsibling\fB.
+.TP 15
+\fBroot\fR
+The root node. You can also use id \f(CW0\fR to indicate
+the root.
+.TP 15
+\fBup\fR
+The last open node (in depth-first order) from the current focus. The
+\fBup\fR of the root node (i.e. the root has focus) is also the root.
+.TP 15
+\fBview.top\fR
+First node that's current visible in the widget.
+.TP 15
+\fBview.bottom\fR
+Last node that's current visible in the widget.
+.TP 15
+\fIpath\fR
+Absolute path of a node. Path names refer to the node name, not
+their entry labels. Paths don't have to start with a separator (see
+the \fB\-separator\fR configuration option), but component names must
+be separated by the designated separator.
+.TP 15
+\fB@\fIx\fB,\fIy\fR
+Indicates the node that covers the point in the treeview window
+specified by \fIx\fR and \fIy\fR (in pixel coordinates). If no
+part of the entryd covers that point, then the closest node to that
+point is used.
+.PP
+A node may be specified as an id or tag. If the specifier is an
+integer then it is assumed to refer to the single node with that id.
+If the specifier is not an integer, it's checked to see if it's a
+special id (such as focus). Otherwise, it's assumed to be tag. Some
+operations only operate on a single node at a time; if a tag refers to
+more than one node, then an error is generated.
+.SH DATA FIELDS
+A node in the tree can have \fIdata fields\fR. A data field is a
+name-value pair, used to represent arbitrary data in the node. Nodes
+can contain different fields (they aren't required to contain the same
+fields). You can optionally display these fields in the
+\fBtreeview\fR widget in columns running on either side of the
+displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a
+specific field is left blank. Columns can be interactively resized,
+hidden, or, moved.
+.SH ENTRY BINDINGS
+You can bind Tcl commands to be invoked when events occur on nodes
+(much like Tk canvas items). You can bind a node using its id or
+its \fIbindtags\fR. Bindtags are simply names that associate a
+binding with one or more nodes. There is a built-in tag \f(CWall\fR
+that all node entries automatically have.
+.SH "TREEVIEW OPERATIONS"
+The \fBtreeview\fR operations are the invoked by specifying
+the widget's pathname, the operation, and any arguments that pertain
+to that operation. The general form is:
+.sp
+.CS
+\fIpathName operation \fR?\fIarg arg ...\fR?
+.CE
+.sp
+\fIOperation\fR and the \fIarg\fRs determine the exact behavior of the
+command. The following operation are available for \fBtreeview\fR widgets:
+.TP
+\fIpathName \fBbbox\fR ?\fB-screen\fR? \fItagOrId...\fR
+Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more \fItagOrId\fR
+arguments.
+If the \fB\-screen\fR flag is given, then the x-y coordinates
+of the bounding box are returned as screen coordinates, not
+virtual coordinates. Virtual coordinates start from \f(CW0\fR from the
+root node.
+The returned list contains the following values.
+.RS
+.TP 1.25i
+\fIx\fR
+X-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIy\fR
+Y-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIwidth\fR
+Width of the bounding box.
+.TP
+\fIheight\fR
+Height of the bounding box.
+.RE
+.TP
+\fIpathName \fBbind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a node with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on \fBtreeview\fR entries,
+rather than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton \fIoperation\fR ?\fIargs\fR?
+This command is used to control the button selectors within a
+\fBtreeview\fR widget.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBbutton activate\fR \fItagOrId\fR
+Designates the node given by \fItagOrId\fR as active.
+When a node is active it's entry is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active entry at a time.
+The special id \fBactive\fR indicates the currently active node.
+.TP
+\fIpathName \fBbutton bind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an button of a
+node entry with this tag, \fIcommand\fR will be invoked. The syntax is
+similar to the \fBbind\fR command except that it operates on
+\fBtreeview\fR buttons, rather than widgets. See the \fBbind\fR
+manual entry for complete details on \fIsequence\fR and the
+substitutions performed on \fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBbutton configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "BUTTON OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBclose \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Closes the node specified by \fItagOrId\fR. In addition, if a Tcl
+script was specified by the \fB\-closecommand\fR option, it is
+invoked. If the node is already closed, this command has no effect.
+If the \fB\-recurse\fR flag is present, each child node is
+recursively closed.
+.TP
+\fIpathName \fBcolumn \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview columns.
+.RS
+.TP
+\fIpathName \fBcolumn activate\fR \fIcolumn\fR
+Sets the active column to \fIcolumn\fR. \fIColumn\fR is the
+name of a column in the widget.
+When a column is active, it's drawn using its \fB\-activetitlebackground\fR
+and \fB\-activetitleforeground\fR options. If \fIcolumn\fR is the \f(CW""\fR,
+then no column will be active. If no column argument is provided, then
+the name of the currently active column is returned.
+.TP
+\fIpathName \fBcolumn cget\fR \fIname\fR \fIoption\fR
+Returns the current value of the column configuration option given
+by \fIoption\fR for \fIname\fR. \fIName\fR is the name of column
+that corresponds to a data field.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBcolumn configure\fR \fIname\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the column designated
+by \fIname\fR. \fIName\fR is the name of the column corresponding
+to a data field.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "COLUMN OPTIONS"
+below.
+.TP
+\fIpathName \fBcolumn delete\fR \fIfield\fR ?\fIfield\fR...?
+Deletes one of more columns designated by \fIfield\fR. Note
+that this does not delete the data fields themselves.
+.TP
+\fIpathName \fBcolumn insert\fR \fIposition\fR \fIfield\fR ?\fIoptions\fR...?
+Inserts one of more columns designated by \fIfield\fR. A column displays
+each node's data field by the same name. If the node doesn't
+have the given field, the cell is left blank.
+\fIPosition\fR
+indicates where in the list of columns to add the new column. It may be
+either a number or \f(CWend\fR.
+.TP
+\fIpathName \fBcolumn invoke\fR \fIfield\fR
+Invokes the Tcl command associated with the column \fIfield\fR,
+if there is one (using the column's \fB\-command\fR option).
+The command is ignored if the column's \fB\-state\fR option
+set to \f(CWdisabled\fR.
+.TP
+\fIpathName \fBcolumn move \fIname\fR \fIdest\fR
+Moves the column \fIname\fR to the destination position.
+\fIDest\fR is the name of another column or a screen position
+in the form \f(CW@\fIx\f(CW,\fIy\fR.
+.TP
+\fIpathName \fBcolumn names\fR
+Returns a list of the names of all columns in the widget.
+The list is ordered as the columns are drawn from left-to-right.
+.TP
+\fIpathName \fBcolumn nearest\fR \fIx\fR ?\fIy\fR?
+Returns the name of the column closest to the given X-Y screen
+coordinate. If you provide a \fIy\fR argument (it's optional),
+a name is returned only when if the point is over a column's title.
+.RE
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TREEVIEW OPTIONS"
+below.
+.TP
+\fIpathName \fBcurselection\fR
+Returns a list containing the ids of all of the entries that are
+currently selected.
+If there are no entries selected, then the empty string is returned.
+.TP
+\fIpathName \fBdelete \fItagOrId\fR...
+Deletes one or more entries given by \fItagOrId\fR and its children.
+.TP
+\fIpathName \fBentry \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview entries.
+.RS
+.TP
+\fIpathName \fBentry activate\fR \fItagOrId\fR
+Sets the active entry to the one specified by \fItagOrId\fR.
+When an entry is active it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active node at a time.
+The special id of the currently active node is \fBactive\fR.
+.TP
+\fIpathName \fBentry cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBentry children\fR \fItagOrId\fR ?\fIfirst\fR? ?\fIlast\fR?
+Returns a list of ids for the given range of children of \fItagOrId\fR.
+\fITagOrId\fR is the id or tag of the node to be examined.
+If only a \fIfirst\fR argument is present, then the id
+of the that child at that numeric position is returned. If both \fIfirst\fR
+and \fIlast\fR arguments are given, then the ids of all the children
+in that range are returned. Otherwise the ids of all children
+are returned.
+.TP
+\fIpathName \fBentry configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.TP
+\fIpathName \fBentry delete\fR \fItagOrId\fR ?\fIfirst\fR ?\fIlast\fR?
+Deletes the one or more children nodes of the parent \fItagOrId\fR.
+If \fIfirst\fR and \fIlast\fR arguments are present, they are
+positions designating a range of children nodes to be deleted.
+.TP
+\fIpathName \fBentry isbefore \fItagOrId1\fR \fItagOrId2\fR
+Returns 1 if \fItagOrId1\fR is before \fItagOrId2\fR and 0 otherwise.
+.TP
+\fIpathName \fBentry ishidden \fItagOrId\fR
+Returns 1 if the node is currently hidden and 0 otherwise. A node is
+also hidden if any of its ancestor nodes are closed or hidden.
+.TP
+\fIpathName \fBentry isopen \fItagOrId\fR
+Returns 1 if the node is currently open and 0 otherwise.
+.TP
+\fIpathName \fBentry size\fR \fB\-recurse\fR \fItagOrId\fR
+Returns the number of children for parent node \fItagOrId\fR.
+If the \fB\-recurse\fR flag is set, the number of all
+its descendants is returned. The node itself is not counted.
+.RE
+.TP
+\fIpathName \fBfind \fR?\fIflags\fR? \fIfirst\fR \fIlast\fR
+Finds for all entries matching the criteria given by \fIflags\fR. A
+list of ids for all matching nodes is returned. \fIFirst\fR and
+\fIlast\fR are ids designating the range of the search in
+depth-first order. If \fIlast\fR is before \fIfirst\fR, then nodes
+are searched in reverse order. The valid flags are:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Patterns must match exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Pick entries that don't match.
+.TP 1.25i
+\fB\-exec\fI string\fR
+Specifies a Tcl script to be invoked for each matching node.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP 1.25i
+\fB\-count\fI number\fR
+Stop searching after \fInumber\fR matches.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBfocus \fR \fItagOrId\fR
+Sets the focus to the node given by \fItagOrId\fR. When a node
+has focus, it can receive keyboard events.
+The special id \fBfocus\fR designates the node that currently has focus.
+.TP
+\fIpathName \fBget \fR?\fB\-full\fR? \fItagOrId\fR \fItagOrId\fR...
+Translates one or more ids to their node entry names. It returns a list of
+names for all the ids specified. If the \fB\-full\fR
+flag is set, then the full pathnames are returned.
+.TP
+\fIpathName \fBhide \fR?\fBflags\fR? \fItagOrId\fR...
+Hides all nodes matching the criteria given by \fIflags\fR. The
+search is performed recursively for each node given by \fItagOrId\fR.
+The valid flags are described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Hide nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBindex \fR?\fB\-at\fR \fItagOrId\fR? \fIstring\fR
+Returns the id of the node specified by \fIstring\fR. \fIString\fR
+may be a tag or node id.
+Some special ids are normally relative to the node that
+has focus. The \fB\-at\fR flag lets you select another node.
+.TP
+\fIpathName \fBinsert \fR?\fB\-at \fItagOrId\fR? \fIposition\fR \fIpath\fR ?\fIoptions...\fR? ?\fIpath\fR? ?\fIoptions...\fR?
+Inserts one or more nodes at \fIposition\fR. \fIPosition\fR is the
+location (number or \f(CWend\fR) where the new nodes are added to
+the parent node. \fIPath\fR is the pathname of the new node.
+Pathnames can be formated either as a Tcl list (each element is a path
+component) or as a string separated by a special character sequence
+(using the \fB\-separator\fR option). Pathnames are normally
+absolute, but the \fB\-at\fR switch lets you select a relative
+starting point. Its value is the id of the starting node.
+.sp
+All ancestors of the new node must already exist, unless the
+\fB\-autocreate\fR option is set. It is also an error if a node
+already exists, unless the \fB\-allowduplicates\fR option is set.
+.sp
+\fIOption\fR and \fIvalue\fR may have any of the values accepted by the
+\fBentry configure\fR operation described in the
+.SB "ENTRY OPERATIONS"
+section below. This command returns a list of the ids of
+the new entries.
+.TP
+\fIpathName \fBmove \fItagOrId\fR \fIhow\fR \fIdestId\fR
+Moves the node given by \fItagOrId\fR to the destination node. The
+node can not be an ancestor of the destination. \fIDestId\fR is
+the id of the destination node and can not be the root of the
+tree. In conjunction with \fIhow\fR, it describes how the move is
+performed.
+.RS
+.TP 8
+\f(CWbefore\fR
+Moves the node before the destination node.
+.TP 8
+\f(CWafter\fR
+Moves the node after the destination node.
+.TP 8
+\f(CWinto\fR
+Moves the node to the end of the destination's list of children.
+.RE
+.TP
+\fIpathName \fBnearest \fIx y\fR ?\fIvarName\fR?
+Returns the id of the node entry closest to the given X-Y screen
+coordinate. The optional argument \fIvarName\fR is the name of
+variable which is set to either \f(CWbutton\fR or \f(CWselect\fR to
+indicate over what part of the node the coordinate lies.
+If the coordinate is not directly over any node, then
+\fIvarName\fR will contain the empty string.
+.TP
+\fIpathName \fBopen \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Opens the one or more nodes specified by \fItagOrId\fR.
+If a node is not already open, the Tcl script specified by the
+\fB\-opencommand\fR option is invoked. If the \fB\-recurse\fR flag
+is present, then each descendant is recursively opened.
+.TP
+\fIpathName \fBrange\fR ?\fB-open\fR? \fIfirst last\fR
+Returns the ids in depth-first order of the nodes
+between the \fIfirst\fR and \fIlast\fR ids. If the \fB\-open\fR
+flag is present, it indicates to consider only open nodes.
+If \fIlast\fR is before \fIfirst\fR, then the ids are
+returned in reverse order.
+.TP
+\fIpathName \fBscan\fR \fIoption args\fR
+This command implements scanning. It has
+two forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBscan mark \fIx y\fR
+Records \fIx\fR and \fIy\fR and the current view in the treeview
+window; used in conjunction with later \fBscan dragto\fR commands.
+Typically this command is associated with a mouse button press in
+the widget. It returns an empty string.
+.TP
+\fIpathName \fBscan dragto \fIx y\fR.
+Computes the difference between its \fIx\fR and \fIy\fR
+arguments and the \fIx\fR and \fIy\fR arguments to the last
+\fBscan mark\fR command for the widget.
+It then adjusts the view by 10 times the
+difference in coordinates. This command is typically associated
+with mouse motion events in the widget, to produce the effect of
+dragging the list at high speed through the window. The return
+value is an empty string.
+.RE
+.TP
+\fIpathName \fBsee\fR ?\fB\-anchor \fIanchor\fR? \fItagOrId\fR
+Adjusts the view of entries so that the node given by \fItagOrId\fR is
+visible in the widget window. It is an error if \fBtagOrId\fR is a
+tag that refers to more than one node. By default the node's entry
+is displayed in the middle of the window. This can changed using the
+\fB\-anchor\fR flag. Its value is a Tk anchor position.
+.TP
+\fIpathName \fBselection \fIoption arg\fR
+This command is used to adjust the selection within a \fBtreeview\fR
+widget. It has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBselection anchor \fItagOrId\fR
+Sets the selection anchor to the node given by \fItagOrId\fR.
+If \fItagOrId\fR refers to a non-existent node, then the closest
+node is used.
+The selection anchor is the end of the selection that is fixed
+while dragging out a selection with the mouse.
+The special id \fBanchor\fR may be used to refer to the anchor
+node.
+.TP
+\fIpathName \fBselection cancel\fR
+Clears the temporary selection of entries back to the
+current anchor. Temporary selections are created by
+the \fBselection mark\fR operation.
+.TP
+\fIpathName \fBselection clear \fIfirst \fR?\fIlast\fR?
+Removes the entries between \fIfirst\fR and \fIlast\fR
+(inclusive) from the selection. Both \fIfirst\fR and
+\fIlast\fR are ids representing a range of entries.
+If \fIlast\fR isn't given, then only \fIfirst\fR is deselected.
+Entries outside the selection are not affected.
+.TP
+\fIpathName \fBselection clearall\fR
+Clears the entire selection.
+.TP
+\fIpathName \fBselection mark \fItagOrId\fR
+Sets the selection mark to the node given by \fItagOrId\fR. This
+causes the range of entries between the anchor and the mark to be
+temporarily added to the selection. The selection mark is the end of
+the selection that is fixed while dragging out a selection with the
+mouse. The special id \fBmark\fR may be used to refer to the current
+mark node.
+If \fItagOrId\fR refers to a non-existent node, then the mark
+is ignored.
+Resetting the mark will unselect
+the previous range. Setting the anchor finalizes the range.
+.TP
+\fIpathName \fBselection includes \fItagOrId\fR
+Returns 1 if the node given by \fItagOrId\fR is currently
+selected, 0 if it isn't.
+.TP
+\fIpathName \fBselection present\fR
+Returns 1 if any nodes are currently selected and 0 otherwise.
+.TP
+\fIpathName \fBselection set \fIfirst \fR?\fIlast\fR?
+Selects all of the nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, without affecting
+the selection state of nodes outside that range.
+.TP
+\fIpathName \fBselection toggle \fIfirst \fR?\fIlast\fR?
+Selects/deselects nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and
+visa versa.
+.RE
+.TP
+\fIpathName \fBshow \fR?\fBflags\fR? \fItagOrId\fR...
+Exposes all nodes matching the criteria given by \fIflags\fR. This
+is the inverse of the \fBhide\fR operation. The search is performed
+recursively for each node given by \fItagOrId\fR. The valid flags are
+described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Expose nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBsort\fR ?\fIoperation\fR? \fIargs...\fR
+.RS
+.TP
+\fIpathName \fBsort auto\fR ?\fIboolean\fR
+Turns on/off automatic sorting of node entries. If \fIboolean\fR is
+true, entries will be automatically sorted as they are opened,
+closed, inserted, or deleted. If no \fIboolean\fR argument is
+provided, the current state is returned.
+.TP
+\fIpathName \fBsort cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBsort configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the sorting configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given sorting option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-column\fI string\fR
+Specifies the column to sort. Entries in the widget are rearranged
+according to this column. If \fIcolumn\fR is \f(CW""\fR then
+no sort is performed.
+.TP
+\fB\-command\fI string\fR
+Specifies a Tcl procedure to be called when sorting nodes.
+The procedure is called with three arguments: the pathname of the widget
+and the fields of two entries. The procedure returns 1 if the first
+node is greater than the second, -1 is the second is greater, and 0
+if equal.
+.TP
+\fB\-decreasing\fI boolean\fR
+Indicates to sort in ascending/descending order. If \fIboolean\fR
+is true, then the entries as in descending order. The default is
+\f(CWno\fR.
+.TP
+\fB\-mode\fI string\fR
+Specifies how to compare entries when sorting. \fIString\fR
+may be one of the following:
+.RS
+.TP 1.5i
+\f(CWascii\fR
+Use string comparison based upon the ASCII collation order.
+.TP 1.5i
+\f(CWdictionary\fR
+Use dictionary-style comparison. This is the same as \f(CWascii\fR
+except (a) case is ignored except as a tie-breaker and (b) if two
+strings contain embedded numbers, the numbers compare as integers, not
+characters. For example, "bigBoy" sorts between
+"bigbang" and "bigboy", and "x10y" sorts between "x9y" and "x11y".
+.TP 1.5i
+\f(CWinteger\fR
+Compares fields as integers.
+.TP 1.5i
+\f(CWreal\fR
+Compares fields as floating point numbers.
+.TP 1.5i
+\f(CWcommand\fR
+Use the Tcl proc specified by the \fB\-command\fR option to compare entries
+when sorting. If no command is specified, the sort reverts to
+\f(CWascii\fR sorting.
+.RE
+.RE
+.TP
+\fIpathName \fBsort once\fR ?\fIflags\fR? \fItagOrId...\fR
+Sorts the children for each entries specified by \fItagOrId\fR.
+By default, entries are sorted by name, but you can specify a
+Tcl proc to do your own comparisons.
+.RS
+.TP 1.5i
+\fB\-recurse\fR
+Recursively sort the entire branch, not just the children.
+.RE
+.RE
+.TP
+\fIpathName \fBtag \fIoperation args\fR
+Tags are a general means of selecting and marking nodes in the tree.
+A tag is just a string of characters, and it may take any form except
+that of an integer. The same tag may be associated with many
+different nodes.
+.sp
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below.
+.RS
+.TP
+\fIpathName\fR \fBtag add\fR \fIstring\fR \fIid\fR...
+Adds the tag \fIstring\fR to one of more entries.
+.TP
+\fIpathName\fR \fBtag delete\fR \fIstring\fR \fIid\fR...
+Deletes the tag \fIstring\fR from one or more entries.
+.TP
+\fIpathName\fR \fBtag forget\fR \fIstring\fR
+Removes the tag \fIstring\fR from all entries. It's not an error if no
+entries are tagged as \fIstring\fR.
+.TP
+\fIpathName\fR \fBtag names\fR ?\fIid\fR?
+Returns a list of tags used. If an \fIid\fR argument
+is present, only those tags used by the node designated by \fIid\fR
+are returned.
+.TP
+\fIpathName\fR \fBtag nodes\fR \fIstring\fR
+Returns a list of ids that have the tag \fIstring\fR. If no node
+is tagged as \fIstring\fR, then an empty string is returned.
+.RE
+.TP
+\fIpathName \fBtext \fIoperation\fR ?\fIargs\fR?
+This operation is used to provide text editing for cells (data
+fields in a column) or entry labels.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBtext apply\fR
+Applies the edited buffer, replacing the entry label
+or data field. The edit window is hidden.
+.TP
+\fIpathName \fBtext cancel\fR
+Cancels the editing operation, reverting the entry label
+or data value back to the previous value. The edit window is hidden.
+.TP
+\fIpathName \fBtext cget\fI value\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBtext configure\fR ?\fIoption value\fR?
+Query or modify the configuration options of the edit window.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TEXT EDITING OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBtext delete\fI first last\fR
+Deletes the characters in the edit buffer between the two given
+character positions.
+.TP
+\fIpathName \fBtext get\fR ?\fI\-root\fR? \fIx y\fR
+.TP
+\fIpathName \fBtext icursor\fI index\fR
+.TP
+\fIpathName \fBtext index\fI index\fR
+Returns the text index of given \fIindex\fR.
+.TP
+\fIpathName \fBtext insert\fI index string\fR
+Insert the text string \fIstring\fR into the edit buffer at the index
+\fIindex\fR. For example, the index 0 will prepend the buffer.
+.TP
+\fIpathName \fBtext selection\fI args\fR
+This operation controls the selection of the editing window. Note
+that this differs from the selection of entries.
+It has the following forms:
+.RS
+.TP
+\fIpathName \fBtext selection adjust\fI index\fR
+Adjusts either the first or last index of the selection.
+.TP
+\fIpathName \fBtext selection clear\fR
+Clears the selection.
+.TP
+\fIpathName \fBtext selection from\fI index\fR
+Sets the anchor of the selection.
+.TP
+\fIpathName \fBtext selection present\fR
+Indicates if a selection is present.
+.TP
+\fIpathName \fBtext selection range\fI start end\fR
+Sets both the anchor and mark of the selection.
+.TP
+\fIpathName \fBtext selection to\fI index\fR
+Sets the unanchored end (mark) of the selection.
+.RE
+.TP
+\fIpathName \fBtoggle \fItagOrId\fR
+Opens or closes the node given by \fItagOrId\fR. If the corresponding
+\fB\-opencommand\fR or \fB\-closecommand\fR option is set, then that
+command is also invoked.
+.TP
+\fIpathName \fBxview \fIargs\fR
+This command is used to query and change the horizontal position of the
+information in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the \fBtreeview\fR widget's text is off-screen to the left,
+the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview\fR \fItagOrId\fR
+Adjusts the view in the window so that the character position given by
+\fItagOrId\fR is displayed at the left edge of the window.
+Character positions are defined by the width of the character \fB0\fR.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the \fBtreeview\fR widget's text is off-screen to the left.
+\fIfraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR character units (the width of the \fB0\fR character)
+on the display; if it is \fBpages\fR then the view adjusts by
+\fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \fByview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \fByview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means
+it is halfway through the treeview window, for example).
+The second element gives the position of the node just after
+the last one in the window, relative to the widget as a whole.
+These are the same values passed to scrollbars via the \fB\-yscrollcommand\fR
+option.
+.TP
+\fIpathName \fByview\fR \fItagOrId\fR
+Adjusts the view in the window so that the node given by
+\fItagOrId\fR is displayed at the top of the window.
+.TP
+\fIpathName \fByview moveto\fI fraction\fR
+Adjusts the view in the window so that the node given by \fIfraction\fR
+appears at the top of the window.
+\fIFraction\fR is a fraction between 0 and 1; 0 indicates the first
+node, 0.33 indicates the node one-third the
+way through the \fBtreeview\fR widget, and so on.
+.TP
+\fIpathName \fByview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR.
+If \fIwhat\fR is \fBunits\fR, the view adjusts up or down by
+\fInumber\fR lines; if it is \fBpages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier nodes
+become visible; if it is positive then later nodes
+become visible.
+.RE
+.SH "TREEVIEW OPTIONS"
+In addition to the \fBconfigure\fR operation, widget configuration
+options may also be set by the Tk \fBoption\fR command. The class
+resource name is \f(CWTreeView\fR.
+.CS
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+.CE
+The following widget options are available:
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active entries. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of the active node. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed for an entry's icon
+when it is active. \fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-autocreate \fIboolean\fR
+If \fIboolean\fR is true, automatically create missing ancestor
+nodes when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-allowduplicates \fIboolean\fR
+If \fIboolean\fR is true, allow nodes with duplicate pathnames
+when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the widget. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is closed. You can
+overrider this for individual entries using the entry's \fB\-closecommand\fR
+option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-dashes \fInumber\fR
+Sets the dash style of the horizontal and vertical lines drawn connecting
+entries. \fINumber\fR is the length in pixels of the dashes and gaps in
+the line. If \fInumber\fR is \f(CW0\fR, solid lines will
+be drawn. The default is \f(CW1\fR (dotted).
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the selection is exported. If the widget is exporting its
+selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type \fBSTRING\fR;
+the value of the selection will be the label of the selected nodes,
+separated by newlines. The default is \f(CWno\fR.
+.TP
+\fB\-flat \fIboolean\fR
+Indicates whether to display the tree as a flattened list.
+If \fIboolean\fR is true, then the hierarchy will be a list of full
+paths for the nodes. This option also has affect on sorting.
+See the
+.SB "SORT OPERATIONS"
+section for more information.
+The default is \f(CWno\fR.
+.TP
+\fB\-focusdashes \fIdashList\fR
+Sets the dash style of the outline rectangle drawn around the entry
+label of the node that current has focus. \fINumber\fR is the length
+in pixels of the dashes and gaps in the line. If
+\fInumber\fR is \f(CW0\fR, a solid line will be drawn. The default is
+\f(CW1\fR.
+.TP
+\fB\-focusforeground \fIcolor\fR
+Sets the color of the focus rectangle.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font for entry labels. You can override this for individual
+entries with the entry's \fB\-font\fR configuration option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of entry labels. You can override this for individual
+entries with the entry's \fB\-foreground\fR configuration option.
+The default is
+\f(CWblack\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW400\fR.
+.TP
+\fB\-hideroot \fIboolean\fR
+If \fIboolean\fR is true, it indicates that no entry for the root node
+should be displayed. The default is \f(CWno\fR.
+.TP
+\fB\-highlightbackground \fIcolor\fR
+Specifies the normal color of the traversal highlight region when
+the widget does not have the input focus.
+.TP
+\fB\-highlightcolor \fIcolor\fR
+Specifies the color of the traversal highlight rectangle when
+the widget has the input focus.
+The default is \f(CWblack\fR.
+.TP
+\fB\-highlightthickness \fIpixels\fR
+Specifies the width of the highlight rectangle indicating when the
+widget has input focus. The value may have any of the forms acceptable
+to \fBTk_GetPixels\fR. If the value is zero, no focus highlight will
+be displayed. The default is \f(CW2\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images for the entry's icon.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-linecolor \fIcolor\fR
+Sets the color of the connecting lines drawn between entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-linespacing \fIpixels\fR
+Sets the number of pixels spacing between entries.
+The default is \f(CW0\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the lines drawn connecting entries. If \fIpixels\fR
+is \f(CW0\fR, no vertical or horizontal lines are drawn.
+The default is \f(CW1\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is open.
+You can override this for individual entries with the entry's
+\fB\-opencommand\fR configuration option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the widget. \fIRelief\fR
+specifies how the \fBtreeview\fR widget should appear relative to widget
+it is packed into; for example, \f(CWraised\fR means the \fBtreeview\fR widget
+should appear to protrude. The default is \f(CWsunken\fR.
+.TP
+\fB\-scrollmode \fImode\fR
+Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is \f(CWhierbox\fR.
+.RS
+.TP 1.25i
+\f(CWlistbox\fR
+Like the \fBlistbox\fR widget, the last entry can always be
+scrolled to the top of the widget window. This allows the scrollbar
+thumb to shrink as the last entry is scrolled upward.
+.TP 1.25i
+\f(CWhierbox\fR
+Like the \fBhierbox\fR widget, the last entry can only be
+viewed at the bottom of the widget window. The scrollbar
+stays a constant size.
+.TP 1.25i
+\f(CWcanvas\fR
+Like the \fBcanvas\fR widget, the entries are bound within
+the scrolling area.
+.RE
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background color selected node entries.
+The default is \f(CW#ffffea\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the raised 3-D border drawn around the labels
+of selected entries. The default is \f(CW0\fR.
+\fB\-selectcommand \fIstring\fR
+Specifies a Tcl script to invoked when the set of selected
+nodes changes.
+The default is \f(CW""\fR.
+.TP
+\fB\-selectforeground \fIcolor\fB
+Sets the color of the labels of selected node entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-selectmode \fImode\fR
+Specifies the selection mode. If \fImode\fR is
+\f(CWsingle\fR, only one node can be selected
+at a time. If \f(CWmultiple\fR more than one
+node can be selected.
+The default is \f(CWsingle\fR.
+.TP
+\fB\-separator \fIstring\fR
+Specifies the character sequence to use when spliting the path components.
+The separator may be several characters wide (such as "::")
+Consecutive separators in a pathname are treated as one.
+If \fIstring\fR is the empty string, the pathnames are Tcl lists.
+Each element is a path component. The default is \f(CW""\fR.
+.TP
+\fB\-showtitles \fIboolean\fR
+If \fIboolean\fR is false, column titles are not be displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-sortselection \fIboolean\fR
+If \fIboolean\fR is true, nodes in the selection are ordered as they
+are currently displayed (depth-first or sorted), not in the order
+they were selected. The default is \f(CWno\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW"1"\fR.
+.TP
+\fB\-trim \fIstring\fR
+Specifies a string leading characters to trim from entry pathnames
+before parsing. This only makes sense if the \fB\-separator\fR is also
+set. The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the widget. If \fIpixels\fR is 0, then
+the with is computed from the contents of the \fBtreeview\fR widget.
+The default is \f(CW200\fR.
+.TP
+\fB\-xscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-xscrollincrement\fR \fIpixels\fR
+Sets the horizontal scrolling distance. The default is 20 pixels.
+.TP
+\fB\-yscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with vertical
+scrollbars. Whenever the vertical view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-yscrollincrement\fR \fIpixels\fR
+Sets the vertical scrolling distance. The default is 20 pixels.
+.SH "ENTRY OPTIONS"
+Many widget configuration options have counterparts in entries. For
+example, there is a \fB\-closecommand\fR configuration option for both
+widget itself and for individual entries. Options set at the widget
+level are global for all entries. If the entry configuration option
+is set, then it overrides the widget option. This is done to avoid
+wasting memory by replicated options. Most entries will have
+redundant options.
+.PP
+There is no resource class or name for entries.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed as the entry's icon
+when it is active. This overrides the global \fB\-activeicons\fR
+configuration option for the specific entry.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for nodes. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events are handled for nodes. Each tag in the list matching the current
+event sequence will have its Tcl command executed. The default value
+is \f(CWall\fR.
+.TP
+\fB\-button \fIstring\fR
+Indicates whether a button should be displayed on the left side
+of the node entry. \fIString\fR can be \f(CWyes\fR, \f(CWno\fR,
+or \f(CWauto\fR. If \f(CWauto\fR, then a button is automatically
+displayed if the node has children. This is the default.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when the node is closed. This
+overrides the global \fB\-closecommand\fR option for this entry.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-data \fIstring\fR
+Sets data fields for the node. \fIString\fR is a list of
+name-value pairs to be set. The default is \f(CW""\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for entry labels. This overrides the widget's
+\fB\-font\fR option for this node. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of the entry label. This overrides the widget's
+\fB\-foreground\fR configuration option. The default is \f(CW""\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images to be displayed for the entry's icon.
+This overrides the global \fB\-icons\fR configuration option.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-label \fIstring\fR
+Sets the text for the entry's label. If not set, this
+defaults to the name of the node. The default is \f(CW""\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when the entry is opened.
+This overrides the widget's \fB\-opencommand\fR option for this node.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.SH "BUTTON OPTIONS"
+Button configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWButton\fR. The resource name is always
+\f(CWbutton\fR.
+.CS
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+.CE
+The following are the configuration options available for buttons.
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the button. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the button.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-closerelief \fIrelief\fR
+Specifies the 3-D effect for the closed button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Sets the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of buttons.
+The default is \f(CWblack\fR.
+.TP
+\fB\-images \fIimages\fR
+Specifies images to be displayed for the button.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the button is open, the
+second when it is closed. If the \fIimages\fR is the empty string,
+then a plus/minus gadget is drawn. The default is \f(CW""\fR.
+.TP
+\fB\-openrelief \fIrelief\fR
+Specifies the 3-D effect of the open button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-size \fIpixels\fR
+Sets the requested size of the button.
+The default is \f(CW0\fR.
+.RE
+.SH "COLUMN OPTIONS"
+Column configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWColumn\fR. The resource name is the
+name of the column.
+.CS
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+.CE
+The following configuration options are available for columns.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the column. This overrides
+the widget's \fB\-background\fR option. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border of the column.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW0\fR.
+.TP
+\fB\-edit \fIboolean\fR
+Indicates if the column's data fields can be edited. If \fIboolean\fR is
+false, the data fields in the column may not be edited.
+The default is \f(CWyes\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Specifies the foreground color of the column.
+You can override this for individual entries with the entry's
+\fB\-foreground\fR option.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for a column. You can override this for individual entries
+with the entry's \fB\-font\fR option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-hide \fIboolean\fR
+If \fIboolean\fR is true, the column is not displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the column data fields title should be justified within
+the column. This matters only when the column is wider than the
+data field to be display.
+\fIJustify\fR must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR.
+The default is \f(CWleft\fR.
+.TP
+\fB\-pad \fIpad\fR
+Specifies how much padding for the left and right sides of the column.
+\fIPad\fR is a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the column is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW2\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the column. \fIRelief\fR
+specifies how the column should appear relative to the widget;
+for example, \f(CWraised\fR means the column should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-state \fIstate\fR
+Sets the state of the column. If \fIstate\fR is \f(CWdisable\fR then
+the column title can not be activated nor invoked.
+The default is \f(CWnormal\fR.
+.TP
+\fB\-text \fIstring\fR
+Sets the title for the column.
+The default is \f(CW""\fR.
+.TP
+\fB\-titleforeground \fIcolor\fR
+Sets the foreground color of the column title.
+The default is \f(CWblack\fR.
+.TP
+\fB\-titleshadow \fIcolor\fR
+Sets the color of the drop shadow of the column title.
+The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the column. This overrides
+the computed with of the column. If \fIpixels\fR is 0,
+the width is computed as from the contents of the column. The
+default is \f(CW0\fR.
+.RE
+.SH "TEXT EDITING OPTIONS"
+Text edit window configuration options may also be set by the
+\fBoption\fR command. The resource class is \f(CWTreeViewEditor\fR.
+The resource name is always \f(CWedit\fR.
+.CS
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+.CE
+The following are the configuration options available for the
+text editing window.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the text edit window. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the edit window.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the text selection is exported. If the edit window is
+exporting its selection then it will observe the standard X11 protocols
+for handling the selection. Selections are available as type \fBSTRING\fR.
+The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the edit window. \fIRelief\fR
+indicates how the background should appear relative to the edit
+window; for example, \f(CWraised\fR means the background should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the selected text in the
+edit window. The \fB\-selectrelief\fR option determines if a border
+is to be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-selectforeground \fIcolor\fR
+Sets the foreground of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectrelief \fIrelief\fR
+Specifies the 3-D effect of the selected text in the edit window.
+\fIRelief\fR indicates how the text should appear relative to the edit
+window; for example, \f(CWraised\fR means the text should
+appear to protrude. The default is \f(CWflat\fR.
+.RE
+.SH "DEFAULT BINDINGS"
+Tk automatically creates class bindings for treeviews that give them
+Motif-like behavior. Much of the behavior of a \fBtreeview\fR widget is determined
+by its \fB\-selectmode\fR option, which selects one of two ways
+of dealing with the selection.
+.PP
+If the selection mode is \fBsingle\fR, only one node can be
+selected at a time.
+Clicking button 1 on an node selects
+it and deselects any other selected item.
+.PP
+If the selection mode is \fBmultiple\fR,
+any number of entries may be selected at once, including discontiguous
+ranges. Clicking Control-Button-1 on a node entry
+toggles its selection state without affecting any other entries.
+Pressing Shift-Button-1 on a node entry selects
+it, extends the selection.
+.IP [1]
+In \fBextended\fR mode, the selected range can be adjusted by pressing
+button 1 with the Shift key down: this modifies the selection to
+consist of the entries between the anchor and the entry under
+the mouse, inclusive.
+The un-anchored end of this new selection can also be dragged with
+the button down.
+.IP [2]
+In \fBextended\fR mode, pressing button 1 with the Control key down
+starts a toggle operation: the anchor is set to the entry under
+the mouse, and its selection state is reversed. The selection state
+of other entries isn't changed.
+If the mouse is dragged with button 1 down, then the selection state
+of all entries between the anchor and the entry under the mouse
+is set to match that of the anchor entry; the selection state of
+all other entries remains what it was before the toggle operation
+began.
+.IP [3]
+If the mouse leaves the treeview window with button 1 down, the window
+scrolls away from the mouse, making information visible that used
+to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the
+button is released, or the end of the hierarchy is reached.
+.IP [4]
+Mouse button 2 may be used for scanning.
+If it is pressed and dragged over the \fBtreeview\fR widget, the contents of
+the hierarchy drag at high speed in the direction the mouse moves.
+.IP [5]
+If the Up or Down key is pressed, the location cursor (active
+entry) moves up or down one entry.
+If the selection mode is \fBbrowse\fR or \fBextended\fR then the
+new active entry is also selected and all other entries are
+deselected.
+In \fBextended\fR mode the new active entry becomes the
+selection anchor.
+.IP [6]
+In \fBextended\fR mode, Shift-Up and Shift-Down move the location
+cursor (active entry) up or down one entry and also extend
+the selection to that entry in a fashion similar to dragging
+with mouse button 1.
+.IP [7]
+The Left and Right keys scroll the \fBtreeview\fR widget view left and right
+by the width of the character \fB0\fR.
+Control-Left and Control-Right scroll the \fBtreeview\fR widget view left and
+right by the width of the window.
+Control-Prior and Control-Next also scroll left and right by
+the width of the window.
+.IP [8]
+The Prior and Next keys scroll the \fBtreeview\fR widget view up and down
+by one page (the height of the window).
+.IP [9]
+The Home and End keys scroll the \fBtreeview\fR widget horizontally to
+the left and right edges, respectively.
+.IP [10]
+Control-Home sets the location cursor to the the first entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [11]
+Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [12]
+In \fBextended\fR mode, Control-Shift-Home extends the selection
+to the first entry and Control-Shift-End extends
+the selection to the last entry.
+.IP [13]
+In \fBmultiple\fR mode, Control-Shift-Home moves the location cursor
+to the first entry and Control-Shift-End moves
+the location cursor to the last entry.
+.IP [14]
+The space and Select keys make a selection at the location cursor
+(active entry) just as if mouse button 1 had been pressed over
+this entry.
+.IP [15]
+In \fBextended\fR mode, Control-Shift-space and Shift-Select
+extend the selection to the active entry just as if button 1
+had been pressed with the Shift key down.
+.IP [16]
+In \fBextended\fR mode, the Escape key cancels the most recent
+selection and restores all the entries in the selected range
+to their previous selection state.
+.IP [17]
+Control-slash selects everything in the widget, except in
+\fBsingle\fR and \fBbrowse\fR modes, in which case it selects
+the active entry and deselects everything else.
+.IP [18]
+Control-backslash deselects everything in the widget, except in
+\fBbrowse\fR mode where it has no effect.
+.IP [19]
+The F16 key (labelled Copy on many Sun workstations) or Meta-w
+copies the selection in the widget to the clipboard, if there is
+a selection.
+.PP
+The behavior of \fBtreeview\fR widgets can be changed by defining new bindings
+for individual widgets or by redefining the class bindings.
+.SS WIDGET BINDINGS
+In addition to the above behavior, the following additional behavior
+is defined by the default widget class (TreeView) bindings.
+.IP \f(CW<ButtonPress-2>\fR
+Starts scanning.
+.IP \f(CW<B2-Motion>\fR
+Adjusts the scan.
+.IP \f(CW<ButtonRelease-2>\fR
+Stops scanning.
+.IP \f(CW<B1-Leave>\fR
+Starts auto-scrolling.
+.IP \f(CW<B1-Enter>\fR
+Starts auto-scrolling
+.IP \f(CW<KeyPress-Up>\fR
+Moves the focus to the previous entry.
+.IP \f(CW<KeyPress-Down>\fR
+Moves the focus to the next entry.
+.IP \f(CW<Shift-KeyPress-Up>\fR
+Moves the focus to the previous sibling.
+.IP \f(CW<Shift-KeyPress-Down>\fR
+Moves the focus to the next sibling.
+.IP \f(CW<KeyPress-Prior>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Next>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Left>\fR
+Closes the entry. It is not an error if the entry has no children.
+.IP \f(CW<KeyPress-Right>\fR
+Opens the entry, displaying its children. It is not an
+error if the entry has no children.
+.IP \f(CW<KeyPress-space>\fR
+In "single" select mode this selects the entry. In "multiple" mode,
+it toggles the entry (if it was previous selected, it is not
+deselected).
+.IP \f(CW<KeyRelease-space>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress-Return>\fR
+Sets the focus to the current entry.
+.IP \f(CW<KeyRelease-Return>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress>\fR
+Moves to the next entry whose label starts with the letter typed.
+.IP \f(CW<KeyPress-Home>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-End>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-F1>\fR
+Opens all entries.
+.IP \f(CW<KeyPress-F2>\fR
+Closes all entries (except root).
+.SS BUTTON BINDINGS
+Buttons have bindings. There are associated with the "all" bindtag
+(see the entry's -bindtag option). You can use the \fBbind\fR
+operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the button of the current entry.
+.IP \f(CW<Leave>\fR
+Returns the button back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the view so that the current entry is visible.
+.SS ENTRY BINDINGS
+Entries have default bindings. There are associated with the "all"
+bindtag (see the entry's -bindtag option). You can use the \fBbind\fR
+operation to modify them.
+.IP \f(CW<Enter>\fR
+Highlights the current entry.
+.IP \f(CW<Leave>\fR
+Returns the entry back to its normal state.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the selection anchor the current entry.
+.IP \f(CW<Double-ButtonPress-1>\fR
+Toggles the selection of the current entry.
+.IP \f(CW<B1-Motion>\fR
+For "multiple" mode only. Saves the current location of the
+pointer for auto-scrolling. Resets the selection mark.
+.IP \f(CW<ButtonRelease-1>\fR
+For "multiple" mode only. Sets the selection anchor to the
+current entry.
+.IP \f(CW<Shift-ButtonPress-1>\fR
+For "multiple" mode only. Extends the selection.
+.IP \f(CW<Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-ButtonRelease-1>\fR
+Stop auto-scrolling.
+.IP \f(CW<Control-ButtonPress-1>\fR
+For "multiple" mode only. Toggles and extends the selection.
+.IP \f(CW<Control-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-ButtonRelease-1>\fR
+Stops auto-scrolling.
+.IP \f(CW<Control-Shift-ButtonPress-1>\fR
+???
+.IP \f(CW<Control-Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.SS COLUMN BINDINGS
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the
+\fBcolumn bind\fR operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the current column title.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Invokes the command (see the column's -command option) if one
+if specified.
+.SS COLUMN RULE BINDINGS
+.IP \f(CW<Enter>\fR
+Highlights the current and activates the ruler.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state. Deactivates the
+ruler.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the resize anchor for the column.
+.IP \f(CW<B1-Motion>\fR
+Sets the resize mark for the column.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the size of the column, based upon the resize anchor and mark
+positions.
+.SH EXAMPLE
+The \fBtreeview\fR command creates a new widget.
+.CS
+treeview .h \-bg white
+.CE
+A new Tcl command \f(CW.h\fR is also created. This command can be used
+to query and modify the \fBtreeview\fR widget. For example, to change the
+background
+color of the table to "green", you use the new command and the widget's
+\fBconfigure\fR operation.
+.CS
+# Change the background color.
+\&.h configure \-background "green"
+.CE
+By default, the \fBtreeview\fR widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the
+widget. Above, the new tree object name is ".h". But you can use the
+\fB\-tree\fR option to specify the name of another tree.
+.CS
+# View the tree "myTree".
+\&.h configure \-tree "myTree"
+.CE
+When a new tree is created, it contains only a root node. The node
+is automatically opened. The id of the root node is always
+\f(CW0\fR (you can use also use the special id \f(CWroot\fR). The
+\fBinsert\fR operation lets you insert one or more new entries into
+the tree. The last argument is the node's \fIpathname\fR.
+.CS
+# Create a new entry named "myEntry"
+set id [\&.h insert end "myEntry"]
+.CE
+This appends a new node named "myEntry". It will positioned as the
+last child of the root of the tree (using the position "end"). You
+can supply another position to order the node within its siblings.
+.CS
+# Prepend "fred".
+set id [\&.h insert 0 "fred"]
+.CE
+Entry names do not need to be unique. By default, the node's label
+is its name. To supply a different text label, add the \fB\-label\fR
+option.
+.CS
+# Create a new node named "fred"
+set id [\&.h insert end "fred" -label "Fred Flintstone"]
+.CE
+The \fBinsert\fR operation returns the id of the new node. You can
+also use the \fBindex\fR operation to get this information.
+.CS
+# Get the id of "fred"
+\&.h index "fred"
+.CE
+To insert a node somewhere other than root, use the \fB\-at\fR switch.
+It takes the id of the node where the new child will be added.
+.CS
+# Create a new node "barney" in "fred".
+\&.h insert -at $id end "barney"
+.CE
+A pathname describes the path to an entry in the hierarchy. It's a
+list of entry names that compose the path in the tree. Therefore, you
+can also add "barney" to "fred" as follows.
+.CS
+# Create a new sub-entry of "fred"
+\&.h insert end "fred barney"
+.CE
+Every name in the list is ancestor of the next. All ancestors must
+already exist. That means that an entry "fred" is an ancestor of
+"barney" and must already exist. But you can use the
+\fB\-autocreate\fR configuration option to force the creation of
+ancestor nodes.
+.CS
+# Force the creation of ancestors.
+\&.h configure -autocreate yes
+\&.h insert end "fred barney wilma betty"
+.CE
+Sometimes the pathname is already separated by a character sequence
+rather than formed as a list. A file name is a good example of this.
+You can use the \fB\-separator\fR option to specify a separator string
+to split the path into its components. Each pathname inserted is
+automatically split using the separator string as a separator.
+Multiple separators are treated as one.
+.CS
+\&.h configure -separator /
+\&.h insert end "/usr/local/tcl/bin"
+.CE
+If the path is prefixed by extraneous characters, you can
+automatically trim it off using the \fB\-trim\fR option. It removed
+the string from the path before it is parsed.
+.CS
+\&.h configure -trim C:/windows -separator /
+\&.h insert end "C:/window/system"
+.CE
+You can insert more than one entry at a time with the \fBinsert\fR
+operation. This can be much faster than looping over a list of names.
+.CS
+# The slow way
+foreach f [glob $dir/*] {
+ \&.h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+.CE
+In this case, the \fBinsert\fR operation will return a list of ids
+of the new entries.
+.PP
+You can delete entries with the \fBdelete\fR operation. It takes one or
+more tags of ids as its argument. It deletes the entry and all its
+children.
+.CS
+\&.h delete $id
+.CE
+Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the \fB\-label\fR
+option that sets the entry's text label. The \fBentry configure\fR
+operation lets you set or modify an entry's configuration options.
+.CS
+\&.h entry configure $id -color red -font fixed
+.CE
+You can hide an entry and its children using the \fB\-hide\fR option.
+.CS
+\&.h entry configure $id -hide yes
+.CE
+More that one entry can be configured at once. All entries specified
+are configured with the same options.
+.CS
+\&.h entry configure $i1 $i2 $i3 $i4 -color brown
+.CE
+An icon is displayed for each entry. It's a Tk image drawn to the
+left of the label. You can set the icon with the entry's
+\fB\-icons\fR option. It takes a list of two image names: one to
+represent the open entry, another when it is closed.
+.CS
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+\&.h entry configure $id -icons "$im1 $im2"
+.CE
+If \fB\-icons\fR is set to the empty string, no icons are display.
+.PP
+If an entry has children, a button is displayed to the left of the
+icon. Clicking the mouse on this button opens or closes the
+sub-hierarchy. The button is normally a \f(CW+\fR or \f(CW\-\fR
+symbol, but can be configured in a variety of ways using the \fBbutton
+configure\fR operation. For example, the \f(CW+\fR and \f(CW\-\fR
+symbols can be replaced with Tk images.
+.CS
+set im1 [image create photo -file closefolder.gif]
+set im2 [image create photo -file downarrow.gif]
+\&.h button configure $id -images "$im1 $im2" \\
+ -openrelief raised -closerelief raised
+.CE
+Entries can contain an arbitrary number of \fIdata fields\fR. Data
+fields are name-value pairs. Both the value and name are strings.
+The entry's \fB\-data\fR option lets you set data fields.
+.CS
+\&.h entry configure $id -data {mode 0666 group users}
+.CE
+The \fB\-data\fR takes a list of name-value pairs.
+.PP
+You can display these data fields as \fIcolumns\fR in the
+\fBtreeview\fR widget. You can create and configure columns with
+the \fBcolumn\fR operation. For example, to add a new column to the
+widget, use the \fBcolumn insert\fR operation. The last argument is
+the name of the data field that you want to display.
+.CS
+\&.h column insert end "mode"
+.CE
+The column title is displayed at the top of the column. By default,
+it's is the field name. You can override this using the column's
+\fB\-text\fR option.
+.CS
+\&.h column insert end "mode" -text "File Permissions"
+.CE
+Columns have several configuration options. The \fBcolumn
+configure\fR operation lets you query or modify column options.
+.CS
+\&.h column configure "mode" -justify left
+.CE
+The \fB\-justify\fR option says how the data is justified within in
+the column. The \fB\-hide\fR option indicates whether the column is
+displayed.
+.CS
+\&.h column configure "mode" -hide yes
+.CE
+Entries can be selected by clicking on the mouse. Selected entries
+are drawn using the colors specified by the \fB\-selectforeground\fR
+and \fB\-selectbackground\fR configuration options.
+The selection itself is managed by the \fBselection\fR operation.
+.CS
+# Clear all selections
+\&.h selection clear 0 end
+# Select the root node
+\&.h selection set 0
+.CE
+The \fBcurselection\fR operation returns a list of ids of
+all the selected entries.
+.CS
+set ids [\&.h curselection]
+.CE
+You can use the \fBget\fR operation to convert the ids to
+their pathnames.
+.CS
+set names [eval .h get -full $ids]
+.CE
+If a treeview is exporting its selection (using the
+\fB\-exportselection\fR option), then it will observe the standard X11
+protocols for handling the selection. Treeview selections are
+available as type \fBSTRING\fR; the value of the selection will be the
+pathnames of the selected entries, separated by newlines.
+.PP
+The \fBtreeview\fR supports two modes of selection: \f(CWsingle\fR
+and \f(CWmultiple\fR. In single select mode, only one entry can be
+selected at a time, while multiple select mode allows several entries
+to be selected. The mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -selectmode "multiple"
+.CE
+You can be notified when the list of selected entries changes. The widget's
+\fB\-selectcommand\fR specifies a Tcl procedure that is called whenever
+the selection changes.
+.CS
+proc SelectNotify { widget } {
+ set ids [\&$widget curselection]
+}
+\&.h configure -selectcommand "SelectNotify .h"
+.CE
+The widget supports the standard Tk scrolling and scanning operations.
+The \fBtreeview\fR can be both horizontally and vertically. You can
+attach scrollbars to the \fBtreeview\fR the same way as the listbox
+or canvas widgets.
+.CS
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+\&.h configure -xscrollcommand ".xbar set" \\
+ -yscrollcommand ".ybar set"
+.CE
+There are three different modes of scrolling: \f(CWlistbox\fR,
+\f(CWcanvas\fR, and \f(CWhierbox\fR. In \f(CWlistbox\fR mode, the last
+entry can always be scrolled to the top of the widget. In \f(CWhierbox\fR
+mode, the last entry is always drawn at the bottom of the widget.
+The scroll mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -scrollmode "listbox"
+.CE
+Entries can be programmatically opened or closed using the \fBopen\fR
+and \fBclose\fR operations respectively.
+.CS
+\&.h open $id
+\&.h close $id
+.CE
+When an entry is opened, a Tcl procedure can be automatically invoked.
+The \fB\-opencommand\fR option specifies this procedure. This
+procedure can lazily insert entries as needed.
+.CS
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+\&.h configure -opencommand "AddEntries %P"
+.CE
+Now when an entry is opened, the procedure \f(CWAddEntries\fR is
+called and adds children to the entry. Before the command is invoked,
+special "%" substitutions (like \fBbind\fR) are performed. Above,
+\f(CW%P\fR is translated to the pathname of the entry.
+.PP
+The same feature exists when an entry is closed. The
+\fB\-closecommand\fR option specifies the procedure.
+.CS
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+\&.h configure -closecommand "DeleteEntries %#"
+.CE
+When an entry is closed, the procedure \f(CWDeleteEntries\fR is called
+and deletes the entry's children using the \fBentry delete\fR operation
+(\f(CW%#\fR is the id of entry).
+.SH KEYWORDS
+treeview, widget
diff --git a/blt/man/hiertable.mann b/blt/man/hiertable.mann
new file mode 100644
index 00000000000..457bf1851f7
--- /dev/null
+++ b/blt/man/hiertable.mann
@@ -0,0 +1,2261 @@
+'\"
+'\" Copyright 2001-2 by Silicon Metrics Corporation.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Silicon Metrics or any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Silicon Metrics disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Silicon Metrics be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" The hierarchical table widget created by George Howlett.
+'\"
+.so man.macros
+.TH treeview n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+treeview \- Create and manipulate hierarchical table widgets
+.BE
+.SH SYNOPSIS
+\fBtreeview\fR \fIpathName \fR?\fIoptions\fR?
+.SH DESCRIPTION
+The \fBtreeview\fR widget displays a tree of data. It replaces both
+the \fBhiertable\fR and \fBhierbox\fR widgets. The \fBtreeview\fR is
+100% syntax compatible with the \fBhiertable\fR widget. The
+\fBhiertable\fR command is retained for sake of script-level
+compatibility. This widget obsoletes the \fBhierbox\fR widget. It
+does everything the old \fBhierbox\fR widget did, but also provides
+data sharing (via \fItree data objects\fR) and the ability to tag
+nodes.
+.SH INTRODUCTION
+The \fBtreeview\fR widget displays hierarchical data. Data is
+represented as nodes in a general-ordered tree. Each node may have
+sub-nodes and these nodes can in turn has their own children.
+.PP
+A node is displayed as a row entry in the widget. Each entry has a
+text label and icon. When a node has children, its entry is drawn
+with a small button to the left of the label. Clicking the mouse over
+this button opens or closes the node. When a node is \fIopen\fR, its
+children are exposed. When it is \fIclosed\fR, the children and their
+descedants are hidden. The button is normally a \f(CW+\fR or
+\f(CW\-\fR symbol (ala Windows Explorer), but can be replaced with a
+pair of Tk images (open and closed images).
+.PP
+If the node has data associated with it, they can be displayed in
+columns running vertically on either side the tree. You can control
+the color, font, etc of each entry. Any entry label or data field can
+be edited in-place.
+.SH "TREE DATA OBJECT"
+The tree is not stored inside the widget but in a tree data object
+(see the \fBtree\fR command for a further explanation). Tree data
+objects can be shared among different clients, such as a
+\fBtreeview\fR widget or the \fBtree\fR command. You can walk the
+tree and manage its data with the \fBtree\fR command tree, while
+displaying it with the \fBtreeview\fR widget. Whenever the tree is
+updated, the \fBtreeview\fR widget is automatically redrawn.
+.PP
+By default, the \fBtreeview\fR widget creates its own tree object.
+The tree initially contains just a root node. But you can also
+display trees created by the \fBtree\fR command using the \fB\-tree\fR
+configuration option. \fBTreeview\fR widgets can share the same tree
+object, possibly displaying different views of the same data.
+.PP
+A tree object has both a Tcl and C API. You can insert or delete
+nodes using \fBtreeview\fR widget or \fBtree\fR command operations,
+but also from C code. For example, you can load the tree from your C
+code while still managing and displaying the tree from Tcl. The widget
+is automatically notified whenever the tree is modified via C or Tcl.
+.SH SYNTAX
+.DS
+\fBtreeview \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBtreeview\fR command creates a new window \fIpathName\fR and
+makes it into a \fBtreeview\fR widget. At the time this command is
+invoked, there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist. Additional options may be
+specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the
+\fBconfigure\fR operation below for the exact details about what
+\fIoption\fR and \fIvalue\fR pairs are valid.
+.PP
+If successful, \fBtreeview\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to invoke various operations that query or modify the widget.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available are described in the
+.SB "TREEVIEW OPERATIONS"
+section.
+.SH "IDS AND TAGS"
+Nodes can be inserted into a tree using the \fBtreeview\fR widget
+.CS
+blt::treeview .t
+set node [.t insert end root "one"]
+.CE
+or \fBtree\fR command.
+.CS
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+.CE
+In both cases, a number identifying the node is returned (the value of
+\f(CW$node\fR). This serial number or \fIid\fR uniquely identifies
+the node. Please note that you can't infer a location or position of
+a node from its id. The only exception is that the root node is
+always id \f(CW0\fR. Since nodes may have the same labels or be moved
+within the tree, ids provide an convenient way to identify nodes. If
+a tree is shared, the ids will be the same regardless if you are using
+by the \fBtreeview\fR widget or the \fBtree\fR command. Ids are
+recycled when the node deleted.
+.PP
+A node may also have any number of \fItags\fR associated with it. A
+tag is just a string of characters, and it may take any form except
+that of an integer. For example, "\f(CWx123\fR" is valid, but
+"\f(CW123\fR" isn't. The same tag may be associated with many
+different nodes. This is typically done to associate a group of
+nodes. Many operations in the \fBtreeview\fR widget take either node
+ids or tag names as arguments. Using a tag says to apply the operation
+to all nodes with that tag.
+.PP
+The tag \fBall\fR is implicitly associated with every node in
+the tree. It may be used to invoke operations on all the nodes in the
+tree.
+.PP
+Tags may be shared, just like trees, between clients. For example,
+you can use the tags created by the \fBtree\fR command with
+\fBtreeview\fR widgets.
+.SH SPECIAL NODE IDS
+There are also several special non-numeric ids. Special ids differ
+from tags in that they are always translated to their numeric
+equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to
+the \fBtreeview\fR widget.
+.TP 15
+\fBactive\fR
+The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+The \fBactive\fR id is changed automatically by moving the mouse
+pointer over another node or by using the \fBentry activate\fR
+operation. Note that there can be only one active node at a time.
+.TP 15
+\fBanchor\fR
+The node representing the fixed end of the current selection.
+The anchor is set by the \fBselection anchor\fR operation.
+.TP 15
+\fBcurrent\fR
+The node where the mouse pointer is currently located.
+But unlike \fBactive\fR, this id changes while the
+selection is dragged. It is used to determine the
+current node during button drags.
+.TP 15
+\fBdown\fR
+The next open node from the current focus. The \fBdown\fR of
+the last open node is the same.
+.TP 15
+\fBend\fR
+The last open node (in depth-first order) on the tree.
+.TP 15
+\fBfocus\fR
+The node that currently has focus. When a node has focus,
+it receives key events. To indicate focus, the node
+is drawn with a dotted line around its label. You can change the
+focus using the \fBfocus\fR operation.
+.TP 15
+\fBlast\fR
+The last open node from the current focus. But unlike \fBup\fR,
+when the focus is at root, \fBlast\fR wraps around to the last
+open node in the tree.
+.TP 15
+\fBmark\fR
+The node representing the non-fixed end of the current selection.
+The mark is set by the \fBselection mark\fR operation.
+.TP 15
+\fBnext\fR
+The next open node from the current focus. But unlike \fBdown\fR,
+when the focus is on last open node, \fBnext\fR wraps around to the
+root node.
+.TP 15
+\fBnextsibling\fR
+The next sibling from the node with the current focus. If the node
+is already the last sibling then it is the \fBnextsibling\fB.
+.TP 15
+\fBparent\fR
+The parent of the node with the current focus. The \fBparent\fR
+of the root is also the root.
+.TP 15
+\fBprevsibling\fR
+The previous sibling from the node with the current focus. If the node
+is already the first sibling then it is the \fBprevsibling\fB.
+.TP 15
+\fBroot\fR
+The root node. You can also use id \f(CW0\fR to indicate
+the root.
+.TP 15
+\fBup\fR
+The last open node (in depth-first order) from the current focus. The
+\fBup\fR of the root node (i.e. the root has focus) is also the root.
+.TP 15
+\fBview.top\fR
+First node that's current visible in the widget.
+.TP 15
+\fBview.bottom\fR
+Last node that's current visible in the widget.
+.TP 15
+\fIpath\fR
+Absolute path of a node. Path names refer to the node name, not
+their entry labels. Paths don't have to start with a separator (see
+the \fB\-separator\fR configuration option), but component names must
+be separated by the designated separator.
+.TP 15
+\fB@\fIx\fB,\fIy\fR
+Indicates the node that covers the point in the treeview window
+specified by \fIx\fR and \fIy\fR (in pixel coordinates). If no
+part of the entryd covers that point, then the closest node to that
+point is used.
+.PP
+A node may be specified as an id or tag. If the specifier is an
+integer then it is assumed to refer to the single node with that id.
+If the specifier is not an integer, it's checked to see if it's a
+special id (such as focus). Otherwise, it's assumed to be tag. Some
+operations only operate on a single node at a time; if a tag refers to
+more than one node, then an error is generated.
+.SH DATA FIELDS
+A node in the tree can have \fIdata fields\fR. A data field is a
+name-value pair, used to represent arbitrary data in the node. Nodes
+can contain different fields (they aren't required to contain the same
+fields). You can optionally display these fields in the
+\fBtreeview\fR widget in columns running on either side of the
+displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a
+specific field is left blank. Columns can be interactively resized,
+hidden, or, moved.
+.SH ENTRY BINDINGS
+You can bind Tcl commands to be invoked when events occur on nodes
+(much like Tk canvas items). You can bind a node using its id or
+its \fIbindtags\fR. Bindtags are simply names that associate a
+binding with one or more nodes. There is a built-in tag \f(CWall\fR
+that all node entries automatically have.
+.SH "TREEVIEW OPERATIONS"
+The \fBtreeview\fR operations are the invoked by specifying
+the widget's pathname, the operation, and any arguments that pertain
+to that operation. The general form is:
+.sp
+.CS
+\fIpathName operation \fR?\fIarg arg ...\fR?
+.CE
+.sp
+\fIOperation\fR and the \fIarg\fRs determine the exact behavior of the
+command. The following operation are available for \fBtreeview\fR widgets:
+.TP
+\fIpathName \fBbbox\fR ?\fB-screen\fR? \fItagOrId...\fR
+Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more \fItagOrId\fR
+arguments.
+If the \fB\-screen\fR flag is given, then the x-y coordinates
+of the bounding box are returned as screen coordinates, not
+virtual coordinates. Virtual coordinates start from \f(CW0\fR from the
+root node.
+The returned list contains the following values.
+.RS
+.TP 1.25i
+\fIx\fR
+X-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIy\fR
+Y-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIwidth\fR
+Width of the bounding box.
+.TP
+\fIheight\fR
+Height of the bounding box.
+.RE
+.TP
+\fIpathName \fBbind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a node with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on \fBtreeview\fR entries,
+rather than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton \fIoperation\fR ?\fIargs\fR?
+This command is used to control the button selectors within a
+\fBtreeview\fR widget.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBbutton activate\fR \fItagOrId\fR
+Designates the node given by \fItagOrId\fR as active.
+When a node is active it's entry is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active entry at a time.
+The special id \fBactive\fR indicates the currently active node.
+.TP
+\fIpathName \fBbutton bind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an button of a
+node entry with this tag, \fIcommand\fR will be invoked. The syntax is
+similar to the \fBbind\fR command except that it operates on
+\fBtreeview\fR buttons, rather than widgets. See the \fBbind\fR
+manual entry for complete details on \fIsequence\fR and the
+substitutions performed on \fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBbutton configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "BUTTON OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBclose \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Closes the node specified by \fItagOrId\fR. In addition, if a Tcl
+script was specified by the \fB\-closecommand\fR option, it is
+invoked. If the node is already closed, this command has no effect.
+If the \fB\-recurse\fR flag is present, each child node is
+recursively closed.
+.TP
+\fIpathName \fBcolumn \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview columns.
+.RS
+.TP
+\fIpathName \fBcolumn activate\fR \fIcolumn\fR
+Sets the active column to \fIcolumn\fR. \fIColumn\fR is the
+name of a column in the widget.
+When a column is active, it's drawn using its \fB\-activetitlebackground\fR
+and \fB\-activetitleforeground\fR options. If \fIcolumn\fR is the \f(CW""\fR,
+then no column will be active. If no column argument is provided, then
+the name of the currently active column is returned.
+.TP
+\fIpathName \fBcolumn cget\fR \fIname\fR \fIoption\fR
+Returns the current value of the column configuration option given
+by \fIoption\fR for \fIname\fR. \fIName\fR is the name of column
+that corresponds to a data field.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBcolumn configure\fR \fIname\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the column designated
+by \fIname\fR. \fIName\fR is the name of the column corresponding
+to a data field.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "COLUMN OPTIONS"
+below.
+.TP
+\fIpathName \fBcolumn delete\fR \fIfield\fR ?\fIfield\fR...?
+Deletes one of more columns designated by \fIfield\fR. Note
+that this does not delete the data fields themselves.
+.TP
+\fIpathName \fBcolumn insert\fR \fIposition\fR \fIfield\fR ?\fIoptions\fR...?
+Inserts one of more columns designated by \fIfield\fR. A column displays
+each node's data field by the same name. If the node doesn't
+have the given field, the cell is left blank.
+\fIPosition\fR
+indicates where in the list of columns to add the new column. It may be
+either a number or \f(CWend\fR.
+.TP
+\fIpathName \fBcolumn invoke\fR \fIfield\fR
+Invokes the Tcl command associated with the column \fIfield\fR,
+if there is one (using the column's \fB\-command\fR option).
+The command is ignored if the column's \fB\-state\fR option
+set to \f(CWdisabled\fR.
+.TP
+\fIpathName \fBcolumn move \fIname\fR \fIdest\fR
+Moves the column \fIname\fR to the destination position.
+\fIDest\fR is the name of another column or a screen position
+in the form \f(CW@\fIx\f(CW,\fIy\fR.
+.TP
+\fIpathName \fBcolumn names\fR
+Returns a list of the names of all columns in the widget.
+The list is ordered as the columns are drawn from left-to-right.
+.TP
+\fIpathName \fBcolumn nearest\fR \fIx\fR ?\fIy\fR?
+Returns the name of the column closest to the given X-Y screen
+coordinate. If you provide a \fIy\fR argument (it's optional),
+a name is returned only when if the point is over a column's title.
+.RE
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TREEVIEW OPTIONS"
+below.
+.TP
+\fIpathName \fBcurselection\fR
+Returns a list containing the ids of all of the entries that are
+currently selected.
+If there are no entries selected, then the empty string is returned.
+.TP
+\fIpathName \fBdelete \fItagOrId\fR...
+Deletes one or more entries given by \fItagOrId\fR and its children.
+.TP
+\fIpathName \fBentry \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview entries.
+.RS
+.TP
+\fIpathName \fBentry activate\fR \fItagOrId\fR
+Sets the active entry to the one specified by \fItagOrId\fR.
+When an entry is active it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active node at a time.
+The special id of the currently active node is \fBactive\fR.
+.TP
+\fIpathName \fBentry cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBentry children\fR \fItagOrId\fR ?\fIfirst\fR? ?\fIlast\fR?
+Returns a list of ids for the given range of children of \fItagOrId\fR.
+\fITagOrId\fR is the id or tag of the node to be examined.
+If only a \fIfirst\fR argument is present, then the id
+of the that child at that numeric position is returned. If both \fIfirst\fR
+and \fIlast\fR arguments are given, then the ids of all the children
+in that range are returned. Otherwise the ids of all children
+are returned.
+.TP
+\fIpathName \fBentry configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.TP
+\fIpathName \fBentry delete\fR \fItagOrId\fR ?\fIfirst\fR ?\fIlast\fR?
+Deletes the one or more children nodes of the parent \fItagOrId\fR.
+If \fIfirst\fR and \fIlast\fR arguments are present, they are
+positions designating a range of children nodes to be deleted.
+.TP
+\fIpathName \fBentry isbefore \fItagOrId1\fR \fItagOrId2\fR
+Returns 1 if \fItagOrId1\fR is before \fItagOrId2\fR and 0 otherwise.
+.TP
+\fIpathName \fBentry ishidden \fItagOrId\fR
+Returns 1 if the node is currently hidden and 0 otherwise. A node is
+also hidden if any of its ancestor nodes are closed or hidden.
+.TP
+\fIpathName \fBentry isopen \fItagOrId\fR
+Returns 1 if the node is currently open and 0 otherwise.
+.TP
+\fIpathName \fBentry size\fR \fB\-recurse\fR \fItagOrId\fR
+Returns the number of children for parent node \fItagOrId\fR.
+If the \fB\-recurse\fR flag is set, the number of all
+its descendants is returned. The node itself is not counted.
+.RE
+.TP
+\fIpathName \fBfind \fR?\fIflags\fR? \fIfirst\fR \fIlast\fR
+Finds for all entries matching the criteria given by \fIflags\fR. A
+list of ids for all matching nodes is returned. \fIFirst\fR and
+\fIlast\fR are ids designating the range of the search in
+depth-first order. If \fIlast\fR is before \fIfirst\fR, then nodes
+are searched in reverse order. The valid flags are:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Patterns must match exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Pick entries that don't match.
+.TP 1.25i
+\fB\-exec\fI string\fR
+Specifies a Tcl script to be invoked for each matching node.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP 1.25i
+\fB\-count\fI number\fR
+Stop searching after \fInumber\fR matches.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBfocus \fR \fItagOrId\fR
+Sets the focus to the node given by \fItagOrId\fR. When a node
+has focus, it can receive keyboard events.
+The special id \fBfocus\fR designates the node that currently has focus.
+.TP
+\fIpathName \fBget \fR?\fB\-full\fR? \fItagOrId\fR \fItagOrId\fR...
+Translates one or more ids to their node entry names. It returns a list of
+names for all the ids specified. If the \fB\-full\fR
+flag is set, then the full pathnames are returned.
+.TP
+\fIpathName \fBhide \fR?\fBflags\fR? \fItagOrId\fR...
+Hides all nodes matching the criteria given by \fIflags\fR. The
+search is performed recursively for each node given by \fItagOrId\fR.
+The valid flags are described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Hide nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBindex \fR?\fB\-at\fR \fItagOrId\fR? \fIstring\fR
+Returns the id of the node specified by \fIstring\fR. \fIString\fR
+may be a tag or node id.
+Some special ids are normally relative to the node that
+has focus. The \fB\-at\fR flag lets you select another node.
+.TP
+\fIpathName \fBinsert \fR?\fB\-at \fItagOrId\fR? \fIposition\fR \fIpath\fR ?\fIoptions...\fR? ?\fIpath\fR? ?\fIoptions...\fR?
+Inserts one or more nodes at \fIposition\fR. \fIPosition\fR is the
+location (number or \f(CWend\fR) where the new nodes are added to
+the parent node. \fIPath\fR is the pathname of the new node.
+Pathnames can be formated either as a Tcl list (each element is a path
+component) or as a string separated by a special character sequence
+(using the \fB\-separator\fR option). Pathnames are normally
+absolute, but the \fB\-at\fR switch lets you select a relative
+starting point. Its value is the id of the starting node.
+.sp
+All ancestors of the new node must already exist, unless the
+\fB\-autocreate\fR option is set. It is also an error if a node
+already exists, unless the \fB\-allowduplicates\fR option is set.
+.sp
+\fIOption\fR and \fIvalue\fR may have any of the values accepted by the
+\fBentry configure\fR operation described in the
+.SB "ENTRY OPERATIONS"
+section below. This command returns a list of the ids of
+the new entries.
+.TP
+\fIpathName \fBmove \fItagOrId\fR \fIhow\fR \fIdestId\fR
+Moves the node given by \fItagOrId\fR to the destination node. The
+node can not be an ancestor of the destination. \fIDestId\fR is
+the id of the destination node and can not be the root of the
+tree. In conjunction with \fIhow\fR, it describes how the move is
+performed.
+.RS
+.TP 8
+\f(CWbefore\fR
+Moves the node before the destination node.
+.TP 8
+\f(CWafter\fR
+Moves the node after the destination node.
+.TP 8
+\f(CWinto\fR
+Moves the node to the end of the destination's list of children.
+.RE
+.TP
+\fIpathName \fBnearest \fIx y\fR ?\fIvarName\fR?
+Returns the id of the node entry closest to the given X-Y screen
+coordinate. The optional argument \fIvarName\fR is the name of
+variable which is set to either \f(CWbutton\fR or \f(CWselect\fR to
+indicate over what part of the node the coordinate lies.
+If the coordinate is not directly over any node, then
+\fIvarName\fR will contain the empty string.
+.TP
+\fIpathName \fBopen \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Opens the one or more nodes specified by \fItagOrId\fR.
+If a node is not already open, the Tcl script specified by the
+\fB\-opencommand\fR option is invoked. If the \fB\-recurse\fR flag
+is present, then each descendant is recursively opened.
+.TP
+\fIpathName \fBrange\fR ?\fB-open\fR? \fIfirst last\fR
+Returns the ids in depth-first order of the nodes
+between the \fIfirst\fR and \fIlast\fR ids. If the \fB\-open\fR
+flag is present, it indicates to consider only open nodes.
+If \fIlast\fR is before \fIfirst\fR, then the ids are
+returned in reverse order.
+.TP
+\fIpathName \fBscan\fR \fIoption args\fR
+This command implements scanning. It has
+two forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBscan mark \fIx y\fR
+Records \fIx\fR and \fIy\fR and the current view in the treeview
+window; used in conjunction with later \fBscan dragto\fR commands.
+Typically this command is associated with a mouse button press in
+the widget. It returns an empty string.
+.TP
+\fIpathName \fBscan dragto \fIx y\fR.
+Computes the difference between its \fIx\fR and \fIy\fR
+arguments and the \fIx\fR and \fIy\fR arguments to the last
+\fBscan mark\fR command for the widget.
+It then adjusts the view by 10 times the
+difference in coordinates. This command is typically associated
+with mouse motion events in the widget, to produce the effect of
+dragging the list at high speed through the window. The return
+value is an empty string.
+.RE
+.TP
+\fIpathName \fBsee\fR ?\fB\-anchor \fIanchor\fR? \fItagOrId\fR
+Adjusts the view of entries so that the node given by \fItagOrId\fR is
+visible in the widget window. It is an error if \fBtagOrId\fR is a
+tag that refers to more than one node. By default the node's entry
+is displayed in the middle of the window. This can changed using the
+\fB\-anchor\fR flag. Its value is a Tk anchor position.
+.TP
+\fIpathName \fBselection \fIoption arg\fR
+This command is used to adjust the selection within a \fBtreeview\fR
+widget. It has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBselection anchor \fItagOrId\fR
+Sets the selection anchor to the node given by \fItagOrId\fR.
+If \fItagOrId\fR refers to a non-existent node, then the closest
+node is used.
+The selection anchor is the end of the selection that is fixed
+while dragging out a selection with the mouse.
+The special id \fBanchor\fR may be used to refer to the anchor
+node.
+.TP
+\fIpathName \fBselection cancel\fR
+Clears the temporary selection of entries back to the
+current anchor. Temporary selections are created by
+the \fBselection mark\fR operation.
+.TP
+\fIpathName \fBselection clear \fIfirst \fR?\fIlast\fR?
+Removes the entries between \fIfirst\fR and \fIlast\fR
+(inclusive) from the selection. Both \fIfirst\fR and
+\fIlast\fR are ids representing a range of entries.
+If \fIlast\fR isn't given, then only \fIfirst\fR is deselected.
+Entries outside the selection are not affected.
+.TP
+\fIpathName \fBselection clearall\fR
+Clears the entire selection.
+.TP
+\fIpathName \fBselection mark \fItagOrId\fR
+Sets the selection mark to the node given by \fItagOrId\fR. This
+causes the range of entries between the anchor and the mark to be
+temporarily added to the selection. The selection mark is the end of
+the selection that is fixed while dragging out a selection with the
+mouse. The special id \fBmark\fR may be used to refer to the current
+mark node.
+If \fItagOrId\fR refers to a non-existent node, then the mark
+is ignored.
+Resetting the mark will unselect
+the previous range. Setting the anchor finalizes the range.
+.TP
+\fIpathName \fBselection includes \fItagOrId\fR
+Returns 1 if the node given by \fItagOrId\fR is currently
+selected, 0 if it isn't.
+.TP
+\fIpathName \fBselection present\fR
+Returns 1 if any nodes are currently selected and 0 otherwise.
+.TP
+\fIpathName \fBselection set \fIfirst \fR?\fIlast\fR?
+Selects all of the nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, without affecting
+the selection state of nodes outside that range.
+.TP
+\fIpathName \fBselection toggle \fIfirst \fR?\fIlast\fR?
+Selects/deselects nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and
+visa versa.
+.RE
+.TP
+\fIpathName \fBshow \fR?\fBflags\fR? \fItagOrId\fR...
+Exposes all nodes matching the criteria given by \fIflags\fR. This
+is the inverse of the \fBhide\fR operation. The search is performed
+recursively for each node given by \fItagOrId\fR. The valid flags are
+described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Expose nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBsort\fR ?\fIoperation\fR? \fIargs...\fR
+.RS
+.TP
+\fIpathName \fBsort auto\fR ?\fIboolean\fR
+Turns on/off automatic sorting of node entries. If \fIboolean\fR is
+true, entries will be automatically sorted as they are opened,
+closed, inserted, or deleted. If no \fIboolean\fR argument is
+provided, the current state is returned.
+.TP
+\fIpathName \fBsort cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBsort configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the sorting configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given sorting option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-column\fI string\fR
+Specifies the column to sort. Entries in the widget are rearranged
+according to this column. If \fIcolumn\fR is \f(CW""\fR then
+no sort is performed.
+.TP
+\fB\-command\fI string\fR
+Specifies a Tcl procedure to be called when sorting nodes.
+The procedure is called with three arguments: the pathname of the widget
+and the fields of two entries. The procedure returns 1 if the first
+node is greater than the second, -1 is the second is greater, and 0
+if equal.
+.TP
+\fB\-decreasing\fI boolean\fR
+Indicates to sort in ascending/descending order. If \fIboolean\fR
+is true, then the entries as in descending order. The default is
+\f(CWno\fR.
+.TP
+\fB\-mode\fI string\fR
+Specifies how to compare entries when sorting. \fIString\fR
+may be one of the following:
+.RS
+.TP 1.5i
+\f(CWascii\fR
+Use string comparison based upon the ASCII collation order.
+.TP 1.5i
+\f(CWdictionary\fR
+Use dictionary-style comparison. This is the same as \f(CWascii\fR
+except (a) case is ignored except as a tie-breaker and (b) if two
+strings contain embedded numbers, the numbers compare as integers, not
+characters. For example, "bigBoy" sorts between
+"bigbang" and "bigboy", and "x10y" sorts between "x9y" and "x11y".
+.TP 1.5i
+\f(CWinteger\fR
+Compares fields as integers.
+.TP 1.5i
+\f(CWreal\fR
+Compares fields as floating point numbers.
+.TP 1.5i
+\f(CWcommand\fR
+Use the Tcl proc specified by the \fB\-command\fR option to compare entries
+when sorting. If no command is specified, the sort reverts to
+\f(CWascii\fR sorting.
+.RE
+.RE
+.TP
+\fIpathName \fBsort once\fR ?\fIflags\fR? \fItagOrId...\fR
+Sorts the children for each entries specified by \fItagOrId\fR.
+By default, entries are sorted by name, but you can specify a
+Tcl proc to do your own comparisons.
+.RS
+.TP 1.5i
+\fB\-recurse\fR
+Recursively sort the entire branch, not just the children.
+.RE
+.RE
+.TP
+\fIpathName \fBtag \fIoperation args\fR
+Tags are a general means of selecting and marking nodes in the tree.
+A tag is just a string of characters, and it may take any form except
+that of an integer. The same tag may be associated with many
+different nodes.
+.sp
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below.
+.RS
+.TP
+\fIpathName\fR \fBtag add\fR \fIstring\fR \fIid\fR...
+Adds the tag \fIstring\fR to one of more entries.
+.TP
+\fIpathName\fR \fBtag delete\fR \fIstring\fR \fIid\fR...
+Deletes the tag \fIstring\fR from one or more entries.
+.TP
+\fIpathName\fR \fBtag forget\fR \fIstring\fR
+Removes the tag \fIstring\fR from all entries. It's not an error if no
+entries are tagged as \fIstring\fR.
+.TP
+\fIpathName\fR \fBtag names\fR ?\fIid\fR?
+Returns a list of tags used. If an \fIid\fR argument
+is present, only those tags used by the node designated by \fIid\fR
+are returned.
+.TP
+\fIpathName\fR \fBtag nodes\fR \fIstring\fR
+Returns a list of ids that have the tag \fIstring\fR. If no node
+is tagged as \fIstring\fR, then an empty string is returned.
+.RE
+.TP
+\fIpathName \fBtext \fIoperation\fR ?\fIargs\fR?
+This operation is used to provide text editing for cells (data
+fields in a column) or entry labels.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBtext apply\fR
+Applies the edited buffer, replacing the entry label
+or data field. The edit window is hidden.
+.TP
+\fIpathName \fBtext cancel\fR
+Cancels the editing operation, reverting the entry label
+or data value back to the previous value. The edit window is hidden.
+.TP
+\fIpathName \fBtext cget\fI value\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBtext configure\fR ?\fIoption value\fR?
+Query or modify the configuration options of the edit window.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TEXT EDITING OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBtext delete\fI first last\fR
+Deletes the characters in the edit buffer between the two given
+character positions.
+.TP
+\fIpathName \fBtext get\fR ?\fI\-root\fR? \fIx y\fR
+.TP
+\fIpathName \fBtext icursor\fI index\fR
+.TP
+\fIpathName \fBtext index\fI index\fR
+Returns the text index of given \fIindex\fR.
+.TP
+\fIpathName \fBtext insert\fI index string\fR
+Insert the text string \fIstring\fR into the edit buffer at the index
+\fIindex\fR. For example, the index 0 will prepend the buffer.
+.TP
+\fIpathName \fBtext selection\fI args\fR
+This operation controls the selection of the editing window. Note
+that this differs from the selection of entries.
+It has the following forms:
+.RS
+.TP
+\fIpathName \fBtext selection adjust\fI index\fR
+Adjusts either the first or last index of the selection.
+.TP
+\fIpathName \fBtext selection clear\fR
+Clears the selection.
+.TP
+\fIpathName \fBtext selection from\fI index\fR
+Sets the anchor of the selection.
+.TP
+\fIpathName \fBtext selection present\fR
+Indicates if a selection is present.
+.TP
+\fIpathName \fBtext selection range\fI start end\fR
+Sets both the anchor and mark of the selection.
+.TP
+\fIpathName \fBtext selection to\fI index\fR
+Sets the unanchored end (mark) of the selection.
+.RE
+.TP
+\fIpathName \fBtoggle \fItagOrId\fR
+Opens or closes the node given by \fItagOrId\fR. If the corresponding
+\fB\-opencommand\fR or \fB\-closecommand\fR option is set, then that
+command is also invoked.
+.TP
+\fIpathName \fBxview \fIargs\fR
+This command is used to query and change the horizontal position of the
+information in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the \fBtreeview\fR widget's text is off-screen to the left,
+the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview\fR \fItagOrId\fR
+Adjusts the view in the window so that the character position given by
+\fItagOrId\fR is displayed at the left edge of the window.
+Character positions are defined by the width of the character \fB0\fR.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the \fBtreeview\fR widget's text is off-screen to the left.
+\fIfraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR character units (the width of the \fB0\fR character)
+on the display; if it is \fBpages\fR then the view adjusts by
+\fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \fByview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \fByview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means
+it is halfway through the treeview window, for example).
+The second element gives the position of the node just after
+the last one in the window, relative to the widget as a whole.
+These are the same values passed to scrollbars via the \fB\-yscrollcommand\fR
+option.
+.TP
+\fIpathName \fByview\fR \fItagOrId\fR
+Adjusts the view in the window so that the node given by
+\fItagOrId\fR is displayed at the top of the window.
+.TP
+\fIpathName \fByview moveto\fI fraction\fR
+Adjusts the view in the window so that the node given by \fIfraction\fR
+appears at the top of the window.
+\fIFraction\fR is a fraction between 0 and 1; 0 indicates the first
+node, 0.33 indicates the node one-third the
+way through the \fBtreeview\fR widget, and so on.
+.TP
+\fIpathName \fByview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR.
+If \fIwhat\fR is \fBunits\fR, the view adjusts up or down by
+\fInumber\fR lines; if it is \fBpages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier nodes
+become visible; if it is positive then later nodes
+become visible.
+.RE
+.SH "TREEVIEW OPTIONS"
+In addition to the \fBconfigure\fR operation, widget configuration
+options may also be set by the Tk \fBoption\fR command. The class
+resource name is \f(CWTreeView\fR.
+.CS
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+.CE
+The following widget options are available:
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active entries. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of the active node. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed for an entry's icon
+when it is active. \fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-autocreate \fIboolean\fR
+If \fIboolean\fR is true, automatically create missing ancestor
+nodes when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-allowduplicates \fIboolean\fR
+If \fIboolean\fR is true, allow nodes with duplicate pathnames
+when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the widget. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is closed. You can
+overrider this for individual entries using the entry's \fB\-closecommand\fR
+option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-dashes \fInumber\fR
+Sets the dash style of the horizontal and vertical lines drawn connecting
+entries. \fINumber\fR is the length in pixels of the dashes and gaps in
+the line. If \fInumber\fR is \f(CW0\fR, solid lines will
+be drawn. The default is \f(CW1\fR (dotted).
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the selection is exported. If the widget is exporting its
+selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type \fBSTRING\fR;
+the value of the selection will be the label of the selected nodes,
+separated by newlines. The default is \f(CWno\fR.
+.TP
+\fB\-flat \fIboolean\fR
+Indicates whether to display the tree as a flattened list.
+If \fIboolean\fR is true, then the hierarchy will be a list of full
+paths for the nodes. This option also has affect on sorting.
+See the
+.SB "SORT OPERATIONS"
+section for more information.
+The default is \f(CWno\fR.
+.TP
+\fB\-focusdashes \fIdashList\fR
+Sets the dash style of the outline rectangle drawn around the entry
+label of the node that current has focus. \fINumber\fR is the length
+in pixels of the dashes and gaps in the line. If
+\fInumber\fR is \f(CW0\fR, a solid line will be drawn. The default is
+\f(CW1\fR.
+.TP
+\fB\-focusforeground \fIcolor\fR
+Sets the color of the focus rectangle.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font for entry labels. You can override this for individual
+entries with the entry's \fB\-font\fR configuration option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of entry labels. You can override this for individual
+entries with the entry's \fB\-foreground\fR configuration option.
+The default is
+\f(CWblack\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW400\fR.
+.TP
+\fB\-hideroot \fIboolean\fR
+If \fIboolean\fR is true, it indicates that no entry for the root node
+should be displayed. The default is \f(CWno\fR.
+.TP
+\fB\-highlightbackground \fIcolor\fR
+Specifies the normal color of the traversal highlight region when
+the widget does not have the input focus.
+.TP
+\fB\-highlightcolor \fIcolor\fR
+Specifies the color of the traversal highlight rectangle when
+the widget has the input focus.
+The default is \f(CWblack\fR.
+.TP
+\fB\-highlightthickness \fIpixels\fR
+Specifies the width of the highlight rectangle indicating when the
+widget has input focus. The value may have any of the forms acceptable
+to \fBTk_GetPixels\fR. If the value is zero, no focus highlight will
+be displayed. The default is \f(CW2\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images for the entry's icon.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-linecolor \fIcolor\fR
+Sets the color of the connecting lines drawn between entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-linespacing \fIpixels\fR
+Sets the number of pixels spacing between entries.
+The default is \f(CW0\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the lines drawn connecting entries. If \fIpixels\fR
+is \f(CW0\fR, no vertical or horizontal lines are drawn.
+The default is \f(CW1\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is open.
+You can override this for individual entries with the entry's
+\fB\-opencommand\fR configuration option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the widget. \fIRelief\fR
+specifies how the \fBtreeview\fR widget should appear relative to widget
+it is packed into; for example, \f(CWraised\fR means the \fBtreeview\fR widget
+should appear to protrude. The default is \f(CWsunken\fR.
+.TP
+\fB\-scrollmode \fImode\fR
+Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is \f(CWhierbox\fR.
+.RS
+.TP 1.25i
+\f(CWlistbox\fR
+Like the \fBlistbox\fR widget, the last entry can always be
+scrolled to the top of the widget window. This allows the scrollbar
+thumb to shrink as the last entry is scrolled upward.
+.TP 1.25i
+\f(CWhierbox\fR
+Like the \fBhierbox\fR widget, the last entry can only be
+viewed at the bottom of the widget window. The scrollbar
+stays a constant size.
+.TP 1.25i
+\f(CWcanvas\fR
+Like the \fBcanvas\fR widget, the entries are bound within
+the scrolling area.
+.RE
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background color selected node entries.
+The default is \f(CW#ffffea\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the raised 3-D border drawn around the labels
+of selected entries. The default is \f(CW0\fR.
+\fB\-selectcommand \fIstring\fR
+Specifies a Tcl script to invoked when the set of selected
+nodes changes.
+The default is \f(CW""\fR.
+.TP
+\fB\-selectforeground \fIcolor\fB
+Sets the color of the labels of selected node entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-selectmode \fImode\fR
+Specifies the selection mode. If \fImode\fR is
+\f(CWsingle\fR, only one node can be selected
+at a time. If \f(CWmultiple\fR more than one
+node can be selected.
+The default is \f(CWsingle\fR.
+.TP
+\fB\-separator \fIstring\fR
+Specifies the character sequence to use when spliting the path components.
+The separator may be several characters wide (such as "::")
+Consecutive separators in a pathname are treated as one.
+If \fIstring\fR is the empty string, the pathnames are Tcl lists.
+Each element is a path component. The default is \f(CW""\fR.
+.TP
+\fB\-showtitles \fIboolean\fR
+If \fIboolean\fR is false, column titles are not be displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-sortselection \fIboolean\fR
+If \fIboolean\fR is true, nodes in the selection are ordered as they
+are currently displayed (depth-first or sorted), not in the order
+they were selected. The default is \f(CWno\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW"1"\fR.
+.TP
+\fB\-trim \fIstring\fR
+Specifies a string leading characters to trim from entry pathnames
+before parsing. This only makes sense if the \fB\-separator\fR is also
+set. The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the widget. If \fIpixels\fR is 0, then
+the with is computed from the contents of the \fBtreeview\fR widget.
+The default is \f(CW200\fR.
+.TP
+\fB\-xscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-xscrollincrement\fR \fIpixels\fR
+Sets the horizontal scrolling distance. The default is 20 pixels.
+.TP
+\fB\-yscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with vertical
+scrollbars. Whenever the vertical view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-yscrollincrement\fR \fIpixels\fR
+Sets the vertical scrolling distance. The default is 20 pixels.
+.SH "ENTRY OPTIONS"
+Many widget configuration options have counterparts in entries. For
+example, there is a \fB\-closecommand\fR configuration option for both
+widget itself and for individual entries. Options set at the widget
+level are global for all entries. If the entry configuration option
+is set, then it overrides the widget option. This is done to avoid
+wasting memory by replicated options. Most entries will have
+redundant options.
+.PP
+There is no resource class or name for entries.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed as the entry's icon
+when it is active. This overrides the global \fB\-activeicons\fR
+configuration option for the specific entry.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for nodes. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events are handled for nodes. Each tag in the list matching the current
+event sequence will have its Tcl command executed. The default value
+is \f(CWall\fR.
+.TP
+\fB\-button \fIstring\fR
+Indicates whether a button should be displayed on the left side
+of the node entry. \fIString\fR can be \f(CWyes\fR, \f(CWno\fR,
+or \f(CWauto\fR. If \f(CWauto\fR, then a button is automatically
+displayed if the node has children. This is the default.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when the node is closed. This
+overrides the global \fB\-closecommand\fR option for this entry.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-data \fIstring\fR
+Sets data fields for the node. \fIString\fR is a list of
+name-value pairs to be set. The default is \f(CW""\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for entry labels. This overrides the widget's
+\fB\-font\fR option for this node. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of the entry label. This overrides the widget's
+\fB\-foreground\fR configuration option. The default is \f(CW""\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images to be displayed for the entry's icon.
+This overrides the global \fB\-icons\fR configuration option.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-label \fIstring\fR
+Sets the text for the entry's label. If not set, this
+defaults to the name of the node. The default is \f(CW""\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when the entry is opened.
+This overrides the widget's \fB\-opencommand\fR option for this node.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.SH "BUTTON OPTIONS"
+Button configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWButton\fR. The resource name is always
+\f(CWbutton\fR.
+.CS
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+.CE
+The following are the configuration options available for buttons.
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the button. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the button.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-closerelief \fIrelief\fR
+Specifies the 3-D effect for the closed button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Sets the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of buttons.
+The default is \f(CWblack\fR.
+.TP
+\fB\-images \fIimages\fR
+Specifies images to be displayed for the button.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the button is open, the
+second when it is closed. If the \fIimages\fR is the empty string,
+then a plus/minus gadget is drawn. The default is \f(CW""\fR.
+.TP
+\fB\-openrelief \fIrelief\fR
+Specifies the 3-D effect of the open button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-size \fIpixels\fR
+Sets the requested size of the button.
+The default is \f(CW0\fR.
+.RE
+.SH "COLUMN OPTIONS"
+Column configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWColumn\fR. The resource name is the
+name of the column.
+.CS
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+.CE
+The following configuration options are available for columns.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the column. This overrides
+the widget's \fB\-background\fR option. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border of the column.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW0\fR.
+.TP
+\fB\-edit \fIboolean\fR
+Indicates if the column's data fields can be edited. If \fIboolean\fR is
+false, the data fields in the column may not be edited.
+The default is \f(CWyes\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Specifies the foreground color of the column.
+You can override this for individual entries with the entry's
+\fB\-foreground\fR option.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for a column. You can override this for individual entries
+with the entry's \fB\-font\fR option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-hide \fIboolean\fR
+If \fIboolean\fR is true, the column is not displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the column data fields title should be justified within
+the column. This matters only when the column is wider than the
+data field to be display.
+\fIJustify\fR must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR.
+The default is \f(CWleft\fR.
+.TP
+\fB\-pad \fIpad\fR
+Specifies how much padding for the left and right sides of the column.
+\fIPad\fR is a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the column is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW2\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the column. \fIRelief\fR
+specifies how the column should appear relative to the widget;
+for example, \f(CWraised\fR means the column should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-state \fIstate\fR
+Sets the state of the column. If \fIstate\fR is \f(CWdisable\fR then
+the column title can not be activated nor invoked.
+The default is \f(CWnormal\fR.
+.TP
+\fB\-text \fIstring\fR
+Sets the title for the column.
+The default is \f(CW""\fR.
+.TP
+\fB\-titleforeground \fIcolor\fR
+Sets the foreground color of the column title.
+The default is \f(CWblack\fR.
+.TP
+\fB\-titleshadow \fIcolor\fR
+Sets the color of the drop shadow of the column title.
+The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the column. This overrides
+the computed with of the column. If \fIpixels\fR is 0,
+the width is computed as from the contents of the column. The
+default is \f(CW0\fR.
+.RE
+.SH "TEXT EDITING OPTIONS"
+Text edit window configuration options may also be set by the
+\fBoption\fR command. The resource class is \f(CWTreeViewEditor\fR.
+The resource name is always \f(CWedit\fR.
+.CS
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+.CE
+The following are the configuration options available for the
+text editing window.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the text edit window. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the edit window.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the text selection is exported. If the edit window is
+exporting its selection then it will observe the standard X11 protocols
+for handling the selection. Selections are available as type \fBSTRING\fR.
+The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the edit window. \fIRelief\fR
+indicates how the background should appear relative to the edit
+window; for example, \f(CWraised\fR means the background should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the selected text in the
+edit window. The \fB\-selectrelief\fR option determines if a border
+is to be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-selectforeground \fIcolor\fR
+Sets the foreground of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectrelief \fIrelief\fR
+Specifies the 3-D effect of the selected text in the edit window.
+\fIRelief\fR indicates how the text should appear relative to the edit
+window; for example, \f(CWraised\fR means the text should
+appear to protrude. The default is \f(CWflat\fR.
+.RE
+.SH "DEFAULT BINDINGS"
+Tk automatically creates class bindings for treeviews that give them
+Motif-like behavior. Much of the behavior of a \fBtreeview\fR widget is determined
+by its \fB\-selectmode\fR option, which selects one of two ways
+of dealing with the selection.
+.PP
+If the selection mode is \fBsingle\fR, only one node can be
+selected at a time.
+Clicking button 1 on an node selects
+it and deselects any other selected item.
+.PP
+If the selection mode is \fBmultiple\fR,
+any number of entries may be selected at once, including discontiguous
+ranges. Clicking Control-Button-1 on a node entry
+toggles its selection state without affecting any other entries.
+Pressing Shift-Button-1 on a node entry selects
+it, extends the selection.
+.IP [1]
+In \fBextended\fR mode, the selected range can be adjusted by pressing
+button 1 with the Shift key down: this modifies the selection to
+consist of the entries between the anchor and the entry under
+the mouse, inclusive.
+The un-anchored end of this new selection can also be dragged with
+the button down.
+.IP [2]
+In \fBextended\fR mode, pressing button 1 with the Control key down
+starts a toggle operation: the anchor is set to the entry under
+the mouse, and its selection state is reversed. The selection state
+of other entries isn't changed.
+If the mouse is dragged with button 1 down, then the selection state
+of all entries between the anchor and the entry under the mouse
+is set to match that of the anchor entry; the selection state of
+all other entries remains what it was before the toggle operation
+began.
+.IP [3]
+If the mouse leaves the treeview window with button 1 down, the window
+scrolls away from the mouse, making information visible that used
+to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the
+button is released, or the end of the hierarchy is reached.
+.IP [4]
+Mouse button 2 may be used for scanning.
+If it is pressed and dragged over the \fBtreeview\fR widget, the contents of
+the hierarchy drag at high speed in the direction the mouse moves.
+.IP [5]
+If the Up or Down key is pressed, the location cursor (active
+entry) moves up or down one entry.
+If the selection mode is \fBbrowse\fR or \fBextended\fR then the
+new active entry is also selected and all other entries are
+deselected.
+In \fBextended\fR mode the new active entry becomes the
+selection anchor.
+.IP [6]
+In \fBextended\fR mode, Shift-Up and Shift-Down move the location
+cursor (active entry) up or down one entry and also extend
+the selection to that entry in a fashion similar to dragging
+with mouse button 1.
+.IP [7]
+The Left and Right keys scroll the \fBtreeview\fR widget view left and right
+by the width of the character \fB0\fR.
+Control-Left and Control-Right scroll the \fBtreeview\fR widget view left and
+right by the width of the window.
+Control-Prior and Control-Next also scroll left and right by
+the width of the window.
+.IP [8]
+The Prior and Next keys scroll the \fBtreeview\fR widget view up and down
+by one page (the height of the window).
+.IP [9]
+The Home and End keys scroll the \fBtreeview\fR widget horizontally to
+the left and right edges, respectively.
+.IP [10]
+Control-Home sets the location cursor to the the first entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [11]
+Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [12]
+In \fBextended\fR mode, Control-Shift-Home extends the selection
+to the first entry and Control-Shift-End extends
+the selection to the last entry.
+.IP [13]
+In \fBmultiple\fR mode, Control-Shift-Home moves the location cursor
+to the first entry and Control-Shift-End moves
+the location cursor to the last entry.
+.IP [14]
+The space and Select keys make a selection at the location cursor
+(active entry) just as if mouse button 1 had been pressed over
+this entry.
+.IP [15]
+In \fBextended\fR mode, Control-Shift-space and Shift-Select
+extend the selection to the active entry just as if button 1
+had been pressed with the Shift key down.
+.IP [16]
+In \fBextended\fR mode, the Escape key cancels the most recent
+selection and restores all the entries in the selected range
+to their previous selection state.
+.IP [17]
+Control-slash selects everything in the widget, except in
+\fBsingle\fR and \fBbrowse\fR modes, in which case it selects
+the active entry and deselects everything else.
+.IP [18]
+Control-backslash deselects everything in the widget, except in
+\fBbrowse\fR mode where it has no effect.
+.IP [19]
+The F16 key (labelled Copy on many Sun workstations) or Meta-w
+copies the selection in the widget to the clipboard, if there is
+a selection.
+.PP
+The behavior of \fBtreeview\fR widgets can be changed by defining new bindings
+for individual widgets or by redefining the class bindings.
+.SS WIDGET BINDINGS
+In addition to the above behavior, the following additional behavior
+is defined by the default widget class (TreeView) bindings.
+.IP \f(CW<ButtonPress-2>\fR
+Starts scanning.
+.IP \f(CW<B2-Motion>\fR
+Adjusts the scan.
+.IP \f(CW<ButtonRelease-2>\fR
+Stops scanning.
+.IP \f(CW<B1-Leave>\fR
+Starts auto-scrolling.
+.IP \f(CW<B1-Enter>\fR
+Starts auto-scrolling
+.IP \f(CW<KeyPress-Up>\fR
+Moves the focus to the previous entry.
+.IP \f(CW<KeyPress-Down>\fR
+Moves the focus to the next entry.
+.IP \f(CW<Shift-KeyPress-Up>\fR
+Moves the focus to the previous sibling.
+.IP \f(CW<Shift-KeyPress-Down>\fR
+Moves the focus to the next sibling.
+.IP \f(CW<KeyPress-Prior>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Next>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Left>\fR
+Closes the entry. It is not an error if the entry has no children.
+.IP \f(CW<KeyPress-Right>\fR
+Opens the entry, displaying its children. It is not an
+error if the entry has no children.
+.IP \f(CW<KeyPress-space>\fR
+In "single" select mode this selects the entry. In "multiple" mode,
+it toggles the entry (if it was previous selected, it is not
+deselected).
+.IP \f(CW<KeyRelease-space>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress-Return>\fR
+Sets the focus to the current entry.
+.IP \f(CW<KeyRelease-Return>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress>\fR
+Moves to the next entry whose label starts with the letter typed.
+.IP \f(CW<KeyPress-Home>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-End>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-F1>\fR
+Opens all entries.
+.IP \f(CW<KeyPress-F2>\fR
+Closes all entries (except root).
+.SS BUTTON BINDINGS
+Buttons have bindings. There are associated with the "all" bindtag
+(see the entry's -bindtag option). You can use the \fBbind\fR
+operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the button of the current entry.
+.IP \f(CW<Leave>\fR
+Returns the button back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the view so that the current entry is visible.
+.SS ENTRY BINDINGS
+Entries have default bindings. There are associated with the "all"
+bindtag (see the entry's -bindtag option). You can use the \fBbind\fR
+operation to modify them.
+.IP \f(CW<Enter>\fR
+Highlights the current entry.
+.IP \f(CW<Leave>\fR
+Returns the entry back to its normal state.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the selection anchor the current entry.
+.IP \f(CW<Double-ButtonPress-1>\fR
+Toggles the selection of the current entry.
+.IP \f(CW<B1-Motion>\fR
+For "multiple" mode only. Saves the current location of the
+pointer for auto-scrolling. Resets the selection mark.
+.IP \f(CW<ButtonRelease-1>\fR
+For "multiple" mode only. Sets the selection anchor to the
+current entry.
+.IP \f(CW<Shift-ButtonPress-1>\fR
+For "multiple" mode only. Extends the selection.
+.IP \f(CW<Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-ButtonRelease-1>\fR
+Stop auto-scrolling.
+.IP \f(CW<Control-ButtonPress-1>\fR
+For "multiple" mode only. Toggles and extends the selection.
+.IP \f(CW<Control-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-ButtonRelease-1>\fR
+Stops auto-scrolling.
+.IP \f(CW<Control-Shift-ButtonPress-1>\fR
+???
+.IP \f(CW<Control-Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.SS COLUMN BINDINGS
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the
+\fBcolumn bind\fR operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the current column title.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Invokes the command (see the column's -command option) if one
+if specified.
+.SS COLUMN RULE BINDINGS
+.IP \f(CW<Enter>\fR
+Highlights the current and activates the ruler.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state. Deactivates the
+ruler.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the resize anchor for the column.
+.IP \f(CW<B1-Motion>\fR
+Sets the resize mark for the column.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the size of the column, based upon the resize anchor and mark
+positions.
+.SH EXAMPLE
+The \fBtreeview\fR command creates a new widget.
+.CS
+treeview .h \-bg white
+.CE
+A new Tcl command \f(CW.h\fR is also created. This command can be used
+to query and modify the \fBtreeview\fR widget. For example, to change the
+background
+color of the table to "green", you use the new command and the widget's
+\fBconfigure\fR operation.
+.CS
+# Change the background color.
+\&.h configure \-background "green"
+.CE
+By default, the \fBtreeview\fR widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the
+widget. Above, the new tree object name is ".h". But you can use the
+\fB\-tree\fR option to specify the name of another tree.
+.CS
+# View the tree "myTree".
+\&.h configure \-tree "myTree"
+.CE
+When a new tree is created, it contains only a root node. The node
+is automatically opened. The id of the root node is always
+\f(CW0\fR (you can use also use the special id \f(CWroot\fR). The
+\fBinsert\fR operation lets you insert one or more new entries into
+the tree. The last argument is the node's \fIpathname\fR.
+.CS
+# Create a new entry named "myEntry"
+set id [\&.h insert end "myEntry"]
+.CE
+This appends a new node named "myEntry". It will positioned as the
+last child of the root of the tree (using the position "end"). You
+can supply another position to order the node within its siblings.
+.CS
+# Prepend "fred".
+set id [\&.h insert 0 "fred"]
+.CE
+Entry names do not need to be unique. By default, the node's label
+is its name. To supply a different text label, add the \fB\-label\fR
+option.
+.CS
+# Create a new node named "fred"
+set id [\&.h insert end "fred" -label "Fred Flintstone"]
+.CE
+The \fBinsert\fR operation returns the id of the new node. You can
+also use the \fBindex\fR operation to get this information.
+.CS
+# Get the id of "fred"
+\&.h index "fred"
+.CE
+To insert a node somewhere other than root, use the \fB\-at\fR switch.
+It takes the id of the node where the new child will be added.
+.CS
+# Create a new node "barney" in "fred".
+\&.h insert -at $id end "barney"
+.CE
+A pathname describes the path to an entry in the hierarchy. It's a
+list of entry names that compose the path in the tree. Therefore, you
+can also add "barney" to "fred" as follows.
+.CS
+# Create a new sub-entry of "fred"
+\&.h insert end "fred barney"
+.CE
+Every name in the list is ancestor of the next. All ancestors must
+already exist. That means that an entry "fred" is an ancestor of
+"barney" and must already exist. But you can use the
+\fB\-autocreate\fR configuration option to force the creation of
+ancestor nodes.
+.CS
+# Force the creation of ancestors.
+\&.h configure -autocreate yes
+\&.h insert end "fred barney wilma betty"
+.CE
+Sometimes the pathname is already separated by a character sequence
+rather than formed as a list. A file name is a good example of this.
+You can use the \fB\-separator\fR option to specify a separator string
+to split the path into its components. Each pathname inserted is
+automatically split using the separator string as a separator.
+Multiple separators are treated as one.
+.CS
+\&.h configure -separator /
+\&.h insert end "/usr/local/tcl/bin"
+.CE
+If the path is prefixed by extraneous characters, you can
+automatically trim it off using the \fB\-trim\fR option. It removed
+the string from the path before it is parsed.
+.CS
+\&.h configure -trim C:/windows -separator /
+\&.h insert end "C:/window/system"
+.CE
+You can insert more than one entry at a time with the \fBinsert\fR
+operation. This can be much faster than looping over a list of names.
+.CS
+# The slow way
+foreach f [glob $dir/*] {
+ \&.h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+.CE
+In this case, the \fBinsert\fR operation will return a list of ids
+of the new entries.
+.PP
+You can delete entries with the \fBdelete\fR operation. It takes one or
+more tags of ids as its argument. It deletes the entry and all its
+children.
+.CS
+\&.h delete $id
+.CE
+Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the \fB\-label\fR
+option that sets the entry's text label. The \fBentry configure\fR
+operation lets you set or modify an entry's configuration options.
+.CS
+\&.h entry configure $id -color red -font fixed
+.CE
+You can hide an entry and its children using the \fB\-hide\fR option.
+.CS
+\&.h entry configure $id -hide yes
+.CE
+More that one entry can be configured at once. All entries specified
+are configured with the same options.
+.CS
+\&.h entry configure $i1 $i2 $i3 $i4 -color brown
+.CE
+An icon is displayed for each entry. It's a Tk image drawn to the
+left of the label. You can set the icon with the entry's
+\fB\-icons\fR option. It takes a list of two image names: one to
+represent the open entry, another when it is closed.
+.CS
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+\&.h entry configure $id -icons "$im1 $im2"
+.CE
+If \fB\-icons\fR is set to the empty string, no icons are display.
+.PP
+If an entry has children, a button is displayed to the left of the
+icon. Clicking the mouse on this button opens or closes the
+sub-hierarchy. The button is normally a \f(CW+\fR or \f(CW\-\fR
+symbol, but can be configured in a variety of ways using the \fBbutton
+configure\fR operation. For example, the \f(CW+\fR and \f(CW\-\fR
+symbols can be replaced with Tk images.
+.CS
+set im1 [image create photo -file closefolder.gif]
+set im2 [image create photo -file downarrow.gif]
+\&.h button configure $id -images "$im1 $im2" \\
+ -openrelief raised -closerelief raised
+.CE
+Entries can contain an arbitrary number of \fIdata fields\fR. Data
+fields are name-value pairs. Both the value and name are strings.
+The entry's \fB\-data\fR option lets you set data fields.
+.CS
+\&.h entry configure $id -data {mode 0666 group users}
+.CE
+The \fB\-data\fR takes a list of name-value pairs.
+.PP
+You can display these data fields as \fIcolumns\fR in the
+\fBtreeview\fR widget. You can create and configure columns with
+the \fBcolumn\fR operation. For example, to add a new column to the
+widget, use the \fBcolumn insert\fR operation. The last argument is
+the name of the data field that you want to display.
+.CS
+\&.h column insert end "mode"
+.CE
+The column title is displayed at the top of the column. By default,
+it's is the field name. You can override this using the column's
+\fB\-text\fR option.
+.CS
+\&.h column insert end "mode" -text "File Permissions"
+.CE
+Columns have several configuration options. The \fBcolumn
+configure\fR operation lets you query or modify column options.
+.CS
+\&.h column configure "mode" -justify left
+.CE
+The \fB\-justify\fR option says how the data is justified within in
+the column. The \fB\-hide\fR option indicates whether the column is
+displayed.
+.CS
+\&.h column configure "mode" -hide yes
+.CE
+Entries can be selected by clicking on the mouse. Selected entries
+are drawn using the colors specified by the \fB\-selectforeground\fR
+and \fB\-selectbackground\fR configuration options.
+The selection itself is managed by the \fBselection\fR operation.
+.CS
+# Clear all selections
+\&.h selection clear 0 end
+# Select the root node
+\&.h selection set 0
+.CE
+The \fBcurselection\fR operation returns a list of ids of
+all the selected entries.
+.CS
+set ids [\&.h curselection]
+.CE
+You can use the \fBget\fR operation to convert the ids to
+their pathnames.
+.CS
+set names [eval .h get -full $ids]
+.CE
+If a treeview is exporting its selection (using the
+\fB\-exportselection\fR option), then it will observe the standard X11
+protocols for handling the selection. Treeview selections are
+available as type \fBSTRING\fR; the value of the selection will be the
+pathnames of the selected entries, separated by newlines.
+.PP
+The \fBtreeview\fR supports two modes of selection: \f(CWsingle\fR
+and \f(CWmultiple\fR. In single select mode, only one entry can be
+selected at a time, while multiple select mode allows several entries
+to be selected. The mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -selectmode "multiple"
+.CE
+You can be notified when the list of selected entries changes. The widget's
+\fB\-selectcommand\fR specifies a Tcl procedure that is called whenever
+the selection changes.
+.CS
+proc SelectNotify { widget } {
+ set ids [\&$widget curselection]
+}
+\&.h configure -selectcommand "SelectNotify .h"
+.CE
+The widget supports the standard Tk scrolling and scanning operations.
+The \fBtreeview\fR can be both horizontally and vertically. You can
+attach scrollbars to the \fBtreeview\fR the same way as the listbox
+or canvas widgets.
+.CS
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+\&.h configure -xscrollcommand ".xbar set" \\
+ -yscrollcommand ".ybar set"
+.CE
+There are three different modes of scrolling: \f(CWlistbox\fR,
+\f(CWcanvas\fR, and \f(CWhierbox\fR. In \f(CWlistbox\fR mode, the last
+entry can always be scrolled to the top of the widget. In \f(CWhierbox\fR
+mode, the last entry is always drawn at the bottom of the widget.
+The scroll mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -scrollmode "listbox"
+.CE
+Entries can be programmatically opened or closed using the \fBopen\fR
+and \fBclose\fR operations respectively.
+.CS
+\&.h open $id
+\&.h close $id
+.CE
+When an entry is opened, a Tcl procedure can be automatically invoked.
+The \fB\-opencommand\fR option specifies this procedure. This
+procedure can lazily insert entries as needed.
+.CS
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+\&.h configure -opencommand "AddEntries %P"
+.CE
+Now when an entry is opened, the procedure \f(CWAddEntries\fR is
+called and adds children to the entry. Before the command is invoked,
+special "%" substitutions (like \fBbind\fR) are performed. Above,
+\f(CW%P\fR is translated to the pathname of the entry.
+.PP
+The same feature exists when an entry is closed. The
+\fB\-closecommand\fR option specifies the procedure.
+.CS
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+\&.h configure -closecommand "DeleteEntries %#"
+.CE
+When an entry is closed, the procedure \f(CWDeleteEntries\fR is called
+and deletes the entry's children using the \fBentry delete\fR operation
+(\f(CW%#\fR is the id of entry).
+.SH KEYWORDS
+treeview, widget
diff --git a/blt/man/htext.mann b/blt/man/htext.mann
new file mode 100644
index 00000000000..4b8be49f6bf
--- /dev/null
+++ b/blt/man/htext.mann
@@ -0,0 +1,384 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Hypertext widget created by George Howlett.
+'\"
+.so man.macros
+.TH htext n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+htext \- Create and manipulate hypertext widgets
+.SH SYNOPSIS
+\fBhtext\fP \fIpathName \fR?\fIoption value\fR?...
+.BE
+.SH DESCRIPTION
+.PP
+The \fBhtext\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a \fBhtext\fP widget.
+Additional options, described above, may be specified on the command line
+or in the option database to configure aspects of the widget such as its
+color and font. At the time this command is invoked, there must not
+exist a window named \fIpathName\fR, but \fIpathName\fR's parent must exist.
+The \fBhtext\fR command returns its \fIpathName\fR.
+.PP
+The \fBhtext\fP widget is hybrid of a non-editable text widget and
+a geometry manager (e.g. the packer). It displays text (optionally read
+from file) in a window. Text can be scrolled either horizontally or
+vertically using the \fBhtext\fR window as a viewport.
+In addition, Tcl commands can be embedded into
+the text which are evaluated as the text is parsed. Text between special
+double characters (percent signs "%%") is immediately passed to the Tcl
+interpreter for evaluation.
+.PP
+Furthermore, any widget or widget hierarchy can be packed in-line and made
+to appear on the current line of the text. Widgets are packed using the
+\fBhtext append\fP command. All widgets must be children of the
+\fBhtext\fP window and must already exist before packing.
+Once a widget has been packed it cannot be moved to a different
+position within the text. Widgets can be resized but they will remain
+at the same position within the text.
+.PP
+Before a file or text string is parsed by the \fBhtext\fR widget,
+all the widget's current children are destroyed. You can reload files or text
+without worrying about unmapping or destroying each child window beforehand.
+.PP
+Setting the either the \fB\-filename\fR or \fB\-text\fR configuration option
+will adjust the value of the other. If both options are set, the file
+takes precedence. When a new file is read using the \fB\-filename\fR option,
+the value of the \fB\-text\fR option is reset to the empty string. Likewise,
+when the \fB\-text\fR option is set, the string representing the
+\fB\-filename\fR option is cleared.
+.SH FILE FORMAT
+The format of \fBhtext\fP text file is typically ASCII text.
+Text enclosed by special double characters (by default, percent signs '%%')
+is interpreted and executed as Tcl commands.
+The special character may be specified by the \fB\-specialchar\fP option.
+In the following example of a \fBhtext\fP file, a button widget
+is appended to the text between the words "\f(CWa\fP" and "\f(CWwhich\fP".
+The \fIpathName\fR of the \fBhtext\fP widget is "\f(CW.ht\fP".
+.CS
+\f(CWThis will be displayed as normal text.
+But this will become a %%
+ button .ht.button -text "button" -fg red
+ .ht append .ht.button
+%% which can invoke a Tcl command.\fR
+.CE
+.LP
+.SH INDICES
+.PP
+Some of the widget operations (\fBselection\fR, \fRgotoline\fR,
+\fBsearch\fR, etc.) take one or more indices as arguments.
+An index is a string used to indicate a particular place within
+the text, such as the first and last characters in a range to be
+selected.
+.LP
+An index must have one of the following forms:
+.TP 12
+\fIline\fB.\fIchar\fR
+Indicates \fIchar\fR'th character on line \fIline\fR.
+Both lines and characters are number from 0, so "0.0" is the
+first beginning of the text. \fIChar\fR may be undesignated. In
+this case a character position of 0 is assumed.
+.TP 12
+\fB@\fIx\fB,\fIy\fR
+Indicates the character that covers the pixel whose x and y coordinates
+within the text's window are \fIx\fR and \fIy\fR.
+.TP 12
+\fBend\fR
+Indicates the end of the text.
+.TP 12
+\fBanchor\fR
+Indicates the anchor point for the selection, which is set with the
+\fBselection\fR operation.
+.TP 12
+\fBsel.first\fR
+Indicates the first character in the selection. It is an error to
+use this form if the selection isn't in the entry window.
+.TP 12
+\fBsel.last\fR
+.VS
+Indicates the character just after the last one in the selection.
+.VE
+It is an error to use this form if the selection isn't in the
+entry window.
+.SH "VARIABLES"
+.PP
+The following global Tcl variables are maintained when an
+\fBhtext\fR file is parsed.
+.TP
+\fBhtext(widget)\fR
+is the pathname of the \fBhtext\fP widget.
+.TP
+\fBhtext(file)\fR
+is the name of the file the \fBhtext\fP widget is currently parsing.
+It is the empty string when the \fB\-text\fP option is used.
+.TP
+\fBhtext(line)\fR
+is the current line number in the text.
+.PP
+This information might be used to construct hyper links
+between different files and/or lines.
+.LP
+.SH "SYNTAX"
+The \fBhtext\fP command creates a new Tcl command whose
+name is \fIpathName\fR. This command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS
+\fIpathName oper \fR?\fIargs\fR?
+.DE
+\fIOper\fR and \fIargs\fR determine the exact behavior of the command.
+.PP
+.SH "OPERATIONS"
+The following operations are available for \fBhtext\fP widgets:
+.TP
+\fIpathName \fBappend \fIwindow \fR?\fIoption value\fR?...
+Embeds the widget \fIwindow\fP into the htext widget. \fIWindow\fP is
+the pathname of the widget to be embedded which must be a child of
+\fIpathName\fR. \fIWindow\fR will be positioned in the htext widget
+at the current location of the text. If \fIoption\fR and \fIvalue\fR
+pairs are present, they configure various aspects how \fIwindow\fR
+appears in \fIpathName\fR. The following options are available.
+.RS
+.TP
+\fB\-anchor \fIanchorPos\fR
+Specifies how \fIwindow\fR will be arranged if there is any extra
+space in the cavity surrounding the window. For example, if
+\fIanchorPos\fR is \fBcenter\fR then the window is centered in the
+cavity; if \fIanchorPos\fR is \fBw\fR then the window will be drawn
+such it touches the leftmost edge of the cavity. The default
+is \f(CWcenter\fR.
+.TP
+\fB\-fill \fIstyle\fR
+Specifies how the \fIwindow\fR should be stretched to occupy the extra
+space in the cavity surrounding it (if any exists). \fIStyle\fR is
+\f(CWnone\fR, \f(CWx\fR, \f(CWy\fR, \f(CWboth\fR. If \fIstyle\fR is \f(CWx\fR,
+the width of \fIwindow\fR is expanded to fill the cavity. If
+\fIstyle\fR is \fBy\fR, the height is expanded. The default is
+\f(CWnone\fR.
+.TP
+\fB\-height \fIpixels\fR
+Sets the height of the cavity surrounding \fIwindow\fR. If
+\fIpixels\fP is zero, the height of the cavity will be the same as the
+requested height of \fIwindow\fR. If \fIpixels\fR is less than the
+requested height of \fIwindow\fR, \fIwindow\fR will be reduced to fit
+the cavity. The default is \f(CW0\fR.
+.TP
+\fB\-ipadx \fIpad\fR
+Sets the amount of internal padding to be added to the width
+\fIwindow\fR. \fIPad\fR can be a list of one or two numbers. If
+\fIpad\fR has two elements, the left side of \fIwindow\fR is extended
+by the first value and the right side by the second value. If
+\fIpad\fR is just one value, both the left and right sides are padded
+by evenly by the value. The default is \f(CW0\fR.
+.TP
+\fB\-ipady \fIpad\fR
+Sets an amount of internal padding to be added to the height of
+\fIwindow\fR. \fIPad\fR can be a list of one or two numbers. If
+\fIpad\fR has two elements, the top of \fIwindow\fR is padded by the
+first value and the bottom by the second value. If \fIpad\fR is just
+one number, both the top and bottom are padded evenly by the value.
+The default is \f(CW0\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Justifies \fIwindow\fR vertically within the cavity containing it
+in relation to the line of text. \fIJustify\fR is \fBtop\fP,
+\fBbottom\fR, or \fBcenter\fR. If \fIjustify\fR is \f(CWcenter\fR the
+widget is centered along the baseline of the line of text. The
+default is \f(CWcenter\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding on the left and right sides of \fIwindow\fR.
+\fIPad\fR can be a list of one or two numbers. If \fIpad\fR has two
+elements, the left side of \fIwindow\fR is padded by the first value
+and the right side by the second value. If \fIpad\fR has just one
+value, both the left and right sides are padded evenly by the value.
+The default is \f(CW0\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below \fIwindow\fR. \fIPad\fR can be a
+list of one or two numbers. If \fIpad\fR has two elements, the area
+above \fIwindow\fR is padded by the first value and the area below by
+the second value. If \fIpad\fR is just one number, both the top and
+bottom are padded by the value. The default is \f(CW0\fR.
+.TP
+\fB\-relheight \fIvalue\fR
+Specifies the height of the cavity containing \fIwindow\fR relative to
+the height of \fIpathName\fR. \fIValue\fP is real number indicating
+the ratio of the height of the cavity to the height of \fIpathName\fR.
+As the height of \fIpathName\fR changes, so will the height of \fIwindow\fR.
+If \fIvalue\fR is 0.0 or less, the height of the cavity is the requested
+height \fIwindow\fR. The default is \f(CW0.0\fR.
+.TP
+\fB\-relwidth \fIvalue\fR
+Specifies the width of the cavity containing \fIwindow\fR relative to
+the width of \fIpathName\fR. \fIValue\fP is real number indicating
+the ratio of the width of the cavity to the width of \IpathName\fR.
+As the height of \fIpathName\fR changes, so will the height of \fIwindow\fR.
+If \fIvalue\fR is 0.0 or less, the width of the cavity is the
+requested width of \fIwindow\fR. The default is \f(CW0.0\fR.
+.TP
+\fB\-width \fIvalue\fR
+Species the width of the cavity containing the child window.
+\fIValue\fP must be in a form accepted by \fBTk_GetPixels\fR.
+If \fIvalue\fP is greater than zero, the cavity is resized to that width.
+If the requested window width is greater than the cavity's width, the
+window will be reduced to fit the cavity.
+By default, the cavity is requested width of the child window.
+.RE
+.TP
+\fIpathName \fBconfigure\fR ?\fIwindow\fR? ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Queries or modifies the configuration options of the text widget or one
+of its embedded widgets. If no \fIwindow\fR argument is present,
+the htext widget itself is configured. Otherwise \fIwindow\fR
+is the pathname of a widget already embedded into the htext widget.
+Then this command configure the options for the embedded widget.
+.PP
+If \fIoption\fR isn't specified, a list describing all of the current
+options for \fIpathName\fR or \fIwindow\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+the option \fIoption\fR is returned. If one or more \fIoption\fR and
+\fIvalue\fR pairs are specified, then for each pair, the htext or embedded
+window option \fIoption\fR is set to \fIvalue\fR.
+.PP
+The following options are valid for the htext widget.
+.RS
+.TP
+\fB\-background\fR \fIcolor\fI
+Sets the background of the htext widget to \fIcolor\fR. This default is
+\f(CWwhite\fR.
+.TP
+\fB\-cursor\fR \fIcursor\fR
+Specifies the cursor for the htext widget. The default cursor is
+\f(CWpencil\fR.
+.TP
+\fB\-filename\fR \fIfileName\fR
+Specifies a \fBhtext\fP file to be displayed in the window.
+If the value is the empty string, the \fB\-text\fR option is used instead.
+See the section
+.SB FILE FORMAT
+for a description of the \fBhtext\fP
+file format.
+.TP
+\fB\-font\fR \fIfontName\fR
+Sets the font of the text in the htext widget to \fIfontName\fR. The
+default is \f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground\fR \fIcolor\fR
+Sets the foreground of the htext widget to \fIcolor\fR. This is
+the color of the text. This default is \f(CWblack\fR.
+.TP
+\fB\-height\fR \fIpixels\fR
+Specifies the height of the htext widget window.
+.TP
+\fB\-linespacing\fR \fIpixels\fR
+Specifies the spacing between each line of text. The value must be in
+a form accepted by \fBTk_GetPixels\fR. The default value is 1 pixel.
+.TP
+\fB\-specialchar\fR \fInumber\fR
+Specifies the ASCII value of the special double character delimiters.
+In \fBhtext\fP files, the text between these special characters is
+evaluated as a block of Tcl commands. The default special character
+is the \f(CW0x25\fR (percent sign).
+.TP
+\fB\-text\fR \fItext\fR
+Specifies the text to be displayed in the htext widget. \fIText\fR
+can be any valid string of characters. See
+.SB "FILE FORMAT"
+for a description.
+.TP
+\fB\-xscrollcommand\fR \fIstring\fR
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars. When the view in the htext widget's window changes (or
+whenever anything else occurs that could change the display in a
+scrollbar, such as a change in the total size of the widget's
+contents), the widget invoke \fIstring\fR concatenated by two numbers.
+Each of the numbers is a fraction between 0 and 1, which indicates a
+position in the document. If this option is not specified, then no
+command will be executed.
+.TP
+\fB\-yscrollcommand\fR \fIstring\fR
+Specifies the prefix for a command used to communicate with vertical
+scrollbars. When the view in the htext widget's window changes (or
+whenever anything else occurs that could change the display in a
+scrollbar, such as a change in the total size of the widget's
+contents), the widget invoke \fIstring\fR concatenated by two numbers.
+Each of the numbers is a fraction between 0 and 1, which indicates a
+position in the document. If this option is not specified, then no
+command will be executed.
+.TP
+\fB\-width\fR \fIpixels\fR
+Specifies the desired width of the viewport window. If the
+\fIpixels\fR is less than one, the window will grow to accommodate the
+widest line of text.
+.TP
+\fB\-xscrollunits\fR \fIpixels\fR
+Specifies the horizontal scrolling distance. The default is 10 pixels.
+.TP
+\fB\-yscrollunits\fR \fIpixels\fR
+Specifies the vertical scrolling distance. The default is 10 pixels.
+.RE
+.TP
+\fIpathName \fBgotoline \fR?\fIindex\fR?
+Sets the top line of the text to \fIindex\fP. \fIIndex\fP must be
+a valid text index (the character offset is ignored).
+If an \fIindex\fP isn't provided, the current line number is
+returned.
+.TP
+\fIpathName \fBscan mark \fIposition\fR
+Records \fIposition\fR and the current view in the text window; used in
+conjunction with later \fBscan dragto\fR commands. \fIPosition\fR must
+be in the form "\fI@x,y\fR, where \fIx\fR and \fIy\fR are window coordinates.
+Typically this
+command is associated with a mouse button press in the widget. It
+returns an empty string.
+.TP
+\fIpathName \fBscan dragto \fIposition\fR
+Computes the difference between \fIposition\fR and the position registered
+in the last \fBscan mark\fR command for the widget.
+The view is then adjusted
+up or down by 10 times the difference in coordinates. This command is
+can be associated with mouse motion events to produce the effect of
+dragging the text at high speed through the window.
+\fIPosition\fR must be in the form "\fI@x,y\fR, where \fIx\fR and
+\fIy\fR are window coordinates. The command returns an empty string.
+.TP
+\fIpathName \fBsearch \fIpattern\fR ?\fIfrom\fR? ?\fIto\fR?
+Returns the number of the next line matching \fIpattern\fR. \fIPattern\fR is
+a string which obeys the matching rules of \fBTcl_StringMatch\fR.
+\fIFrom\fR and \fIto\fR are text line numbers (inclusive) which
+bound the search.
+If no match for \fIpattern\fR can be found, \fB-1\fR is returned.
+.TP
+\fIpathName \fBxview \fR?\fIposition\fR?
+Moves the viewport horizontally to the new text x-coordinate position.
+\fIPosition\fR is the offset from the left side of the text to the current
+position and must be in a form accepted by \fBTk_GetPixels\fR. If \fIposition\fR
+is not present, the current text position is returned.
+.TP
+\fIpathName \fByview \fR?\fIposition\fR?
+Moves the viewport vertically to the new text y-coordinate position.
+\fIPosition\fR is the offset from the top of the text to the current
+position and must be in a form accepted by \fBTk_GetPixels\fR. If \fIposition\fR
+is not present, the current text position is returned.
+.SH BUGS
+Text with embedded tabs can be obscured by child windows when scrolled
+horizontally.
+.SH KEYWORDS
+hypertext, widget
diff --git a/blt/man/man.macros b/blt/man/man.macros
new file mode 100644
index 00000000000..30b38d3f268
--- /dev/null
+++ b/blt/man/man.macros
@@ -0,0 +1,240 @@
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id$
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+.ft CW
+.sp
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+.ft R
+.sp
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
diff --git a/blt/man/spline.mann b/blt/man/spline.mann
new file mode 100644
index 00000000000..69f19844a75
--- /dev/null
+++ b/blt/man/spline.mann
@@ -0,0 +1,181 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Spline command created by George Howlett.
+'\"
+.so man.macros
+.TH spline n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+spline \- Fit curves with spline interpolation
+.SH SYNOPSIS
+.sp
+\fBspline natural \fIx y sx sy\fR
+.sp
+\fBspline quadratic \fIx y sx sy\fR
+.BE
+.SH DESCRIPTION
+The \fBspline\fR command computes a spline fitting a set of data
+points (x and y vectors) and produces a vector of the interpolated
+images (y-coordinates) at a given set of x-coordinates.
+.SH INTRODUCTION
+Curve fitting has many applications. In graphs, curve fitting can
+be useful for displaying curves which are aesthetically pleasing to the
+eye. Another advantage is that you can quickly generate arbitrary points
+on the curve from a small set of data points.
+.PP
+A spline is a device used in drafting to produce smoothed curves. The
+points of the curve, known as \fIknots\fR, are fixed and the
+\fIspline\fR, typically a thin strip of wood or metal, is bent around
+the knots to create the smoothed curve. Spline interpolation is the
+mathematical equivalent. The curves between adjacent knots are
+piecewise functions such that the resulting spline runs exactly
+through all the knots. The order and coefficients of the polynominal
+determine the "looseness" or "tightness" of the curve fit from the
+line segments formed by the knots.
+.PP
+The \fBspline\fR command performs spline interpolation using cubic
+("natural") or quadratic polynomial functions. It computes the spline
+based upon the knots, which are given as x and y vectors. The
+interpolated new points are determined by another vector which
+represents the abscissas (x-coordinates) or the new points. The
+ordinates (y-coordinates) are interpolated using the spline and
+written to another vector.
+.SH EXAMPLE
+Before we can use the \fBspline\fR command, we need to create two BLT
+vectors which will represent the knots (x and y coordinates) of the
+data that we're going to fit. Obviously, both vectors must be the
+same length.
+.CS
+# Create sample data of ten points.
+vector x(10) y(10)
+
+for {set i 10} {$i > 0} {incr i -1} {
+ set x($i-1) [expr $i*$i]
+ set y($i-1) [expr sin($i*$i*$i)]
+}
+.CE
+We now have two vectors \f(CWx\fR and \f(CWy\fR representing the ten data
+points we're trying to fit. The order of the values of \f(CWx\fR must
+be monotonically increasing. We can use the vector's \fBsort\fR operation
+to sort the vectors.
+.CS
+x sort y
+.CE
+The components of \f(CWx\fR are sorted in increasing order. The
+components of \f(CWy\fR are rearranged so that the original x,y
+coordinate pairings are retained.
+.PP
+A third vector is needed to indicate the abscissas (x-coordinates) of
+the new points to be interpolated by the spline. Like the x vector,
+the vector of abscissas must be monotonically increasing. All the
+abscissas must lie between the first and last knots (x vector)
+forming the spline.
+.PP
+How the abscissas are picked is arbitrary. But if we are going to
+plot the spline, we will want to include the knots too. Since both
+the quadratic and natural splines preserve the knots (an abscissa from
+the x vector will always produce the corresponding ordinate from the y
+vector), we can simply make the new vector a superset of \f(CWx\fR.
+It will contain the same coordinates as \f(CWx\fR, but also the
+abscissas of the new points we want interpolated. A simple way is to
+use the vector's \fBpopulate\fR operation.
+.CS
+x populate sx 10
+.CE
+This creates a new vector \f(CWsx\fR. It contains the abscissas of
+\f(CWx\fR, but in addition \f(CWsx\fR will have ten evenly distributed
+values between each abscissa. You can interpolate any points you
+wish, simply by setting the vector values.
+.PP
+Finally, we generate the ordinates (the images of the spline) using
+the \fBspline\fR command. The ordinates are stored in a fourth
+vector.
+.CS
+spline natural x y sx sy
+.CE
+This creates a new vector \f(CWsy\fR. It will have the same length as
+\f(CWsx\fR. The vectors \f(CWsx\fR and \f(CWsy\fR represent the smoothed
+curve which we can now plot.
+.CS
+graph .graph
+\&.graph element create original -x x -y x -color blue
+\&.graph element create spline -x sx -y sy -color red
+table . .graph
+.CE
+The \fBnatural\fR operation employs a cubic interpolant when forming
+the spline. In terms of the draftmen's spline, a \fInatural spline\fR
+requires the least amount of energy to bend the spline (strip of
+wood), while still passing through each knot. In mathematical terms,
+the second derivatives of the first and last points are zero.
+.PP
+Alternatively, you can generate a spline using the \fBquadratic\fR
+operation. Quadratic interpolation produces a spline which follows
+the line segments of the data points much more closely.
+.CS
+spline quadratic x y sx sy
+.CE
+.SH OPERATIONS
+.TP
+\fBspline natural \fIx y sx sy\fR
+Computes a cubic spline from the data points represented by the
+vectors \fIx\fR and \fIy\fR and interpolates new points using vector
+\fIsx\fR as the x-coordinates. The resulting y-coordinates are
+written to a new vector \fIsy\fR. The vectors \fIx\fR and \fIy\fR must
+be the same length and contain at least three components. The order
+of the components of \fIx\fR must be monotonically increasing.
+\fISx\fR is the vector containing the x-coordinates of the points to
+be interpolated. No component of \fIsx\fR can be less than first
+component of \fIx\fR or greater than the last component. The order
+of the components of \fIsx\fR must be monotonically increasing.
+\fISy\fR is the name of the vector where the calculated y-coordinates
+will be stored. If \fIsy\fR does not already exist, a new vector will be
+created.
+.TP
+\fBspline quadratic \fIx y sx sy\fR
+Computes a quadratic spline from the data points represented by the
+vectors \fIx\fR and \fIy\fR and interpolates new points using vector
+\fIsx\fR as the x-coordinates. The resulting y-coordinates are
+written to a new vector \fIsy\fR. The vectors \fIx\fR and \fIy\fR must
+be the same length and contain at least three components. The order
+of the components of \fIx\fR must be monotonically increasing.
+\fISx\fR is the vector containing the x-coordinates of the points to
+be interpolated. No component of \fIsx\fR can be less than first
+component of \fIx\fR or greater than the last component. The order of
+the components of \fIsx\fR must be monotonically increasing. \fISy\fR
+is the name of the vector where the calculated y-coordinates are
+stored. If \fIsy\fR does not already exist, a new vector will be
+created.
+.SH REFERENCES
+.nf
+.sp
+Numerical Analysis
+by R. Burden, J. Faires and A. Reynolds.
+Prindle, Weber & Schmidt, 1981, pp. 112
+.sp
+Shape Preserving Quadratic Splines
+by D.F.Mcallister & J.A.Roulier
+Coded by S.L.Dodd & M.Roulier N.C.State University.
+.sp
+.fi
+The original code for the quadratric spline can be found in TOMS #574.
+.SH KEYWORDS
+spline, vector, graph
+
diff --git a/blt/man/stripchart.mann b/blt/man/stripchart.mann
new file mode 100644
index 00000000000..15536474d6e
--- /dev/null
+++ b/blt/man/stripchart.mann
@@ -0,0 +1,2168 @@
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Stripchart widget created by Sani Nassif and George Howlett.
+'\"
+.so man.macros
+.TH stripchart n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+stripchart \- 2D strip chart for plotting x and y coordinate data.
+.SH SYNOPSIS
+\fBstripchart\fI \fIpathName \fR?\fIoption value\fR?...
+.BE
+.SH DESCRIPTION
+The \fBstripchart\fR command creates a strip chart for plotting
+two-dimensional data (x,y coordinates). It has many configurable
+components: coordinate axes, elements, legend, grid lines, cross
+hairs, etc. They allow you to customize the look and feel of the
+strip chart.
+.PP
+The \fBstripchart\fR is essentially the same as the \fBgraph\fR
+widget. It works almost exactly the very same way.
+.PP
+The use of a strip chart differs in that the X-axis typically refers
+to time points. Data values are added at intervals. The strip chart
+lets you automatically maintain a view of the most recent time points.
+The axis options \fB\-shiftby\fR and \fB\-autorange\fR control this.
+You can specify different line styles for data points (see the
+\fB\-styles\fR option).
+.SH INTRODUCTION
+The \fBstripchart\fR command creates a new window for plotting
+two-dimensional data (x,y coordinates). Data points are plotted in a
+box displayed in the center of the new window. This is the
+\fIplotting area\fR. The coordinate axes are displayed in the
+margins around the plotting area. By default, the legend is displayed
+in the right margin. The title is displayed in top margin.
+.PP
+A strip chart is composed of several components: coordinate axes, data
+elements, legend, grid, cross hairs, pens, postscript, and annotation
+markers.
+.TP 1i
+\f(CWaxis\fR
+The stripchart widget can display up to four coordinate axes (two
+X-coordinate and two Y-coordinate axes), but you can create and use
+any number of axes. Axes control what region of data is displayed and
+how the data is scaled. Each axis consists of the axis line, title,
+major and minor ticks, and tick labels. Tick labels display the value
+of each major tick.
+.TP 1i
+\f(CWcrosshairs\fR
+Cross hairs are used to finely position the mouse pointer in relation
+to the coordinate axes. Two perpendicular lines are drawn across the
+plotting area, intersecting at the current location of the mouse
+pointer.
+.TP 1i
+\f(CWelement\fR
+An element represents a set of data points. Elements can be plotted
+with a symbol at each data point and lines connecting the points.
+The appearance of the element, such as its symbol, line width, and
+color is configurable.
+.TP 1i
+\f(CWgrid\fR
+Extends the major and minor ticks of the X\-axis and/or Y\-axis across the
+plotting area.
+.TP 1i
+\f(CWlegend\fR
+The legend displays the name and symbol of each data element.
+The legend can be drawn in any margin or in the plotting area.
+.TP 1i
+\f(CWmarker\fR
+Markers are used annotate or highlight areas of the graph. For
+example, you could use a polygon marker to fill an area under a
+curve, or a text marker to label a particular data point. Markers
+come in various forms: text strings, bitmaps, connected line
+segments, images, polygons, or embedded widgets.
+.TP 1i
+\f(CWpen\fR
+Pens define attributes (both symbol and line style) for elements.
+Data elements use pens to specify how they should be drawn. A data
+element may use many pens at once. Here, the particular pen
+used for a data point is determined from each element's weight
+vector (see the element's \fB\-weight\fR and \fB\-style\fR options).
+.TP 1i
+\f(CWpostscript\fR
+The widget can generate encapsulated PostScript output. This component
+has several options to configure how the PostScript is generated.
+.SH SYNTAX
+.DS
+\fBstripchart \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBstripchart\fR command creates a new window \fIpathName\fR and makes
+it into a \fBstripchart\fR widget. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist. Additional options may may be specified on the
+command line or in the option database to configure aspects of the
+strip chart such as its colors and font. See the \fBconfigure\fR operation
+below for the exact details as to what \fIoption\fR and \fIvalue\fR
+pairs are valid.
+.PP
+If successful, \fBstripchart\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to perform various operations that query or modify the graph.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for the strip chart are described in
+the
+.SB "STRIPCHART OPERATIONS"
+section.
+.PP
+The command can also be used to access components of the strip chart.
+.DS
+\fIpathName component operation\fR ?\fIarg\fR?...
+.DE
+The operation, now located after the name of the component, is the
+function to be performed on that component. Each component has its own
+set of operations that manipulate that component. They will be
+described below in their own sections.
+.SH EXAMPLE
+The \fBstripchart\fR command creates a new strip chart.
+.CS
+# Create a new strip chart. Plotting area is black.
+stripchart .s -plotbackground black
+.CE
+A new Tcl command \f(CW.s\fR is also created. This command can be used
+to query and modify the strip chart. For example, to change the title of
+the strip chart to "My Plot", you use the new command and the widget's
+\fBconfigure\fR operation.
+.CS
+# Change the title.
+\&.s configure \-title "My Plot"
+.CE
+A strip chart has several components. To access a particular component you
+use the component's name. For example, to add data elements, you use
+the new command and the \fBelement\fR component.
+.CS
+# Create a new element named "line1"
+\&.s element create line1 \\
+ \-xdata { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 } \\
+ \-ydata { 26.18 50.46 72.85 93.31 111.86 128.47 143.14
+ 155.85 166.60 175.38 }
+.CE
+The element's X and Y coordinates are specified using lists of
+numbers. Alternately, BLT vectors could be used to hold the X\-Y
+coordinates.
+.CS
+# Create two vectors and add them to the strip chart.
+vector xVec yVec
+xVec set { 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 }
+yVec set { 26.18 50.46 72.85 93.31 111.86 128.47 143.14 155.85
+ 166.60 175.38 }
+\&.s element create line1 \-xdata xVec \-ydata yVec
+.CE
+The advantage of using vectors is that when you modify one, the graph
+is automatically redrawn to display the new values.
+.CS
+# Change the X\-Y coordinates of the first point.
+set xVec(0) 0.18
+set yVec(0) 25.18
+.CE
+An element named \f(CWline1\fR is now created in \f(CW.s\fR. By
+default, the element's label in the legend will be also \f(CWline1\fR.
+You can change the label, or specify no legend entry, again using the
+element's \fBconfigure\fR operation.
+.CS
+# Don't display "line1" in the legend.
+\&.s element configure line1 -label ""
+.CE
+You can configure more than just the element's label. An element has
+many attributes such as symbol type and size, dashed or solid lines,
+colors, line width, etc.
+.CS
+\&.s element configure line1 -symbol square -color red \\
+ -dashes { 2 4 2 } -linewidth 2 -pixels 2c
+.CE
+Four coordinate axes are automatically created: \f(CWx\fR, \f(CWx2\fR,
+\f(CWy\fR, and \f(CWy2\fR. And by default, elements are mapped onto the
+axes \f(CWx\fR and \f(CWy\fR. This can be changed with the \fB\-mapx\fR
+and \fB\-mapy\fR options.
+.CS
+# Map "line1" on the alternate Y-axis "y2".
+\&.s element configure line1 -mapy y2
+.CE
+Axes can be configured in many ways too. For example, you change the
+scale of the Y-axis from linear to log using the \fBaxis\fR operation.
+.CS
+# Y-axis is log scale.
+\&.s axis configure y -logscale yes
+.CE
+Axis limits are reset by simply specifying new axis
+limits using the \fB\-min\fR and \fB\-max\fR configuration options.
+.CS
+\&.s axis configure x -min 1.0 -max 1.5
+\&.s axis configure y -min 12.0 -max 55.15
+.CE
+By default, the limits of the axis are determined from data values.
+To reset back to the default limits, set the \fB\-min\fR and
+\fB\-max\fR options to the empty value.
+.CS
+# Reset the axes to autoscale again.
+\&.s axis configure x -min {} -max {}
+\&.s axis configure y -min {} -max {}
+.CE
+It's common with strip charts to automatically maintain a view of
+the most recent time points. You can do this my setting the
+\fB\-autorange\fR option.
+.CS
+\&.s axis configure x -autorange 20.0
+.CE
+If the time points are added in X-coordinates 1.0 unit, only the last
+twenty time points will be displayed. As more data
+is added, the view will march along.
+.PP
+Sometimes the rate of data is so high that changing the axis limits
+with each additional time point is prohibitive. You can use the
+\fB\-shiftby\fR option to define an increment to shift the view
+when needed.
+.CS
+\&.s axis configure x -shiftby 15.0
+.CE
+When the view is shifted, it will allow a range of 15
+new time points to be added until the axis limits are recomputed.
+.PP
+By default, the legend is displayed in the right margin. You can
+change this or any other legend configuration options using the
+\fBlegend\fR component.
+.CS
+# Configure the legend font, color, and relief
+\&.s legend configure -position left -relief raised \\
+ -font fixed -fg blue
+.CE
+To prevent the legend from being displayed, turn on the \fB\-hide\fR
+option.
+.CS
+# Don't display the legend.
+\&.s legend configure -hide yes\fR
+.CE
+The \fBstripchart\fR widget has simple drawing procedures called markers.
+They can be used to highlight or annotate data in the strip chart. The types
+of markers available are bitmaps, images, polygons, lines, or windows.
+Markers can be used, for example, to mark or brush points. Here
+is a text marker which labels the data first point. Markers
+are created using the \fBmarker\fR operation.
+.CS
+# Create a label for the first data point of "line1".
+\&.s marker create text \-name first_marker \-coords { 0.2 26.18 } \\
+ \-text "start" \-anchor se \-xoffset -10 \-yoffset -10
+.CE
+This creates a text marker named \f(CWfirst_marker\fR. It will display
+the text "start" near the coordinates of the first data point. The
+\fB\-anchor\fR, \fB\-xoffset\fR, and \fB\-yoffset\fR options are used
+to display the marker above and to the left of the data point, so that
+the actual data point isn't covered by the marker. By default,
+markers are drawn last, on top of data. You can change this with the
+\fB\-under\fR option.
+.CS
+# Draw the label before elements are drawn.
+\&.s marker configure first_marker -under yes
+.CE
+You can add cross hairs or grid lines using the \fBcrosshairs\fR and
+\fBgrid\fR operations.
+.CS
+# Display both cross hairs and grid lines.
+\&.s crosshairs configure \-hide no \-color red
+\&.s grid configure \-hide no \-dashes { 2 2 }
+.CE
+Finally, to get hardcopy of the strip chart, use the \fBpostscript\fR
+operation.
+.CS
+# Print the strip chart into file "file.ps"
+\&.s postscript output file.ps \-maxpect yes \-decorations no
+.CE
+This generates a file \f(CWfile.ps\fR containing the encapsulated
+PostScript of the strip chart. The option \fB\-maxpect\fR says to scale the
+plot to the size of the page. Turning off the \fB\-decorations\fR
+option indicates that no borders or color backgrounds should be
+displayed (i.e. the background of the margins, legend, and plotting
+area will be white).
+.SH "STRIPCHART OPERATIONS"
+.TP
+\fIpathName \fBaxis \fIoperation\fR ?\fIarg\fR?...
+See the
+.SB "AXIS COMPONENTS"
+section.
+.TP
+\fIpathName \fBbar \fIelemName \fR?\fIoption value\fR?...
+Creates a new barchart element \fIelemName\fR. It's an
+error if an element \fIelemName\fR already exists.
+See the manual for \fBbarchart\fR for details about
+what \fIoption\fR and \fIvalue\fR pairs are valid.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the stripchart configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBconfigure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the strip chart. If
+\fIoption\fR isn't specified, a list describing all of the current
+options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the stripchart option \fIoption\fR is set to \fIvalue\fR.
+The following options are valid for the stripchart.
+.RS
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color. This includes the margins and
+legend, but not the plotting area.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-bottommargin \fIpixels\fR
+Specifies the size of the margin below the X\-coordinate axis. If
+\fIpixels\fR is \f(CW0\fR, the size of the margin is selected automatically.
+The default is \f(CW0\fR.
+.TP
+\fB\-bufferelements \fIboolean\fR
+Indicates whether to draw elements into a pixmap before displaying
+them on the screen. The advantage of buffering elements is when markers
+are used heavily. Markers can be moved and redrawn without requiring
+every element to be redrawn again. The disadvantage is that it takes
+slightly longer to draw the graph. If \fIboolean\fR is true, data elements are
+drawn to an internal pixmap. The option should be turned off if the plot
+is updated frequently. See the
+.SB "SPEED TIPS"
+section.
+The default is \f(CW1\fR.
+.TP
+\fB\-buffergraph \fIboolean\fR
+Indicates whether to draw the graph into a pixmap first.
+If \fIboolean\fR is true, the entire graph is drawn into a pixmap and then
+copied onto the screen. This reduces flashing. If false, the graph is
+drawn directly into the window. Especially under Windows, turning off the
+option can be helpful when the stripchart is updated frequently. Turning
+off this option also turns \fB\-bufferelements\fR off. See the
+.SB "SPEED TIPS"
+section.
+The default is \f(CW1\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default
+cursor is \f(CWcrosshair\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the title font. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-18-180-*\fR.
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a maximum distance to consider when searching for the
+closest data point (see the element's \fBclosest\fR operation below).
+Data points further than \fIpixels\fR away are ignored. The default is
+\f(CW0.5i\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW4i\fR.
+.TP
+\fB\-invertxy \fIboolean\fR
+Indicates whether the placement X\-axis and Y\-axis should be inverted. If
+\fIboolean\fR is true, the X and Y axes are swapped. The default is
+\f(CW0\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the title should be justified. This matters only when
+the title contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-leftmargin \fIpixels\fR
+Sets the size of the margin from the left edge of the window to
+the Y\-coordinate axis. If \fIpixels\fR is \f(CW0\fR, the size is
+calculated automatically. The default is \f(CW0\fR.
+.TP
+\fB\-plotbackground \fIcolor\fR
+Specifies the background color of the plotting area. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-plotborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the plotting area. The
+\fB\-plotrelief\fR option determines if a border is drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-plotpadx \fIpad\fR
+Sets the amount of padding to be added to the left and right sides of
+the plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the left side of the
+plotting area entry is padded by the first distance and the right side
+by the second. If \fIpad\fR is just one distance, both the left and
+right sides are padded evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotpady \fIpad\fR
+Sets the amount of padding to be added to the top and bottom of the
+plotting area. \fIPad\fR can be a list of one or two screen
+distances. If \fIpad\fR has two elements, the top of the plotting
+area is padded by the first distance and the bottom by the second. If
+\fIpad\fR is just one distance, both the top and bottom are padded
+evenly. The default is \f(CW8\fR.
+.TP
+\fB\-plotrelief \fIrelief\fR
+Specifies the 3-D effect for the plotting area. \fIRelief\fR
+indicates how the interior of the plotting area should appear relative
+to rest of the strip chart; for example, \f(CWraised\fR means the plot should
+appear to protrude from the strip chart, relative to the surface of the
+strip chart. The default is \f(CWsunken\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the widget. \fIRelief\fR
+indicates how the strip chart should appear relative to widget it is packed
+into; for example, \f(CWraised\fR means the strip chart should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-rightmargin \fIpixels\fR
+Sets the size of margin from the plotting area to the right edge of
+the window. By default, the legend is displayed in this margin. If
+\fIpixels\fR is than 1, the margin size is selected automatically.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW""\fR.
+.TP
+\fB\-tile \fIimage\fR
+Specifies a tiled background. If \fIimage\fR isn't
+\f(CW""\fR, the background is tiled using \fIimage\fR.
+Otherwise, the normal background color is drawn (see the
+\fB\-background\fR option). \fIImage\fR must be an image created
+using the Tk \fBimage\fR command. The default is \f(CW""\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title to \fItext\fR. If \fItext\fR is \f(CW""\fR,
+no title will be displayed.
+.TP
+\fB\-topmargin \fIpixels\fR
+Specifies the size of the margin above the x2 axis. If \fIpixels\fR
+is \f(CW0\fR, the margin size is calculated automatically.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the requested width of the widget. The default is
+\f(CW5i\fR.
+.RE
+.TP
+\fIpathName \fBcrosshairs \fIoperation \fR?\fIarg\fR?
+See the
+.SB "CROSSHAIRS COMPONENT"
+section.
+.TP
+\fIpathName \fBelement \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "ELEMENT COMPONENTS"
+section.
+.TP
+\fIpathName \fBextents \fIitem\fR
+Returns the size of a particular item in the strip chart. \fIItem\fR must
+be either \f(CWleftmargin\fR, \f(CWrightmargin\fR, \f(CWtopmargin\fR,
+\f(CWbottommargin\fR, \f(CWplotwidth\fR, or \f(CWplotheight\fR.
+.TP
+\fIpathName \fBgrid \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "GRID COMPONENT"
+section.
+.TP
+\fIpathName \fBinvtransform \fIwinX winY\fR
+Performs an inverse coordinate transformation, mapping window
+coordinates back to graph coordinates, using the standard X\-axis and Y\-axis.
+Returns a list of containing the graph coordinates.
+.TP
+\fIpathName \fBlegend \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "LEGEND COMPONENT"
+section.
+.TP
+\fIpathName \fBline \fIelemName\fR ?\fIoption value\fR?...
+The operation is the same as \fBelement\fR.
+.TP
+\fIpathName \fBmarker \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "MARKER COMPONENTS"
+section.
+.TP
+\fIpathName\fR \fBmetafile\fR ?\fIfileName\fR?
+\fIThis operation is for Window platforms only\fR.
+Creates a Windows enhanced metafile of the stripchart.
+If present, \fIfileName\fR is the file name of the new metafile.
+Otherwise, the metafile is automatically added to the clipboard.
+.TP
+\fIpathName \fBpostscript \fIoperation \fR?\fIarg\fR?...
+See the
+.SB "POSTSCRIPT COMPONENT"
+section.
+.TP
+\fIpathName \fBsnap \fIphotoName\fR
+Takes a snapshot of the strip chart and stores the contents in the photo
+image \fIphotoName\fR. \fIPhotoName\fR is the name of a Tk photo
+image that must already exist.
+.TP
+\fIpathName \fBtransform \fIx y\fR
+Performs a coordinate transformation, mapping graph coordinates to
+window coordinates, using the standard X\-axis and Y\-axis.
+Returns a list containing the X\-Y screen coordinates.
+.TP
+\fIpathName \fBxaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBx2axis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fByaxis \fIoperation\fR ?\fIarg\fR?...
+.TP
+\fIpathName \fBy2axis \fIoperation\fR ?\fIarg\fR?...
+See the
+.SB "AXIS COMPONENTS"
+section.
+.SH "STRIPCHART COMPONENTS"
+A strip chart is composed of several components: coordinate axes, data
+elements, legend, grid, cross hairs, postscript, and annotation
+markers. Instead of one big set of configuration options and
+operations, the strip chart is partitioned, where each component has its own
+configuration options and operations that specifically control that
+aspect or part of the strip chart.
+.SS "AXIS COMPONENTS"
+Four coordinate axes are automatically created: two X\-coordinate axes
+(\f(CWx\fR and \f(CWx2\fR) and two Y\-coordinate axes (\f(CWy\fR, and
+\f(CWy2\fR). By default, the axis \f(CWx\fR is located in the bottom
+margin, \f(CWy\fR in the left margin, \f(CWx2\fR in the top margin, and
+\f(CWy2\fR in the right margin.
+.PP
+An axis consists of the axis line, title, major and minor ticks, and
+tick labels. Major ticks are drawn at uniform intervals along the
+axis. Each tick is labeled with its coordinate value. Minor ticks
+are drawn at uniform intervals within major ticks.
+.PP
+The range of the axis controls what region of data is plotted.
+Data points outside the minimum and maximum limits of the axis are
+not plotted. By default, the minimum and maximum limits are
+determined from the data, but you can reset either limit.
+.PP
+You can create and use several axes. To create an axis, invoke
+the axis component and its create operation.
+.CS
+# Create a new axis called "temperature"
+\&.s axis create temperature
+.CE
+You map data elements to an axis using the element's \-mapy and \-mapx
+configuration options. They specify the coordinate axes an element
+is mapped onto.
+.CS
+# Now map the temperature data to this axis.
+\&.s element create "temp" \-xdata $x \-ydata $tempData \\
+ \-mapy temperature
+.CE
+While you can have many axes, only four axes can be displayed
+simultaneously. They are drawn in each of the margins surrounding the
+plotting area. The axes x and y are drawn in the bottom and left
+margins. The axes x2 and y2 are drawn in top and right margins.
+Only x and y are shown by default. Note that the axes can have
+different scales.
+.PP
+To display a different axis, you invoke one of the following
+components: \fBxaxis\fR, \fByaxis\fR, \fBx2axis\fR, and \fBy2axis\fR.
+The \fBuse\fR operation designates the axis to be drawn in the
+corresponding margin: \fBxaxis\fR in the bottom, \fByaxis\fR in the left,
+\fBx2axis\fR in the top, and \fBy2axis\fR in the right.
+.CS
+# Display the axis temperature in the left margin.
+\&.s yaxis use temperature
+.CE
+.PP
+You can configure axes in many ways. The axis scale can be linear or
+logarithmic. The values along the axis can either monotonically
+increase or decrease. If you need custom tick labels, you can specify
+a Tcl procedure to format the label as you wish. You can
+control how ticks are drawn, by changing the major tick interval
+or the number of minor ticks. You can define non-uniform tick intervals,
+such as for time-series plots.
+.PP
+.TP
+\fIpathName \fBaxis \fBcget \fIaxisName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIaxisName\fR. \fIOption\fR may be any option described below
+for the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBconfigure \fIaxisName \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of \fIaxisName\fR. If
+\fIoption\fR isn't specified, a list describing all the current
+options for \fIaxisName\fR is returned. If \fIoption\fR is specified, but
+not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the axis option \fIoption\fR is set to
+\fIvalue\fR.
+The following options are valid for axes.
+.RS
+.TP
+\fB\-autorange \fIrange\fR
+Sets the range of values for the axis to \fIrange\fR. The axis limits
+are automatically reset to display the most recent data points in this range.
+If \fIrange\fR is 0.0, the range is
+determined from the limits of the data. If \fB\-min\fR or \fB-max\fR
+are specified, they override this option. The default is \f(CW0.0\fR.
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the axis and tick labels.
+The default is \f(CWblack\fR.
+.TP
+\fB\-command \fIprefix\fR
+Specifies a Tcl command to be invoked when formatting the axis tick
+labels. \fIPrefix\fR is a string containing the name of a Tcl proc and
+any extra arguments for the procedure. This command is invoked for each
+major tick on the axis. Two additional arguments are passed to the
+procedure: the pathname of the widget and the current the numeric
+value of the tick. The procedure returns the formatted tick label. If
+\f(CW""\fR is returned, no label will appear next to the tick. You can
+get the standard tick labels again by setting \fIprefix\fR to
+\f(CW""\fR. The default is \f(CW""\fR.
+.sp 1
+Please note that this procedure is invoked while the strip chart is redrawn.
+You may query the configuration options. But do not reset them, because
+this can have unexpected results.
+.TP
+\fB\-descending \fIboolean\fR
+Indicates whether the values along the axis are monotonically increasing or
+decreasing. If \fIboolean\fR is true, the axis values will be
+decreasing. The default is \f(CW0\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the axis is displayed.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the axis title should be justified. This matters only
+when the axis title contains more than one line of text. \fIJustify\fR
+must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-limits \fIformatStr\fR
+Specifies a printf-like description to format the minimum and maximum
+limits of the axis. The limits are displayed at the top/bottom or
+left/right sides of the plotting area. \fIFormatStr\fR is a list of
+one or two format descriptions. If one description is supplied, both
+the minimum and maximum limits are formatted in the same way. If two,
+the first designates the format for the minimum limit, the second for
+the maximum. If \f(CW""\fR is given as either description, then
+the that limit will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the axis and tick lines. The default is \f(CW1\fR
+pixel.
+.TP
+\fB\-logscale \fIboolean\fR
+Indicates whether the scale of the axis is logarithmic or linear. If
+\fIboolean\fR is true, the axis is logarithmic. The default scale is
+linear.
+.TP
+\fB\-loose \fIboolean\fR
+Indicates whether the limits of the axis should fit the data points tightly,
+at the outermost data points, or loosely, at the outer tick intervals.
+This is relevant only when the axis limit is automatically calculated.
+If \fIboolean\fR is true, the axis range is "loose".
+The default is \f(CW0\fR.
+.TP
+\fB\-majorticks \fImajorList\fR
+Specifies where to display major axis ticks. You can use this option
+to display ticks at non-uniform intervals. \fIMajorList\fR is a list
+of axis coordinates designating the location of major ticks. No
+minor ticks are drawn. If \fImajorList\fR is \f(CW""\fR,
+major ticks will be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-max \fIvalue\fR
+Sets the maximum limit of \fIaxisName\fR. Any data point greater
+than \fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the maximum limit is calculated using the largest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-min \fIvalue\fR
+Sets the minimum limit of \fIaxisName\fR. Any data point less than
+\fIvalue\fR is not displayed. If \fIvalue\fR is \f(CW""\fR,
+the minimum limit is calculated using the smallest data value.
+The default is \f(CW""\fR.
+.TP
+\fB\-minorticks \fIminorList\fR
+Specifies where to display minor axis ticks. You can use this option
+to display minor ticks at non-uniform intervals. \fIMinorList\fR is a
+list of real values, ranging from 0.0 to 1.0, designating the placement of
+a minor tick. No minor ticks are drawn if the \fB\-majortick\fR
+option is also set. If \fIminorList\fR is \f(CW""\fR, minor ticks will
+be automatically computed. The default is \f(CW""\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the how many degrees to rotate the axis tick labels.
+\fITheta\fR is a real value representing the number of degrees
+to rotate the tick labels. The default is \f(CW0.0\fR degrees.
+.TP
+\fB\-shiftby \fIvalue\fR
+Specifies how much to automatically shift the range of the axis.
+When the new data exceeds the current axis maximum, the maximum
+is increased in increments of \fIvalue\fR. You can use this
+option to prevent the axis limits from being recomputed
+at each new time point. If \fIvalue\fR is 0.0, then no automatic
+shifting is done. The default is \f(CW0.0\fR.
+.TP
+\fB\-showticks \fIboolean\fR
+Indicates whether axis ticks should be drawn. If \fIboolean\fR is
+true, ticks are drawn. If false, only the
+axis line is drawn. The default is \f(CW1\fR.
+.TP
+\fB\-stepsize \fIvalue\fR
+Specifies the interval between major axis ticks. If \fIvalue\fR isn't
+a valid interval (must be less than the axis range),
+the request is ignored and the step size is automatically calculated.
+.TP
+\fB\-subdivisions \fInumber\fR
+Indicates how many minor axis ticks are
+to be drawn. For example, if \fInumber\fR is two, only one minor
+tick is drawn. If \fInumber\fR is one, no minor ticks are
+displayed. The default is \f(CW2\fR.
+.TP
+\fB\-tickfont \fIfontName\fR
+Specifies the font for axis tick labels. The default is
+\f(CW*-Courier-Bold-R-Normal-*-100-*\fR.
+.TP
+\fB\-ticklength \fIpixels\fR
+Sets the length of major and minor ticks (minor ticks are half the
+length of major ticks). If \fIpixels\fR is less than zero, the axis
+will be inverted with ticks drawn pointing towards the plot. The
+default is \f(CW0.1i\fR.
+.TP
+\fB\-title \fItext\fR
+Sets the title of the axis. If \fItext\fR is
+\f(CW""\fR, no axis title will be displayed.
+.TP
+\fB\-titlecolor \fIcolor\fR
+Sets the color of the axis title. The default is \f(CWblack\fR.
+.TP
+\fB\-titlefont \fIfontName\fR
+Specifies the font for axis title. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-14-140-*\fR.
+.PP
+Axis configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWAxis\fR. The resource names
+are the names of the axes (such as \f(CWx\fR or \f(CWx2\fR).
+.CS
+option add *Stripchart.Axis.Color blue
+option add *Stripchart.x.LogScale true
+option add *Stripchart.x2.LogScale false
+.CE
+.RE
+.TP
+\fIpathName \fBaxis \fBcreate \fIaxisName \fR?\fIoption value\fR?...
+Creates a new axis by the name \fIaxisName\fR. No axis by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the axis \fBconfigure\fR operation.
+.TP
+\fIpathName \fBaxis \fBdelete \fR?\fIaxisName\fR?...
+Deletes the named axes. An axis is not really
+deleted until it is not longer in use, so it's safe to delete
+axes mapped to elements.
+.TP
+\fIpathName \fBaxis invtransform \fIaxisName value\fR
+Performs the inverse transformation, changing the screen coordinate
+\fIvalue\fR to a graph coordinate, mapping the value mapped to
+\fIaxisName\fR. Returns the graph coordinate.
+.TP
+\fIpathName \fBaxis limits \fIaxisName\fR
+Returns a list of the minimum and maximum limits for \fIaxisName\fR. The order
+of the list is \f(CWmin max\fR.
+.TP
+\fIpathName \fBaxis names \fR?\fIpattern\fR?...
+Returns a list of axes matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all axes are returned.
+.TP
+\fIpathName \fBaxis transform \fIaxisName value\fR
+Transforms the coordinate \fIvalue\fR to a screen coordinate by mapping
+the it to \fIaxisName\fR. Returns the transformed screen coordinate.
+.PP
+Only four axes can be displayed simultaneously. By default, they are
+\f(CWx\fR, \f(CWy\fR, \f(CWx2\fR, and \f(CWy2\fR. You can swap in a different
+axis with \fBuse\fR operation of the special axis components:
+\fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR.
+.CS
+\&.g create axis temp
+\&.g create axis time
+\&...
+\&.g xaxis use temp
+\&.g yaxis use time
+.CE
+Only the axes specified for use are displayed on the screen.
+.PP
+The \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, and \fBy2axis\fR
+components operate on an axis location rather than a specific axis
+like the more general \fBaxis\fR component does. The \fBxaxis\fR
+component manages the X-axis located in the bottom margin (whatever
+axis that happens to be). Likewise, \fByaxis\fR uses the Y-axis in
+the left margin, \fBx2axis\fR the top X-axis, and \fBy2axis\fR the
+right Y-axis.
+.PP
+They implicitly control the axis that is currently using to that
+location. By default, \fBxaxis\fR uses the \f(CWx\fR axis, \fByaxis\fR
+uses \f(CWy\fR, \fBx2axis\fR uses \f(CWx2\fR, and \fBy2axis\fR uses
+\f(CWy2\fR. These components can be more convenient to use than always
+determining what axes are current being displayed by the graph.
+.PP
+The following operations are available for axes. They mirror exactly
+the operations of the \fBaxis\fR component. The \fIaxis\fR argument
+must be \fBxaxis\fR, \fBx2axis\fR, \fByaxis\fR, or \fBy2axis\fR.
+.TP
+\fIpathName \fIaxis \fBcget \fIoption\fR
+.TP
+\fIpathName \fIaxis \fBconfigure \fR?\fIoption value\fR?...
+.TP
+\fIpathName \fIaxis\fB invtransform \fIvalue\fR
+.TP
+\fIpathName \fIaxis \fBlimits\fR
+.TP
+\fIpathName \fIaxis\fB transform \fIvalue\fR
+.TP
+\fIpathName \fIaxis\fB use \fR?\fIaxisName\fR?
+Designates the axis \fIaxisName\fR is to be displayed at this
+location. \fIAxisName\fR can not be already in use at another location.
+This command returns the name of the axis currently using this location.
+.SS "CROSSHAIRS COMPONENT"
+Cross hairs consist of two intersecting lines (one vertical and one horizontal)
+drawn completely across the plotting area. They are used to position
+the mouse in relation to the coordinate axes. Cross hairs differ from line
+markers in that they are implemented using XOR drawing primitives.
+This means that they can be quickly drawn and erased without redrawing
+the entire strip chart.
+.PP
+The following operations are available for cross hairs:
+.TP
+\fIpathName \fBcrosshairs cget \fIoption\fR
+Returns the current value of the cross hairs configuration option
+given by \fIoption\fR. \fIOption\fR may be any option
+described below for the cross hairs \fBconfigure\fR operation.
+.TP
+\fIpathName \fBcrosshairs configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of the cross hairs. If
+\fIoption\fR isn't specified, a list describing all the current
+options for the cross hairs is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the cross hairs option \fIoption\fR is set to
+\fIvalue\fR.
+The following options are available for cross hairs.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the cross hairs. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the cross hairs. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the cross hair lines. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the cross hairs will be solid
+lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether cross hairs are drawn. If \fIboolean\fR is true,
+cross hairs are not drawn. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the cross hair lines. The default is \f(CW1\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies the screen position where the cross hairs intersect.
+\fIPos\fR must be in the form "\fI@x,y\fR", where \fIx\fR and \fIy\fR
+are the window coordinates of the intersection.
+.PP
+Cross hairs configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWcrosshairs\fR and \f(CWCrosshairs\fR respectively.
+.CS
+option add *Stripchart.Crosshairs.LineWidth 2
+option add *Stripchart.Crosshairs.Color red
+.CE
+.RE
+.TP
+\fIpathName \fBcrosshairs off\fR
+Turns of the cross hairs.
+.TP
+\fIpathName \fBcrosshairs on\fR
+Turns on the display of the cross hairs.
+.TP
+\fIpathName \fBcrosshairs toggle\fR
+Toggles the current state of the cross hairs, alternately mapping and
+unmapping the cross hairs.
+.SS "ELEMENT COMPONENTS"
+A data element represents a set of data. It contains x and y vectors
+containing the coordinates of the data points. Elements can be
+displayed with a symbol at each data point and lines connecting the
+points. Elements also control the appearance of the data, such as the
+symbol type, line width, color etc.
+.PP
+When new data elements are created, they are automatically added to a
+list of displayed elements. The display list controls what elements
+are drawn and in what order.
+.PP
+The following operations are available for elements.
+.TP
+\fIpathName \fBelement activate \fIelemName \fR?\fIindex\fR?...
+Specifies the data points of element \fIelemName\fR to be drawn
+using active foreground and background colors. \fIElemName\fR is the
+name of the element and \fIindex\fR is a number representing the index
+of the data point. If no indices are present then all data points
+become active.
+.TP
+\fIpathName \fBelement cget \fIelemName \fIoption\fR
+Returns the current value of the element configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described below
+for the element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement closest \fIx y\fR \fIvarName\fR ?\fIoption value\fR?... ?\fIelemName\fR?...
+Finds the data point closest to the window coordinates \fIx\fR and
+\fIy\fR in the element \fIelemName\fR. \fIElemName\fR is the name of
+an element, that must not be hidden. If no elements are specified,
+then all visible elements are searched. It returns via the array
+variable \fIvarName\fR the name of the closest element, the index of
+its closest point, and the graph coordinates of the point. Returns
+\f(CW0\fR, if no data point within the threshold distance can be found,
+otherwise \f(CW1\fR is returned. The following
+\fIoption\fR\-\fIvalue\fR pairs are available.
+.RS
+.TP
+\fB\-halo \fIpixels\fR
+Specifies a threshold distance where selected data points are ignored.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+If this option isn't specified, then it defaults to the value of the
+stripchart's \fB\-halo\fR option.
+.TP
+\fB\-interpolate \fIboolean\fR
+Indicates that both the data points and interpolated points along
+the line segment formed should be considered. If \fIboolean\fR
+is true, the closest line segment will be selected instead of the
+closest point. If this option isn't specified, \fIboolean\fR defaults
+to \f(CW0\fR.
+.RE
+.TP
+\fIpathName \fBelement configure \fIelemName \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for elements. If
+\fIoption\fR isn't specified, a list describing all the current
+options for \fIelemName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing the option \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the element option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for elements.
+.RS
+.TP
+\fB\-activepen \fIpenName\fR
+Specifies pen to use to draw active element. If \fIpenName\fR is
+\f(CW""\fR, no active elements will be drawn. The default is
+\f(CWactiveLine\fR.
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the traces connecting the data points.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of element line. \fIDashList\fR is a list of up to
+11 numbers that alternately represent the lengths of the dashes and
+gaps on the element line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the lines will be solid.
+.TP
+\fB\-data \fIcoordList\fR
+Specifies the X\-Y coordinates of the data. \fICoordList\fR is a
+list of numeric expressions representing the X\-Y coordinate pairs
+of each data point.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the interior color of symbols. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the symbol is transparent. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the element is displayed. The default is \f(CWno\fR.
+.TP
+\fB\-label \fItext\fR
+Sets the element's label in the legend. If \fItext\fR
+is \f(CW""\fR, the element will have no entry in the legend.
+The default label is the element's name.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the connecting lines between data points. If
+\fIpixels\fR is \f(CW0\fR, no connecting lines will be drawn between
+symbols. The default is \f(CW0\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Selects the X\-axis to map the element's X\-coordinates onto.
+\fIXAxis\fR must be the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Selects the Y\-axis to map the element's Y\-coordinates onto.
+\fIYAxis\fR must be the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-offdash \fIcolor\fR
+Sets the color of the stripes when traces are dashed (see the
+\fB\-dashes\fR option). If \fIcolor\fR is \f(CW""\fR, then the "off"
+pixels will represent gaps instead of stripes. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color or the outline around each symbol. If \fIcolor\fR is
+\f(CW""\fR, then no outline is drawn. If \fIcolor\fR is \f(CWdefcolor\fR,
+then the color will be the same as the \fB\-color\fR option. The
+default is \f(CWdefcolor\fR.
+.TP
+\fB\-outlinewidth \fIpixels\fR
+Sets the width of the outline bordering each symbol. If \fIpixels\fR
+is \f(CW0\fR, no outline will be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-pixels \fIpixels\fR
+Sets the size of symbols. If \fIpixels\fR is \f(CW0\fR, no symbols will
+be drawn. The default is \f(CW0.125i\fR.
+.TP
+\fB\-scalesymbols \fIboolean\fR
+If \fIboolean\fR is true, the size of the symbols
+drawn for \fIelemName\fR will change with scale of the X\-axis and Y\-axis.
+At the time this option is set, the current ranges of the axes are
+saved as the normalized scales (i.e scale factor is 1.0) and the
+element is drawn at its designated size (see the \fB\-pixels\fR
+option). As the scale of the axes change, the symbol will be scaled
+according to the smaller of the X\-axis and Y\-axis scales. If \fIboolean\fR
+is false, the element's symbols are drawn at the designated size,
+regardless of axis scales. The default is \f(CW0\fR.
+.TP
+\fB\-smooth \fIsmooth\fR
+Specifies how connecting line segments are drawn between data points.
+\fISmooth\fR can be either \f(CWlinear\fR, \f(CWstep\fR, \f(CWnatural\fR, or
+\f(CWquadratic\fR. If \fIsmooth\fR is \f(CWlinear\fR, a single line
+segment is drawn, connecting both data points. When \fIsmooth\fR is
+\f(CWstep\fR, two line segments are drawn. The first is a horizontal
+line segment which steps the next x-coordinate. The second is a
+vertical line, moving to the next y-coordinate. Both \fInatural\fR and
+\fIquadratic\fR generate multiple segments between data points. If
+\fInatural\fR, the segments are generated using a cubic spline. If
+\fIquadratic\fR, a quadratic spline is used. The default is
+\fIlinear\fR.
+.TP
+\fB\-styles \fIstyleList\fR
+Specifies what pen to use based upon the range of weights given.
+\fIStyleList\fR is a list of style specifications. Each style
+specification, in turn, is a list consisting of a pen name, and
+optionally a minimum and maximum range. Data points whose weight (see
+the \fB\-weight\fR option) falls in this range, are drawn with this
+pen. If no range is specified it defaults to the number of the pen in
+the list.
+.TP
+\fB\-symbol \fIsymbol\fR
+Specifies the symbol for data points. \fISymbol\fR can be either
+\f(CWsquare\fR, \f(CWcircle\fR, \f(CWdiamond\fR, \f(CWplus\fR, \f(CWcross\fR,
+\f(CWsplus\fR, \f(CWscross\fR, \f(CWtriangle\fR, \f(CW""\fR (where no symbol
+is drawn), or a bitmap. Bitmaps are specified as "\fIsource\fR
+?\fImask\fR?", where \fIsource\fR is the name of the bitmap, and
+\fImask\fR is the bitmap's optional mask. The default is
+\f(CWcircle\fR.
+.TP
+\fB\-weights \fIwVec\fR
+Specifies the weights of the individual data points. This, in
+conjunction with the list pen styles (see the \fB\-styles\fR option)
+controls how data points are drawn. \fIWVec\fR is the name of a BLT
+vector or a list of numeric expressions representing the weights for
+each data point.
+.TP
+\fB\-xdata \fIxVec\fR
+Specifies the x-coordinates of the data. \fIXVec\fR is the name of
+a BLT vector or a list of numeric expressions.
+.TP
+\fB\-ydata \fIyVec\fR
+Specifies the y-coordinates of the data. \fIYVec\fR is the name of
+a BLT vector or a list of numeric expressions.
+.PP
+Element configuration options may also be set by the \fBoption\fR
+command. The resource class is \f(CWElement\fR. The resource name is
+the name of the element.
+.CS
+option add *Stripchart.Element.symbol line
+option add *Stripchart.e1.symbol line
+.CE
+.RE
+.TP
+\fIpathName \fBelement create \fIelemName\fR ?\fIoption value\fR?...
+Creates a new element \fIelemName\fR. It's an error is
+an element \fIelemName\fR already exists. If
+additional arguments are present, they specify options valid for
+element \fBconfigure\fR operation.
+.TP
+\fIpathName \fBelement deactivate \fIelemName\fR ?\fIelemName\fR?...
+Deactivates all the elements matching \fIpattern\fR.
+Elements whose names match any of the patterns given are redrawn using
+their normal colors.
+.TP
+\fIpathName \fBelement delete\fR ?\fIelemName\fR?...
+Deletes all the named elements. The graph is automatically redrawn.
+.TP
+\fIpathName \fBelement exists \fIelemName\fR
+Returns \f(CW1\fR if an element \fIelemName\fR currently exists and \f(CW0\fR otherwise.
+.TP
+\fIpathName \fBelement names \fR?\fIpattern\fR?...
+Returns the elements matching one or more pattern. If no
+\fIpattern\fR is given, the names of all elements is returned.
+.TP
+\fIpathName \fBelement show\fR ?\fInameList\fR?
+Queries or modifies the element display list. The element display
+list designates the elements drawn and in what
+order. \fINameList\fR is a list of elements to be displayed in the
+order they are named. If there is no \fInameList\fR argument,
+the current display list is returned.
+.TP
+\fIpathName \fBelement type\fR \fIelemName\fR
+Returns the type of \fIelemName\fR.
+If the element is a bar element, the commands returns the string
+\f(CW"bar"\fR, otherwise it returns \f(CW"line"\fR.
+.CE
+.SS "GRID COMPONENT"
+Grid lines extend from the major and minor ticks of each axis
+horizontally or vertically across the plotting area. The following
+operations are available for grid lines.
+.TP
+\fIpathName \fBgrid cget \fIoption\fR
+Returns the current value of the grid line configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described below
+for the grid \fBconfigure\fR operation.
+.TP
+\fIpathName \fBgrid configure\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for grid lines. If
+\fIoption\fR isn't specified, a list describing all the current
+grid options for \fIpathName\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the grid line option \fIoption\fR is set to
+\fIvalue\fR. The following options are valid for grid lines.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the grid lines. The default is \f(CWblack\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the grid lines. \fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the grid lines. Each number must be between 1 and 255.
+If \fIdashList\fR is \f(CW""\fR, the grid will be solid lines.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the grid should be drawn. If \fIboolean\fR
+is true, grid lines are not shown. The default is \f(CWyes\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of grid lines. The default width is \f(CW1\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to display grid lines. \fIXAxis\fR
+must be the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to display grid lines. \fIYAxis\fR
+must be the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-minor \fIboolean\fR
+Indicates whether the grid lines should be drawn for minor ticks.
+If \fIboolean\fR is true, the lines will appear at
+minor tick intervals. The default is \f(CW1\fR.
+.PP
+Grid configuration options may also be set by the
+\fBoption\fR command. The resource name and class are \f(CWgrid\fR and
+\f(CWGrid\fR respectively.
+.CS
+option add *Stripchart.grid.LineWidth 2
+option add *Stripchart.Grid.Color black
+.CE
+.RE
+.TP
+\fIpathName \fBgrid off\fR
+Turns off the display the grid lines.
+.TP
+\fIpathName \fBgrid on\fR
+Turns on the display the grid lines.
+.TP
+\fIpathName \fBgrid toggle\fR
+Toggles the display of the grid.
+.SS "LEGEND COMPONENT"
+The legend displays a list of the data elements. Each entry consists
+of the element's symbol and label. The legend can appear in any
+margin (the default location is in the right margin). It
+can also be positioned anywhere within the plotting area.
+.PP
+The following operations are valid for the legend.
+.TP
+\fIpathName \fBlegend activate \fIpattern\fR...
+Selects legend entries to be drawn using the active legend colors and relief.
+All entries whose element names match \fIpattern\fR are selected. To
+be selected, the element name must match only one \fIpattern\fR.
+.TP
+\fIpathName \fBlegend cget \fIoption\fR
+Returns the current value of a legend configuration option.
+\fIOption\fR may be any option described below in the
+legend \fBconfigure\fR operation.
+.TP
+\fIpathName \fBlegend configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for the legend. If
+\fIoption\fR isn't specified, a list describing the current
+legend options for \fIpathName\fR is returned. If \fIoption\fR is
+specified, but not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the legend option \fIoption\fR is set
+to \fIvalue\fR. The following options are valid for the legend.
+.RS
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active legend entries. All legend
+entries marked active (see the legend \fBactivate\fR operation) are
+drawn using this background color.
+.TP
+\fB\-activeborderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the active legend
+entries. The default is \f(CW2\fR.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color for active legend entries. All legend
+entries marked as active (see the legend \fBactivate\fR operation) are
+drawn using this foreground color.
+.TP
+\fB\-activerelief \fIrelief\fR
+Specifies the 3-D effect desired for active legend entries.
+\fIRelief\fR denotes how the interior of the entry should appear
+relative to the legend; for example, \f(CWraised\fR means the entry
+should appear to protrude from the legend, relative to the surface of
+the legend. The default is \f(CWflat\fR.
+.TP
+\fB\-anchor \fIanchor\fR
+Tells how to position the legend relative to the positioning point for
+the legend. This is dependent on the value of the \fB\-position\fR
+option. The default is \f(CWcenter\fR.
+.RS
+.TP 1.25i
+\f(CWleft\fR or \f(CWright\fR
+The anchor describes how to position the legend vertically.
+.TP
+\f(CWtop\fR or \f(CWbottom\fR
+The anchor describes how to position the legend horizontally.
+.TP
+\f(CW@x,y\fR
+The anchor specifies how to position the legend relative to the
+positioning point. For example, if \fIanchor\fR is \f(CWcenter\fR then
+the legend is centered on the point; if \fIanchor\fR is \f(CWn\fR then
+the legend will be drawn such that the top center point of the
+rectangular region occupied by the legend will be at the positioning
+point.
+.TP
+\f(CWplotarea\fR
+The anchor specifies how to position the legend relative to the
+plotting area. For example, if \fIanchor\fR is \f(CWcenter\fR then the
+legend is centered in the plotting area; if \fIanchor\fR is \f(CWne\fR
+then the legend will be drawn such that occupies the upper right
+corner of the plotting area.
+.RE
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the legend. If \fIcolor\fR is \f(CW""\fR,
+the legend background with be transparent.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3-D border around the outside edge of the legend (if
+such border is being drawn; the \fBrelief\fR option determines this).
+The default is \f(CW2\fR pixels.
+.TP
+\fB\-font \fIfontName\fR
+\fIFontName\fR specifies a font to use when drawing the labels of each
+element into the legend. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of the text drawn for the element's label.
+The default is \f(CWblack\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the legend should be displayed. If \fIboolean\fR is
+true, the legend will not be draw. The default is \f(CWno\fR.
+.TP
+\fB\-ipadx \fIpad\fR
+Sets the amount of internal padding to be added to the width of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the left side of the legend entry is
+padded by the first distance and the right side by the second. If
+\fIpad\fR is just one distance, both the left and right sides are padded
+evenly. The default is \f(CW2\fR.
+.TP
+\fB\-ipady \fIpad\fR
+Sets an amount of internal padding to be added to the height of each
+legend entry. \fIPad\fR can be a list of one or two screen distances. If
+\fIpad\fR has two elements, the top of the entry is padded by the
+first distance and the bottom by the second. If \fIpad\fR is just
+one distance, both the top and bottom of the entry are padded evenly.
+The default is \f(CW2\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the legend.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the legend is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the legend. \fIPad\fR can be a list
+of one or two screen distances. If \fIpad\fR has two elements, the area above
+the legend is padded by the first distance and the area below by the
+second. If \fIpad\fR is just one distance, both the top and
+bottom areas are padded evenly. The default is \f(CW0\fR.
+.TP
+\fB\-position \fIpos\fR
+Specifies where the legend is drawn. The
+\fB\-anchor\fR option also affects where the legend is positioned. If
+\fIpos\fR is \f(CWleft\fR, \f(CWleft\fR, \f(CWtop\fR, or \f(CWbottom\fR, the
+legend is drawn in the specified margin. If \fIpos\fR is
+\f(CWplotarea\fR, then the legend is drawn inside the plotting area at a
+particular anchor. If \fIpos\fR is in the form "\fI@x,y\fR", where
+\fIx\fR and \fIy\fR are the window coordinates, the legend is drawn in
+the plotting area at the specified coordinates. The default is
+\f(CWright\fR.
+.TP
+\fB\-raised \fIboolean\fR
+Indicates whether the legend is above or below the data elements. This
+matters only if the legend is in the plotting area. If \fIboolean\fR
+is true, the legend will be drawn on top of any elements that may
+overlap it. The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the border around the legend.
+\fIRelief\fR specifies how the interior of the legend should appear
+relative to the strip chart; for example, \f(CWraised\fR means the legend
+should appear to protrude from the strip chart, relative to the surface of
+the strip chart. The default is \f(CWsunken\fR.
+.PP
+Legend configuration options may also be set by the \fBoption\fR
+command. The resource name and class are \f(CWlegend\fR and
+\f(CWLegend\fR respectively.
+.CS
+option add *Stripchart.legend.Foreground blue
+option add *Stripchart.Legend.Relief raised
+.CE
+.RE
+.TP
+\fIpathName \fBlegend deactivate \fIpattern\fR...
+Selects legend entries to be drawn using the normal legend colors and
+relief. All entries whose element names match \fIpattern\fR are
+selected. To be selected, the element name must match only one
+\fIpattern\fR.
+.TP
+\fIpathName \fBlegend get \fIpos\fR
+Returns the name of the element whose entry is at the screen position
+\fIpos\fR in the legend. \fIPos\fR must be in the form "\fI@x,y\fR",
+where \fIx\fR and \fIy\fR are window coordinates. If the given
+coordinates do not lie over a legend entry, \f(CW""\fR is returned.
+.SS "PEN COMPONENTS"
+Pens define attributes (both symbol and line style) for elements.
+Pens mirror the configuration options of data elements that pertain to
+how symbols and lines are drawn. Data elements use pens to determine
+how they are drawn. A data element may use several pens at once. In
+this case, the pen used for a particular data point is determined from
+each element's weight vector (see the element's \fB\-weight\fR and
+\fB\-style\fR options).
+.PP
+One pen, called \f(CWactiveLine\fR, is automatically created.
+It's used as the default active pen for elements. So you can change
+the active attributes for all elements by simply reconfiguring this
+pen.
+.CS
+\&.s pen configure "activeLine" -color green
+.CE
+You can create and use any number of pens. To create a pen, invoke
+the pen component and its create operation.
+.CS
+\&.s pen create myPen
+.CE
+You map pens to a data element using either the element's
+\fB\-pen\fR or \fB\-activepen\fR options.
+.CS
+\&.s element create "line1" -xdata $x -ydata $tempData \\
+ -pen myPen
+.CE
+An element can use several pens at once. This is done by specifying
+the name of the pen in the element's style list (see the
+\fB\-styles\fR option).
+.CS
+\&.s element configure "line1" -styles { myPen 2.0 3.0 }
+.CE
+This says that any data point with a weight between 2.0 and 3.0
+is to be drawn using the pen \f(CWmyPen\fR. All other points
+are drawn with the element's default attributes.
+.PP
+The following operations are available for pen components.
+.PP
+.TP
+\fIpathName \fBpen \fBcget \fIpenName \fIoption\fR
+Returns the current value of the option given by \fIoption\fR for
+\fIpenName\fR. \fIOption\fR may be any option described below
+for the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBconfigure \fIpenName \fR?\fIoption value\fR?...
+Queries or modifies the configuration options of \fIpenName\fR. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fIpenName\fR is returned. If \fIoption\fR is specified, but
+not \fIvalue\fR, then a list describing \fIoption\fR is
+returned. If one or more \fIoption\fR and \fIvalue\fR pairs are
+specified, then for each pair, the pen option \fIoption\fR is set to
+\fIvalue\fR.
+The following options are valid for pens.
+.RS
+.TP
+\fB\-color \fIcolor\fR
+Sets the color of the traces connecting the data points.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of element line. \fIDashList\fR is a list of up to
+11 numbers that alternately represent the lengths of the dashes and
+gaps on the element line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the lines will be solid.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the interior color of symbols. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the symbol is transparent. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the connecting lines between data points. If
+\fIpixels\fR is \f(CW0\fR, no connecting lines will be drawn between
+symbols. The default is \f(CW0\fR.
+.TP
+\fB\-offdash \fIcolor\fR
+Sets the color of the stripes when traces are dashed (see the
+\fB\-dashes\fR option). If \fIcolor\fR is \f(CW""\fR, then the "off"
+pixels will represent gaps instead of stripes. If \fIcolor\fR is
+\f(CWdefcolor\fR, then the color will be the same as the \fB\-color\fR
+option. The default is \f(CWdefcolor\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color or the outline around each symbol. If \fIcolor\fR is
+\f(CW""\fR, then no outline is drawn. If \fIcolor\fR is \f(CWdefcolor\fR,
+then the color will be the same as the \fB\-color\fR option. The
+default is \f(CWdefcolor\fR.
+.TP
+\fB\-outlinewidth \fIpixels\fR
+Sets the width of the outline bordering each symbol. If \fIpixels\fR
+is \f(CW0\fR, no outline will be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-pixels \fIpixels\fR
+Sets the size of symbols. If \fIpixels\fR is \f(CW0\fR, no symbols will
+be drawn. The default is \f(CW0.125i\fR.
+.TP
+\fB\-symbol \fIsymbol\fR
+Specifies the symbol for data points. \fISymbol\fR can be either
+\f(CWsquare\fR, \f(CWcircle\fR, \f(CWdiamond\fR, \f(CWplus\fR, \f(CWcross\fR,
+\f(CWsplus\fR, \f(CWscross\fR, \f(CWtriangle\fR, \f(CW""\fR (where no symbol
+is drawn), or a bitmap. Bitmaps are specified as "\fIsource\fR
+?\fImask\fR?", where \fIsource\fR is the name of the bitmap, and
+\fImask\fR is the bitmap's optional mask. The default is
+\f(CWcircle\fR.
+.TP
+\fB\-type \fIelemType\fR
+Specifies the type of element the pen is to be used with.
+This option should only be employed when creating the pen. This
+is for those that wish to mix different types of elements (bars and
+lines) on the same graph. The default type is "line".
+.PP
+Pen configuration options may be also be set by the \fBoption\fR
+command. The resource class is \f(CWPen\fR. The resource names
+are the names of the pens.
+.CS
+option add *Stripchart.Pen.Color blue
+option add *Stripchart.activeLine.color green
+.CE
+.RE
+.TP
+\fIpathName \fBpen \fBcreate \fIpenName \fR?\fIoption value\fR?...
+Creates a new pen by the name \fIpenName\fR. No pen by the same
+name can already exist. \fIOption\fR and \fIvalue\fR are described
+in above in the pen \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpen \fBdelete \fR?\fIpenName\fR?...
+Deletes the named pens. A pen is not really
+deleted until it is not longer in use, so it's safe to delete
+pens mapped to elements.
+.TP
+\fIpathName \fBpen names \fR?\fIpattern\fR?...
+Returns a list of pens matching zero or more patterns. If no
+\fIpattern\fR argument is give, the names of all pens are returned.
+.SS "POSTSCRIPT COMPONENT"
+The strip chart can generate encapsulated PostScript output. There
+are several configuration options you can specify to control how the
+plot is generated. You can change the page dimensions and
+borders. The plot itself can be scaled, centered, or rotated to
+landscape. The PostScript output can be written directly to a file or
+returned through the interpreter.
+.PP
+The following postscript operations are available.
+.TP
+\fIpathName \fBpostscript cget \fIoption\fR
+Returns the current value of the postscript option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below for the postscript \fBconfigure\fR operation.
+.TP
+\fIpathName \fBpostscript configure \fR?\fIoption value\fR?...
+Queries or modifies the configuration options for PostScript
+generation. If \fIoption\fR isn't specified, a list describing
+the current postscript options for \fIpathName\fR is returned. If
+\fIoption\fR is specified, but not \fIvalue\fR, then a list describing
+\fIoption\fR is returned. If one or more \fIoption\fR and \fIvalue\fR
+pairs are specified, then for each pair, the postscript option
+\fIoption\fR is set to \fIvalue\fR. The following postscript options
+are available.
+.RS
+.TP
+\fB\-center \fIboolean\fR
+Indicates whether the plot should be centered on the PostScript page. If
+\fIboolean\fR is false, the plot will be placed in the upper left
+corner of the page. The default is \f(CW1\fR.
+.TP
+\fB\-colormap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a color mapping from the X color name to PostScript. Each
+element of \fIvarName\fR must consist of PostScript code to set a
+particular color value (e.g. ``\f(CW1.0 1.0 0.0 setrgbcolor\fR''). When
+outputting color information in PostScript, the array variable \fIvarName\fR
+is checked to see if an element of the name of the color exists. If so,
+it uses the value of the element as the PostScript
+command to set the color. If this option hasn't been specified, or if
+there isn't an entry in \fIvarName\fR for a given color, then it uses
+the red, green, and blue intensities from the X color.
+.TP
+\fB\-colormode \fImode\fR
+Specifies how to output color information. \fIMode\fR must be either
+\f(CWcolor\fR (for full color output), \f(CWgray\fR (convert all colors to
+their gray-scale equivalents) or \f(CWmono\fR (convert foreground colors
+to black and background colors to white). The default mode is
+\f(CWcolor\fR.
+.TP
+\fB\-fontmap \fIvarName\fR
+\fIVarName\fR must be the name of a global array variable that
+specifies a font mapping from the X font name to PostScript. Each
+element of \fIvarName\fR must consist of a Tcl list with one or two
+elements, which are the name and point size of a PostScript font.
+When outputting PostScript commands for a particular font, the
+array variable \fIvarName\fR is checked to see an element of the specified
+font exists. If there is such an element, then the font
+information contained in that element is used in the PostScript
+output. (If the point size is omitted from the list, the point size
+of the X font is used). Otherwise the X font is examined in an
+attempt to guess what PostScript font to use. This works only for
+fonts whose foundry property is \fIAdobe\fR (such as Times, Helvetica,
+Courier, etc.). If all of this fails then the font defaults to
+\f(CWHelvetica-Bold\fR.
+.TP
+\fB\-decorations \fIboolean\fR
+Indicates if PostScript commands to generate color backgrounds and 3-D
+borders should be output. If \fIboolean\fR is false, the background will
+be white and no 3-D borders will be generated. The
+default is \f(CW1\fR.
+.TP
+\fB\-height \fIpixels\fR
+Sets the height of the plot. This lets you plot the stripchart with a
+height different from the one displayed on the screen. If
+\fIpixels\fR is 0, the height is the same as the displayed height.
+The default is \f(CW0\fR.
+.TP
+\fB\-landscape \fIboolean\fR
+If \fIboolean\fR is true, this specifies the printed area is to be
+rotated 90 degrees. In non-rotated output the X-axis of the printed
+area runs along the short dimension of the page (``portrait''
+orientation); in rotated output the X-axis runs along the long
+dimension of the page (``landscape'' orientation). Defaults to
+\f(CW0\fR.
+.TP
+\fB\-maxpect \fIboolean\fR
+Indicates to scale the the plot so that it fills the PostScript page.
+The aspect ratio of the strip chart is still retained. The default is
+\f(CW0\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the horizontal padding for the left and right page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the left border is padded
+by the first distance and the right border by the second. If
+\fIpad\fR has just one distance, both the left and right borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the vertical padding for the top and bottom page borders. The
+borders are exterior to the plot. \fIPad\fR can be a list of one or
+two screen distances. If \fIpad\fR has two elements, the top border is padded
+by the first distance and the bottom border by the second. If
+\fIpad\fR has just one distance, both the top and bottom borders are
+padded evenly. The default is \f(CW1i\fR.
+.TP
+\fB\-paperheight \fIpixels\fR
+Sets the height of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default height is
+\f(CW11.0i\fR.
+.TP
+\fB\-paperwidth \fIpixels\fR
+Sets the width of the postscript page. This can be used to select
+between different page sizes (letter, A4, etc). The default width is
+\f(CW8.5i\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the width of the plot. This lets you plot the strip chart with a
+width different from the one drawn on the screen. If \fIpixels\fR
+is 0, the width is the same as the widget's width. The default is
+\f(CW0\fR.
+.PP
+Postscript configuration options may be also be set by the
+\fBoption\fR command. The resource name and class are
+\f(CWpostscript\fR and \f(CWPostscript\fR respectively.
+.CS
+option add *Stripchart.postscript.Decorations false
+option add *Stripchart.Postscript.Landscape true
+.CE
+.RE
+.TP
+\fIpathName \fBpostscript output \fR?\fIfileName\fR? ?\fIoption value\fR?...
+Outputs a file of encapsulated PostScript. If a
+\fIfileName\fR argument isn't present, the command returns the
+PostScript. If any \fIoption-value\fR pairs are present, they set
+configuration options controlling how the PostScript is generated.
+\fIOption\fR and \fIvalue\fR can be anything accepted by the
+postscript \fBconfigure\fR operation above.
+.SS "MARKER COMPONENTS"
+Markers are simple drawing procedures used to annotate or highlight
+areas of the strip chart. Markers have various types: text strings,
+bitmaps, images, connected lines, windows, or polygons. They can be
+associated with a particular element, so that when the element is
+hidden or un-hidden, so is the marker. By default, markers are the
+last items drawn, so that data elements will appear in
+behind them. You can change this by configuring the \fB\-under\fR
+option.
+.PP
+Markers, in contrast to elements, don't affect the scaling of the
+coordinate axes. They can also have \fIelastic\fR coordinates
+(specified by \f(CW-Inf\fR and \f(CWInf\fR respectively) that translate
+into the minimum or maximum limit of the axis. For example, you can
+place a marker so it always remains in the lower left corner of the
+plotting area, by using the coordinates \f(CW-Inf\fR,\f(CW-Inf\fR.
+.PP
+The following operations are available for markers.
+.TP
+\fIpathName \fBmarker after \fImarkerId\fR ?\fIafterId\fR?
+Changes the order of the markers, drawing the first
+marker after the second. If no second \fIafterId\fR argument is
+specified, the marker is placed at the end of the display list. This
+command can be used to control how markers are displayed since markers
+are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker before \fImarkerId\fR ?\fIbeforeId\fR?
+Changes the order of the markers, drawing the first
+marker before the second. If no second \fIbeforeId\fR argument is
+specified, the marker is placed at the beginning of the display list.
+This command can be used to control how markers are displayed since
+markers are drawn in the order of this display list.
+.TP
+\fIpathName \fBmarker cget \fIoption\fR
+Returns the current value of the marker configuration option given by
+\fIoption\fR. \fIOption\fR may be any option described
+below in the \fBconfigure\fR operation.
+.TP
+\fIpathName \fBmarker configure \fImarkerId\fR ?\fIoption value\fR?...
+Queries or modifies the configuration options for markers. If
+\fIoption\fR isn't specified, a list describing the current
+options for \fImarkerId\fR is returned. If \fIoption\fR is specified,
+but not \fIvalue\fR, then a list describing \fIoption\fR is returned.
+If one or more \fIoption\fR and \fIvalue\fR pairs are specified, then
+for each pair, the marker option \fIoption\fR is set to \fIvalue\fR.
+.sp
+The following options are valid for all markers.
+Each type of marker also has its own type-specific options.
+They are described in the sections below.
+.RS
+.TP
+\fB\-coords \fIcoordList\fR
+Specifies the coordinates of the marker. \fICoordList\fR is
+a list of graph coordinates. The number of coordinates required
+is dependent on the type of marker. Text, image, and window markers
+need only two coordinates (an X\-Y coordinate). Bitmap markers
+can take either two or four coordinates (if four, they represent the
+corners of the bitmap). Line markers
+need at least four coordinates, polygons at least six.
+If \fIcoordList\fR is \f(CW""\fR, the marker will not be displayed.
+The default is \f(CW""\fR.
+.TP
+\fB\-element \fIelemName\fR
+Links the marker with the element \fIelemName\fR. The marker is
+drawn only if the element is also currently displayed (see the
+element's \fBshow\fR operation). If \fIelemName\fR is \f(CW""\fR, the
+marker is always drawn. The default is \f(CW""\fR.
+.TP
+\fB\-hide \fIboolean\fR
+Indicates whether the marker is drawn. If \fIboolean\fR is true,
+the marker is not drawn. The default is \f(CWno\fR.
+.TP
+\fB\-mapx \fIxAxis\fR
+Specifies the X\-axis to map the marker's X\-coordinates onto.
+\fIXAxis\fR must the name of an axis. The default is \f(CWx\fR.
+.TP
+\fB\-mapy \fIyAxis\fR
+Specifies the Y\-axis to map the marker's Y\-coordinates onto.
+\fIYAxis\fR must the name of an axis. The default is \f(CWy\fR.
+.TP
+\fB\-name \fImarkerId\fR
+Changes the identifier for the marker. The identifier \fImarkerId\fR
+can not already be used by another marker. If this option
+isn't specified, the marker's name is uniquely generated.
+.TP
+\fB\-under \fIboolean\fR
+Indicates whether the marker is drawn below/above data
+elements. If \fIboolean\fR is true, the marker is be drawn
+underneath the data element symbols and lines. Otherwise, the marker is
+drawn on top of the element. The default is \f(CW0\fR.
+.TP
+\fB\-xoffset \fIpixels\fR
+Specifies a screen distance to offset the marker horizontally.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.TP
+\fB\-yoffset \fIpixels\fR
+Specifies a screen distance to offset the markers vertically.
+\fIPixels\fR is a valid screen distance, such as \f(CW2\fR or \f(CW1.2i\fR.
+The default is \f(CW0\fR.
+.PP
+Marker configuration options may also be set by the \fBoption\fR command.
+The resource class is either \f(CWBitmapMarker\fR, \f(CWImageMarker\fR,
+\f(CWLineMarker\fR, \f(CWPolygonMarker\fR, \f(CWTextMarker\fR, or \f(CWWindowMarker\fR,
+depending on the type of marker. The resource name is the name of the
+marker.
+.CS
+option add *Stripchart.TextMarker.Foreground white
+option add *Stripchart.BitmapMarker.Foreground white
+option add *Stripchart.m1.Background blue
+.CE
+.RE
+.TP
+\fIpathName \fBmarker create \fItype\fR ?\fIoption value\fR?...
+Creates a marker of the selected type. \fIType\fR may be either
+\f(CWtext\fR, \f(CWline\fR, \f(CWbitmap\fR, \f(CWimage\fR, \f(CWpolygon\fR, or
+\f(CWwindow\fR. This command returns the marker identifier,
+used as the \fImarkerId\fR argument in the other marker-related
+commands. If the \fB\-name\fR option is used, this overrides the
+normal marker identifier. If the name provided is already used for
+another marker, the new marker will replace the old.
+.TP
+\fIpathName \fBmarker delete\fR ?\fIname\fR?...
+Removes one of more markers. The graph will automatically be redrawn
+without the marker.\fR.
+.TP
+\fIpathName \fBmarker exists \fImarkerId\fR
+Returns \f(CW1\fR if the marker \fImarkerId\fR exists and \f(CW0\fR
+otherwise.
+.TP
+\fIpathName \fBmarker names\fR ?\fIpattern\fR?
+Returns the names of all the markers that currently exist. If
+\fIpattern\fR is supplied, only those markers whose names match it
+will be returned.
+.TP
+\fIpathName \fBmarker type \fImarkerId\fR
+Returns the type of the marker given by \fImarkerId\fR, such as
+\f(CWline\fR or \f(CWtext\fR. If \fImarkerId\fR is not a valid a marker
+identifier, \f(CW""\fR is returned.
+.SS "BITMAP MARKERS"
+A bitmap marker displays a bitmap. The size of the
+bitmap is controlled by the number of coordinates specified. If two
+coordinates, they specify the position of the top-left corner of the
+bitmap. The bitmap retains its normal width and height. If four
+coordinates, the first and second pairs of coordinates represent the
+corners of the bitmap. The bitmap will be stretched or reduced as
+necessary to fit into the bounding rectangle.
+.PP
+Bitmap markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create bitmap \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs, each
+sets a configuration options for the marker. These
+same \fIoption\fR\-\fIvalue\fR pairs may be used with the marker's
+\fBconfigure\fR operation.
+.PP
+The following options are specific to bitmap markers:
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the bitmap. If \fIcolor\fR is \f(CW""\fR,
+the background color will be transparent. The default background
+color is \f(CWwhite\fR.
+.TP
+\fB\-bitmap \fIbitmap\fR
+Specifies the bitmap to be displayed. If \fIbitmap\fR is \f(CW""\fR,
+the marker will not be displayed. The default is \f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of the bitmap. The default foreground color
+is \f(CWblack\fR.
+.TP
+\fB\-mask \fImask\fR
+Specifies a mask for the bitmap to be displayed. This mask is a bitmap
+itself, denoting the pixels that are transparent. If \fImask\fR is
+\f(CW""\fR, all pixels of the bitmap will be drawn. The default is
+\f(CW""\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Sets the rotation of the bitmap. \fITheta\fR is a real number
+representing the angle of rotation in degrees. The marker is first
+rotated and then placed according to its anchor position. The default
+rotation is \f(CW0.0\fR.
+.SS "IMAGE MARKERS"
+A image marker displays an image. Image markers are
+created with the marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create image \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to image markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the image relative to the
+positioning point for the image. For example, if \fIanchor\fR
+is \f(CWcenter\fR then the image is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the image will be drawn such that
+the top center point of the rectangular region occupied by the
+image will be at the positioning point.
+This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-image \fIimage\fR
+Specifies the image to be drawn.
+If \fIimage\fR is \f(CW""\fR, the marker will not be
+drawn. The default is \f(CW""\fR.
+.SS "LINE MARKERS"
+A line marker displays one or more connected line segments.
+Line markers are created with marker's \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create line \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to line markers:
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the line.
+The option is affects the line color only when the \fB\-stipple\fR option
+is set.
+If this option isn't specified then it defaults to \f(CWwhite\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the line. \fIDashList\fR is a list of up to 11
+numbers that alternately represent the lengths of the dashes and gaps
+on the line. Each number must be between 1 and 255. If
+\fIdashList\fR is \f(CW""\fR, the marker line will be solid.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color. The default foreground color is \f(CWblack\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the lines.
+The default width is \f(CW0\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern used to draw the line, rather than
+a solid line.
+\fIBitmap\fR specifies a bitmap to use as the stipple
+pattern. If \fIbitmap\fR is \f(CW""\fR, then the
+line is drawn in a solid fashion. The default is \f(CW""\fR.
+.SS "POLYGON MARKERS"
+A polygon marker displays a closed region described as two or more
+connected line segments. It is assumed the first and
+last points are connected. Polygon markers are created using the
+marker \fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create polygon \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the \fBmarker configure\fR command to change the marker's
+configuration.
+The following options are supported for polygon markers:
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the outline of the polygon. \fIDashList\fR is a
+list of up to 11 numbers that alternately represent the lengths of
+the dashes and gaps on the outline. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the outline will be a solid line.
+.TP
+\fB\-fill \fIcolor\fR
+Sets the fill color of the polygon. If \fIcolor\fR is \f(CW""\fR, then
+the interior of the polygon is transparent.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Sets the width of the outline of the polygon. If \fIpixels\fR is zero,
+no outline is drawn. The default is \f(CW0\fR.
+.TP
+\fB\-outline \fIcolor\fR
+Sets the color of the outline of the polygon. If the polygon is
+stippled (see the \fB\-stipple\fR option), then this represents the
+foreground color of the stipple. The default is \f(CWblack\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies that the polygon should be drawn with a stippled pattern
+rather than a solid color. \fIBitmap\fR specifies a bitmap to use as
+the stipple pattern. If \fIbitmap\fR is \f(CW""\fR, then the polygon is
+filled with a solid color (if the \fB\-fill\fR option is set). The
+default is \f(CW""\fR.
+.SS "TEXT MARKERS"
+A text marker displays a string of characters on one or more lines of
+text. Embedded newlines cause line breaks. They may be used to
+annotate regions of the strip chart. Text markers are created with the
+\fBcreate\fR operation in the form:
+.DS
+\fIpathName \fBmarker create text \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR pairs,
+each sets a configuration option for the text marker.
+These same \fIoption\fR\-\fIvalue\fR pairs may be used with the
+marker's \fBconfigure\fR operation.
+.PP
+The following options are specific to text markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the text relative to the
+positioning point for the text. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the text is centered on the point; if
+\fIanchor\fR is \f(CWn\fR then the text will be drawn such that the
+top center point of the rectangular region occupied by the text will
+be at the positioning point. This default is \f(CWcenter\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the text string. If \fIcolor\fR is
+\f(CW""\fR, the background will be transparent. The default is
+\f(CWwhite\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font of the text. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of the text. The default is \f(CWblack\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the text should be justified. This matters only when
+the marker contains more than one line of text. \fIJustify\fR must be
+\f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right exteriors of the text.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the text is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW4\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the text. \fIPad\fR can be a list of
+one or two screen distances. If \fIpad\fR has two elements, the area above the
+text is padded by the first distance and the area below by the second.
+If \fIpad\fR is just one distance, both the top and bottom areas
+are padded evenly. The default is \f(CW4\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the number of degrees to rotate the text. \fITheta\fR is a
+real number representing the angle of rotation. The marker is first
+rotated along its center and is then drawn according to its anchor
+position. The default is \f(CW0.0\fR.
+.TP
+\fB\-text \fItext\fR
+Specifies the text of the marker. The exact way the text is
+displayed may be affected by other options such as \fB\-anchor\fR or
+\fB\-rotate\fR.
+.SS "WINDOW MARKERS"
+A window marker displays a widget at a given position.
+Window markers are created with the marker's \fBcreate\fR operation in
+the form:
+.DS
+\fIpathName \fBmarker create window \fR?\fIoption value\fR?...
+.DE
+There may be many \fIoption\fR-\fIvalue\fR
+pairs, each sets a configuration option
+for the marker. These same \fIoption\fR\-\fIvalue\fR pairs may be
+used with the marker's \fBconfigure\fR command.
+.PP
+The following options are specific to window markers:
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR tells how to position the widget relative to the
+positioning point for the widget. For example, if \fIanchor\fR is
+\f(CWcenter\fR then the widget is centered on the point; if \fIanchor\fR
+is \f(CWn\fR then the widget will be displayed such that the top center
+point of the rectangular region occupied by the widget will be at the
+positioning point. This option defaults to \f(CWcenter\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the height to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever height the widget requests internally.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the width to assign to the marker's window. If this option
+isn't specified, or if it is specified as \f(CW""\fR, then the window is
+given whatever width the widget requests internally.
+.TP
+\fB\-window \fIpathName\fR
+Specifies the widget to be managed. \fIPathName\fR must
+be a child of the \fBstripchart\fR widget.
+.SH "GRAPH COMPONENT BINDINGS"
+Specific stripchart components, such as elements, markers and legend
+entries, can have a command trigger when event occurs in them, much
+like canvas items in Tk's canvas widget. Not all event sequences are
+valid. The only binding events that may be specified are those
+related to the mouse and keyboard (such as \fBEnter\fR, \fBLeave\fR,
+\fBButtonPress\fR, \fBMotion\fR, and \fBKeyPress\fR).
+.sp
+Only one element or marker can be picked during an event. This means,
+that if the mouse is directly over both an element and a marker, only
+the uppermost component is selected. This isn't true for legend entries.
+Both a legend entry and an element (or marker) binding commands
+will be invoked if both items are picked.
+.sp
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the
+element name and another is associated with one of the element's tags
+(see the \fB\-bindtags\fR option). When this occurs, all of the
+matching bindings are invoked. A binding associated with the element
+name is invoked first, followed by one binding for each of the element's
+bindtags. If there are multiple matching bindings for a single tag,
+then only the most specific binding is invoked. A continue command
+in a binding script terminates that script, and a break command
+terminates that script and skips any remaining scripts for the event,
+just as for the bind command.
+.sp
+The \fB\-bindtags\R option for these components controls addition
+tag names which can be matched. Implicitly elements and markers
+always have tags matching their names. Setting the value of
+the \fB\-bindtags\fR option doesn't change this.
+.SH "C LANGUAGE API"
+You can manipulate data elements from the C language. There
+may be situations where it is too expensive to translate the data
+values from ASCII strings. Or you might want to read data in a
+special file format.
+.PP
+Data can manipulated from the C language using BLT vectors.
+You specify the x and y data coordinates of an element as vectors and
+manipulate the vector from C. The strip chart will be redrawn automatically
+after the vectors are updated.
+.PP
+From Tcl, create the vectors and configure the element to use them.
+.CS
+vector X Y
+\&.s element configure line1 -xdata X -ydata Y
+.CE
+To set data points from C, you pass the values as arrays of doubles
+using the \fBBlt_ResetVector\fR call. The vector is reset with the
+new data and at the next idle point (when Tk re-enters its event
+loop), the strip chart will be redrawn automatically.
+.CS
+#include <tcl.h>
+#include <blt.h>
+
+register int i;
+Blt_Vector *xVec, *yVec;
+double x[50], y[50];
+
+/* Get the BLT vectors "X" and "Y" (created above from Tcl) */
+if ((Blt_GetVector(interp, "X", 50, &xVec) != TCL_OK) ||
+ (Blt_GetVector(interp, "Y", 50, &yVec) != TCL_OK)) {
+ return TCL_ERROR;
+}
+
+for (i = 0; i < 50; i++) {
+ x[i] = i * 0.02;
+ y[i] = sin(x[i]);
+}
+
+/* Put the data into BLT vectors */
+if ((Blt_ResetVector(xVec, x, 50, 50, TCL_VOLATILE) != TCL_OK) ||
+ (Blt_ResetVector(yVec, y, 50, 50, TCL_VOLATILE) != TCL_OK)) {
+ return TCL_ERROR;
+}
+.CE
+See the \fBvector\fR manual page for more details.
+.SH SPEED TIPS
+There may be cases where the strip chart needs to be drawn and updated as
+quickly as possible. If drawing speed becomes a big
+problem, here are a few tips to speed up displays.
+.TP 2
+\(bu
+Try to minimize the number of data points. The more data points
+the looked at, the more work the strip chart must do.
+.TP 2
+\(bu
+If your data is generated as floating point values, the time required
+to convert the data values to and from ASCII strings can be
+significant, especially when there any many data points. You can
+avoid the redundant string-to-decimal conversions using the C API to
+BLT vectors.
+.TP 2
+\(bu
+Data elements without symbols are drawn faster than with symbols.
+Set the data element's \fB\-symbol\fR option to \f(CWnone\fR. If you need to
+draw symbols, try using the simple symbols such as \f(CWsplus\fR and
+\f(CWscross\fR.
+.TP 2
+\(bu
+Don't stipple or dash the element. Solid lines are much faster.
+.TP 2
+\(bu
+If you update data elements frequently, try turning off the
+widget's \fB\-bufferelements\fR option. When the strip chart is first
+displayed, it draws data elements into an internal pixmap. The pixmap
+acts as a cache, so that when the strip chart needs to be redrawn again, and
+the data elements or coordinate axes haven't changed, the pixmap is
+simply copied to the screen. This is especially useful when you are
+using markers to highlight points and regions on the strip chart. But if
+the strip chart is updated frequently, changing either the element data or
+coordinate axes, the buffering becomes redundant.
+.SH LIMITATIONS
+Auto-scale routines do not use requested min/max limits as boundaries
+when the axis is logarithmically scaled.
+.PP
+The PostScript output generated for polygons with more than 1500
+points may exceed the limits of some printers (See PostScript Language
+Reference Manual, page 568). The work-around is to break the polygon
+into separate pieces.
+.SH "FUTURE INCOMPATIBILITY"
+The \fB\-mapped\fR options are obsoleted and will be removed. You can
+achieve the same results using the \fB\-hide\fR option instead.
+.CS
+# Works for now.
+\&.s legend configure -mapped no
+
+# Instead use this.
+\&.s legend configure -hide yes
+.CE
+.SH KEYWORDS
+stripchart, graph, widget
diff --git a/blt/man/table.mann b/blt/man/table.mann
new file mode 100644
index 00000000000..76372f1e15c
--- /dev/null
+++ b/blt/man/table.mann
@@ -0,0 +1,757 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" The table geometry manager created by George Howlett.
+'\"
+.so man.macros
+.TH table n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+table \- Arranges widgets in a table
+.SH SYNOPSIS
+\fBtable \fIcontainer\fR ?\fIwidget index option value\fR?...
+.sp
+\fBtable arrange\fR \fIcontainer\fR
+.sp
+\fBtable cget \fIcontainer\fR ?\fIitem\fR? \fIoption\fR
+.sp
+\fBtable configure \fIcontainer\fR ?\fIitem\fR?... ?\fIoption value\fR?...
+.sp
+\fBtable extents \fIcontainer\fR \fIitem\fR
+.sp
+\fBtable forget \fIwidget\fR ?\fIwidget\fR?...
+.sp
+\fBtable info \fIcontainer\fR \fIitem\fR
+.sp
+\fBtable locate \fIcontainer\fR \fIx y\fR
+.sp
+\fBtable containers \fR?\fIswitch\fR? ?\fIarg\fR?
+.sp
+\fBtable save \fIcontainer\fR
+.sp
+\fBtable search \fIcontainer\fR ?\fIswitch arg\fR?...
+.BE
+.SH DESCRIPTION
+The \fBtable\fR command arranges widgets in a table. The alignment of
+widgets is detemined by their row and column positions and the number
+of rows or columns that they span.
+.SH INTRODUCTION
+Probably the most painstaking aspect of building a graphical
+application is getting the placement and size of the widgets just right.
+It usually takes many iterations to align widgets and adjust their spacing.
+That's because managing the geometry of widgets is simply not a
+packing problem, but also graphical design problem. Attributes
+such as alignment, symmetry, and balance are more important than
+minimizing the amount of space used for packing.
+.PP
+The \fBtable\fR geometry manager arranges widgets in a table. It's
+easy to align widgets (horizontally and vertically) or to create empty
+space to balance the arrangement of the widgets. Widgets (called
+\fIslaves\fR in the Tk parlance) are arranged inside a containing
+widget (called the \fImaster\fR). Widgets are positioned at
+row,column locations and may span any number of rows or columns. More
+than one widget can occupy a single location.
+.PP
+The placement of widget windows determines both the size and
+arrangement of the table. The table queries the requested size of
+each widget. The \fIrequested size\fR of a widget is the natural size
+of the widget (before the widget is shrunk or expanded). The height
+of each row and the width of each column is the largest widget spanning
+that row or column. The size of the table is in turn the sum of the
+row and column sizes. This is the table's \fInormal size\fR.
+.PP
+The total number of rows and columns in a table is determined from the
+indices specified. The table grows dynamically as windows are added
+at larger indices.
+.SH EXAMPLE
+The table geometry manager is created by invoking the \fBtable\fR command.
+.CS
+# Create a table in the root window
+table .
+.CE
+The window \f(CW.\fR is now the \fIcontainer\fR of the table. Widgets
+are packed into the table and displayed within the confines of the
+container.
+.PP
+You add widgets to the table by row and column location. Row and
+column indices start from zero.
+.CS
+label .title -text "This is a title"
+
+# Add a label to the table
+table . .title 0,0
+.CE
+The label \f(CW.title\fR is added to the table. We can add more widgets
+in the same way.
+.CS
+button .ok -text "Ok"
+button .cancel -text "Cancel"
+
+# Add two buttons
+table . .ok 1,0
+table . .cancel 1,1
+.CE
+Two buttons \f(CW.ok\fR and \f(CW.cancel\fR are now packed into the second
+row of the table. They each occupy one cell of the table. By
+default, widgets span only a single row and column.
+.PP
+The first column contains two widgets, \f(CW.title\fR and \f(CW.ok\fR. By
+default, the widest of the two widgets will define the width of the
+column. However, we want \f(CW.title\fR to be centered horizontally
+along the top of the table. We can make \f(CW.title\fR span two columns
+using the \fBconfigure\fR operation.
+.CS
+# Make the label span both columns
+table configure . .title -cspan 2
+.CE
+The label \f(CW.title\fR will now be centered along the top row of the
+table.
+.PP
+In the above example, we've create and arranged the layout for the
+table invoking the \fBtable\fR command several times. Alternately, we
+could have used a single \fBtable\fR command.
+.CS
+label .title -text "This is a title"
+button .ok -text "Ok"
+button .cancel -text "Cancel"
+
+# Create and pack the table
+table . \\
+ .title 0,0 -cspan 2 \\
+ .ok 1,0 \\
+ .cancel 1,1
+.CE
+The table will override the requested width and height of the container
+so that the window fits the table exactly. This also means
+that any change to the size of table will be propagated up through the
+Tk window hierarchy. This feature can be turned off using the
+\fBconfigure\fR operation again.
+.CS
+table configure . -propagate no
+.CE
+You can also set the width of height of the table to a specific
+value. This supersedes the calculated table size.
+.CS
+# Make the container 4 inches wide, 3 inches high
+table configure . -reqwidth 4i -reqheight 3i
+.CE
+If a widget is smaller than the cell(s) it occupies, the widget will
+float within the extra space. By default, the widget will be centered
+within the space, but you can anchor the widget to any side of cell
+using the \fB\-anchor\fR configuration option.
+.CS
+table configure . .ok -anchor w
+.CE
+The \fB\-fill\fR option expands the widget to fill the
+extra space either vertically or horizontally (or both).
+.CS
+# Make the title label fill the entire top row
+table configure . .title -cspan 2 -fill x
+
+# Each button will be as height of the 2nd row.
+table configure . .ok .cancel -fill y
+.CE
+The width of \f(CW.title\fR will be the combined widths of both columns.
+Both \f(CW.ok\fR and \f(CW.cancel\fR will become as tall as the second row.
+.PP
+The \fB\-padx\fR and \fB\-pady\fR options control the amount of padding
+around the widget. Both options take a list of one or two values.
+.CS
+# Pad the title by two pixels above and below.
+table configure . .title -pady 2
+
+# Pad each button 2 pixels on the left, and 4 on the right.
+table configure . .ok .cancel -padx { 2 4 }
+.CE
+If the list has only one value, then both exterior sides (top and bottom
+or left and right) of the widget are padded by that amount. If the
+list has two elements, the first specifies padding for the top or left
+side and the second for the bottom or right side.
+.PP
+Like the container, you can also override the requested widths and
+heights of widgets using the \fB\-reqwidth\fR and
+\fB\-reqheight\fR options. This is especially useful with
+character-based widgets (such as buttons, labels, text, listbox, etc)
+that let you specify their size only in units of characters and lines,
+instead of pixels.
+.CS
+# Make all buttons one inch wide
+table configure . .ok .cancel -reqwidth 1i
+.CE
+.PP
+Each row and column of the table can be configured, again using the
+\fBconfigure\fR operation. Rows are and columns are designated by
+\f(CWR\fIi\fR and \f(CWC\fIi\fR respectively, where \fIi\fR is the index
+of the row or column.
+.PP
+For example, you can set the size of a row or column.
+.CS
+# Make the 1st column 2 inches wide
+table configure . c0 -width 2.0i
+
+# Make the 2nd row 1/2 inch high.
+table configure . r1 -height 0.5i
+.CE
+The new size for the row or column overrides its calculated size. If
+no widgets span the row or column, its height or width is zero.
+So you can use the \fB\-width\fR and \fB\-height\fR options to create
+empty spaces in the table.
+.CS
+# Create an empty row and column
+table configure . r2 c2 -width 1i
+.CE
+The \fB\-pady\fR option lets you add padding to the top and bottom
+sides of rows. The \fB\-padx\fR option adds padding to the left and
+right sides of columns. Both options take a list of one or two
+values.
+.CS
+# Pad above the title by two pixels
+table configure . r0 -pady { 2 0 }
+
+# Pad each column 4 pixels on the left, and 2 on the right.
+table configure . c* -padx { 2 4 }
+.CE
+.PP
+Notice that you can configure all the rows and columns using either
+\f(CWR*\fR or \f(CWC*\fR.
+.PP
+When the container is resized, the rows and columns of the table are
+also resized. Only the rows or columns that contain widgets (a widget
+spans the row or column) grow or shrink. The \fB\-resize\fR option
+indicates whether the row or column can be shrunk or stretched. If
+the value is \f(CWshrink\fR, the row or column can only be resized
+smaller. If \f(CWexpand\fR, it can only be resized larger. If
+\f(CWnone\fR, the row or column is frozen at its requested size.
+.CS
+# Let the 1st column get smaller, but not bigger
+table configure . c0 -resize shrink
+
+# Let the 2nd column get bigger, not smaller
+table configure . c1 -resize expand
+
+# Don't resize the first row
+table configure . r0 -resize none
+.CE
+The following example packs a canvas, two scrollbars, and a title.
+The rows and columns containing the scrollbars are frozen at their
+requested size, so that even if the frame is resized, the scrollbars will
+remain the same width.
+.CS
+table . \\
+ .title 0,0 -cspan 3 \\
+ .canvas 1,1 -fill both \\
+ .vscroll 1,2 -fill y \\
+ .hscroll 2,1 -fill x
+
+# Don't let the scrollbars resize
+table configure . c2 r2 -resize none
+
+# Create an empty space to balance the scrollbar
+table configure . c0 -width .vscroll
+.CE
+Note that the value of the \fB\-width\fR option is the name of a widget
+window. This indicates that the width of the column should be the
+same as the requested width of \f(CW.vscroll\fR.
+.PP
+Finally, the \fBforget\fR operation removes widgets from the table.
+.CS
+# Remove the windows from the table
+table forget .quit .frame
+.CE
+It's not necessary to specify the container. The \fBtable\fR
+command determines the container from the widget name.
+.SH OPERATIONS
+The following operations are available for the \fBtable\fR:
+.TP
+\fBtable \fIcontainer\fR ?\fIwidget index option value\fR?...
+Adds the widget \fIwidget\fR to the table at \fIindex\fR. \fIIndex\fR
+is a row,column position in the table. It must be in the form
+\fIrow\fR,\fIcolumn\fR where \fIrow\fR and \fIcolumn\fR are the
+respective row and column numbers, starting from zero (0,0 is the
+upper leftmost position). \fIRow\fR and \fIcolumn\fR may also be
+numeric expressions that are recursively evaluated. If a table
+doesn't exist for \fIcontainer\fR, one is created. \fIWidget\fR is the
+path name of the window, that must already exist, to be arranged
+inside of \fIcontainer\fR. \fIOption\fR and \fIvalue\fR are described
+in the
+.SB WIDGET OPTIONS
+section.
+.TP
+\fBtable arrange\fR \fIcontainer\fR
+Forces the table to compute its layout immediately. Normally, the
+table geometry manager will wait until the next idle point, before
+calculating the size of its rows and columns. This is useful for
+collecting the \fInormal\fR sizes of rows and columns, that are
+based upon the requested widget sizes.
+.TP
+\fBtable cget\fR \fIcontainer \fR?\fIitem\fR?\fI option\fR
+Returns the current value of the configuration option specific to
+\fIitem\fR given by \fIoption\fR. \fIItem\fR is either a row or
+column index, or the path name of a widget. \fIItem\fR can be
+in any form describe in the \fBconfigure\fR operation below. If no
+\fIitem\fR argument is provided, then the configuration option is
+for the table itself. \fIOption\fR may be any one of the options
+described in the appropiate section for \fIitem\fR.
+.TP
+\fBtable configure\fR \fIcontainer item\fR... ?\fIoption value\fR?...
+Queries or modifies the configuration options specific to \fIitem\fR.
+If no \fIoption\fR is specified, this command returns a list
+describing all of the available options for \fIitem\fR
+If the argument \fIitem\fR is omitted, then the specified
+configuration options are for the table itself. Otherwise
+\fIitem\fR must be either a row or column specification, or the path
+name of a widget.
+The following \fIitem\fR types are available.
+.RS
+.TP
+\f(CWC\fIi\fR
+Specifies the column of \fIcontainer\fR to be configured. \fIItem\fR
+must be in the form \f(CWC\fIn\fR, where \fIi\fR is the index of
+the column. See the
+.SB COLUMN OPTIONS
+section.
+.TP
+\f(CWR\fIi\fR
+Specifies the row of \fIcontainer\fR to be configured. \fIItem\fR must be
+in the form \f(CWR\fIi\fR, where \fIi\fR is the index of the row. See
+the
+.SB ROW OPTIONS
+section.
+.TP
+\fIwidget\fR
+Specifies a widget of \fIcontainer\fR to be queried. \fIWidget\fR
+is the path name of a widget packed in \fIcontainer\fR. See the
+.SB WIDGET OPTIONS
+section.
+.TP
+No argument
+Specifies that the table itself is to be queried.
+See the
+.SB TABLE OPTIONS
+section for a description of the option-value pairs for the table.
+.RE
+.RS
+.sp
+The \fIoption\fI and \fIvalue\fR pairs are specific to \fIitem\fR. If
+\fIoption\fR is specified with no \fIvalue\fR, then the command
+returns a list describing the one named option (this list will be
+identical to the corresponding sublist of the value returned if no
+\fIoption\fR is specified). If one or more \fIoption\-value\fR pairs
+are specified, then the command modifies the given option(s) to have
+the given value(s); in this case the command returns the empty string.
+.RE
+.TP
+\fBtable extents \fIcontainer\fR \fIindex\fR
+Queries the location and dimensions of row and columns in the table.
+\fIIndex\fR can be either a row or column index or a table index.
+Returns a list of the x,y coordinates (upperleft corner) and
+dimensions (width and height) of the cell, row, or column.
+.TP
+\fBtable forget \fIwidget\fR ?\fIwidget\fR?...
+Requests that \fIwidget\fR no longer have its geometry managed.
+\fIWidget\fR is the pathname of the window currently
+managed by some table. The window will be unmapped so that it no longer
+appears on the screen. If \fIwidget\fR is not currently managed by any table,
+an error message is returned, otherwise the empty string.
+.TP
+\fBtable info \fIcontainer\fR \fIitem\fR
+Returns a list of the current configuration options for \fIitem\fR.
+The list returned is exactly in the form that might be specified to the
+\fBtable\fR command. It can be used to save and reset table
+configurations. \fIItem\fR must be one of the following.
+.RS
+.TP .75i
+\f(CWC\fIi\fR
+Specifies the column of \fIcontainer\fR to be queried. \fIItem\fR
+must be in the form \f(CWC\fIn\fR, where \fIn\fR is the index of
+the column.
+.TP
+\f(CWR\fIi\fR
+Specifies the row of \fIcontainer\fR to be queried. \fIItem\fR must be
+in the form \f(CWR\fIi\fR, where \fIi\fR is the index of the row.
+.TP
+\fIwidget\fR
+Specifies a widget of \fIcontainer\fR to be queried.
+\fIWidget\fR is the path name of a widget packed in \fIcontainer\fR.
+.TP
+No argument
+Specifies that the table itself is to be queried.
+.RE
+.TP
+\fBtable locate \fIcontainer\fR \fIx y\fR
+Returns the table index (row,column) of the cell containing the given
+screen coordinates. The \fIx\fR and \fIy\fR arguments represent
+the x and y coordinates of the sample point to be tested.
+.TP
+\fBtable containers \fR?\fIswitch arg\fR?
+Returns a list of all container windows matching a given criteria (using
+\fIswitch\fR and \fIarg\fR). If no \fIswitch\fR and \fIarg\fR
+arguments are given, the names of all container windows (only those using
+the \fBtable\fR command) are returned. The following are valid
+switches:
+.RS
+.TP
+\fB\-pattern\fR \fIpattern\fR
+Returns a list of pathnames of all container windows matching \fIpattern\fR.
+.TP
+\fB\-slave\fR \fIwindow\fR
+Returns the name of the container window of table managing \fIwindow\fR.
+\fIWindow\fR must be the path name of widget. If \fIwindow\fR is not
+managed by any table, the empty string is returned.
+.RE
+.TP
+\fBtable search \fIcontainer\fR ?\fIswitch arg\fR?...
+Returns the names of all the widgets in \fIcontainer\fR matching
+the criteria given by \fIswitch\fR and \fIarg\fR. \fIContainer\fR is
+name of the container window associated with the table to be searched.
+The name of the widget is returned if any one
+\fIswitch\fR-\fIarg\fR criteria matches. If no \fIswitch\fR-\fIarg\fR
+arguments are given, the names of all widgets managed by
+\fIcontainer\fR are returned. The following are switches are available:
+.RS
+.TP
+\fB\-pattern\fR \fIpattern\fR
+Returns the names of any names of the widgets matching
+\fIpattern\fR.
+.TP
+\fB\-span\fR \fIindex\fR
+Returns the names of widgets that span \fIindex\fR. A widget
+does not need to start at \fIindex\fR to be included.
+\fIIndex\fR must be in the form \fIrow\fR,\fIcolumn\fR, where
+\fIrow\fR and \fIcolumn\fR are valid row and column numbers.
+.TP
+\fB\-start\fR \fIindex\fR
+Returns the names of widgets that start at \fIindex\fR.
+\fIIndex\fR must be in the form \fIrow\fR,\fIcolumn\fR, where
+\fIrow\fR and \fIcolumn\fR are valid row and column numbers.
+.RE
+.SH TABLE OPTIONS
+To configure the table itself, you omit the \fIitem\fR argument
+when invoking the \fBconfigure\fR operation.
+.CS
+\fBtable configure\fR \fIcontainer\fR ?\fIoption value\fR?...
+.CE
+The following options are available for the table:
+.RS
+.TP
+\fB\-padx \fIpad\fR
+Sets how much padding to add to the left and right exteriors of the table.
+\fIPad\fR can be a list of one or two numbers. If \fIpad\fR
+has two elements, the left side of the table is padded by the first
+value and the right side by the second value. If \fIpad\fR has just
+one value, both the left and right sides are padded evenly by the
+value. The default is \f(CW0\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets how much padding to add to the top and bottom exteriors of the table.
+\fIPad\fR can be a list of one or two numbers. If
+\fIpad\fR has two elements, the area above the table is padded by
+the first value and the area below by the second value. If \fIpad\fR
+is just one number, both the top and bottom areas are padded by the
+value. The default is \f(CW0\fR.
+.TP
+\fB\-propagate \fIboolean\fR
+Indicates if the table should override the requested width and height
+of the \fIcontainer\fR window. If \fIboolean\fR is false, \fIcontainer\fR
+will not be resized. \fIContainer\fR will be its requested size. The
+default is \f(CW1\fR.
+.RE
+.SH WIDGET OPTIONS
+widgets are configured by specifying the name of the widget
+when invoking the \fBconfigure\fR operation.
+.DS
+\fBtable configure\fR \fIcontainer \fIwidget\fR ?\fIoption value\fR?...
+.DE
+\fIWidget\fR must be the path name of a window already packed in
+the table associated with \fIcontainer\fR. The following options
+are available for widgets:
+.RS
+.TP
+\fB\-anchor \fIanchor\fR
+Anchors \fIwidget\fR to a particular edge of the cell(s) it resides.
+This option has effect only if the space of the spans surrounding
+\fIwidget\fR is larger than \fIwidget\fR. \fIAnchor\fR specifies
+how \fIwidget\fR will be positioned in the space. For example, if
+\fIanchor\fR is \f(CWcenter\fR then the window is centered in the rows
+and columns it spans; if \fIanchor\fR is \f(CWw\fR then the window will
+be aligned with the leftmost edge of the span. The default is
+\f(CWcenter\fR.
+.TP
+\fB\-columnspan \fInumber\fR
+Sets the number of columns \fIwidget\fR will span.
+The default is \f(CW1\fR.
+.TP
+\fB\-columncontrol \fIcontrol\fR
+Specifies how the width of \fIwidget\fR should control the
+width of the columns it spans. \fIControl\fR is
+either \f(CWnormal\fR, \f(CWnone\fR, or \f(CWfull\fR.
+The default is \f(CWnormal\fR.
+.RS
+.TP 1i
+\f(CWnone\fR
+The width of \fIwidget\fR is not considered.
+.TP 1i
+\f(CWfull\fR
+Only the width of \fIwidget\fR will be considered when computing the
+widths of the columns.
+.TP 1i
+\f(CWnormal\fR
+Indicates that the widest widget spanning the column will determine
+the width of the span.
+.RE
+.TP
+\fB\-fill \fIfill\fR
+Specifies if \fIwidget\fR should be stretched to fill any free space
+in the span surrounding \fIwidget\fR. \fIFill\fR is either \f(CWnone\fR,
+\f(CWx\fR, \f(CWy\fR, \f(CWboth\fR. The default is \f(CWnone\fR.
+.RS
+.TP 1i
+\f(CWx\fR
+The widget can grow horizontally.
+.TP 1i
+\f(CWy\fR
+The widget can grow vertically.
+.TP 1i
+\f(CWboth\fR
+The widget can grow both vertically and horizontally.
+.TP 1i
+\f(CWnone\fR
+The widget does not grow along with the span.
+.RE
+.TP
+\fB\-ipadx \fIpixels\fR
+Sets how much horizontal padding to add internally on the left and
+right sides of \fIwidget\fR. \fIPixels\fR must be a valid screen distance
+like \f(CW2\fR or \f(CW0.3i\fR. The default is \f(CW0\fR.
+.TP
+\fB\-ipady \fIpixels\fR
+Sets how much vertical padding to add internally on the top and bottom
+of \fIwidget\fR. \fIPixels\fR must be a valid screen distance
+like \f(CW2\fR or \f(CW0.3i\fR. The default is \f(CW0\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets how much padding to add to the left and right exteriors of \fIwidget\fR.
+\fIPad\fR can be a list of one or two numbers. If \fIpad\fR
+has two elements, the left side of \fIwidget\fR is padded by the first
+value and the right side by the second value. If \fIpad\fR has just
+one value, both the left and right sides are padded evenly by the
+value. The default is \f(CW0\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets how much padding to add to the top and bottom exteriors of
+\fIwidget\fR. \fIPad\fR can be a list of one or two numbers. If
+\fIpad\fR has two elements, the area above \fIwidget\fR is padded by
+the first value and the area below by the second value. If \fIpad\fR
+is just one number, both the top and bottom areas are padded by the
+value. The default is \f(CW0\fR.
+.TP
+\fB\-reqheight \fIheight\fR
+Specifies the limits of the requested height for \fIwidget\fR.
+\fIHeight\fR is a list of bounding values. See the
+.SB BOUNDING SIZES
+section for a description of this list. By default, the height of
+\fIwidget\fR is its requested height with its internal padding
+(see the \fB\-ipady\fR option). The bounds specified by \fIheight\fR
+either override the height completely, or bound the height between two sizes.
+The default is \f(CW""\fR.
+.TP
+\fB\-reqwidth \fIwidth\fR
+Specifies the limits of the requested width for \fIwidget\fR.
+\fIWidth\fR is a list of bounding values. See the
+.SB BOUNDING SIZES
+section for a description of this list. By default, the width of
+\fIwidget\fR is its requested width with its internal padding
+(set the \fB\-ipadx\fR option). The bounds specified by \fIwidth\fR
+either override the width completely, or bound the height between two sizes.
+The default is \f(CW""\fR.
+.TP
+\fB\-rowspan \fInumber\fR
+Sets the number of rows \fIwidget\fR will span. The default is \f(CW1\fR.
+.TP
+\fB\-rowcontrol \fIcontrol\fR
+Specifies how the height of \fIwidget\fR should control the
+height of the rows it spans. \fIControl\fR is
+either \f(CWnormal\fR, \f(CWnone\fR, or \f(CWfull\fR.
+The default is \f(CWnormal\fR.
+.RS
+.TP 1i
+\f(CWnone\fR
+The height of \fIwidget\fR is not considered.
+.TP 1i
+\f(CWfull\fR
+Only the height of \fIwidget\fR will be considered when computing the
+heights of the rows.
+.TP 1i
+\f(CWnormal\fR
+Indicates that the tallest widget spanning the row will determine
+the height of the span.
+.RE
+.RE
+.SH COLUMN OPTIONS
+To configure a column in the table, specify the column index as
+\f(CWC\fIi\fR, where \fIi\fR is the index of the column to be
+configured.
+.DS
+\fBtable configure\fR \fIcontainer \f(CWC\fIi\fR ?\fIoption value\fR?...
+.DE
+If the index is specified as \f(CWC*\fR, then all columns of the table
+will be configured. The following options are available for table
+columns.
+.RS
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding to the left and right of the column.
+\fIPad\fR can be a list of one or two numbers. If \fIpad\fR has two
+elements, the left side of the column is padded by the first value and
+the right side by the second value. If \fIpad\fR has just one value,
+both the left and right sides are padded evenly by the value. The
+default is \f(CW0\fR.
+.TP
+\fB\-resize \fImode\fR
+Indicates that the column can expand or shrink from its requested width
+when the table is resized.
+\fIMode\fR must be one of the following:
+\f(CWnone\fR, \f(CWexpand\fR, \f(CWshrink\fR, or \f(CWboth\fR. If \fImode\fR is
+\f(CWexpand\fR the width of the column is expanded if there is extra space
+in the container window. If \fImode\fR is \f(CWshrink\fR its width may be
+reduced beyond its requested width if there is not enough space in the container.
+The default is \f(CWnone\fR.
+.TP
+\fB\-width \fIwidth\fR
+Specifies the limits within that the width of the column may expand
+or shrink. \fIWidth\fR is a list of bounding values. See the section
+.SB BOUNDING SIZES
+for a description of this list.
+By default there are no constraints.
+.RE
+.SH ROW OPTIONS
+To configure a row in the table, specify the row index as \f(CWR\fIi\fR,
+where \fIi\fR is the index of the row to be configured.
+.DS
+\fBtable configure\fR \fIcontainer \f(CWR\fIi\fR ?\fIoption value\fR?...
+.DE
+If the index is specified as \f(CWR*\fR, then all rows of the table will
+be configured. The following options are available for table rows.
+.RS
+.TP
+\fB\-height \fIheight\fR
+Specifies the limits of the height that the row may expand or shrink to.
+\fIHeight\fR is a list of bounding values. See the section
+.SB BOUNDING SIZES
+for a description of this list.
+By default there are no constraints.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding above and below the row. \fIPad\fR can be a list
+of one or two numbers. If \fIpad\fR has two elements, the area above
+the row is padded by the first value and the area below by the
+second value. If \fIpad\fR is just one number, both the top and
+bottom areas are padded by the value. The default is \f(CW0\fR.
+.TP
+\fB\-resize \fImode\fR
+Indicates that the row can expand or shrink from its requested height
+when the table is resized.
+\fIMode\fR must be one of the following:
+\f(CWnone\fR, \f(CWexpand\fR, \f(CWshrink\fR, or \f(CWboth\fR. If \fImode\fR is
+\f(CWexpand\fR the height of the row is expanded if there is extra space
+in the container. If \fImode\fR is \f(CWshrink\fR its height may be
+reduced beyond its requested height if there is not enough space in
+the container. The default is \f(CWnone\fR.
+.RE
+.SH BOUNDING SIZES
+Sometimes it's more useful to limit resizes to an acceptable range,
+than to fix the size to a particular value or disallow resizing
+altogether. Similar to the way the \fBwm\fR command lets you specify
+a \fBminsize\fR and \fBmaxsize\fR for a toplevel window, you can bound
+the sizes the container, a widget, row, or column may take.
+The \fB\-width\fR, \fB\-height\fR, \fB\-reqwidth\fR, and
+\fB\-reqheight\fR options, take a list of one, two, or three values.
+We can take a previous example and instead preventing resizing,
+bound the size of the scrollbars between two values.
+.CS
+table . \\
+ .title 0,0 -cspan 3 \\
+ .canvas 1,1 -fill both \\
+ .vscroll 1,2 -fill y \\
+ .hscroll 2,1 -fill x
+
+# Bound the scrollbars between 1/8 and 1/2 inch
+table configure . c2 -width { 0.125 0.5 }
+table configure . r2 -height { 0.125 0.5 }
+table configure . vscroll .hscroll -fill both
+.CE
+The scrollbars will get no smaller than 1/8 of an inch, or bigger
+than 1/2 inch. The initial size will be their requested size,
+so long as it is within the specified bounds.
+.PP
+How the elements of the list are interpreted is dependent upon the
+number of elements in the list.
+.RS
+.TP 1i
+{\fI\fR}
+Empty list. No bounds are set. The default sizing is performed.
+.TP
+{\fI x \fR}
+Fixes the size to \fIx\fR. The window or partition cannot grow or
+shrink.
+.TP
+{\fI min max \fR}
+Sets up minimum and maximum limits for the size of the window or
+partition. The window or partition can be reduced less than
+\fImin\fR, nor can it be stretched beyond \fImax\fR.
+.TP
+{\fI min max nom \fR}
+Specifies minimum and maximum size limits, but also specifies a
+nominal size \fInom\fR. This overrides the calculated size of the
+window or partition.
+.RE
+.SH MISCELLANEOUS
+Another feature is that you can put two widgets in the
+same cell of the table. This is useful when you want to add
+decorations around a widget.
+.CS
+frame .frame -bd 1 -relief sunken
+button .quit -text "Quit"
+
+# Put both the frame and the button in the same cell.
+table . \\
+ .quit 1,0 -padx 2 -pady 2 \\
+ .frame 1,0 -fill both
+.CE
+.SH LIMITATIONS
+A long standing bug in Tk (circa 1993), there is no way to detect if a
+window is already a container of a different geometry manager. This
+is usually done by accident, such as the following where all three
+widgets are arranged in the same container ".", but using different
+geometry managers.
+.CS
+ table .f1
+ ...
+ pack .f2
+ ...
+ grid .f3
+.CE
+This leads to bizarre window resizing, as each geometry manager
+applies its own brand of layout policies. When the container is a top
+level window (such as "."), your window manager may become locked
+as it responds to the never-ending stream of resize requests.
+.SH KEYWORDS
+frame, geometry manager, location, table, size
+
diff --git a/blt/man/tabset.mann b/blt/man/tabset.mann
new file mode 100644
index 00000000000..b266b3db01b
--- /dev/null
+++ b/blt/man/tabset.mann
@@ -0,0 +1,920 @@
+'\"
+'\" Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Tabset widget created by George Howlett.
+'\"
+.so man.macros
+.TH tabset n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tabset \- Create and manipulate tabset widgets
+.BE
+.SH SYNOPSIS
+\fBtabset\fR \fIpathName \fR?\fIoptions\fR?
+.SH DESCRIPTION
+The \fBtabset\fR widget displays a series of overlapping folders. Only
+the contents of one folder at a time is displayed. By clicking on the
+tab's of a folder, you can view other folders. Each folder may
+contain any Tk widget that can be automatically positioned and resized
+in the folder.
+.PP
+There's no limit to the number of folders. Tabs can be tiered or
+scrolled. Pages (i.e. embedded widgets) can be torn off and displayed
+in another toplevel widget, and also restored. A tabset can also be
+used as just a set of tabs, without a displaying any pages. You can
+bind events to individual tabs, so it's easy to add features like
+"balloon help".
+.SH INTRODUCTION
+Notebooks are a popular graphical paradigm. They allow you to organize
+many windows in a single widget. For example, you might have an
+application the displays several X-Y graphs at the same time.
+Typically, you can't pack the graphs into the same \fBframe\fR because
+they are too large. The other alternative is to pack the graphs into
+several \fBtoplevel\fR widgets, allowing them to overlap on the
+screen. The problem is that all the different toplevel windows
+clutter the screen and are difficult to manage.
+.PP
+The \fBtabset\fR widget lets organize your application by displaying
+each graph as a page in a folder of a notebook. Only one page is
+visible at a time. When you click on a tab, the folder (graph)
+corresponding to the tab is displayed in the \fBtabset\fR widget. The
+tabset also lets you temporarily tear pages out of the notebook into a
+separate toplevel widget, and put them back in the tabset later. For
+example, you could compare two graphs side-by-side by tearing them
+out, and then replace them when you are finished.
+.PP
+A tabset may contain an unlimited number of folders. If there are too
+many tabs to view, you can arrange them as multiple tiers or scroll
+the tabs. The tabset uses the conventional Tk scrollbar syntax, so you
+can attach a scrollbar too.
+.SH EXAMPLE
+You create a tabset widget with the \fBtabset\fR command.
+.CS
+# Create a new tabset
+tabset .ts -relief sunken -borderwidth 2
+.CE
+A new Tcl command \f(CW.ts\fR is also created. This command can be
+used to query and modify the tabset. For example, to change the
+default font used by all the tab labels, you use the new command and
+the tabset's \fBconfigure\fR operation.
+.CS
+# Change the default font.
+\&.ts configure \-font "fixed"
+.CE
+You can then add folders using the \fBinsert\fR operation.
+.CS
+# Create a new folder "f1"
+\&.ts insert 0 "f1"
+.CE
+This inserts the new tab named "f1" into the tabset. The index
+\f(CW0\fR indicates location to insert the new tab. You can also use
+the index \f(CWend\fR to append a tab to the end of the tabset. By
+default, the text of the tab is the name of the tab. You can change
+this by configuring the \fB\-text\fR option.
+.CS
+# Change the label of "f1"
+\&.ts tab configure "f1" -text "Tab #1"
+.CE
+The \fBinsert\fR operation lets you add one or more folders at a time.
+.CS
+\&.ts insert end "f2" -text "Tab #2" "f3" "f4"
+.CE
+The tab on each folder contains a label. A label may display both
+an image and a text string. You can reconfigure the tab's attributes
+(foreground/background colors, font, rotation, etc) using the \fBtab
+configure\fR operation.
+.CS
+# Add an image to the label of "f1"
+set image [image create photo -file stopsign.gif]
+\&.ts tab configure "f1" -image $image
+\&.ts tab configure "f2" -rotate 90
+.CE
+Each folder may contain an embedded widget to represent its contents.
+The widget to be embedded must be a child of the tabset widget. Using
+the \fB\-window\fR option, you specify the name of widget to be
+embedded. But don't pack the widget, the tabset takes care of placing
+and arranging the widget for you.
+.CS
+graph .ts.graph
+\&.ts tab configure "f1" -window ".ts.graph" \\
+ -fill both -padx 0.25i -pady 0.25i
+.CE
+The size of the folder is determined the sizes of the Tk widgets
+embedded inside each folder. The folder will be as wide as the widest
+widget in any folder. The tallest determines the height. You can use
+the tab's \fB\-pagewidth\fR and \fB\-pageheight\fR options override this.
+.PP
+Other options control how the widget appears in the folder. The
+\fB\-fill\fR option says that you wish to have the widget stretch to
+fill the available space in the folder.
+.CS
+\&.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i
+.CE
+.PP
+Now when you click the left mouse button on "f1", the
+graph will be displayed in the folder. It will be automatically
+hidden when another folder is selected. If you click on the right
+mouse button, the embedded widget will be moved into a toplevel widget
+of its own. Clicking again on the right mouse button puts it back into
+the folder.
+.PP
+If you want to share a page between two different folders, the
+\fB\-command\fR option lets you specify a Tcl command to be invoked
+whenever the folder is selected. You can reset the \fB\-window\fR
+option for the tab whenever it's clicked.
+.CS
+\&.ts tab configure "f2" -command {
+ \&.ts tab configure "f2" -window ".ts.graph"
+}
+\&.ts tab configure "f1" -command {
+ \&.ts tab configure "f1" -window ".ts.graph"
+}
+.CE
+If you have many folders, you may wish to stack tabs in multiple
+tiers. The tabset's \fB\-tiers\fR option requests a maximum
+number of tiers. The default is one tier.
+.CS
+\&.ts configure -tiers 2
+.CE
+If the tabs can fit in less tiers, the widget will use that many.
+Whenever there are more tabs than can be displayed in the maximum number
+of tiers, the tabset will automatically let you scroll the tabs. You
+can even attach a scrollbar to the tabset.
+.CS
+\&.ts configure -scrollcommand { .sbar set } -scrollincrement 20
+\&.sbar configure -orient horizontal -command { .ts view }
+.CE
+By default tabs are along the top of the tabset from left to right.
+But tabs can be placed on any side of the tabset using the \fB\-side\fR
+option.
+.CS
+# Arrange tabs along the right side of the tabset.
+\&.ts configure -side right -rotate 270
+.CE
+.SH SYNTAX
+The \fBtabset\fR command creates a new window using the \fIpathName\fR
+argument and makes it into a tabset widget.
+.DS
+\fBtabset \fIpathName \fR?\fIoption value\fR?...
+.DE
+Additional options may be specified on the command line or in the
+option database to configure aspects of the tabset such as its colors,
+font, text, and relief. The \fBtabset\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked, there
+must not exist a window named \fIpathName\fR, but \fIpathName\fR's
+parent must exist.
+.PP
+When first created, a new tabset contains no tabs. Tabs are added or
+deleted using widget operations described below. It is not necessary
+for all the tabs to be displayed in the tabset window at once;
+commands described below may be used to change the view in the window.
+Tabsets allow scrolling of tabs using the \fB\-scrollcommand\fR
+option. They also support scanning (see the \fBscan\fR operation).
+Tabs may be arranged along any side of the tabset window using the
+\fB\-side\fR option.
+.PP
+The size of the tabset window is determined the number of tiers of
+tabs and the sizes of the Tk widgets embedded inside each folder.
+The widest widget determines the width of the folder. The tallest
+determines the height. If no folders contain an embedded widget, the
+size is detemined solely by the size of the tabs.
+.PP
+You can override either dimension with the tabset's \fB\-width\fR
+and \fB\-height\fR options.
+.SH "TABSET INDICES"
+Indices refer to individual tabs/folders in the tabset. Many of
+the operations for tabset widgets take one or more indices as
+arguments. An index may take several forms:
+.TP 12
+\fInumber\fR
+Unique node id of the tab.
+.TP 12
+\fB@\fIx\fB,\fIy\fR
+Tab that covers the point in the tabset window
+specified by \fIx\fR and \fIy\fR (in screen coordinates). If no
+tab covers that point, then the index is ignored.
+.TP 12
+\fBselect\fR
+The currently selected tab. The \fBselect\fR index is
+typically changed by either clicking on the tab with the left mouse
+button or using the widget's \fBinvoke\fR operation.
+.TP 12
+\fBactive\fR
+The tab where the mouse pointer is currently located. The label
+is drawn using its active colors (see the \fB\-activebackground\fR and
+\fB\-activeforeground\fR options). The \fBactive\fR index is typically
+changed by moving the mouse pointer over a tab or using the widget's
+\fBactivate\fR operation. There can be only one active tab at a time.
+If there is no tab located under the mouse pointer, the index
+is ignored.
+.TP 12
+\fBfocus\fR
+Tab that currently has the widget's focus.
+This tab is displayed with a dashed line around its label. You can
+change this using the \fBfocus\fR operation. If no tab has focus,
+then the index is ignored.
+.TP 12
+\fBdown\fR
+Tab immediately below the tab that currently has focus,
+if there is one. If there is no tab below, the current
+tab is returned.
+.TP 12
+\fBleft\fR
+Tab immediately to the left the tab that currently has focus,
+if there is one. If there is no tab to the left, the current
+tab is returned.
+.TP 12
+\fBright\fR
+Tab immediately to the right the tab that currently has focus, if
+there is one. If there is no tab to the right, the current tab is
+returned.
+.TP 12
+\fBup\fR
+Tab immediately above, if there is one, to the tab that currently has
+focus. If there is no tab above, the current tab is returned.
+.TP 12
+\fBend\fR
+Last tab in the tabset. If there are no tabs in the tabset then the
+index is ignored.
+.LP
+Some indices may not always be available. For example, if the mouse
+is not over any tab, "active" does not have an index. For most
+tabset operations this is harmless and ignored.
+.SH "TABSET OPERATIONS"
+All \fBtabset\fR operations are invoked by specifying the widget's
+pathname, the operation, and any arguments that pertain to that
+operation. The general form is:
+.sp
+.DS
+ \fIpathName operation \fR?\fIarg arg ...\fR?
+.DE
+.sp
+\fIOperation\fR and the \fIarg\fRs determine the exact behavior of the
+command. The following operations are available for tabset widgets:
+.TP
+\fIpathName \fBactivate\fR \fIindex\fR
+Sets the active tab to the one indicated by \fIindex\fR. The
+active tab is drawn with its \fIactive\fR colors (see the
+\fB\-activebackground\fR and \fB\-activeforeground\fR options) and may
+be retrieved with the index \fBactive\fR. Only one tab may be active
+at a time. If \fIindex\fR is the empty string, then all tabs will
+be drawn with their normal foreground and background colors.
+.TP
+\fIpathName \fBbind\fR \fItagName\fR ?\fIsequence\fR? ?\fIcommand\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a tab with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on tabs, rather
+than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the default active background color for tabs. A tab is active
+when the mouse is positioned over it or set by the \fBactivate\fR
+operation. Individual tabs may override this option by setting the
+tab's \fB\-activebackground\fR option.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the default active foreground color for tabs. A tab is active
+when the mouse is positioned over it or set by the \fBactivate\fR
+operation. Individual tabs may override this option by setting the
+tab's \fB\-activeforeground\fR option.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the tabset.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines how the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-dashes \fIdashList\fR
+Sets the dash style of the focus outline. When a tab has the widget's
+focus, it is drawn with a dashed outline around its label.
+\fIDashList\fR is a list of up
+to 11 numbers that alternately represent the lengths of the dashes
+and gaps on the cross hair lines. Each number must be between 1 and
+255. If \fIdashList\fR is \f(CW""\fR, the outline will be a solid
+line. The default value is \f(CW5 2\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the default font for the text in tab labels. Individual tabs may
+override this by setting the tab's \fB\-font\fR option. The default value is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the default color of tab labels. Individual tabs may
+override this option by setting the tab's \fB\-foreground\fR option.
+The default value is \f(CWblack\fR.
+.TP
+\fB\-gap \fIsize\fR
+Sets the gap (in pixels) between tabs. The default value is \f(CW2\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. If \fIpixels\fR is
+0, then the height of the widget will be calculated based on
+the size the tabs and their pages.
+The default is \f(CW0\fR.
+.TP
+\fB\-highlightbackground \fIcolor\fR
+Sets the color to display in the traversal highlight region when
+the tabset does not have the input focus.
+.TP
+\fB\-highlightcolor \fIcolor\fR
+Sets the color to use for the traversal highlight rectangle that is
+drawn around the widget when it has the input focus.
+The default is \f(CWblack\fR.
+.TP
+\fB\-highlightthickness \fIpixels\fR
+Sets the width of the highlight rectangle to draw around the outside of
+the widget when it has the input focus. \fIPixels\fR is a non-negative
+value and may have any of the forms acceptable to \fBTk_GetPixels\fR.
+If the value is zero, no focus highlight is drawn around the widget.
+The default is \f(CW2\fR.
+.TP
+\fB\-outerpad \fIpixels\fR
+Padding around the exterior of the tabset and folder.
+.TP
+\fB\-pageheight \fIpixels\fR
+Sets the requested height of the page. The page is the area under the
+tab used to display the page contents. If \fIpixels\fR is \f(CW0\fR,
+the maximum height of all embedded tab windows is used. The default
+is \f(CW0\fR.
+.TP
+\fB\-pagewidth \fIpixels\fR
+Sets the requested width of the page. The page is the area under the
+tab used to display the page contents. If \fIpixels\fR is \f(CW0\fR,
+the maximum width of all embedded tab windows is used. The default
+is \f(CW0\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the tabset widget. \fIRelief\fR
+specifies how the tabset should appear relative to widget that
+it is packed into; for example, \f(CWraised\fR means the tabset should
+appear to protrude. The default is \f(CWsunken\fR.
+.TP
+\fB\-rotate \fItheta\fR
+Specifies the degrees to rotate text in tab labels.
+\fITheta\fR is a real value representing the number of degrees
+to rotate the tick labels. The default is \f(CW0.0\fR degrees.
+.TP
+\fB\-samewidth \fIboolean\fR
+Indicates if each tab should be the same width. If true, each tab will
+be as wide as the widest tab. The default is \f(CWno\fR.
+.TP
+\fB\-scrollcommand \fIstring\fR
+Specifies the prefix for a command for communicating with
+scrollbars. Whenever the view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-scrollincrement \fIpixels\fR
+Sets the smallest number of pixels to scroll the tabs.
+If \fIpixels\fR is greater than 0, this sets the units for
+scrolling (e.g., when you the change the view by clicking
+on the left and right arrows of a scrollbar).
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the color to use when displaying background of the selected
+tab. Individual tabs can override this option by setting the tab's
+\fB\-selectbackground\fR option.
+'\".TP
+'\" \fB\-selectborderwidth \fIpixels\fR
+'\" Sets the width of the raised 3-D border to draw around the label of
+'\" the selected tab. \fIPixels\fR must be a non-negative value.
+'\" The default value is \f(CW1\fR.
+.TP
+\fB\-selectcommand \fIstring\fR
+Specifies a default Tcl script to be associated with tabs. This
+command is typically invoked when left mouse button is released over
+the tab. Individual tabs may override this with the tab's
+\fB\-command\fR option. The default value is \f(CW""\fR.
+.TP
+\fB\-selectforeground \fIcolor\fB
+Sets the default color of the selected tab's text label.
+Individual tabs can override this option by setting the tab's
+\fB\-selectforeground\fR option. The default value is \f(CWblack\fR.
+.TP
+\fB\-selectpad \fIpixels\fB
+Specifies extra padding to be displayed around the selected tab.
+The default value is \f(CW3\fR.
+.TP
+\fB\-side \fIside\fB
+Specifies the side of the widget to place tabs. The following
+values are valid for \fIside\fR. The default value is \f(CWtop\fR.
+.RS
+.TP 1i
+\f(CWtop\fR
+Tabs are drawn along the top.
+.TP 1i
+\f(CWleft\fR
+Tabs are drawn along the left side.
+.TP 1i
+\f(CWright\fR
+Tabs are drawn along the right side.
+.TP 1i
+\f(CWboth\fR
+Tabs are drawn along the bottom side.
+.RE
+.TP
+\fB\-slant \fIslant\fR
+Specifies if the tabs should be slanted 45 degrees on the left and/or
+right sides. The following values are valid for \fIslant\fR. The default
+is \f(CWnone\fR.
+.RS
+.TP 1i
+\f(CWnone\fR
+Tabs are drawn as a rectangle.
+.TP 1i
+\f(CWleft\fR
+The left side of the tab is slanted.
+.TP 1i
+\f(CWright\fR
+The right side of the tab is slanted.
+.TP 1i
+\f(CWboth\fR
+Boths sides of the tab are slanted.
+.RE
+.TP
+\fB\-tabbackground \fIcolor\fR
+Sets the default background color of tabs.
+Individual tabs can override this option by setting the tab's
+\fB\-background\fR option.
+.TP
+\fB\-tabborderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the tab. The
+\fB\-tabrelief\fR option determines how the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-tabforeground \fIcolor\fR
+Specifies the color to use when displaying a tab's label.
+Individual tabs can override this option by setting the tab's
+\fB\-foreground\fR option.
+.TP
+\fB\-tabrelief \fIrelief\fR
+Specifies the 3-D effect for both tabs and folders. \fIRelief\fR
+specifies how the tabs should appear relative to background of the
+widget; for example, \f(CWraised\fR means the tab should
+appear to protrude. The default is \f(CWraised\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts decide whether to focus on the window.
+The default is \f(CW1\fR.
+.TP
+\fB\-tearoff \fIboolean\fR
+.TP
+\fB\-textside \fIside\fB
+If both images and text are specified for a tab, this option determines on
+which side of the tab the text is to be displayed. The
+valid sides are \f(CWleft\fR, \f(CWright\fR, \f(CWtop\fR, and
+\f(CWbottom\fR. The default value is \f(CWleft\fR.
+.TP
+\fB\-tiers \fInumber\fB
+Specifies the maximum number of tiers to use to display the tabs.
+The default value is \f(CW1\fR.
+.TP
+\fB\-tile \fIimage\fR
+Specifies a tiled background for the widget. If \fIimage\fR isn't
+\f(CW""\fR, the background is tiled using \fIimage\fR.
+Otherwise, the normal background color is drawn (see the
+\fB\-background\fR option). \fIImage\fR must be an image created
+using the Tk \fBimage\fR command. The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Specifies the requested width of the widget. If \fIpixels\fR is
+0, then the width of the widget will be calculated based on
+the size the tabs and their pages.
+The default is \f(CW0\fR.
+.RE
+.TP
+\fIpathName \fBdelete \fIfirst \fR?\fIlast\fR?
+Deletes one or more tabs from the tabset. \fIFirst\fR and \fIlast\fR
+are the first and last indices, defining a range of tabs to be deleted.
+If \fIlast\fR isn't specified, then only the tab at \fIfirst\fR
+is deleted.
+.TP
+\fIpathName \fBfocus \fIindex\fR
+Designates a tab to get the widget's focus. This tab is displayed
+with a dashed line around its label.
+.TP
+\fIpathName \fBget\fR \fIindex\fR
+Returns the name of the tab. The value of \fIindex\fR may
+be in any form described in the section
+.SB "TABSET INDICES".
+.TP
+\fIpathName \fBindex\fR ?\fIflag\fR? \fIstring\fR
+Returns the node id of the tab specified by \fIstring\fR. If
+\fIflag\fR is \fB\-name\fR, then \fIstring\fR is the name of a tab.
+If \fIflag\fR is \fB\-index\fR, \fIstring\fR is an index such as
+"active" or "focus". If \fIflag\fR isn't specified, it defaults to
+\fB\-index\fR.
+.TP
+\fIpathName \fBinsert\fR \fIposition \fIname\fR ?\fIoption value\fR?...
+Inserts new tabs into the tabset. Tabs are inserted just before the
+tab given by \fIposition\fR. \fIPosition\fR may be either a number,
+indicating where in the list the new tab should be added, or \fBend\fR,
+indicating that the new tab is to be added the end of the list.
+\fIName\fR is the symbolic name of the tab. \fIBe careful not to use
+a number. Otherwise the tabset will confuse it with tab indices\fR. Returns
+a list of indices for all the new tabs.
+.TP
+\fIpathName \fBinvoke \fIindex\fR
+Selects the tab given by \fIindex\fR, maps the tab's embedded widget, and
+invokes the Tcl command associated with the tab, if there is one.
+The return value is the return value from the Tcl command, or an empty
+string if there is no command associated with the tab.
+This command is ignored if the tab's state (see the \fB\-state\fR option)
+is disabled.
+.TP
+\fIpathName \fBmove\fR \fIindex\fR \fBbefore\fR|\fBafter\fR \fIindex\fR
+Moves the tab \fIindex\fR to a new position in the tabset.
+.TP
+\fIpathName \fBnearest\fR \fIx\fR \fIy\fR
+Returns the name of the tab nearest to given X-Y screen coordinate.
+.TP
+\fIpathName \fBscan\fR \fIoption args\fR
+This command implements scanning on tabsets. It has
+two forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBscan mark \fIx y\fR
+Records \fIx\fR and \fIy\fR and the current view in the tabset
+window; used with later \fBscan dragto\fR commands.
+Typically this command is associated with a mouse button press in
+the widget. It returns an empty string.
+.TP
+\fIpathName \fBscan dragto \fIx y\fR.
+This command computes the difference between its \fIx\fR and \fIy\fR
+arguments and the \fIx\fR and \fIy\fR arguments to the last
+\fBscan mark\fR command for the widget.
+It then adjusts the view by 10 times the
+difference in coordinates. This command is typically associated
+with mouse motion events in the widget, to produce the effect of
+dragging the list at high speed through the window. The return
+value is an empty string.
+.RE
+.TP
+\fIpathName \fBsee \fIindex\fR
+Scrolls the tabset so that the tab
+\fIindex\fR is visible in the widget's window.
+.TP
+\fIpathName \fBsize\fR
+Returns the number of tabs in the tabset.
+.TP
+\fIpathName \fBtab \fIoperation\fR ?\fIargs\fR?
+See the
+.SB "TAB OPERATIONS"
+section below.
+.TP
+\fIpathName \fBview \fIargs\fR
+This command queries or changes the position of the
+tabset in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBview\fR
+Returns a list of two numbers between 0.0 and
+1.0 that describe the amount and position of the tabset that is
+visible in the window. For example, if \fIview\fR is "0.2 0.6", 20%
+of the tabset's text is off-screen to the left, 40% is visible in the
+window, and 40% of the tabset is off-screen to the right. These are
+the same values passed to scrollbars via the \fB\-scrollcommand\fR
+option.
+.TP
+\fIpathName \fBview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the tabset text is off-screen to the left.
+\fIfraction\fR must be a number between 0.0 and 1.0.
+.TP
+\fIpathName \fBview scroll \fInumber what\fR
+This command shifts the view in the window (left/top or right/bottom)
+according to \fInumber\fR and \fIwhat\fR. \fINumber\fR must be an
+integer. \fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an
+abbreviation of these. If \fIwhat\fR is \fBunits\fR, the view adjusts
+left or right by \fInumber\fR scroll units (see the
+\fB\-scrollincrement\fR option). ; if it is \fBpages\fR then the view
+adjusts by \fInumber\fR widget windows. If \fInumber\fR is negative
+then tabs farther to the left become visible; if it is positive then
+tabs farther to the right become visible.
+.RE
+.SH "TAB OPERATIONS"
+.TP
+\fIpathName \fBtab cget\fR \fInameOrIndex\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBtab configure\fR
+operation described below.
+.TP
+\fIpathName \fBtab configure\fR \fInameOrIndex\fR ?\fInameOrIndex\fR...? \fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of one or more tabs.
+If no \fIoption\fR is specified, this operation returns a list
+describing all the available options for \fInameOrIndex\fR.
+\fINameOrIndex\fR can be either the name of a tab or its index. Names
+of tabs take precedence over their indices. That means a tab named
+\fIfocus\fR is picked over the "focus" tab.
+.P
+If \fIoption\fR is specified, but not \fIvalue\fR, then a list describing the
+one named option is returned. If one or more \fIoption\-value\fR pairs
+are specified, then each named tab (specified by \fInameOrIndex\fR) will
+have its configurations option(s) set the given value(s). In
+this last case, the empty string is returned.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the active background color for \fInameOrIndex\fR. A tab is active
+when the mouse is positioned over it or set by the \fBactivate\fR
+operation. This overrides the widget's \fB-activebackground\fR
+option.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the default active foreground color \fInameOrIndex\fR. A tab is "active"
+when the mouse is positioned over it or set by the \fBactivate\fR
+operation. Individual tabs may override this option by setting the
+tab's \fB-activeforeground\fR option.
+.TP
+\fB\-anchor \fIanchor\fR
+Anchors the tab's embedded widget to a particular edge of the folder.
+This option has effect only if the space in the folder surrounding the
+embedded widget is larger than the widget itself. \fIAnchor\fR specifies
+how the widget will be positioned in the extra space. For example, if
+\fIanchor\fR is \f(CWcenter\fR then the window is centered in the folder
+; if \fIanchor\fR is \f(CWw\fR then the window will
+be aligned with the leftmost edge of the folder. The default value is
+\f(CWcenter\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color for \fInameOrIndex\fR. Setting this option overides the
+widget's \fB\-tabbackground\fR option.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for this tab. \fITagList\fR is a list of
+binding tag names. The tags and their order will determine how
+commands for events in tabs are invoked. Each tag in the list matching
+the event sequence will have its Tcl command executed. Implicitly the
+name of the tab is always the first tag in the list. The default value is
+\f(CWall\fR.
+.TP
+\fB\-command \fIstring\fR
+Specifies a Tcl script to be associated with \fInameOrIndex\fR. This
+command is typically invoked when left mouse button is released over
+the tab. Setting this option overrides the widget's
+\fB\-selectcommand\fR option.
+.TP
+\fB\-data \fIstring\fR
+Specifies a string to be associated with \fInameOrIndex\fR.
+This value isn't used in the widget code. It may be used in Tcl bindings
+to associate extra data (other than the image or text) with the
+tab. The default value is \f(CW""\fR.
+.TP
+\fB\-fill \fIfill\fR
+If the space in the folder surrounding the tab's embedded widget is
+larger than the widget, then \fIfill\fR indicates if the embedded widget
+should be stretched to occupy the extra space. \fIFill\fR is either
+\f(CWnone\fR,
+\f(CWx\fR, \f(CWy\fR, \f(CWboth\fR. For example, if \fIfill\fR is \f(CWx\fR,
+then the widget is stretched horizontally. If \fIfill\fR is \f(CWy\fR,
+the widget is stretched vertically. The default is \f(CWnone\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for the text in tab labels. If \fIfontName\fR is not
+the empty string, this overrides the tabset's \fB\-font\fR option.
+The default value is \f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the color of the label for \fInameOrIndex\fR. If \fIcolor\fR
+is not the empty string, this overrides the widget's \fB\-tabforeground\fR
+option. The default value is \f(CW""\fR.
+.TP
+\fB\-image \fIimageName\fR
+Specifies the image to be drawn in label for \fInameOrIndex\fR.
+If \fIimage\fR is \f(CW""\fR, no image will be drawn. Both text and
+images may be displayed at the same time in tab labels.
+The default value is \f(CW""\fR.
+.TP
+\fB\-ipadx \fIpad\fR
+Sets the padding to the left and right of the label.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the label is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default value is \f(CW0\fR.
+.TP
+\fB\-ipady \fIpad\fR
+Sets the padding to the top and bottom of the label.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the top of the label is padded by the first
+distance and the bottom by the second. If \fIpad\fR has just one
+distance, both the top and bottom sides are padded evenly. The
+default value is \f(CW0\fR.
+.TP
+\fB\-padx \fIpad\fR
+Sets the padding around the left and right of the embedded widget, if
+one exists.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the widget is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default value is \f(CW0\fR.
+.TP
+\fB\-pady \fIpad\fR
+Sets the padding around the top and bottom of the embedded widget, if
+one exists.
+\fIPad\fR can be a list of one or two screen distances. If \fIpad\fR
+has two elements, the top of the widget is padded by the first
+distance and the bottom by the second. If \fIpad\fR has just one
+distance, both the top and bottom sides are padded evenly. The
+default value is \f(CW0\fR.
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the color to use when displaying background of the selected
+tab. If \fIcolor\fR is not the empty string, this overrides the
+widget's \fB\-selectbackground\fR option. The default value is
+\f(CW""\fR.
+.TP
+\fB\-shadow \fIcolor\fR
+Sets the shadow color for the text in the tab's label. Drop shadows
+are useful when both the foreground and background of the tab
+have similar color intensities.
+If \fIcolor\fR is the empty string, no shadow is drawn.
+The default value is \f(CW""\fR.
+.TP
+\fB\-state \fIstate\fR
+Sets the state of the tab. If \fIstate\fR is \f(CWdisable\fR the
+text of the tab is drawn as engraved and operations on the tab
+(such as \fBinvoke\fR and \fBtab tearoff\fR) are ignored.
+The default is \f(CWnormal\fR.
+.TP
+\fB\-stipple \fIbitmap\fR
+Specifies a stipple pattern to use for the background of the folder
+when the window is torn off.
+\fIBitmap\fR specifies a bitmap to use as the stipple
+pattern. The default is \f(CWBLT\fR.
+.TP
+\fB\-text \fItext\fR
+Specifies the text of the tab's label. The exact way the text is
+drawn may be affected by other options such as \fB\-state\fR or
+\fB\-rotate\fR.
+.TP
+\fB\-window \fIpathName\fR
+Specifies the widget to be embedded into the tab. \fIPathName\fR must
+be a child of the \fBtabset\fR widget. The tabset will "pack" and
+manage the size and placement of \fIpathName\fR. The default value
+is \f(CW""\fR.
+.TP
+\fB\-windowheight \fIpixels\fR
+Sets the requested height of the page. The page is the area under the
+tab used to display the page contents. If \fIpixels\fR is \f(CW0\fR,
+the maximum height of all embedded tab windows is used. The default
+is \f(CW0\fR.
+.TP
+\fB\-windowwidth \fIpixels\fR
+Sets the requested width of the page. The page is the area under the
+tab used to display the page contents. If \fIpixels\fR is \f(CW0\fR,
+the maximum width of all embedded tab windows is used. The default
+is \f(CW0\fR.
+.RE
+.TP
+\fIpathName \fBtab names\fR ?\fIpattern\fR?
+Returns the names of all the tabs matching the given pattern. If
+no \fIpattern\fR argument is provided, then all tab names are returned.
+.TP
+\fIpathName \fBtab tearoff \fIindex\fR ?\fInewName\fR?
+Reparents the widget embedded into \fIindex\fR, placing
+it inside of \fInewName\fR. \fINewName\fR is either the name of
+an new widget that will contain the embedded widget or the name
+of the \fBtabset\fR widget. It the last case, the embedded widget
+is put back into the folder.
+.sp
+If no \fInewName\fR argument is provided, then the name of the current
+parent of the embedded widget is returned.
+.SH "DEFAULT BINDINGS"
+.PP
+BLT automatically generates class bindings that supply tabsets their
+default behaviors. The following event sequences are set by default
+for tabsets (via the class bind tag \f(CWTabset\fR):
+.IP \fB<ButtonPress-2>\fR
+.IP \fB<B2-Motion>\fR
+.IP \fB<ButtonRelease-2>\fR
+Mouse button 2 may be used for scanning.
+If it is pressed and dragged over the tabset, the contents of
+the tabset drag at high speed in the direction the mouse moves.
+.IP \fB<KeyPress-Up>\fR
+.IP \fB<KeyPress-Down>\fR
+The up and down arrow keys move the focus to the tab immediately above
+or below the current focus tab. The tab with focus is drawn
+with the a dashed outline around the tab label.
+.IP \fB<KeyPress-Left>\fR
+.IP \fB<KeyPress-Right>\fR
+The left and right arrow keys move the focus to the tab immediately to the left
+or right of the current focus tab. The tab with focus is drawn
+with the a dashed outline around the tab label.
+.IP \fB<KeyPress-space>\fR
+.IP \fB<KeyPress-Return>\fR
+The space and return keys select the current tab given focus.
+When a folder is selected, it's command is invoked and the
+embedded widget is mapped.
+.PP
+Each tab, by default, also has a set of bindings (via the tag
+\f(CWall\fR). These bindings may be reset using the tabset's
+\fBbind\fR operation.
+.IP \fB<Enter>\fR
+.IP \fB<Leave>\fR
+When the mouse pointer enters a tab, it is activated (i.e. drawn in
+its active colors) and when the pointer leaves, it is redrawn in
+its normal colors.
+.IP \fB<ButtonRelease-1>\fR
+Clicking with the left mouse button on a tab causes the tab to be
+selected and its Tcl script (see the \fB\-command\fR or
+\fB\-selectcommand\fR options) to be invoked. The folder and any embedded
+widget (if one is specified) is automatically mapped.
+.IP \fB<ButtonRelease-3>\fR
+.IP \fB<Control-ButtonRelease-1>\fR
+Clicking on the right mouse button (or the left mouse button with the
+Control key held down) tears off the current page into its own toplevel
+widget. The embedded widget is re-packed into a new toplevel and
+an outline of the widget is drawn in the folder. Clicking again
+(toggling) will reverse this operation and replace the page back in
+the folder.
+.SH "BIND TAGS"
+You can bind commands to tabs that are triggered when a particular
+event sequence occurs in them, much like canvas items in Tk's
+canvas widget. Not all event sequences are valid. The only binding
+events that may be specified are those related to the mouse and
+keyboard (such as \fBEnter\fR, \fBLeave\fR, \fBButtonPress\fR,
+\fBMotion\fR, and \fBKeyPress\fR).
+.PP
+It is possible for multiple bindings to match a particular event.
+This could occur, for example, if one binding is associated with the
+tab name and another is associated with the tab's tags
+(see the \fB\-bindtags\fR option). When this occurs, all the
+matching bindings are invoked. A binding associated with the tab
+name is invoked first, followed by one binding for each of the tab's
+bindtags. If there are multiple matching bindings for a single tag,
+then only the most specific binding is invoked. A continue command
+in a binding script terminates that script, and a break command
+terminates that script and skips any remaining scripts for the event,
+just as for the bind command.
+.PP
+The \fB\-bindtags\fR option for tabs controls addition tag names that
+can be matched. Implicitly the first tag for each tab is its name.
+Setting the value of the \fB\-bindtags\fR option doesn't change this.
+.SH KEYWORDS
+tabset, widget
diff --git a/blt/man/tile.mann b/blt/man/tile.mann
new file mode 100644
index 00000000000..d8717ca8f44
--- /dev/null
+++ b/blt/man/tile.mann
@@ -0,0 +1,108 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Tile command created by George Howlett.
+'\"
+.so man.macros
+.TH tile n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tile \- Tiling versions of Tk widgets
+.SH SYNOPSIS
+.sp
+\fBtile::button \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::checkbutton \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::frame \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::label \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::radiobutton \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::scrollbar \fIpathName\fR \fIoption value\fR...
+.sp
+\fBtile::toplevel \fIpathName\fR \fIoption value\fR...
+.sp
+.BE
+.SH DESCRIPTION
+The tile widgets let you create textured backgrounds. The texture is
+a Tk image which is tiled over the entire background of the widget.
+.SH INTRODUCTION
+With the advent of Tk 4.0, images are now easy to create and use in
+applications. Images add interest to applications and they convey
+more information. But one area where Tk hasn't taken advantage of
+images is using images as textures for widgets. Since tiling is a
+standard feature of windowing systems, it's very easy to use images
+as textures.
+.PP
+The tile widgets take the standard Tk 4.0 widgets and add tiling
+configuration options to them. Textures are specified by the name
+of the image you wish to be tiled across the background of the widget.
+.SH EXAMPLE
+To add tiling to a widget, you simply create an image using
+Tk's \fBimage\fR command and use the image name as the value for
+the \fB\-tile\fR configuration option of the widget.
+.CS
+image create photo my_texture -file tan_paper.gif
+blt::tile::frame .f -tile my_texture
+.CE
+The image \f(CWmy_texture\fR is added to the frame.
+If \f(CWmy_texture\fR is updated, so will the widget background.
+.CS
+image create photo my_texture -file rain.gif
+.CE
+The tile widget commands reside in the "blt::tile" namespace, so
+as not to collide with the normal Tk widgets.
+An easy way to add tiling to existing programs is to import
+the tile widget commands into the global namespace.
+.CS
+image create photo my_texture -file tan_paper.gif
+namespace import -force blt::tile::*
+frame .f -tile my_texture
+.CE
+To use one image for all texturing, you can use the "Tile" option
+class name to specify the same image for all tile widgets.
+.CS
+image create photo my_texture -file tan_paper.gif
+option add *Tile my_texture
+.CE
+.SH OPTIONS
+The following configurations options are added to the widgets. If
+a \fB\-tile\fB or \fB\-activetile\fR option is specified, it overrides
+the background color of the widget.
+.TP
+\fB\-activetile \fIimage\fR
+Specifies a textured background to display when the widget is active.
+This option is available for the \fBtilebutton\fR,
+\fBtilecheckbutton\fR, \fBtileradiobutton\fR, and \fBtilescrollbar\fR
+widgets. \fIImage\fR is the name an image created using Tk's
+\fBimage\fR command. The background of the widget is tiled with
+\fIimage\fR. If \fIimage\fR is \f(CW""\fR, then the active background
+color is displayed. The default is \f(CW""\fR.
+.TP
+\fB\-tile \fIimage\fR
+Specifies a textured background to display for the widget.
+\fIImage\fR is the name an image created using Tk's \fBimage\fR
+command. The background of the widget is tiled with \fIimage\fR. If
+\fIimage\fR is \f(CW""\fR, then the normal background color is
+displayed. The default is \f(CW""\fR.
+.SH KEYWORDS
+tile, texture, button, label, radiobutton, checkbutton, scrollbar, frame, toplevel
diff --git a/blt/man/tree.mann b/blt/man/tree.mann
new file mode 100644
index 00000000000..b284c49e72c
--- /dev/null
+++ b/blt/man/tree.mann
@@ -0,0 +1,897 @@
+'\"
+'\" Copyright 1991-1997 by Lucent Technologies, Inc.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Tree command created by George Howlett.
+'\"
+.so man.macros
+.TH tree n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tree \- Create and manage tree data objects.
+.SH SYNOPSIS
+\fBblt::tree create \fR?\fItreeName\fR?
+.sp
+\fBblt::tree destroy\fR \fItreeName\fR...
+.sp
+\fBblt::tree names\fR \fR?\fIpattern\fR?
+.BE
+.SH DESCRIPTION
+The \fBtree\fR command creates tree data objects. A \fItree object\fR
+is general ordered tree of nodes. Each node has both a label and a
+key-value list of data. Data can be heterogeneous, since nodes do not
+have to contain the same data keys. It is associated with a Tcl
+command that you can use to access and modify the its structure and
+data. Tree objects can also be managed via a C API.
+.SH INTRODUCTION
+
+.SH EXAMPLE
+
+.SH SYNTAX
+.TP
+\fBtree create\fR ?\fItreeName\fR?
+Creates a new tree object. The name of the new tree is returned. If
+no \fItreeName\fR argument is present, then the name of the tree is
+automatically generated in the form "\f(CWtree0\fR", "\f(CWtree1\fR",
+etc. If the substring "\f(CW#auto\fR" is found in \fItreeName\fR, it
+is automatically substituted by a generated name. For example, the
+name \f(CW.foo.#auto.bar\fR will be translated to \f(CW.foo.tree0.bar\fR.
+.sp
+A new Tcl command (by the same name as the tree) is also created.
+Another Tcl command or tree object can not already exist as
+\fItreeName\fR. If the Tcl command is deleted, the tree will also be
+freed. The new tree will contain just the root node. Trees are by
+default, created in the current namespace, not the global namespace,
+unless \fItreeName\fR contains a namespace qualifier, such as
+"\f(CWfred::myTree\fR".
+.TP
+\fBtree destroy\fR \fItreeName\fR...
+Releases one of more trees. The Tcl command associated with
+\fItreeName\fR is also removed. Trees are reference counted. The
+internal tree data object isn't destroyed until no one else is using
+the tree.
+.TP
+\fBtree names \fR?\fIpattern\fR?
+Returns the names of all tree objects. if a \fIpattern\fR argument
+is given, then the only those trees whose name matches pattern will
+be listed.
+.SH NODE IDS AND TAGS
+Nodes in a tree object may be referred in either of two ways: by id or by
+tag. Each node has a unique serial number or id that is assigned to that
+node when it's created. The id of an node never changes and id numbers
+are not re-used.
+.PP
+A node may also have any number of tags associated with it. A tag is
+just a string of characters, and it may take any form except that of
+an integer. For example, "\f(CWx123\fR" is valid, but "\f(CW123\fR"
+isn't. The same tag may be associated with many different nodes.
+This is commonly done to group nodes in various interesting ways.
+.sp
+There are two built-in tags: The tag \fBall\fR is implicitly
+associated with every node in the tree. It may be used to invoke
+operations on all the nodes in the tree. The tag \fBroot\fR is
+managed automatically by the tree object. It applies to the node
+currently set as root.
+.sp
+When specifying nodes in tree object commands, if the specifier is an
+integer then it is assumed to refer to the single node with that id.
+If the specifier is not an integer, then it is assumed to refer to all
+of the nodes in the tree that have a tag matching the specifier. The
+symbol \fInode\fR is used below to indicate that an argument specifies
+either an id that selects a single node or a tag that selects zero or
+more nodes. Many tree commands only operate on a single node at a
+time; if \fInode\fR is specified in a way that names multiple items, then
+an error "refers to more than one node" is generated.
+.SH NODE MODIFIERS
+You can also specify node in relation to another node by appending one
+or more modifiers to the node id or tag. A modifier refers to a node
+in relation to the specified node. For example,
+"\f(CWroot->firstchild\fR"
+selects the first subtree of the root node.
+.PP
+The following modifiers are available:
+.RS
+.TP 1i
+\fBfirstchild\fR
+Selects the first child of the node.
+.TP 1i
+\fBlastchild\fR
+Selects the last child of the node.
+.TP 1i
+\fBnext\fR
+Selects the next node in preorder to the node.
+.TP 1i
+\fBnextsibling\fR
+Selects the next sibling of the node.
+.TP 1i
+\fBparent\fR
+Selects the parent of the node.
+.TP 1i
+\fBprevious\fR
+Selects the previous node in preorder to the node.
+.TP 1i
+\fBprevsibling\fR
+Selects the previous sibling of the node.
+.TP 1i
+"\fIlabel\fR"
+Selects the node whose label is \fIlabel\fR. Enclosing \fIlabel\fR in
+quotes indicates to always search for a node by its label (for example,
+even if the node is labeled "parent").
+.RE
+.sp
+It's an error the node can't be found. For example,
+\fBlastchild\fR and \fBfirstchild\fR will generate errors if the node
+has no children. The exception to this is the \fBindex\fR operation.
+You can use \fBindex\fR to test if a modifier is valid.
+.SH TREE OPERATIONS
+Once you create a tree object, you can use its Tcl command
+to query or modify it. The
+general form is
+.DS
+\fItreeName\fR \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for trees are listed below.
+.TP
+\fItreeName\fR \fBancestor\fR \fInode1\fR \fInode2\fR
+Returns the mutual ancestor of the two nodes \fInode1\fR and
+\fInode2\fR. The ancestor can be one of the two nodes. For example,
+if \fInode1\fR and \fInode2\fR are the same nodes, their ancestor is
+\fInode1\fR.
+.TP
+\fItreeName\fR \fBapply\fR \fInode\fR ?\fIswitches\fR?
+Runs commands for all nodes matching the criteria given by
+\fIswitches\fR for the subtree designated by \fInode\fR. By default
+all nodes match, but you can set switches to narrow the match. This
+operation differs from \fBfind\fR in two ways: 1) Tcl commands can be
+invoked both pre- and post-traversal of a node and 2) the tree is
+always traversed in depth first order.
+.sp
+The \fB\-exact\fR, \fB\-glob\fR,
+and \fB\-regexp\fR switches indicate both what kind of pattern matching
+to perform and the pattern. Pattern matching is done, by default, against
+each node's label. But if the \fB\-path\fR switch is present, it
+will match the full path of the node (a list containing the labels of
+the node's ancestors too). If the \fB\-key\fR switch is
+used, it designates the data field to be matched.
+.sp
+The valid switches are listed
+below:
+.RS
+.TP 1i
+\fB\-depth\fR \fInumber\fR
+Descend at most \fInumber\fR (a non-negative integer) levels
+If \fInumber\fR is \f(CW1\fR this means only apply the tests
+to the children of \fInode\fR.
+.TP 1i
+\fB\-exact\fR \fIstring\fR
+Matches each node using \fIstring\fR. The node must match \fIstring\fR
+exactly.
+.TP 1i
+\fB\-glob\fR \fIstring\fR
+Test each node to \fIstring\fR using global pattern
+matching. Matching is done in a fashion similar to that used by the
+C-shell.
+.TP 1i
+\fB\-invert\fR
+Select non-matching nodes. Any node that \fIdoesn't\fR match
+the given criteria will be selected.
+.TP 1i
+\fB\-key\fR \fIkey\fR
+If pattern matching is selected (using the \fB\-exact\fR,
+\fB\-glob\fR, or \fB\-regexp\fR switches), compare the values of the
+data field keyed by \fIkey\fR instead of the node's label. If no
+pattern matching switches are set, then any node with this data key
+will match.
+.TP 1i
+\fB\-leafonly\fR
+Only test nodes with no children.
+.TP 1i
+\fB\-nocase\fR
+Ignore case when matching patterns.
+.TP 1i
+\fB\-path\fR
+Use the node's full path when comparing nodes.
+.TP 1i
+\fB\-precommand\fR \fIcommand\fR
+Invoke \fIcommand\fR for each matching node. Before \fIcommand\fR is
+invoked, the id of the node is appended. You can control
+processing by the return value of \fIcommand\fR. If \fIcommand\fR
+generates an error, processing stops and the \fBfind\fR operation
+returns an error. But if \fIcommand\fR returns \fBbreak\fR, then
+processing stops, no error is generated. If \fIcommand\fR returns
+\fBcontinue\fR, then processing
+stops on that subtree and continues on the next.
+.TP 1i
+\fB\-postcommand\fR \fIcommand\fR
+Invoke \fIcommand\fR for each matching node. Before \fIcommand\fR is
+invoked, the id of the node is appended. You can control
+processing by the return value of \fIcommand\fR. If \fIcommand\fR
+generates an error, processing stops and the \fBfind\fR operation
+returns an error. But if \fIcommand\fR returns \fBbreak\fR, then
+processing stops, no error is generated. If \fIcommand\fR returns
+\fBcontinue\fR, then processing
+stops on that subtree and continues on the next.
+.TP 1i
+\fB\-regexp\fR \fIstring\fR
+Test each node using \fIstring\fR as a regular expression pattern.
+.TP 1i
+\fB\-tag\fR \fIstring\fR
+Only test nodes that have the tag \fIstring\fR.
+.RE
+.TP
+\fItreeName\fR \fBattach\fR \fItreeObject\fR
+Attaches to an existing tree object \fItreeObject\fR. This is for cases
+where the tree object was previously created via the C API. The current
+tree associated with \fItreeName\fR is discarded. In addition, the
+current set of tags, notifier events, and traces are removed.
+.TP
+\fItreeName\fR \fBchildren\fR \fInode\fR
+Returns a list of children for \fInode\fR. If \fInode\fR is a leaf,
+then an empty string is returned.
+.TP
+\fItreeName\fR \fBcopy\fR \fIsrcNode\fR ?\fIdestTree\fR? \fIparentNode\fR ?\fIswitches\fR?
+Copies \fIsrcNode\fR into \fIparentNode\fR. Both nodes \fIsrcNode\fR and
+\fIparentNode\fR must already exist. The id of the new node is
+returned. You can copy from one tree to another. If a \fIdestTree\fR
+argument is present, it indicates the name of the destination tree.
+By default both the source and destination trees are the same. The valid
+\fIswitches\fR are listed below:
+.RS
+.TP
+\fB\-label\fR \fIstring\fR
+Label \fIdestNode\fR as \fIstring\fR. By default, \fIdestNode\fR has
+the same label as \fIsrcNode\fR.
+.TP
+\fB\-overwrite\fR
+Overwrite nodes that already exist. Normally nodes are always
+created, even if there already exists a node by the same name. This
+switch indicates to add or overwrite the node's data fields.
+.TP
+\fB\-recurse\fR
+Recursively copy all the subtrees of \fIsrcNode\fR as well. In this case,
+\fIsrcNode\fR can't be an ancestor of \fIdestNode\fR as it would result
+in a cyclic copy.
+.TP
+\fB\-tags\fR
+Copy tag inforation. Normally the following node is copied: its
+label and data fields. This indicates to copy tags as well.
+.RE
+.TP
+\fItreeName\fR \fBdegree\fR \fInode\fR
+Returns the number of children of \fInode\fR.
+.TP
+\fItreeName\fR \fBdelete\fR \fInode\fR...
+Recursively deletes one or more nodes from the tree.
+The node and all its descendants are removed. The one exception
+is the root node. In this case, only its descendants are removed.
+The root node will remain. Any tags or
+traces on the nodes are released.
+.TP
+\fItreeName\fR \fBdepth\fR \fInode\fR
+Returns the depth of the node. The depth is the number of
+steps from the node to the root of the tree. The depth of the
+root node is \f(CW0\fR.
+.TP
+\fItreeName\fR \fBdump\fR \fInode\fR
+Returns a list of the paths and respective data for \fInode\fR
+and its descendants. The subtree designated by \fInode\fR is
+traversed returning the following information for each node: 1) the node's
+path relative to \fInode\fR, 2) a sublist key value pairs
+representing the node's data fields, and 3) a sublist of tags.
+This list returned can be used
+later to copy or restore the tree with the \fBrestore\fR operation.
+.TP
+\fItreeName\fR \fBdumpfile\fR \fInode\fR \fIfileName\fR
+Writes a list of the paths and respective data for \fInode\fR
+and its descendants to the given file \fIfileName\fR.
+The subtree designated by \fInode\fR is traversed returning the
+following information for each node: 1) the node's
+path relative to \fInode\fR, 2) a sublist key value pairs
+representing the node's data fields, and 3) a sublist of tags.
+This list returned can be used
+later to copy or restore the tree with the \fBrestore\fR operation.
+.TP
+\fItreeName\fR \fBexists\fR \fInode\fR ?\fIkey\fR?
+Indicates if \fInode\fR exists in the tree. If a \fIkey\fR argument
+is present then the command also indicates if the named data field
+exists.
+.TP
+\fItreeName\fR \fBfind\fR \fInode\fR ?\fIswitches\fR?
+Finds for all nodes matching the criteria given by \fIswitches\fR
+for the subtree designated by \fInode\fR. A list of the selected
+nodes is returned. By default all nodes match, but you can set
+switches to narrow the match.
+.sp
+The \fB\-exact\fR, \fB\-glob\fR,
+and \fB\-regexp\fR switches indicate both what kind of pattern matching
+to perform and the pattern. Pattern matching is done, by default, against
+each node's label. But if the \fB\-path\fR switch is present, it
+will match the full path of the node. If the \fB\-key\fR switch is
+used, it designates the data field to be matched.
+.sp
+The order in
+which the nodes are traversed is controlled by the \fB\-order\fR switch.
+The possible orderings are \fBpreorder\fR, \fBpostorder\fR, \fBinorder\fR,
+and \fBbreadthfirst\fR. The default is \fBpostorder\fR.
+.sp
+The valid switches are listed
+below:
+.RS
+.TP 1i
+\fB\-addtag\fR \fIstring\fR
+Add the tag \fIstring\fR to each selected node.
+.TP 1i
+\fB\-count\fR \fInumber\fR
+Stop processing after \fInumber\fR (a positive integer) matches.
+.TP 1i
+\fB\-depth\fR \fInumber\fR
+Descend at most \fInumber\fR (a non-negative integer) levels
+If \fInumber\fR is \f(CW1\fR this means only apply the tests
+to the children of \fInode\fR.
+.TP 1i
+\fB\-exact\fR \fIstring\fR
+Matches each node using \fIstring\fR. The node must match \fIstring\fR
+exactly.
+.TP 1i
+\fB\-exec\fR \fIcommand\fR
+Invoke \fIcommand\fR for each matching node. Before \fIcommand\fR is
+invoked, the id of the node is appended. You can control
+processing by the return value of \fIcommand\fR. If \fIcommand\fR
+generates an error, processing stops and the \fBfind\fR operation
+returns an error. But if \fIcommand\fR returns \fBbreak\fR, then
+processing stops, no error is generated. If \fIcommand\fR returns
+\fBcontinue\fR, then processing
+stops on that subtree and continues on the next.
+.TP 1i
+\fB\-glob\fR \fIstring\fR
+Test each node to \fIstring\fR using global pattern
+matching. Matching is done in a fashion similar to that used by the
+C-shell.
+.TP 1i
+\fB\-invert\fR
+Select non-matching nodes. Any node that \fIdoesn't\fR match
+the given criteria will be selected.
+.TP 1i
+\fB\-key\fR \fIkey\fR
+If pattern matching is selected (using the \fB\-exact\fR,
+\fB\-glob\fR, or \fB\-regexp\fR switches), compare the values of the
+data field keyed by \fIkey\fR instead of the node's label. If no
+pattern matching switches are set, then any node with this data key
+will match.
+.TP 1i
+\fB\-leafonly\fR
+Only test nodes with no children.
+.TP 1i
+\fB\-nocase\fR
+Ignore case when matching patterns.
+.TP
+\fB\-order\fR \fIstring\fR
+Traverse the tree and process nodes according to \fIstring\fR. \fIString\fR
+can be one of the following:
+.RS
+.TP 1i
+\fBbreadthfirst\fR
+Process the node and the subtrees at each sucessive level. Each node
+on a level is processed before going to the next level.
+.TP 1i
+\fBinorder\fR
+Recursively process the nodes of the first subtree, the node itself,
+and any the remaining subtrees.
+.TP 1i
+\fBpostorder\fR
+Recursively process all subtrees before the node.
+.TP 1i
+\fBpreorder\fR
+Recursively process the node first, then any subtrees.
+.RE
+.TP
+\fB\-path\fR
+Use the node's full path when comparing nodes.
+.TP
+\fB\-regexp\fR \fIstring\fR
+Test each node using \fIstring\fR as a regular expression pattern.
+.TP
+\fB\-tag\fR \fIstring\fR
+Only test nodes that have the tag \fIstring\fR.
+.RE
+.TP
+\fItreeName\fR \fBfindchild\fR \fInode\fR \fIlabel\fR
+Searches for a child node \Ilabel\fR in \fInode\fR. The id of the
+child node is returned if found. Otherwise \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBfirstchild\fR \fInode\fR
+Returns the id of the first child in the \fInode\fR's list
+of subtrees. If \fInode\fR is a leaf (has no children),
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBget\fR \fInode\fR ?\fIkey\fR? ?\fIdefaultValue\fR?
+Returns a list of key-value pairs of data for the node. If \fIkey\fR
+is present, then onlyx the value for that particular data field is
+returned. It's normally an error if \fInode\fR does not contain the
+data field \fIkey\fR. But if you provide a \fIdefaultValue\fR
+argument, this value is returned instead (\fInode\fR will still not
+contain \fIkey\fR). This feature can be used to access a data field of
+\fInode\fR without first testing if it exists. This operation may
+trigger \fBread\fR data traces.
+.TP
+\fItreeName\fR \fBindex\fR \fInode\fR
+Returns the id of \fInode\fR. If \fInode\fR is a tag, it
+can only specify one node. If \fInode\fR does not represent a valid
+node id or tag, or has modifiers that are invalid, then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBinsert\fR \fIparent\fR ?\fIswitches\fR?
+Inserts a new node into parent node \fIparent\fR.
+The id of the new node is returned. The following switches
+are available:
+.RS
+.TP 1i
+\fB\-at\fR \fInumber\fR
+Inserts the node into \fIparent\fR's list of children at
+position \fInumber\fR. The default is to append \fInode\fR.
+.TP 1i
+\fB\-data\fR \fIdataList\fR
+Sets the value for each data field in \fIdataList\fR for the
+new node. \fIDataList\fR is a list of key-value pairs.
+.TP 1i
+\fB\-label\fR \fIstring\fR
+Designates the labels of the node as \fIstring\fR. By default, nodes
+are labeled as \f(CWnode0\fR, \f(CWnode1\fR, etc.
+.TP 1i
+\fB\-tags\fR \fItagList\fR
+Adds each tag in \fItagList\fR to the new node. \fITagList\fR is a list
+of tags, so be careful if a tag has embedded space.
+.RE
+.TP
+\fItreeName\fR \fBis\fR \fIproperty\fR \fIargs\fR
+Indicates the property of a node. Both \fIproperty\fR and \fIargs\fR
+determine the property being tested. Returns \f(CW1\fR if true and
+\f(CW0\fR otherwise. The following \fIproperty\fR and \fIargs\fR
+are valid:
+.RS
+.TP 1i
+\fBancestor\fR \fInode1\fR \fInode2\fR
+Indicates if \fInode1\fR is an ancestor of \fInode2\fR.
+.TP 1i
+\fBbefore\fR \fInode1\fR \fInode2\fR
+Indicates if \fInode1\fR is before \fInode2\fR in depth first traversal.
+.TP 1i
+\fBleaf\fR \fInode\fR
+Indicates if \fInode\fR is a leaf (it has no subtrees).
+.TP 1i
+\fBroot\fR \fInode\fR
+Indicates if \fInode\fR is the designated root. This can be changed
+by the \fBroot\fR operation.
+.RE
+.TP
+\fItreeName\fR \fBlabel\fR \fInode\fR ?\fInewLabel\fR?
+Returns the label of the node designated by \fInode\fR. If \fInewLabel\fR
+is present, the node is relabeled using it as the new label.
+.TP
+\fItreeName\fR \fBlastchild\fR \fInode\fR
+Returns the id of the last child in the \fInode\fR's list
+of subtrees. If \fInode\fR is a leaf (has no children),
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBmove\fR \fInode\fR \fInewParent\fR ?\fIswitches\fR?
+Moves \fInode\fR into \fInewParent\fR. \fINode\fR is appended to the
+list children of \fInewParent\fR. \fINode\fR can not be an ancestor
+of \fInewParent\fR. The valid flags for \fIswitches\fR are described below.
+.RS
+.TP 1i
+\fB\-after\fR \fIchild\fR
+Position \fInode\fR after \fIchild\fR. The node \fIchild\fR must be a
+child of \fInewParent\fR.
+.TP 1i
+\fB\-at\fR \fInumber\fR
+Inserts \fInode\fR into \fIparent\fR's list of children at
+position \fInumber\fR. The default is to append the node.
+.TP 1i
+\fB\-before\fR \fIchild\fR
+Position \fInode\fR before \fIchild\fR. The node \fIchild\fR must be a
+child of \fInewParent\fR.
+.RE
+.TP
+\fItreeName\fR \fBnext\fR \fInode\fR
+Returns the next node from \fInode\fR in a preorder traversal.
+If \fInode\fR is the last node in the tree,
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBnextsibling\fR \fInode\fR
+Returns the node representing the next subtree from \fInode\fR
+in its parent's list of children. If \fInode\fR is the last child,
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBnotify\fR \fIargs\fR
+Manages notification events that indicate that the tree structure has
+been changed.
+See the
+.SB "NOTIFY OPERATIONS"
+section below.
+.TP
+\fItreeName\fR \fBparent\fR \fInode\fR
+Returns the parent node of \fInode\fR. If \fInode\fR is the root
+of the tree,
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBpath\fR \fInode\fR
+Returns the full path (from root) of \fInode\fR.
+.TP
+\fItreeName\fR \fBposition\fR \fInode\fR
+Returns the position of the node in its parent's list of children.
+Positions are numbered from 0.
+The position of the root node is always 0.
+.TP
+\fItreeName\fR \fBprevious\fR \fInode\fR
+Returns the previous node from \fInode\fR in a preorder traversal.
+If \fInode\fR is the root of the tree,
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBprevsibling\fR \fInode\fR
+Returns the node representing the previous subtree from \fInode\fR
+in its parent's list of children. If \fInode\fR is the first child,
+then \f(CW-1\fR is returned.
+.TP
+\fItreeName\fR \fBrestore\fR \fInode\fR \fIdataString\fR \fIswitches\fR
+Performs the inverse function of the \fBdump\fR operation, restoring
+nodes to the tree. The format of \fIdataString\fR is exactly what is
+returned by the \fBdump\fR operation. It's a list containing information
+for each node to be restored. The information consists of 1) the relative
+path of the node, 2) a sublist of key value pairs representing the
+node's data, and 3) a list of tags for the node. Nodes are created
+starting from \fInode\fR. Nodes can be listed in any order. If a node's
+path describes ancestor nodes that do not already exist, they are
+automatically created. The valid \fIswitches\fR are listed below:
+.RS
+.TP
+\fB\-overwrite\fR
+Overwrite nodes that already exist. Normally nodes are always
+created, even if there already exists a node by the same name. This
+switch indicates to add or overwrite the node's data fields.
+.RE
+.TP
+\fItreeName\fR \fBrestorefile\fR \fInode\fR \fIfileName\fR \fIswitches\fR
+Performs the inverse function of the \fBdumpfile\fR operation, restoring
+nodes to the tree from the file \fIfileName\fR. The format of
+\fIfileName\fR is exactly what is returned by the \fBdumpfile\fR operation.
+It's a list containing information for each node to be restored.
+The information consists of 1) the relative path of the node, 2)
+a sublist of key value pairs representing the node's data, and 3)
+a list of tags for the node. Nodes are created
+starting from \fInode\fR. Nodes can be listed in any order. If a node's
+path describes ancestor nodes that do not already exist, they are
+automatically created. The valid \fIswitches\fR are listed below:
+.RS
+.TP
+\fB\-overwrite\fR
+Overwrite nodes that already exist. Normally nodes are always
+created, even if there already exists a node by the same name. This
+switch indicates to add or overwrite the node's data fields.
+.RE
+.TP
+\fItreeName\fR \fBroot\fR ?\fInode\fR?
+Returns the id of the root node. Normally this is node \f(CW0\fR. If
+a \fInode\fR argument is provided, it will become the new root of the
+tree. This lets you temporarily work within a subset of the tree.
+Changing root affects operations such as \fBnext\fR, \fBpath\fR,
+\fBprevious\fR, etc.
+.TP
+\fItreeName\fR \fBset\fR \fInode\fR \fIkey value\fR ?\fIkey value\fR...?
+Sets one or more data fields in \fInode\fR. \fINode\fR may
+be a tag that represents several nodes. \fIKey\fR is the
+name of the data field to be set and \fIvalue\fR is its respective
+value. This operation may trigger \fBwrite\fR and \fBcreate\fR data traces.
+.TP
+\fItreeName\fR \fBsize\fR \fInode\fR
+Returns the number of nodes in the subtree. This includes the node
+and all its descendants. The size of a leaf node is 1.
+.TP
+\fItreeName\fR \fBsort\fR \fInode\fR ?\fIswitches\fR?
+.RS
+.TP 1i
+\fB\-ascii\fR
+Compare strings using the ASCII collation order.
+.TP 1i
+\fB\-command\fR \fIstring\fR
+Use command \fIstring\fR as a comparison command. To compare two
+elements, evaluate a Tcl script consisting of command with the two
+elements appended as additional arguments. The script should return
+an integer less than, equal to, or greater than zero if the first
+element is to be considered less than, equal to, or greater than the
+second, respectively.
+.TP 1i
+\fB\-decreasing\fR
+Sort in decreasing order (largest items come first).
+.TP 1i
+\fB\-dictionary\fR
+Compare strings using a dictionary-style comparison. This is the same
+as \fB\-ascii\fR except (a) case is ignored except as a tie-breaker and (b)
+if two strings contain embedded numbers, the numbers compare as integers, not
+characters. For example, in \fB\-dictionary\fR mode, bigBoy sorts between
+bigbang and bigboy, and x10y sorts between x9y and x11y.
+.TP 1i
+\fB\-integer\fR
+Compare the nodes as integers.
+.TP 1i
+\fB\-key\fR \fIstring\fR
+Sort based upon the node's data field keyed by \fIstring\fR. Normally
+nodes are sorted according to their label.
+.TP 1i
+\fB\-path\fR
+Compare the full path of each node. The default is to compare only its
+label.
+.TP 1i
+\fB\-real\fR
+Compare the nodes as real numbers.
+.TP 1i
+\fB\-recurse\fR
+Recursively sort the entire subtree rooted at \fInode\fR.
+.TP 1i
+\fB\-reorder\fR
+Recursively sort subtrees for each node. \fBWarning\fR. Unlike
+the normal flat sort, where a list of nodes is returned, this will
+reorder the tree.
+.RE
+.TP
+\fItreeName\fR \fBtag\fR \fIargs\fR
+Manages tags for the tree object.
+See the
+.SB "TAG OPERATIONS"
+section below.
+.TP
+\fItreeName\fR \fBtrace\fR \fIargs\fR
+Manages traces for data fields in the tree object.
+Traces cause Tcl commands to be executed whenever a data field of a
+node is created, read, written, or unset. Traces can be set for a
+specific node or a tag, representing possibly many nodes.
+See the
+.SB "TRACE OPERATIONS"
+section below.
+.TP
+\fItreeName\fR \fBunset\fR \fInode\fR \fIkey\fR...
+Removes one or more data fields from \fInode\fR. \fINode\fR may
+be a tag that represents several nodes. \fIKey\fR is the
+name of the data field to be removed. It's not an error is
+\fInode\fR does not contain \fIkey\fR.
+This operation may trigger \fBunset\fR data traces.
+.RE
+.SH TAG OPERATIONS
+Tags are a general means of selecting and marking nodes in the tree.
+A tag is just a string of characters, and it may take any form except
+that of an integer. The same tag may be associated with many
+different nodes.
+.sp
+There are two built-in tags: The tag \fBall\fR is implicitly
+associated with every node in the tree. It may be used to invoke
+operations on all the nodes in the tree. The tag \fBroot\fR is
+managed automatically by the tree object. It specifies the node
+that is currently set as the root of the tree.
+.sp
+Most tree operations use tags. And several operations let you
+operate on multiple nodes at once. For example, you can use the
+\fBset\fR operation with the tag \fBall\fR to set a data field in
+for all nodes in the tree.
+.PP
+Tags are invoked by the \fBtag\fR operation. The
+general form is
+.DS
+\fItreeName\fR \fBtag\fR \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below.
+.TP
+\fItreeName\fR \fBtag add\fR \fIstring\fR \fInode\fR...
+Adds the tag \fIstring\fR to one of more nodes.
+.TP
+\fItreeName\fR \fBtag delete\fR \fIstring\fR \fInode\fR...
+Deletes the tag \fIstring\fR from one or more nodes.
+.TP
+\fItreeName\fR \fBtag forget\fR \fIstring\fR
+Removes the tag \fIstring\fR from all nodes. It's not an error if no
+nodes are tagged as \fIstring\fR.
+.TP
+\fItreeName\fR \fBtag names\fR ?\fInode\fR?
+Returns a list of tags used by the tree. If a \fInode\fR argument
+is present, only those tags used by \fInode\fR are returned.
+.TP
+\fItreeName\fR \fBtag nodes\fR \fIstring\fR
+Returns a list of nodes that have the tag \fIstring\fR. If no node
+is tagged as \fIstring\fR, then an empty string is returned.
+.SH TRACE OPERATIONS
+Data fields can be traced much in the same way that you can trace Tcl
+variables. Data traces cause Tcl commands to be executed whenever a
+particular data field of a node is created, read, written, or unset.
+A trace can apply to one or more nodes. You can trace a specific node
+by using its id, or a group of nodes by a their tag.
+.PP
+The tree's \fBget\fR, \fBset\fR, and \fBunset\fR operations can
+trigger various traces. The \fBget\fR operation can cause
+a \fIread\fR trace to fire. The \fBset\fR operation causes a \fIwrite\fR
+trace to fire. And if the data field is written for the first time, you
+will also get a \fIcreate\fR trace.
+The \fBunset\fR operation triggers \fIunset\fR traces.
+.PP
+Data traces are invoked by the \fBtrace\fR
+operation. The general form is
+.DS
+\fItreeName\fR \fBtrace\fR \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for traces are listed below.
+.TP
+\fItreeName\fR \fBtrace create\fR \fInode\fR \fIkey\fR \fIops\fR \fIcommand\fR
+Creates a trace for \fInode\fR on data field \fIkey\fR. \fINode\fR
+can refer to more than one node (for example, the tag \fBall\fR). If
+\fInode\fR is a tag, any node with that tag can possibly trigger a trace,
+invoking \fIcommand\fR.
+\fICommand\fR is command prefix, typically a procedure name.
+Whenever a trace is triggered, four arguments are appended to
+\fIcommand\fR before it is invoked: \fItreeName\fR, id of the
+node, \fIkey\fR and, \fIops\fR.
+Note that no nodes need have the field \fIkey\fR.
+A trace identifier in the form "\f(CWtrace0\fR", "\f(CWtrace1\fR", etc.
+is returned.
+.sp
+\fIOps\fR indicates which operations are of
+interest, and consists of one or more of the following letters:
+.RS
+.TP
+\fBr\fR
+Invoke \fIcommand\fR whenever \fIkey\fR is read. Both read and
+write traces are temporarily disabled when \fIcommand\fR is executed.
+.TP
+\fBw\fR
+Invoke \fIcommand\fR whenever \fIkey\fR is written. Both read and
+write traces are temporarily disabled when \fIcommand\fR is executed.
+.TP
+\fBc\fR
+Invoke \fIcommand\fR whenever \fIkey\fR is created.
+.TP
+\fBu\fR
+Invoke \fIcommand\fR whenever \fIkey\fR is unset. Data fields are
+typically unset with the \fBunset\fR command. Data fields are also
+unset when the tree is released, but all traces are disabled prior
+to that.
+.sp
+.RE
+.TP
+\fItreeName\fR \fBtrace delete\fR \fItraceId\fR...
+Deletes one of more traces. \fITraceId\fR is
+the trace identifier returned by the \fBtrace create\fR operation.
+.TP
+\fItreeName\fR \fBtrace info\fR \fItraceId\fR
+Returns information about the trace \fItraceId\fR. \fITraceId\fR
+is a trace identifier previously returned by the \fBtrace create\fR operation.
+It's the same information specified for the \fBtrace create\fR operation.
+It consists of the node id or tag, data field key, a string of letters
+indicating the operations that are traced (it's in the same
+form as \fIops\fR) and, the command prefix.
+.TP
+\fItreeName\fR \fBtrace names\fR
+Returns a list of identifers for all the current traces.
+.SH NOTIFY OPERATIONS
+Tree objects can be shared among many clients, such as a
+\fBhiertable\fR widget. Any client can create or delete nodes,
+sorting the tree, etc. You can request to be notified whenever these
+events occur. Notify events cause Tcl commands to be executed
+whenever the tree structure is changed.
+.PP
+Notifications are handled by the \fBnotify\fR operation. The
+general form is
+.DS
+\fItreeName\fR \fBnotify\fR \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for events are listed below.
+.TP
+\fItreeName\fR \fBnotify create\fR ?\fIswitches\fR? \fIcommand\fR \fR?\fIargs\fR?...
+Creates a notifier for the tree. A notify identifier in the form
+"\f(CWnotify0\fR", "\f(CWnotify1\fR", etc. is returned.
+.sp
+\fICommand\fR and \fIargs\fR are saved and invoked whenever the tree
+structure is changed (according to \fIswitches\fR). Two arguments are
+appended to \fIcommand\fR and \fIargs\fR before it's invoked: the id
+of the node and a string representing the type of event that occured.
+One of more switches can be set to indicate the events that are of
+interest. The valid switches are as follows:
+.RS
+.TP 1i
+\fB\-create\fR
+Invoke \fIcommand\fR whenever a new node has been added.
+.TP 1i
+\fB\-delete\fR
+Invoke \fIcommand\fR whenever a node has been deleted.
+.TP 1i
+\fB\-move\fR
+Invoke \fIcommand\fR whenever a node has been moved.
+.TP 1i
+\fB\-sort\fR
+Invoke \fIcommand\fR whenever the tree has been sorted and reordered.
+.TP 1i
+\fB\-relabel\fR
+Invoke \fIcommand\fR whenever a node has been relabeled.
+.TP 1i
+\fB\-allevents\fR
+Invoke \fIcommand\fR whenever any of the above events occur.
+.TP 1i
+\fB\-whenidle\fR
+When an event occurs don't invoke \fIcommand\fR immediately, but
+queue it to be run the next time the event loop is entered and there
+are no events to process. If subsequent events occur before
+the event loop is entered, \fIcommand\fR will still be
+invoked only once.
+.RE
+.TP
+\fItreeName\fR \fBnotify delete\fR \fInotifyId\fR
+Deletes one or more notifiers from the tree. \fINotifyId\fR is the
+notifier identifier returned by the \fBnotify create\fR operation.
+.TP
+\fItreeName\fR \fBnotify info\fR \fInotifyId\fR
+Returns information about the notify event \fInotifyId\fR. \fINotifyId\fR
+is a notify identifier previously returned by the \fBnotify create\fR operation.
+It's the same information specified for the \fBnotify create\fR operation.
+It consists of the notify id, a sublist of event flags (it's in the same
+form as \fIflags\fR) and, the command prefix.
+.TP
+\fItreeName\fR \fBnotify names\fR
+Returns a list of identifers for all the current notifiers.
+.SH C LANGUAGE API
+Blt_TreeApply,
+Blt_TreeApplyBFS,
+Blt_TreeApplyDFS,
+Blt_TreeChangeRoot,
+Blt_TreeCreate,
+Blt_TreeCreateEventHandler,
+Blt_TreeCreateNode,
+Blt_TreeCreateTrace,
+Blt_TreeDeleteEventHandler,
+Blt_TreeDeleteNode,
+Blt_TreeDeleteTrace,
+Blt_TreeExists,
+Blt_TreeFindChild,
+Blt_TreeFirstChild,
+Blt_TreeFirstKey,
+Blt_TreeGetNode,
+Blt_TreeGetToken,
+Blt_TreeGetValue,
+Blt_TreeIsAncestor,
+Blt_TreeIsBefore,
+Blt_TreeIsLeaf,
+Blt_TreeLastChild,
+Blt_TreeMoveNode,
+Blt_TreeName,
+Blt_TreeNextKey,
+Blt_TreeNextNode,
+Blt_TreeNextSibling,
+Blt_TreeNodeDegree,
+Blt_TreeNodeDepth,
+Blt_TreeNodeId,
+Blt_TreeNodeLabel,
+Blt_TreeNodeParent,
+Blt_TreePrevNode,
+Blt_TreePrevSibling,
+Blt_TreeRelabelNode,
+Blt_TreeReleaseToken,
+Blt_TreeRootNode,
+Blt_TreeSetValue,
+Blt_TreeSize,
+Blt_TreeSortNode, and
+Blt_TreeUnsetValue.
+.SH KEYWORDS
+tree, hiertable, widget
diff --git a/blt/man/treeview.mann b/blt/man/treeview.mann
new file mode 100644
index 00000000000..d4b155c901c
--- /dev/null
+++ b/blt/man/treeview.mann
@@ -0,0 +1,2264 @@
+'\"
+'\" Copyright 2001-2 by Silicon Metrics Corporation.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Silicon Metrics or any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Silicon Metrics disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Silicon Metrics be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" The hierarchical table widget created by George Howlett.
+'\"
+.so man.macros
+.TH treeview n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+treeview \- Create and manipulate hierarchical table widgets
+.BE
+.SH SYNOPSIS
+\fBtreeview\fR \fIpathName \fR?\fIoptions\fR?
+.SH DESCRIPTION
+The \fBtreeview\fR widget displays a tree of data. It replaces both
+the \fBhiertable\fR and \fBhierbox\fR widgets. The \fBtreeview\fR is
+100% syntax compatible with the \fBhiertable\fR widget. The
+\fBhiertable\fR command is retained for sake of script-level
+compatibility. This widget obsoletes the \fBhierbox\fR widget. It
+does everything the old \fBhierbox\fR widget did, but also provides
+data sharing (via \fItree data objects\fR) and the ability to tag
+nodes.
+.SH INTRODUCTION
+The \fBtreeview\fR widget displays hierarchical data. Data is
+represented as nodes in a general-ordered tree. Each node may have
+sub-nodes and these nodes can in turn has their own children.
+.PP
+A node is displayed as a row entry in the widget. Each entry has a
+text label and icon. When a node has children, its entry is drawn
+with a small button to the left of the label. Clicking the mouse over
+this button opens or closes the node. When a node is \fIopen\fR, its
+children are exposed. When it is \fIclosed\fR, the children and their
+descedants are hidden. The button is normally a \f(CW+\fR or
+\f(CW\-\fR symbol (ala Windows Explorer), but can be replaced with a
+pair of Tk images (open and closed images).
+.PP
+If the node has data associated with it, they can be displayed in
+columns running vertically on either side the tree. You can control
+the color, font, etc of each entry. Any entry label or data field can
+be edited in-place.
+.SH "TREE DATA OBJECT"
+The tree is not stored inside the widget but in a tree data object
+(see the \fBtree\fR command for a further explanation). Tree data
+objects can be shared among different clients, such as a
+\fBtreeview\fR widget or the \fBtree\fR command. You can walk the
+tree and manage its data with the \fBtree\fR command tree, while
+displaying it with the \fBtreeview\fR widget. Whenever the tree is
+updated, the \fBtreeview\fR widget is automatically redrawn.
+.PP
+By default, the \fBtreeview\fR widget creates its own tree object.
+The tree initially contains just a root node. But you can also
+display trees created by the \fBtree\fR command using the \fB\-tree\fR
+configuration option. \fBTreeview\fR widgets can share the same tree
+object, possibly displaying different views of the same data.
+.PP
+A tree object has both a Tcl and C API. You can insert or delete
+nodes using \fBtreeview\fR widget or \fBtree\fR command operations,
+but also from C code. For example, you can load the tree from your C
+code while still managing and displaying the tree from Tcl. The widget
+is automatically notified whenever the tree is modified via C or Tcl.
+.SH SYNTAX
+.DS
+\fBtreeview \fIpathName \fR?\fIoption value\fR?...
+.DE
+The \fBtreeview\fR command creates a new window \fIpathName\fR and
+makes it into a \fBtreeview\fR widget. At the time this command is
+invoked, there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist. Additional options may be
+specified on the command line or in the option database to configure
+aspects of the widget such as its colors and font. See the
+\fBconfigure\fR operation below for the exact details about what
+\fIoption\fR and \fIvalue\fR pairs are valid.
+.PP
+If successful, \fBtreeview\fR returns the path name of the widget. It
+also creates a new Tcl command by the same name. You can use this
+command to invoke various operations that query or modify the widget.
+The general form is:
+.DS
+\fIpathName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available are described in the
+.SB "TREEVIEW OPERATIONS"
+section.
+.SH "IDS AND TAGS"
+Nodes can be inserted into a tree using the \fBtreeview\fR widget
+.CS
+blt::treeview .t
+set node [.t insert end root "one"]
+.CE
+or \fBtree\fR command.
+.CS
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+.CE
+In both cases, a number identifying the node is returned (the value of
+\f(CW$node\fR). This serial number or \fIid\fR uniquely identifies
+the node. Please note that you can't infer a location or position of
+a node from its id. The only exception is that the root node is
+always id \f(CW0\fR. Since nodes may have the same labels or be moved
+within the tree, ids provide an convenient way to identify nodes. If
+a tree is shared, the ids will be the same regardless if you are using
+by the \fBtreeview\fR widget or the \fBtree\fR command. Ids are
+recycled when the node deleted.
+.PP
+A node may also have any number of \fItags\fR associated with it. A
+tag is just a string of characters, and it may take any form except
+that of an integer. For example, "\f(CWx123\fR" is valid, but
+"\f(CW123\fR" isn't. The same tag may be associated with many
+different nodes. This is typically done to associate a group of
+nodes. Many operations in the \fBtreeview\fR widget take either node
+ids or tag names as arguments. Using a tag says to apply the operation
+to all nodes with that tag.
+.PP
+The tag \fBall\fR is implicitly associated with every node in
+the tree. It may be used to invoke operations on all the nodes in the
+tree.
+.PP
+Tags may be shared, just like trees, between clients. For example,
+you can use the tags created by the \fBtree\fR command with
+\fBtreeview\fR widgets.
+.SH SPECIAL NODE IDS
+There are also several special non-numeric ids. Special ids differ
+from tags in that they are always translated to their numeric
+equivalent. They also take precedence over tags. For example, you
+can't use a tag name that is a special id. These ids are specific to
+the \fBtreeview\fR widget.
+.TP 15
+\fBactive\fR
+The node where the mouse pointer is currently located.
+When a node is active, it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+The \fBactive\fR id is changed automatically by moving the mouse
+pointer over another node or by using the \fBentry activate\fR
+operation. Note that there can be only one active node at a time.
+.TP 15
+\fBanchor\fR
+The node representing the fixed end of the current selection.
+The anchor is set by the \fBselection anchor\fR operation.
+.TP 15
+\fBcurrent\fR
+The node where the mouse pointer is currently located.
+But unlike \fBactive\fR, this id changes while the
+selection is dragged. It is used to determine the
+current node during button drags.
+.TP 15
+\fBdown\fR
+The next open node from the current focus. The \fBdown\fR of
+the last open node is the same.
+.TP 15
+\fBend\fR
+The last open node (in depth-first order) on the tree.
+.TP 15
+\fBfocus\fR
+The node that currently has focus. When a node has focus,
+it receives key events. To indicate focus, the node
+is drawn with a dotted line around its label. You can change the
+focus using the \fBfocus\fR operation.
+.TP 15
+\fBlast\fR
+The last open node from the current focus. But unlike \fBup\fR,
+when the focus is at root, \fBlast\fR wraps around to the last
+open node in the tree.
+.TP 15
+\fBmark\fR
+The node representing the non-fixed end of the current selection.
+The mark is set by the \fBselection mark\fR operation.
+.TP 15
+\fBnext\fR
+The next open node from the current focus. But unlike \fBdown\fR,
+when the focus is on last open node, \fBnext\fR wraps around to the
+root node.
+.TP 15
+\fBnextsibling\fR
+The next sibling from the node with the current focus. If the node
+is already the last sibling then it is the \fBnextsibling\fB.
+.TP 15
+\fBparent\fR
+The parent of the node with the current focus. The \fBparent\fR
+of the root is also the root.
+.TP 15
+\fBprevsibling\fR
+The previous sibling from the node with the current focus. If the node
+is already the first sibling then it is the \fBprevsibling\fB.
+.TP 15
+\fBroot\fR
+The root node. You can also use id \f(CW0\fR to indicate
+the root.
+.TP 15
+\fBup\fR
+The last open node (in depth-first order) from the current focus. The
+\fBup\fR of the root node (i.e. the root has focus) is also the root.
+.TP 15
+\fBview.top\fR
+First node that's current visible in the widget.
+.TP 15
+\fBview.bottom\fR
+Last node that's current visible in the widget.
+.TP 15
+\fB@\fIx\fB,\fIy\fR
+Indicates the node that covers the point in the treeview window
+specified by \fIx\fR and \fIy\fR (in pixel coordinates). If no
+part of the entryd covers that point, then the closest node to that
+point is used.
+.PP
+A node may be specified as an id or tag. If the specifier is an
+integer then it is assumed to refer to the single node with that id.
+If the specifier is not an integer, it's checked to see if it's a
+special id (such as focus). Otherwise, it's assumed to be tag. Some
+operations only operate on a single node at a time; if a tag refers to
+more than one node, then an error is generated.
+.SH DATA FIELDS
+A node in the tree can have \fIdata fields\fR. A data field is a
+name-value pair, used to represent arbitrary data in the node. Nodes
+can contain different fields (they aren't required to contain the same
+fields). You can optionally display these fields in the
+\fBtreeview\fR widget in columns running on either side of the
+displayed tree. A node's value for the field is drawn in the column
+along side its node in the hierarchy. Any node that doesn't have a
+specific field is left blank. Columns can be interactively resized,
+hidden, or, moved.
+.SH ENTRY BINDINGS
+You can bind Tcl commands to be invoked when events occur on nodes
+(much like Tk canvas items). You can bind a node using its id or
+its \fIbindtags\fR. Bindtags are simply names that associate a
+binding with one or more nodes. There is a built-in tag \f(CWall\fR
+that all node entries automatically have.
+.SH "TREEVIEW OPERATIONS"
+The \fBtreeview\fR operations are the invoked by specifying
+the widget's pathname, the operation, and any arguments that pertain
+to that operation. The general form is:
+.sp
+.CS
+\fIpathName operation \fR?\fIarg arg ...\fR?
+.CE
+.sp
+\fIOperation\fR and the \fIarg\fRs determine the exact behavior of the
+command. The following operation are available for \fBtreeview\fR widgets:
+.TP
+\fIpathName \fBbbox\fR ?\fB-screen\fR? \fItagOrId...\fR
+Returns a list of 4 numbers, representing a bounding box of around
+the specified entries. The entries is given by one or more \fItagOrId\fR
+arguments.
+If the \fB\-screen\fR flag is given, then the x-y coordinates
+of the bounding box are returned as screen coordinates, not
+virtual coordinates. Virtual coordinates start from \f(CW0\fR from the
+root node.
+The returned list contains the following values.
+.RS
+.TP 1.25i
+\fIx\fR
+X-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIy\fR
+Y-coordinate of the upper-left corner of the bounding box.
+.TP
+\fIwidth\fR
+Width of the bounding box.
+.TP
+\fIheight\fR
+Height of the bounding box.
+.RE
+.TP
+\fIpathName \fBbind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for a node with this
+tag, \fIcommand\fR will be invoked. The syntax is similar to the
+\fBbind\fR command except that it operates on \fBtreeview\fR entries,
+rather than widgets. See the \fBbind\fR manual entry for
+complete details on \fIsequence\fR and the substitutions performed on
+\fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton \fIoperation\fR ?\fIargs\fR?
+This command is used to control the button selectors within a
+\fBtreeview\fR widget.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBbutton activate\fR \fItagOrId\fR
+Designates the node given by \fItagOrId\fR as active.
+When a node is active it's entry is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active entry at a time.
+The special id \fBactive\fR indicates the currently active node.
+.TP
+\fIpathName \fBbutton bind\fR \fItagName\fR ?\fIsequence command\fR?
+Associates \fIcommand\fR with \fItagName\fR such that whenever the
+event sequence given by \fIsequence\fR occurs for an button of a
+node entry with this tag, \fIcommand\fR will be invoked. The syntax is
+similar to the \fBbind\fR command except that it operates on
+\fBtreeview\fR buttons, rather than widgets. See the \fBbind\fR
+manual entry for complete details on \fIsequence\fR and the
+substitutions performed on \fIcommand\fR before invoking it.
+.sp
+If all arguments are specified then a new binding is created, replacing
+any existing binding for the same \fIsequence\fR and \fItagName\fR.
+If the first character of \fIcommand\fR is \f(CW+\fR then \fIcommand\fR
+augments an existing binding rather than replacing it.
+If no \fIcommand\fR argument is provided then the command currently
+associated with \fItagName\fR and \fIsequence\fR (it's an error occurs
+if there's no such binding) is returned. If both \fIcommand\fR and
+\fIsequence\fR are missing then a list of all the event sequences for
+which bindings have been defined for \fItagName\fR.
+.TP
+\fIpathName \fBbutton cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBbutton configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "BUTTON OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBclose \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Closes the node specified by \fItagOrId\fR. In addition, if a Tcl
+script was specified by the \fB\-closecommand\fR option, it is
+invoked. If the node is already closed, this command has no effect.
+If the \fB\-recurse\fR flag is present, each child node is
+recursively closed.
+.TP
+\fIpathName \fBcolumn \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview columns.
+.RS
+.TP
+\fIpathName \fBcolumn activate\fR \fIcolumn\fR
+Sets the active column to \fIcolumn\fR. \fIColumn\fR is the
+name of a column in the widget.
+When a column is active, it's drawn using its \fB\-activetitlebackground\fR
+and \fB\-activetitleforeground\fR options. If \fIcolumn\fR is the \f(CW""\fR,
+then no column will be active. If no column argument is provided, then
+the name of the currently active column is returned.
+.TP
+\fIpathName \fBcolumn cget\fR \fIname\fR \fIoption\fR
+Returns the current value of the column configuration option given
+by \fIoption\fR for \fIname\fR. \fIName\fR is the name of column
+that corresponds to a data field.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBcolumn configure\fR \fIname\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the column designated
+by \fIname\fR. \fIName\fR is the name of the column corresponding
+to a data field.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "COLUMN OPTIONS"
+below.
+.TP
+\fIpathName \fBcolumn delete\fR \fIfield\fR ?\fIfield\fR...?
+Deletes one of more columns designated by \fIfield\fR. Note
+that this does not delete the data fields themselves.
+.TP
+\fIpathName \fBcolumn insert\fR \fIposition\fR \fIfield\fR ?\fIoptions\fR...?
+Inserts one of more columns designated by \fIfield\fR. A column displays
+each node's data field by the same name. If the node doesn't
+have the given field, the cell is left blank.
+\fIPosition\fR
+indicates where in the list of columns to add the new column. It may be
+either a number or \f(CWend\fR.
+.TP
+\fIpathName \fBcolumn invoke\fR \fIfield\fR
+Invokes the Tcl command associated with the column \fIfield\fR,
+if there is one (using the column's \fB\-command\fR option).
+The command is ignored if the column's \fB\-state\fR option
+set to \f(CWdisabled\fR.
+.TP
+\fIpathName \fBcolumn move \fIname\fR \fIdest\fR
+Moves the column \fIname\fR to the destination position.
+\fIDest\fR is the name of another column or a screen position
+in the form \f(CW@\fIx\f(CW,\fIy\fR.
+.TP
+\fIpathName \fBcolumn names\fR
+Returns a list of the names of all columns in the widget.
+The list is ordered as the columns are drawn from left-to-right.
+.TP
+\fIpathName \fBcolumn nearest\fR \fIx\fR ?\fIy\fR?
+Returns the name of the column closest to the given X-Y screen
+coordinate. If you provide a \fIy\fR argument (it's optional),
+a name is returned only when if the point is over a column's title.
+.RE
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TREEVIEW OPTIONS"
+below.
+.TP
+\fIpathName \fBcurselection\fR
+Returns a list containing the ids of all of the entries that are
+currently selected.
+If there are no entries selected, then the empty string is returned.
+.TP
+\fIpathName \fBdelete \fItagOrId\fR...
+Deletes one or more entries given by \fItagOrId\fR and its children.
+.TP
+\fIpathName \fBentry \fIoperation\fR ?\fIargs\fR?
+The following operations are available for treeview entries.
+.RS
+.TP
+\fIpathName \fBentry activate\fR \fItagOrId\fR
+Sets the active entry to the one specified by \fItagOrId\fR.
+When an entry is active it is drawn using its active icon
+(see the \fB\-activeicon\fR option).
+Note that there can be only one active node at a time.
+The special id of the currently active node is \fBactive\fR.
+.TP
+\fIpathName \fBentry cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBentry children\fR \fItagOrId\fR ?\fIfirst\fR? ?\fIlast\fR?
+Returns a list of ids for the given range of children of \fItagOrId\fR.
+\fITagOrId\fR is the id or tag of the node to be examined.
+If only a \fIfirst\fR argument is present, then the id
+of the that child at that numeric position is returned. If both \fIfirst\fR
+and \fIlast\fR arguments are given, then the ids of all the children
+in that range are returned. Otherwise the ids of all children
+are returned.
+.TP
+\fIpathName \fBentry configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.TP
+\fIpathName \fBentry delete\fR \fItagOrId\fR ?\fIfirst\fR ?\fIlast\fR?
+Deletes the one or more children nodes of the parent \fItagOrId\fR.
+If \fIfirst\fR and \fIlast\fR arguments are present, they are
+positions designating a range of children nodes to be deleted.
+.TP
+\fIpathName \fBentry isbefore \fItagOrId1\fR \fItagOrId2\fR
+Returns 1 if \fItagOrId1\fR is before \fItagOrId2\fR and 0 otherwise.
+.TP
+\fIpathName \fBentry ishidden \fItagOrId\fR
+Returns 1 if the node is currently hidden and 0 otherwise. A node is
+also hidden if any of its ancestor nodes are closed or hidden.
+.TP
+\fIpathName \fBentry isopen \fItagOrId\fR
+Returns 1 if the node is currently open and 0 otherwise.
+.TP
+\fIpathName \fBentry size\fR \fB\-recurse\fR \fItagOrId\fR
+Returns the number of children for parent node \fItagOrId\fR.
+If the \fB\-recurse\fR flag is set, the number of all
+its descendants is returned. The node itself is not counted.
+.RE
+.TP
+\fIpathName \fBfind \fR?\fIflags\fR? \fIfirst\fR \fIlast\fR
+Finds for all entries matching the criteria given by \fIflags\fR. A
+list of ids for all matching nodes is returned. \fIFirst\fR and
+\fIlast\fR are ids designating the range of the search in
+depth-first order. If \fIlast\fR is before \fIfirst\fR, then nodes
+are searched in reverse order. The valid flags are:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Patterns must match exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Pick entries that don't match.
+.TP 1.25i
+\fB\-exec\fI string\fR
+Specifies a Tcl script to be invoked for each matching node.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP 1.25i
+\fB\-count\fI number\fR
+Stop searching after \fInumber\fR matches.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBfocus \fR \fItagOrId\fR
+Sets the focus to the node given by \fItagOrId\fR. When a node
+has focus, it can receive keyboard events.
+The special id \fBfocus\fR designates the node that currently has focus.
+.TP
+\fIpathName \fBget \fR?\fB\-full\fR? \fItagOrId\fR \fItagOrId\fR...
+Translates one or more ids to their node entry names. It returns a list of
+names for all the ids specified. If the \fB\-full\fR
+flag is set, then the full pathnames are returned.
+.sp
+Note: If the \fB\-separator\fR option is the empty string (the default),
+the result is always a list of lists, even if there is only one node
+specified.
+.TP
+\fIpathName \fBhide \fR?\fBflags\fR? \fItagOrId\fR...
+Hides all nodes matching the criteria given by \fIflags\fR. The
+search is performed recursively for each node given by \fItagOrId\fR.
+The valid flags are described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the node entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Hide nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBindex \fR?\fB\-at\fR ?\fB\-path\fR? \fItagOrId\fR? \fIstring\fR
+Returns the id of the node specified by \fIstring\fR. \fIString\fR
+may be a tag or node id.
+Some special ids are normally relative to the node that
+has focus. The \fB\-at\fR flag lets you select another node.
+.TP
+\fIpathName \fBinsert \fR?\fB\-at \fItagOrId\fR? \fIposition\fR \fIpath\fR ?\fIoptions...\fR? ?\fIpath\fR? ?\fIoptions...\fR?
+Inserts one or more nodes at \fIposition\fR. \fIPosition\fR is the
+location (number or \f(CWend\fR) where the new nodes are added to
+the parent node. \fIPath\fR is the pathname of the new node.
+Pathnames can be formated either as a Tcl list (each element is a path
+component) or as a string separated by a special character sequence
+(using the \fB\-separator\fR option). Pathnames are normally
+absolute, but the \fB\-at\fR switch lets you select a relative
+starting point. Its value is the id of the starting node.
+.sp
+All ancestors of the new node must already exist, unless the
+\fB\-autocreate\fR option is set. It is also an error if a node
+already exists, unless the \fB\-allowduplicates\fR option is set.
+.sp
+\fIOption\fR and \fIvalue\fR may have any of the values accepted by the
+\fBentry configure\fR operation described in the
+.SB "ENTRY OPERATIONS"
+section below. This command returns a list of the ids of
+the new entries.
+.TP
+\fIpathName \fBmove \fItagOrId\fR \fIhow\fR \fIdestId\fR
+Moves the node given by \fItagOrId\fR to the destination node. The
+node can not be an ancestor of the destination. \fIDestId\fR is
+the id of the destination node and can not be the root of the
+tree. In conjunction with \fIhow\fR, it describes how the move is
+performed.
+.RS
+.TP 8
+\f(CWbefore\fR
+Moves the node before the destination node.
+.TP 8
+\f(CWafter\fR
+Moves the node after the destination node.
+.TP 8
+\f(CWinto\fR
+Moves the node to the end of the destination's list of children.
+.RE
+.TP
+\fIpathName \fBnearest \fIx y\fR ?\fIvarName\fR?
+Returns the id of the node entry closest to the given X-Y screen
+coordinate. If the coordinate is not directly over any node, then the
+empty string is returned. If the argument \fIvarName\fR is present,
+this is a Tcl variable that is set to either \f(CWbutton\fR,
+\f(CWlabel\fR, \f(CWlabel\fR, or \f(CW""\fR depending what part of the
+entry the coordinate lies.
+.TP
+\fIpathName \fBopen \fR?\fB\-recurse\fR? \fItagOrId...\fR
+Opens the one or more nodes specified by \fItagOrId\fR.
+If a node is not already open, the Tcl script specified by the
+\fB\-opencommand\fR option is invoked. If the \fB\-recurse\fR flag
+is present, then each descendant is recursively opened.
+.TP
+\fIpathName \fBrange\fR ?\fB-open\fR? \fIfirst last\fR
+Returns the ids in depth-first order of the nodes
+between the \fIfirst\fR and \fIlast\fR ids. If the \fB\-open\fR
+flag is present, it indicates to consider only open nodes.
+If \fIlast\fR is before \fIfirst\fR, then the ids are
+returned in reverse order.
+.TP
+\fIpathName \fBscan\fR \fIoption args\fR
+This command implements scanning. It has
+two forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBscan mark \fIx y\fR
+Records \fIx\fR and \fIy\fR and the current view in the treeview
+window; used in conjunction with later \fBscan dragto\fR commands.
+Typically this command is associated with a mouse button press in
+the widget. It returns an empty string.
+.TP
+\fIpathName \fBscan dragto \fIx y\fR.
+Computes the difference between its \fIx\fR and \fIy\fR
+arguments and the \fIx\fR and \fIy\fR arguments to the last
+\fBscan mark\fR command for the widget.
+It then adjusts the view by 10 times the
+difference in coordinates. This command is typically associated
+with mouse motion events in the widget, to produce the effect of
+dragging the list at high speed through the window. The return
+value is an empty string.
+.RE
+.TP
+\fIpathName \fBsee\fR ?\fB\-anchor \fIanchor\fR? \fItagOrId\fR
+Adjusts the view of entries so that the node given by \fItagOrId\fR is
+visible in the widget window. It is an error if \fBtagOrId\fR is a
+tag that refers to more than one node. By default the node's entry
+is displayed in the middle of the window. This can changed using the
+\fB\-anchor\fR flag. Its value is a Tk anchor position.
+.TP
+\fIpathName \fBselection \fIoption arg\fR
+This command is used to adjust the selection within a \fBtreeview\fR
+widget. It has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBselection anchor \fItagOrId\fR
+Sets the selection anchor to the node given by \fItagOrId\fR.
+If \fItagOrId\fR refers to a non-existent node, then the closest
+node is used.
+The selection anchor is the end of the selection that is fixed
+while dragging out a selection with the mouse.
+The special id \fBanchor\fR may be used to refer to the anchor
+node.
+.TP
+\fIpathName \fBselection cancel\fR
+Clears the temporary selection of entries back to the
+current anchor. Temporary selections are created by
+the \fBselection mark\fR operation.
+.TP
+\fIpathName \fBselection clear \fIfirst \fR?\fIlast\fR?
+Removes the entries between \fIfirst\fR and \fIlast\fR
+(inclusive) from the selection. Both \fIfirst\fR and
+\fIlast\fR are ids representing a range of entries.
+If \fIlast\fR isn't given, then only \fIfirst\fR is deselected.
+Entries outside the selection are not affected.
+.TP
+\fIpathName \fBselection clearall\fR
+Clears the entire selection.
+.TP
+\fIpathName \fBselection mark \fItagOrId\fR
+Sets the selection mark to the node given by \fItagOrId\fR. This
+causes the range of entries between the anchor and the mark to be
+temporarily added to the selection. The selection mark is the end of
+the selection that is fixed while dragging out a selection with the
+mouse. The special id \fBmark\fR may be used to refer to the current
+mark node.
+If \fItagOrId\fR refers to a non-existent node, then the mark
+is ignored.
+Resetting the mark will unselect
+the previous range. Setting the anchor finalizes the range.
+.TP
+\fIpathName \fBselection includes \fItagOrId\fR
+Returns 1 if the node given by \fItagOrId\fR is currently
+selected, 0 if it isn't.
+.TP
+\fIpathName \fBselection present\fR
+Returns 1 if any nodes are currently selected and 0 otherwise.
+.TP
+\fIpathName \fBselection set \fIfirst \fR?\fIlast\fR?
+Selects all of the nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, without affecting
+the selection state of nodes outside that range.
+.TP
+\fIpathName \fBselection toggle \fIfirst \fR?\fIlast\fR?
+Selects/deselects nodes in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, from the selection.
+If a node is currently selected, it becomes deselected, and
+visa versa.
+.RE
+.TP
+\fIpathName \fBshow \fR?\fBflags\fR? \fItagOrId\fR...
+Exposes all nodes matching the criteria given by \fIflags\fR. This
+is the inverse of the \fBhide\fR operation. The search is performed
+recursively for each node given by \fItagOrId\fR. The valid flags are
+described below:
+.RS
+.TP 1.25i
+\fB\-name\fI pattern\fR
+Specifies pattern to match against node names.
+.TP 1.25i
+\fB\-full\fI pattern\fR
+Specifies pattern to match against node pathnames.
+.TP 1.25i
+\fB\-\fIoption\fI pattern\fR
+Specifies pattern to match against the entry's configuration option.
+.TP 1.25i
+\fB\-exact\fR
+Match patterns exactly. The is the default.
+.TP 1.25i
+\fB\-glob\fR
+\fB\-glob\fR
+Use global pattern matching. Matching is done in a fashion
+similar to that used by the C-shell. For the two
+strings to match, their contents must be identical
+except that the following special sequences may
+appear in pattern:
+.RS
+.TP 5
+\f(CW*\fR
+Matches any sequence of characters in
+string, including a null string.
+.TP 5
+\f(CW?\fR
+Matches any single character in string.
+.TP 5
+\f(CW[\fIchars\f(CW]\fR
+Matches any character in the set given by \fIchars\fR. If a sequence of the
+form \fIx\fR-\fIy\fR appears in \fIchars\fR, then any character between
+\fIx\fR and \fIy\fR,
+inclusive, will match.
+.TP 5
+\f(CW\\\fIx\fR
+Matches the single character \fIx\fR. This
+provides a way of avoiding the special
+interpretation of the characters \f(CW*?[]\\\fR in
+the pattern.
+.RE
+.TP 1.25i
+\fB\-regexp\fR
+Use regular expression pattern matching (i.e. the same as implemented
+by the \fBregexp\fR command).
+.TP 1.25i
+\fB\-nonmatching\fR
+Expose nodes that don't match.
+.TP 1.25i
+\fB\-\-\fR
+Indicates the end of flags.
+.RE
+.TP
+\fIpathName \fBsort\fR ?\fIoperation\fR? \fIargs...\fR
+.RS
+.TP
+\fIpathName \fBsort auto\fR ?\fIboolean\fR
+Turns on/off automatic sorting of node entries. If \fIboolean\fR is
+true, entries will be automatically sorted as they are opened,
+closed, inserted, or deleted. If no \fIboolean\fR argument is
+provided, the current state is returned.
+.TP
+\fIpathName \fBsort cget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBsort configure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the sorting configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given sorting option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described below:
+.RS
+.TP
+\fB\-column\fI string\fR
+Specifies the column to sort. Entries in the widget are rearranged
+according to this column. If \fIcolumn\fR is \f(CW""\fR then
+no sort is performed.
+.TP
+\fB\-command\fI string\fR
+Specifies a Tcl procedure to be called when sorting nodes.
+The procedure is called with three arguments: the pathname of the widget
+and the fields of two entries. The procedure returns 1 if the first
+node is greater than the second, -1 is the second is greater, and 0
+if equal.
+.TP
+\fB\-decreasing\fI boolean\fR
+Indicates to sort in ascending/descending order. If \fIboolean\fR
+is true, then the entries as in descending order. The default is
+\f(CWno\fR.
+.TP
+\fB\-mode\fI string\fR
+Specifies how to compare entries when sorting. \fIString\fR
+may be one of the following:
+.RS
+.TP 1.5i
+\f(CWascii\fR
+Use string comparison based upon the ASCII collation order.
+.TP 1.5i
+\f(CWdictionary\fR
+Use dictionary-style comparison. This is the same as \f(CWascii\fR
+except (a) case is ignored except as a tie-breaker and (b) if two
+strings contain embedded numbers, the numbers compare as integers, not
+characters. For example, "bigBoy" sorts between
+"bigbang" and "bigboy", and "x10y" sorts between "x9y" and "x11y".
+.TP 1.5i
+\f(CWinteger\fR
+Compares fields as integers.
+.TP 1.5i
+\f(CWreal\fR
+Compares fields as floating point numbers.
+.TP 1.5i
+\f(CWcommand\fR
+Use the Tcl proc specified by the \fB\-command\fR option to compare entries
+when sorting. If no command is specified, the sort reverts to
+\f(CWascii\fR sorting.
+.RE
+.RE
+.TP
+\fIpathName \fBsort once\fR ?\fIflags\fR? \fItagOrId...\fR
+Sorts the children for each entries specified by \fItagOrId\fR.
+By default, entries are sorted by name, but you can specify a
+Tcl proc to do your own comparisons.
+.RS
+.TP 1.5i
+\fB\-recurse\fR
+Recursively sort the entire branch, not just the children.
+.RE
+.RE
+.TP
+\fIpathName \fBtag \fIoperation args\fR
+Tags are a general means of selecting and marking nodes in the tree.
+A tag is just a string of characters, and it may take any form except
+that of an integer. The same tag may be associated with many
+different nodes.
+.sp
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for tags are listed below.
+.RS
+.TP
+\fIpathName\fR \fBtag add\fR \fIstring\fR \fIid\fR...
+Adds the tag \fIstring\fR to one of more entries.
+.TP
+\fIpathName\fR \fBtag delete\fR \fIstring\fR \fIid\fR...
+Deletes the tag \fIstring\fR from one or more entries.
+.TP
+\fIpathName\fR \fBtag forget\fR \fIstring\fR
+Removes the tag \fIstring\fR from all entries. It's not an error if no
+entries are tagged as \fIstring\fR.
+.TP
+\fIpathName\fR \fBtag names\fR ?\fIid\fR?
+Returns a list of tags used. If an \fIid\fR argument
+is present, only those tags used by the node designated by \fIid\fR
+are returned.
+.TP
+\fIpathName\fR \fBtag nodes\fR \fIstring\fR
+Returns a list of ids that have the tag \fIstring\fR. If no node
+is tagged as \fIstring\fR, then an empty string is returned.
+.RE
+.TP
+\fIpathName \fBtext \fIoperation\fR ?\fIargs\fR?
+This operation is used to provide text editing for cells (data
+fields in a column) or entry labels.
+It has several forms, depending on \fIoperation\fR:
+.RS
+.TP
+\fIpathName \fBtext apply\fR
+Applies the edited buffer, replacing the entry label
+or data field. The edit window is hidden.
+.TP
+\fIpathName \fBtext cancel\fR
+Cancels the editing operation, reverting the entry label
+or data value back to the previous value. The edit window is hidden.
+.TP
+\fIpathName \fBtext cget\fI value\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBconfigure\fR
+operation described below.
+.TP
+\fIpathName \fBtext configure\fR ?\fIoption value\fR?
+Query or modify the configuration options of the edit window.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options (see \fBTk_ConfigureInfo\fR for
+information on the format of this list). If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR and \fIvalue\fR are described in the section
+.SB "TEXT EDITING OPTIONS"
+below.
+.RE
+.TP
+\fIpathName \fBtext delete\fI first last\fR
+Deletes the characters in the edit buffer between the two given
+character positions.
+.TP
+\fIpathName \fBtext get\fR ?\fI\-root\fR? \fIx y\fR
+.TP
+\fIpathName \fBtext icursor\fI index\fR
+.TP
+\fIpathName \fBtext index\fI index\fR
+Returns the text index of given \fIindex\fR.
+.TP
+\fIpathName \fBtext insert\fI index string\fR
+Insert the text string \fIstring\fR into the edit buffer at the index
+\fIindex\fR. For example, the index 0 will prepend the buffer.
+.TP
+\fIpathName \fBtext selection\fI args\fR
+This operation controls the selection of the editing window. Note
+that this differs from the selection of entries.
+It has the following forms:
+.RS
+.TP
+\fIpathName \fBtext selection adjust\fI index\fR
+Adjusts either the first or last index of the selection.
+.TP
+\fIpathName \fBtext selection clear\fR
+Clears the selection.
+.TP
+\fIpathName \fBtext selection from\fI index\fR
+Sets the anchor of the selection.
+.TP
+\fIpathName \fBtext selection present\fR
+Indicates if a selection is present.
+.TP
+\fIpathName \fBtext selection range\fI start end\fR
+Sets both the anchor and mark of the selection.
+.TP
+\fIpathName \fBtext selection to\fI index\fR
+Sets the unanchored end (mark) of the selection.
+.RE
+.TP
+\fIpathName \fBtoggle \fItagOrId\fR
+Opens or closes the node given by \fItagOrId\fR. If the corresponding
+\fB\-opencommand\fR or \fB\-closecommand\fR option is set, then that
+command is also invoked.
+.TP
+\fIpathName \fBxview \fIargs\fR
+This command is used to query and change the horizontal position of the
+information in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the \fBtreeview\fR widget's text is off-screen to the left,
+the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview\fR \fItagOrId\fR
+Adjusts the view in the window so that the character position given by
+\fItagOrId\fR is displayed at the left edge of the window.
+Character positions are defined by the width of the character \fB0\fR.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the \fBtreeview\fR widget's text is off-screen to the left.
+\fIfraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR character units (the width of the \fB0\fR character)
+on the display; if it is \fBpages\fR then the view adjusts by
+\fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \fByview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \fByview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the node at the
+top of the window, relative to the widget as a whole (0.5 means
+it is halfway through the treeview window, for example).
+The second element gives the position of the node just after
+the last one in the window, relative to the widget as a whole.
+These are the same values passed to scrollbars via the \fB\-yscrollcommand\fR
+option.
+.TP
+\fIpathName \fByview\fR \fItagOrId\fR
+Adjusts the view in the window so that the node given by
+\fItagOrId\fR is displayed at the top of the window.
+.TP
+\fIpathName \fByview moveto\fI fraction\fR
+Adjusts the view in the window so that the node given by \fIfraction\fR
+appears at the top of the window.
+\fIFraction\fR is a fraction between 0 and 1; 0 indicates the first
+node, 0.33 indicates the node one-third the
+way through the \fBtreeview\fR widget, and so on.
+.TP
+\fIpathName \fByview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR.
+If \fIwhat\fR is \fBunits\fR, the view adjusts up or down by
+\fInumber\fR lines; if it is \fBpages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier nodes
+become visible; if it is positive then later nodes
+become visible.
+.RE
+.SH "TREEVIEW OPTIONS"
+In addition to the \fBconfigure\fR operation, widget configuration
+options may also be set by the Tk \fBoption\fR command. The class
+resource name is \f(CWTreeView\fR.
+.CS
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+.CE
+The following widget options are available:
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color for active entries. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of the active node. A node
+is active when the mouse passes over it's entry or using the
+\fBactivate\fR operation.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed for an entry's icon
+when it is active. \fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-autocreate \fIboolean\fR
+If \fIboolean\fR is true, automatically create missing ancestor
+nodes when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-allowduplicates \fIboolean\fR
+If \fIboolean\fR is true, allow nodes with duplicate pathnames
+when inserting new nodes. Otherwise flag an error.
+The default is \f(CWno\fR.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the widget. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the outside edge of the widget. The
+\fB\-relief\fR option determines if the border is to be drawn. The
+default is \f(CW2\fR.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is closed. You can
+overrider this for individual entries using the entry's \fB\-closecommand\fR
+option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-cursor \fIcursor\fR
+Specifies the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-dashes \fInumber\fR
+Sets the dash style of the horizontal and vertical lines drawn connecting
+entries. \fINumber\fR is the length in pixels of the dashes and gaps in
+the line. If \fInumber\fR is \f(CW0\fR, solid lines will
+be drawn. The default is \f(CW1\fR (dotted).
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the selection is exported. If the widget is exporting its
+selection then it will observe the standard X11 protocols for handling
+the selection. Selections are available as type \fBSTRING\fR;
+the value of the selection will be the label of the selected nodes,
+separated by newlines. The default is \f(CWno\fR.
+.TP
+\fB\-flat \fIboolean\fR
+Indicates whether to display the tree as a flattened list.
+If \fIboolean\fR is true, then the hierarchy will be a list of full
+paths for the nodes. This option also has affect on sorting.
+See the
+.SB "SORT OPERATIONS"
+section for more information.
+The default is \f(CWno\fR.
+.TP
+\fB\-focusdashes \fIdashList\fR
+Sets the dash style of the outline rectangle drawn around the entry
+label of the node that current has focus. \fINumber\fR is the length
+in pixels of the dashes and gaps in the line. If
+\fInumber\fR is \f(CW0\fR, a solid line will be drawn. The default is
+\f(CW1\fR.
+.TP
+\fB\-focusforeground \fIcolor\fR
+Sets the color of the focus rectangle.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Specifies the font for entry labels. You can override this for individual
+entries with the entry's \fB\-font\fR configuration option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of entry labels. You can override this for individual
+entries with the entry's \fB\-foreground\fR configuration option.
+The default is
+\f(CWblack\fR.
+.TP
+\fB\-height \fIpixels\fR
+Specifies the requested height of widget. The default is
+\f(CW400\fR.
+.TP
+\fB\-hideroot \fIboolean\fR
+If \fIboolean\fR is true, it indicates that no entry for the root node
+should be displayed. The default is \f(CWno\fR.
+.TP
+\fB\-highlightbackground \fIcolor\fR
+Specifies the normal color of the traversal highlight region when
+the widget does not have the input focus.
+.TP
+\fB\-highlightcolor \fIcolor\fR
+Specifies the color of the traversal highlight rectangle when
+the widget has the input focus.
+The default is \f(CWblack\fR.
+.TP
+\fB\-highlightthickness \fIpixels\fR
+Specifies the width of the highlight rectangle indicating when the
+widget has input focus. The value may have any of the forms acceptable
+to \fBTk_GetPixels\fR. If the value is zero, no focus highlight will
+be displayed. The default is \f(CW2\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images for the entry's icon.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-linecolor \fIcolor\fR
+Sets the color of the connecting lines drawn between entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-linespacing \fIpixels\fR
+Sets the number of pixels spacing between entries.
+The default is \f(CW0\fR.
+.TP
+\fB\-linewidth \fIpixels\fR
+Set the width of the lines drawn connecting entries. If \fIpixels\fR
+is \f(CW0\fR, no vertical or horizontal lines are drawn.
+The default is \f(CW1\fR.
+.TP
+\fB\-newtags \fIboolean\fR
+If \fIboolean\fR is true, when sharing a tree object (see the
+\fB\-tree\fR option), don't share its tags too.
+The default is \f(CW0\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when a node is open.
+You can override this for individual entries with the entry's
+\fB\-opencommand\fR configuration option. The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect for the widget. \fIRelief\fR
+specifies how the \fBtreeview\fR widget should appear relative to widget
+it is packed into; for example, \f(CWraised\fR means the \fBtreeview\fR widget
+should appear to protrude. The default is \f(CWsunken\fR.
+.TP
+\fB\-scrollmode \fImode\fR
+Specifies the style of scrolling to be used. The following
+styles are valid. This is the default is \f(CWhierbox\fR.
+.RS
+.TP 1.25i
+\f(CWlistbox\fR
+Like the \fBlistbox\fR widget, the last entry can always be
+scrolled to the top of the widget window. This allows the scrollbar
+thumb to shrink as the last entry is scrolled upward.
+.TP 1.25i
+\f(CWhierbox\fR
+Like the \fBhierbox\fR widget, the last entry can only be
+viewed at the bottom of the widget window. The scrollbar
+stays a constant size.
+.TP 1.25i
+\f(CWcanvas\fR
+Like the \fBcanvas\fR widget, the entries are bound within
+the scrolling area.
+.RE
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background color selected node entries.
+The default is \f(CW#ffffea\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the raised 3-D border drawn around the labels
+of selected entries. The default is \f(CW0\fR.
+\fB\-selectcommand \fIstring\fR
+Specifies a Tcl script to invoked when the set of selected
+nodes changes.
+The default is \f(CW""\fR.
+.TP
+\fB\-selectforeground \fIcolor\fB
+Sets the color of the labels of selected node entries.
+The default is \f(CWblack\fR.
+.TP
+\fB\-selectmode \fImode\fR
+Specifies the selection mode. If \fImode\fR is
+\f(CWsingle\fR, only one node can be selected
+at a time. If \f(CWmultiple\fR more than one
+node can be selected.
+The default is \f(CWsingle\fR.
+.TP
+\fB\-separator \fIstring\fR
+Specifies the character sequence to use when spliting the path components.
+The separator may be several characters wide (such as "::")
+Consecutive separators in a pathname are treated as one.
+If \fIstring\fR is the empty string, the pathnames are Tcl lists.
+Each element is a path component. The default is \f(CW""\fR.
+.TP
+\fB\-showtitles \fIboolean\fR
+If \fIboolean\fR is false, column titles are not be displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-sortselection \fIboolean\fR
+If \fIboolean\fR is true, nodes in the selection are ordered as they
+are currently displayed (depth-first or sorted), not in the order
+they were selected. The default is \f(CWno\fR.
+.TP
+\fB\-takefocus\fR \fIfocus\fR
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and Shift-Tab). If \fIfocus\fR is
+\f(CW0\fR, this means that this window should be skipped entirely during
+keyboard traversal. \f(CW1\fR means that the this window should always
+receive the input focus. An empty value means that the traversal
+scripts make the decision whether to focus on the window.
+The default is \f(CW"1"\fR.
+.TP
+\fB\-trim \fIstring\fR
+Specifies a string leading characters to trim from entry pathnames
+before parsing. This only makes sense if the \fB\-separator\fR is also
+set. The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the widget. If \fIpixels\fR is 0, then
+the with is computed from the contents of the \fBtreeview\fR widget.
+The default is \f(CW200\fR.
+.TP
+\fB\-xscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars. Whenever the horizontal view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-xscrollincrement\fR \fIpixels\fR
+Sets the horizontal scrolling distance. The default is 20 pixels.
+.TP
+\fB\-yscrollcommand \fIstring\fR
+Specifies the prefix for a command used to communicate with vertical
+scrollbars. Whenever the vertical view in the widget's window
+changes, the widget will generate a Tcl command by concatenating the
+scroll command and two numbers. If this option is not specified, then
+no command will be executed.
+.TP
+\fB\-yscrollincrement\fR \fIpixels\fR
+Sets the vertical scrolling distance. The default is 20 pixels.
+.SH "ENTRY OPTIONS"
+Many widget configuration options have counterparts in entries. For
+example, there is a \fB\-closecommand\fR configuration option for both
+widget itself and for individual entries. Options set at the widget
+level are global for all entries. If the entry configuration option
+is set, then it overrides the widget option. This is done to avoid
+wasting memory by replicated options. Most entries will have
+redundant options.
+.PP
+There is no resource class or name for entries.
+.TP
+\fB\-activeicons \fIimages\fR
+Specifies images to be displayed as the entry's icon
+when it is active. This overrides the global \fB\-activeicons\fR
+configuration option for the specific entry.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-bindtags \fItagList\fR
+Specifies the binding tags for nodes. \fITagList\fR is a list
+of binding tag names. The tags and their order will determine how
+events are handled for nodes. Each tag in the list matching the current
+event sequence will have its Tcl command executed. The default value
+is \f(CWall\fR.
+.TP
+\fB\-button \fIstring\fR
+Indicates whether a button should be displayed on the left side
+of the node entry. \fIString\fR can be \f(CWyes\fR, \f(CWno\fR,
+or \f(CWauto\fR. If \f(CWauto\fR, then a button is automatically
+displayed if the node has children. This is the default.
+.TP
+\fB\-closecommand \fIstring\fR
+Specifies a Tcl script to be invoked when the node is closed. This
+overrides the global \fB\-closecommand\fR option for this entry.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.TP
+\fB\-data \fIstring\fR
+Sets data fields for the node. \fIString\fR is a list of
+name-value pairs to be set. The default is \f(CW""\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for entry labels. This overrides the widget's
+\fB\-font\fR option for this node. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the text color of the entry label. This overrides the widget's
+\fB\-foreground\fR configuration option. The default is \f(CW""\fR.
+.TP
+\fB\-icons \fIimages\fR
+Specifies images to be displayed for the entry's icon.
+This overrides the global \fB\-icons\fR configuration option.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the node is open, the
+second when it is closed.
+.TP
+\fB\-label \fIstring\fR
+Sets the text for the entry's label. If not set, this
+defaults to the name of the node. The default is \f(CW""\fR.
+.TP
+\fB\-opencommand \fIstring\fR
+Specifies a Tcl script to be invoked when the entry is opened.
+This overrides the widget's \fB\-opencommand\fR option for this node.
+The default is \f(CW""\fR.
+Percent substitutions are performed on \fIstring\fR before
+it is executed. The following substitutions are valid:
+.RS
+.TP 5
+\f(CW%W\fR
+The pathname of the widget.
+.TP 5
+\f(CW%p\fR
+The name of the node.
+.TP 5
+\f(CW%P\fR
+The full pathname of the node.
+.TP 5
+\f(CW%#\fR
+The id of the node.
+.TP 5
+\f(CW%%\fR
+Translates to a single percent.
+.RE
+.SH "BUTTON OPTIONS"
+Button configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWButton\fR. The resource name is always
+\f(CWbutton\fR.
+.CS
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+.CE
+The following are the configuration options available for buttons.
+.TP
+\fB\-activebackground \fIcolor\fR
+Sets the background color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-activeforeground \fIcolor\fR
+Sets the foreground color of active buttons. A button
+is made active when the mouse passes over it or by the
+\fBbutton activate\fR operation.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the button. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the button.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-closerelief \fIrelief\fR
+Specifies the 3-D effect for the closed button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-cursor \fIcursor\fR
+Sets the widget's cursor. The default cursor is \f(CW""\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Sets the foreground color of buttons.
+The default is \f(CWblack\fR.
+.TP
+\fB\-images \fIimages\fR
+Specifies images to be displayed for the button.
+\fIImages\fR is a list of two Tk images:
+the first image is displayed when the button is open, the
+second when it is closed. If the \fIimages\fR is the empty string,
+then a plus/minus gadget is drawn. The default is \f(CW""\fR.
+.TP
+\fB\-openrelief \fIrelief\fR
+Specifies the 3-D effect of the open button. \fIRelief\fR
+indicates how the button should appear relative to the widget;
+for example, \f(CWraised\fR means the button should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-size \fIpixels\fR
+Sets the requested size of the button.
+The default is \f(CW0\fR.
+.RE
+.SH "COLUMN OPTIONS"
+Column configuration options may also be set by the \fBoption\fR command.
+The resource subclass is \f(CWColumn\fR. The resource name is the
+name of the column.
+.CS
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+.CE
+The following configuration options are available for columns.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background color of the column. This overrides
+the widget's \fB\-background\fR option. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border of the column.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW0\fR.
+.TP
+\fB\-edit \fIboolean\fR
+Indicates if the column's data fields can be edited. If \fIboolean\fR is
+false, the data fields in the column may not be edited.
+The default is \f(CWyes\fR.
+.TP
+\fB\-foreground \fIcolor\fR
+Specifies the foreground color of the column.
+You can override this for individual entries with the entry's
+\fB\-foreground\fR option.
+The default is \f(CWblack\fR.
+.TP
+\fB\-font \fIfontName\fR
+Sets the font for a column. You can override this for individual entries
+with the entry's \fB\-font\fR option. The default is
+\f(CW*-Helvetica-Bold-R-Normal-*-12-120-*\fR.
+.TP
+\fB\-hide \fIboolean\fR
+If \fIboolean\fR is true, the column is not displayed.
+The default is \f(CWyes\fR.
+.TP
+\fB\-justify \fIjustify\fR
+Specifies how the column data fields title should be justified within
+the column. This matters only when the column is wider than the
+data field to be display.
+\fIJustify\fR must be \f(CWleft\fR, \f(CWright\fR, or \f(CWcenter\fR.
+The default is \f(CWleft\fR.
+.TP
+\fB\-pad \fIpad\fR
+Specifies how much padding for the left and right sides of the column.
+\fIPad\fR is a list of one or two screen distances. If \fIpad\fR
+has two elements, the left side of the column is padded by the first
+distance and the right side by the second. If \fIpad\fR has just one
+distance, both the left and right sides are padded evenly. The
+default is \f(CW2\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the column. \fIRelief\fR
+specifies how the column should appear relative to the widget;
+for example, \f(CWraised\fR means the column should
+appear to protrude. The default is \f(CWflat\fR.
+.TP
+\fB\-state \fIstate\fR
+Sets the state of the column. If \fIstate\fR is \f(CWdisable\fR then
+the column title can not be activated nor invoked.
+The default is \f(CWnormal\fR.
+.TP
+\fB\-text \fIstring\fR
+Sets the title for the column.
+The default is \f(CW""\fR.
+.TP
+\fB\-titleforeground \fIcolor\fR
+Sets the foreground color of the column title.
+The default is \f(CWblack\fR.
+.TP
+\fB\-titleshadow \fIcolor\fR
+Sets the color of the drop shadow of the column title.
+The default is \f(CW""\fR.
+.TP
+\fB\-width \fIpixels\fR
+Sets the requested width of the column. This overrides
+the computed with of the column. If \fIpixels\fR is 0,
+the width is computed as from the contents of the column. The
+default is \f(CW0\fR.
+.RE
+.SH "TEXT EDITING OPTIONS"
+Text edit window configuration options may also be set by the
+\fBoption\fR command. The resource class is \f(CWTreeViewEditor\fR.
+The resource name is always \f(CWedit\fR.
+.CS
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+.CE
+The following are the configuration options available for the
+text editing window.
+.TP
+\fB\-background \fIcolor\fR
+Sets the background of the text edit window. The default is \f(CWwhite\fR.
+.TP
+\fB\-borderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the edit window.
+The \fB\-relief\fR option determines if a border is to be drawn. The
+default is \f(CW1\fR.
+.TP
+\fB\-exportselection \fIboolean\fR
+Indicates if the text selection is exported. If the edit window is
+exporting its selection then it will observe the standard X11 protocols
+for handling the selection. Selections are available as type \fBSTRING\fR.
+The default is \f(CWno\fR.
+.TP
+\fB\-relief \fIrelief\fR
+Specifies the 3-D effect of the edit window. \fIRelief\fR
+indicates how the background should appear relative to the edit
+window; for example, \f(CWraised\fR means the background should
+appear to protrude. The default is \f(CWsolid\fR.
+.TP
+\fB\-selectbackground \fIcolor\fR
+Sets the background of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectborderwidth \fIpixels\fR
+Sets the width of the 3\-D border around the selected text in the
+edit window. The \fB\-selectrelief\fR option determines if a border
+is to be drawn. The default is \f(CW1\fR.
+.TP
+\fB\-selectforeground \fIcolor\fR
+Sets the foreground of the selected text in the edit window.
+The default is \f(CWwhite\fR.
+.TP
+\fB\-selectrelief \fIrelief\fR
+Specifies the 3-D effect of the selected text in the edit window.
+\fIRelief\fR indicates how the text should appear relative to the edit
+window; for example, \f(CWraised\fR means the text should
+appear to protrude. The default is \f(CWflat\fR.
+.RE
+.SH "DEFAULT BINDINGS"
+Tk automatically creates class bindings for treeviews that give them
+Motif-like behavior. Much of the behavior of a \fBtreeview\fR widget is determined
+by its \fB\-selectmode\fR option, which selects one of two ways
+of dealing with the selection.
+.PP
+If the selection mode is \fBsingle\fR, only one node can be
+selected at a time.
+Clicking button 1 on an node selects
+it and deselects any other selected item.
+.PP
+If the selection mode is \fBmultiple\fR,
+any number of entries may be selected at once, including discontiguous
+ranges. Clicking Control-Button-1 on a node entry
+toggles its selection state without affecting any other entries.
+Pressing Shift-Button-1 on a node entry selects
+it, extends the selection.
+.IP [1]
+In \fBextended\fR mode, the selected range can be adjusted by pressing
+button 1 with the Shift key down: this modifies the selection to
+consist of the entries between the anchor and the entry under
+the mouse, inclusive.
+The un-anchored end of this new selection can also be dragged with
+the button down.
+.IP [2]
+In \fBextended\fR mode, pressing button 1 with the Control key down
+starts a toggle operation: the anchor is set to the entry under
+the mouse, and its selection state is reversed. The selection state
+of other entries isn't changed.
+If the mouse is dragged with button 1 down, then the selection state
+of all entries between the anchor and the entry under the mouse
+is set to match that of the anchor entry; the selection state of
+all other entries remains what it was before the toggle operation
+began.
+.IP [3]
+If the mouse leaves the treeview window with button 1 down, the window
+scrolls away from the mouse, making information visible that used
+to be off-screen on the side of the mouse.
+The scrolling continues until the mouse re-enters the window, the
+button is released, or the end of the hierarchy is reached.
+.IP [4]
+Mouse button 2 may be used for scanning.
+If it is pressed and dragged over the \fBtreeview\fR widget, the contents of
+the hierarchy drag at high speed in the direction the mouse moves.
+.IP [5]
+If the Up or Down key is pressed, the location cursor (active
+entry) moves up or down one entry.
+If the selection mode is \fBbrowse\fR or \fBextended\fR then the
+new active entry is also selected and all other entries are
+deselected.
+In \fBextended\fR mode the new active entry becomes the
+selection anchor.
+.IP [6]
+In \fBextended\fR mode, Shift-Up and Shift-Down move the location
+cursor (active entry) up or down one entry and also extend
+the selection to that entry in a fashion similar to dragging
+with mouse button 1.
+.IP [7]
+The Left and Right keys scroll the \fBtreeview\fR widget view left and right
+by the width of the character \fB0\fR.
+Control-Left and Control-Right scroll the \fBtreeview\fR widget view left and
+right by the width of the window.
+Control-Prior and Control-Next also scroll left and right by
+the width of the window.
+.IP [8]
+The Prior and Next keys scroll the \fBtreeview\fR widget view up and down
+by one page (the height of the window).
+.IP [9]
+The Home and End keys scroll the \fBtreeview\fR widget horizontally to
+the left and right edges, respectively.
+.IP [10]
+Control-Home sets the location cursor to the the first entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [11]
+Control-End sets the location cursor to the the last entry,
+selects that entry, and deselects everything else
+in the widget.
+.IP [12]
+In \fBextended\fR mode, Control-Shift-Home extends the selection
+to the first entry and Control-Shift-End extends
+the selection to the last entry.
+.IP [13]
+In \fBmultiple\fR mode, Control-Shift-Home moves the location cursor
+to the first entry and Control-Shift-End moves
+the location cursor to the last entry.
+.IP [14]
+The space and Select keys make a selection at the location cursor
+(active entry) just as if mouse button 1 had been pressed over
+this entry.
+.IP [15]
+In \fBextended\fR mode, Control-Shift-space and Shift-Select
+extend the selection to the active entry just as if button 1
+had been pressed with the Shift key down.
+.IP [16]
+In \fBextended\fR mode, the Escape key cancels the most recent
+selection and restores all the entries in the selected range
+to their previous selection state.
+.IP [17]
+Control-slash selects everything in the widget, except in
+\fBsingle\fR and \fBbrowse\fR modes, in which case it selects
+the active entry and deselects everything else.
+.IP [18]
+Control-backslash deselects everything in the widget, except in
+\fBbrowse\fR mode where it has no effect.
+.IP [19]
+The F16 key (labelled Copy on many Sun workstations) or Meta-w
+copies the selection in the widget to the clipboard, if there is
+a selection.
+.PP
+The behavior of \fBtreeview\fR widgets can be changed by defining new bindings
+for individual widgets or by redefining the class bindings.
+.SS WIDGET BINDINGS
+In addition to the above behavior, the following additional behavior
+is defined by the default widget class (TreeView) bindings.
+.IP \f(CW<ButtonPress-2>\fR
+Starts scanning.
+.IP \f(CW<B2-Motion>\fR
+Adjusts the scan.
+.IP \f(CW<ButtonRelease-2>\fR
+Stops scanning.
+.IP \f(CW<B1-Leave>\fR
+Starts auto-scrolling.
+.IP \f(CW<B1-Enter>\fR
+Starts auto-scrolling
+.IP \f(CW<KeyPress-Up>\fR
+Moves the focus to the previous entry.
+.IP \f(CW<KeyPress-Down>\fR
+Moves the focus to the next entry.
+.IP \f(CW<Shift-KeyPress-Up>\fR
+Moves the focus to the previous sibling.
+.IP \f(CW<Shift-KeyPress-Down>\fR
+Moves the focus to the next sibling.
+.IP \f(CW<KeyPress-Prior>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Next>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-Left>\fR
+Closes the entry. It is not an error if the entry has no children.
+.IP \f(CW<KeyPress-Right>\fR
+Opens the entry, displaying its children. It is not an
+error if the entry has no children.
+.IP \f(CW<KeyPress-space>\fR
+In "single" select mode this selects the entry. In "multiple" mode,
+it toggles the entry (if it was previous selected, it is not
+deselected).
+.IP \f(CW<KeyRelease-space>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress-Return>\fR
+Sets the focus to the current entry.
+.IP \f(CW<KeyRelease-Return>\fR
+Turns off select mode.
+.IP \f(CW<KeyPress>\fR
+Moves to the next entry whose label starts with the letter typed.
+.IP \f(CW<KeyPress-Home>\fR
+Moves the focus to first entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-End>\fR
+Move the focus to the last entry. Closed or hidden entries
+are ignored.
+.IP \f(CW<KeyPress-F1>\fR
+Opens all entries.
+.IP \f(CW<KeyPress-F2>\fR
+Closes all entries (except root).
+.SS BUTTON BINDINGS
+Buttons have bindings. There are associated with the "all" bindtag
+(see the entry's -bindtag option). You can use the \fBbind\fR
+operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the button of the current entry.
+.IP \f(CW<Leave>\fR
+Returns the button back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the view so that the current entry is visible.
+.SS ENTRY BINDINGS
+Entries have default bindings. There are associated with the "all"
+bindtag (see the entry's -bindtag option). You can use the \fBbind\fR
+operation to modify them.
+.IP \f(CW<Enter>\fR
+Highlights the current entry.
+.IP \f(CW<Leave>\fR
+Returns the entry back to its normal state.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the selection anchor the current entry.
+.IP \f(CW<Double-ButtonPress-1>\fR
+Toggles the selection of the current entry.
+.IP \f(CW<B1-Motion>\fR
+For "multiple" mode only. Saves the current location of the
+pointer for auto-scrolling. Resets the selection mark.
+.IP \f(CW<ButtonRelease-1>\fR
+For "multiple" mode only. Sets the selection anchor to the
+current entry.
+.IP \f(CW<Shift-ButtonPress-1>\fR
+For "multiple" mode only. Extends the selection.
+.IP \f(CW<Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Shift-ButtonRelease-1>\fR
+Stop auto-scrolling.
+.IP \f(CW<Control-ButtonPress-1>\fR
+For "multiple" mode only. Toggles and extends the selection.
+.IP \f(CW<Control-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-B1-Motion>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-ButtonRelease-1>\fR
+Stops auto-scrolling.
+.IP \f(CW<Control-Shift-ButtonPress-1>\fR
+???
+.IP \f(CW<Control-Shift-Double-ButtonPress-1>\fR
+Place holder. Does nothing.
+.IP \f(CW<Control-Shift-B1-Motion>\fR
+Place holder. Does nothing.
+.SS COLUMN BINDINGS
+Columns have bindings too. They are associated with the column's
+"all" bindtag (see the column -bindtag option). You can use the
+\fBcolumn bind\fR operation to change them.
+.IP \f(CW<Enter>\fR
+Highlights the current column title.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state.
+.IP \f(CW<ButtonRelease-1>\fR
+Invokes the command (see the column's -command option) if one
+if specified.
+.SS COLUMN RULE BINDINGS
+.IP \f(CW<Enter>\fR
+Highlights the current and activates the ruler.
+.IP \f(CW<Leave>\fR
+Returns the column back to its normal state. Deactivates the
+ruler.
+.IP \f(CW<ButtonPress-1>\fR
+Sets the resize anchor for the column.
+.IP \f(CW<B1-Motion>\fR
+Sets the resize mark for the column.
+.IP \f(CW<ButtonRelease-1>\fR
+Adjust the size of the column, based upon the resize anchor and mark
+positions.
+.SH EXAMPLE
+The \fBtreeview\fR command creates a new widget.
+.CS
+treeview .h \-bg white
+.CE
+A new Tcl command \f(CW.h\fR is also created. This command can be used
+to query and modify the \fBtreeview\fR widget. For example, to change the
+background
+color of the table to "green", you use the new command and the widget's
+\fBconfigure\fR operation.
+.CS
+# Change the background color.
+\&.h configure \-background "green"
+.CE
+By default, the \fBtreeview\fR widget will automatically create a new tree object
+to contain the data. The name of the new tree is the pathname of the
+widget. Above, the new tree object name is ".h". But you can use the
+\fB\-tree\fR option to specify the name of another tree.
+.CS
+# View the tree "myTree".
+\&.h configure \-tree "myTree"
+.CE
+When a new tree is created, it contains only a root node. The node
+is automatically opened. The id of the root node is always
+\f(CW0\fR (you can use also use the special id \f(CWroot\fR). The
+\fBinsert\fR operation lets you insert one or more new entries into
+the tree. The last argument is the node's \fIpathname\fR.
+.CS
+# Create a new entry named "myEntry"
+set id [\&.h insert end "myEntry"]
+.CE
+This appends a new node named "myEntry". It will positioned as the
+last child of the root of the tree (using the position "end"). You
+can supply another position to order the node within its siblings.
+.CS
+# Prepend "fred".
+set id [\&.h insert 0 "fred"]
+.CE
+Entry names do not need to be unique. By default, the node's label
+is its name. To supply a different text label, add the \fB\-label\fR
+option.
+.CS
+# Create a new node named "fred"
+set id [\&.h insert end "fred" -label "Fred Flintstone"]
+.CE
+The \fBinsert\fR operation returns the id of the new node. You can
+also use the \fBindex\fR operation to get this information.
+.CS
+# Get the id of "fred"
+\&.h index "fred"
+.CE
+To insert a node somewhere other than root, use the \fB\-at\fR switch.
+It takes the id of the node where the new child will be added.
+.CS
+# Create a new node "barney" in "fred".
+\&.h insert -at $id end "barney"
+.CE
+A pathname describes the path to an entry in the hierarchy. It's a
+list of entry names that compose the path in the tree. Therefore, you
+can also add "barney" to "fred" as follows.
+.CS
+# Create a new sub-entry of "fred"
+\&.h insert end "fred barney"
+.CE
+Every name in the list is ancestor of the next. All ancestors must
+already exist. That means that an entry "fred" is an ancestor of
+"barney" and must already exist. But you can use the
+\fB\-autocreate\fR configuration option to force the creation of
+ancestor nodes.
+.CS
+# Force the creation of ancestors.
+\&.h configure -autocreate yes
+\&.h insert end "fred barney wilma betty"
+.CE
+Sometimes the pathname is already separated by a character sequence
+rather than formed as a list. A file name is a good example of this.
+You can use the \fB\-separator\fR option to specify a separator string
+to split the path into its components. Each pathname inserted is
+automatically split using the separator string as a separator.
+Multiple separators are treated as one.
+.CS
+\&.h configure -separator /
+\&.h insert end "/usr/local/tcl/bin"
+.CE
+If the path is prefixed by extraneous characters, you can
+automatically trim it off using the \fB\-trim\fR option. It removed
+the string from the path before it is parsed.
+.CS
+\&.h configure -trim C:/windows -separator /
+\&.h insert end "C:/window/system"
+.CE
+You can insert more than one entry at a time with the \fBinsert\fR
+operation. This can be much faster than looping over a list of names.
+.CS
+# The slow way
+foreach f [glob $dir/*] {
+ \&.h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+.CE
+In this case, the \fBinsert\fR operation will return a list of ids
+of the new entries.
+.PP
+You can delete entries with the \fBdelete\fR operation. It takes one or
+more tags of ids as its argument. It deletes the entry and all its
+children.
+.CS
+\&.h delete $id
+.CE
+Entries have several configuration options. They control the appearance
+of the entry's icon and label. We have already seen the \fB\-label\fR
+option that sets the entry's text label. The \fBentry configure\fR
+operation lets you set or modify an entry's configuration options.
+.CS
+\&.h entry configure $id -color red -font fixed
+.CE
+You can hide an entry and its children using the \fB\-hide\fR option.
+.CS
+\&.h entry configure $id -hide yes
+.CE
+More that one entry can be configured at once. All entries specified
+are configured with the same options.
+.CS
+\&.h entry configure $i1 $i2 $i3 $i4 -color brown
+.CE
+An icon is displayed for each entry. It's a Tk image drawn to the
+left of the label. You can set the icon with the entry's
+\fB\-icons\fR option. It takes a list of two image names: one to
+represent the open entry, another when it is closed.
+.CS
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+\&.h entry configure $id -icons "$im1 $im2"
+.CE
+If \fB\-icons\fR is set to the empty string, no icons are display.
+.PP
+If an entry has children, a button is displayed to the left of the
+icon. Clicking the mouse on this button opens or closes the
+sub-hierarchy. The button is normally a \f(CW+\fR or \f(CW\-\fR
+symbol, but can be configured in a variety of ways using the \fBbutton
+configure\fR operation. For example, the \f(CW+\fR and \f(CW\-\fR
+symbols can be replaced with Tk images.
+.CS
+set im1 [image create photo -file closefolder.gif]
+set im2 [image create photo -file downarrow.gif]
+\&.h button configure $id -images "$im1 $im2" \\
+ -openrelief raised -closerelief raised
+.CE
+Entries can contain an arbitrary number of \fIdata fields\fR. Data
+fields are name-value pairs. Both the value and name are strings.
+The entry's \fB\-data\fR option lets you set data fields.
+.CS
+\&.h entry configure $id -data {mode 0666 group users}
+.CE
+The \fB\-data\fR takes a list of name-value pairs.
+.PP
+You can display these data fields as \fIcolumns\fR in the
+\fBtreeview\fR widget. You can create and configure columns with
+the \fBcolumn\fR operation. For example, to add a new column to the
+widget, use the \fBcolumn insert\fR operation. The last argument is
+the name of the data field that you want to display.
+.CS
+\&.h column insert end "mode"
+.CE
+The column title is displayed at the top of the column. By default,
+it's is the field name. You can override this using the column's
+\fB\-text\fR option.
+.CS
+\&.h column insert end "mode" -text "File Permissions"
+.CE
+Columns have several configuration options. The \fBcolumn
+configure\fR operation lets you query or modify column options.
+.CS
+\&.h column configure "mode" -justify left
+.CE
+The \fB\-justify\fR option says how the data is justified within in
+the column. The \fB\-hide\fR option indicates whether the column is
+displayed.
+.CS
+\&.h column configure "mode" -hide yes
+.CE
+Entries can be selected by clicking on the mouse. Selected entries
+are drawn using the colors specified by the \fB\-selectforeground\fR
+and \fB\-selectbackground\fR configuration options.
+The selection itself is managed by the \fBselection\fR operation.
+.CS
+# Clear all selections
+\&.h selection clear 0 end
+# Select the root node
+\&.h selection set 0
+.CE
+The \fBcurselection\fR operation returns a list of ids of
+all the selected entries.
+.CS
+set ids [\&.h curselection]
+.CE
+You can use the \fBget\fR operation to convert the ids to
+their pathnames.
+.CS
+set names [eval .h get -full $ids]
+.CE
+If a treeview is exporting its selection (using the
+\fB\-exportselection\fR option), then it will observe the standard X11
+protocols for handling the selection. Treeview selections are
+available as type \fBSTRING\fR; the value of the selection will be the
+pathnames of the selected entries, separated by newlines.
+.PP
+The \fBtreeview\fR supports two modes of selection: \f(CWsingle\fR
+and \f(CWmultiple\fR. In single select mode, only one entry can be
+selected at a time, while multiple select mode allows several entries
+to be selected. The mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -selectmode "multiple"
+.CE
+You can be notified when the list of selected entries changes. The widget's
+\fB\-selectcommand\fR specifies a Tcl procedure that is called whenever
+the selection changes.
+.CS
+proc SelectNotify { widget } {
+ set ids [\&$widget curselection]
+}
+\&.h configure -selectcommand "SelectNotify .h"
+.CE
+The widget supports the standard Tk scrolling and scanning operations.
+The \fBtreeview\fR can be both horizontally and vertically. You can
+attach scrollbars to the \fBtreeview\fR the same way as the listbox
+or canvas widgets.
+.CS
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+\&.h configure -xscrollcommand ".xbar set" \\
+ -yscrollcommand ".ybar set"
+.CE
+There are three different modes of scrolling: \f(CWlistbox\fR,
+\f(CWcanvas\fR, and \f(CWhierbox\fR. In \f(CWlistbox\fR mode, the last
+entry can always be scrolled to the top of the widget. In \f(CWhierbox\fR
+mode, the last entry is always drawn at the bottom of the widget.
+The scroll mode is set by the widget's \fB\-selectmode\fR
+option.
+.CS
+\&.h configure -scrollmode "listbox"
+.CE
+Entries can be programmatically opened or closed using the \fBopen\fR
+and \fBclose\fR operations respectively.
+.CS
+\&.h open $id
+\&.h close $id
+.CE
+When an entry is opened, a Tcl procedure can be automatically invoked.
+The \fB\-opencommand\fR option specifies this procedure. This
+procedure can lazily insert entries as needed.
+.CS
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+\&.h configure -opencommand "AddEntries %P"
+.CE
+Now when an entry is opened, the procedure \f(CWAddEntries\fR is
+called and adds children to the entry. Before the command is invoked,
+special "%" substitutions (like \fBbind\fR) are performed. Above,
+\f(CW%P\fR is translated to the pathname of the entry.
+.PP
+The same feature exists when an entry is closed. The
+\fB\-closecommand\fR option specifies the procedure.
+.CS
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+\&.h configure -closecommand "DeleteEntries %#"
+.CE
+When an entry is closed, the procedure \f(CWDeleteEntries\fR is called
+and deletes the entry's children using the \fBentry delete\fR operation
+(\f(CW%#\fR is the id of entry).
+.SH KEYWORDS
+treeview, widget
diff --git a/blt/man/vector.mann b/blt/man/vector.mann
new file mode 100644
index 00000000000..8e55cc8fe1d
--- /dev/null
+++ b/blt/man/vector.mann
@@ -0,0 +1,1104 @@
+'\"
+'\" Copyright 1991-1997 by Lucent Technologies, Inc.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Vector command created by George Howlett.
+'\"
+.so man.macros
+.TH vector n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+vector \- Vector data type for Tcl
+.SH SYNOPSIS
+\fBvector create \fIvecName \fR?\fIvecName\fR...? ?\fIswitches\fR?
+.sp
+\fBvector destroy \fIvecName \fR?\fIvecName\fR...?
+.sp
+\fBvector expr \fIexpression\fR
+.sp
+\fBvector names \fR?\fIpattern\fR...?
+.BE
+.SH DESCRIPTION
+The \fBvector\fR command creates a vector of floating point
+values. The vector's components can be manipulated in three ways:
+through a Tcl array variable, a Tcl command, or the C API.
+.SH INTRODUCTION
+A vector is simply an ordered set of numbers. The components of a
+vector are real numbers, indexed by counting numbers.
+.PP
+Vectors are common data structures for many applications. For
+example, a graph may use two vectors to represent the X-Y
+coordinates of the data plotted. The graph will automatically
+be redrawn when the vectors are updated or changed. By using vectors,
+you can separate
+data analysis from the graph widget. This makes it easier, for
+example, to add data transformations, such as splines. It's possible
+to plot the same data to in multiple graphs, where each graph presents
+a different view or scale of the data.
+.PP
+You could try to use Tcl's associative arrays as vectors. Tcl arrays
+are easy to use. You can access individual elements randomly by
+specifying the index, or the set the entire array by providing a list
+of index and value pairs for each element. The disadvantages of
+associative arrays as vectors lie in the fact they are implemented as
+hash tables.
+.TP 2
+\(bu
+There's no implied ordering to the associative arrays. If you used
+vectors for plotting, you would want to insure the second component
+comes after the first, an so on. This isn't possible since arrays
+are actually hash tables. For example, you can't get a range of
+values between two indices. Nor can you sort an array.
+.TP 2
+\(bu
+Arrays consume lots of memory when the number of elements becomes
+large (tens of thousands). This is because each element's index and
+value are stored as strings in the hash table.
+.TP 2
+\(bu
+The C programming interface is unwieldy. Normally with vectors, you
+would like to view the Tcl array as you do a C array, as an array of
+floats or doubles. But with hash tables, you must convert both the
+index and value to and from decimal strings, just to access
+an element in the array. This makes it cumbersome to perform operations on
+the array as a whole.
+.PP
+The \fBvector\fR command tries to overcome these disadvantages while
+still retaining the ease of use of Tcl arrays. The \fBvector\fR
+command creates both a new Tcl command and associate array which are
+linked to the vector components. You can randomly access vector
+components though the elements of array. Not have all indices are
+generated for the array, so printing the array (using the \fBparray\fR
+procedure) does not print out all the component values. You can use
+the Tcl command to access the array as a whole. You can copy, append,
+or sort vector using its command. If you need greater performance, or
+customized behavior, you can write your own C code to manage vectors.
+.SH EXAMPLE
+You create vectors using the \fBvector\fR command and its \fBcreate\fR
+operation.
+.CS
+# Create a new vector.
+vector create y(50)
+.CE
+This creates a new vector named \f(CWy\fR. It has fifty components, by
+default, initialized to \f(CW0.0\fR. In addition, both a Tcl command
+and array variable, both named \f(CWy\fR, are created. You can use
+either the command or variable to query or modify components of the
+vector.
+.CS
+# Set the first value.
+set y(0) 9.25
+puts "y has [y length] components"
+.CE
+The array \f(CWy\fR can be used to read or set individual components of
+the vector. Vector components are indexed from zero. The array index
+must be a number less than the number of components. For example,
+it's an error if you try to set the 51st element of \f(CWy\fR.
+.CS
+# This is an error. The vector only has 50 components.
+set y(50) 0.02
+.CE
+You can also specify a range of indices using a colon (:) to separate
+the first and last indices of the range.
+.CS
+# Set the first six components of y
+set y(0:5) 25.2
+.CE
+If you don't include an index, then it will default to the first
+and/or last component of the vector.
+.CS
+# Print out all the components of y
+puts "y = $y(:)"
+.CE
+There are special non-numeric indices. The index \f(CWend\fR, specifies
+the last component of the vector. It's an error to use this index if
+the vector is empty (length is zero). The index \f(CW++end\fR can be
+used to extend the vector by one component and initialize it to a specific
+value. You can't read from the array using this index, though.
+.CS
+# Extend the vector by one component.
+set y(++end) 0.02
+.CE
+The other special indices are \f(CWmin\fR and \f(CWmax\fR. They return the
+current smallest and largest components of the vector.
+.CS
+# Print the bounds of the vector
+puts "min=$y(min) max=$y(max)"
+.CE
+To delete components from a vector, simply unset the corresponding
+array element. In the following example, the first component of
+\f(CWy\fR is deleted. All the remaining components of \f(CWy\fR will be
+moved down by one index as the length of the vector is reduced by
+one.
+.CS
+# Delete the first component
+unset y(0)
+puts "new first element is $y(0)"
+.CE
+The vector's Tcl command can also be used to query or set the vector.
+.CS
+# Create and set the components of a new vector
+vector create x
+x set { 0.02 0.04 0.06 0.08 0.10 0.12 0.14 0.16 0.18 0.20 }
+.CE
+Here we've created a vector \f(CWx\fR without a initial length specification.
+In this case, the length is zero. The \fBset\fR operation resets the vector,
+extending it and setting values for each new component.
+.PP
+There are several operations for vectors. The \fBrange\fR operation
+lists the components of a vector between two indices.
+.CS
+# List the components
+puts "x = [x range 0 end]"
+.CE
+You can search for a particular value using the \fBsearch\fR
+operation. It returns a list of indices of the components with the
+same value. If no component has the same value, it returns \f(CW""\fR.
+.CS
+# Find the index of the biggest component
+set indices [x search $x(max)]
+.CE
+Other operations copy, append, or sort vectors. You can append
+vectors or new values onto an existing vector with the \fBappend\fR
+operation.
+.CS
+# Append assorted vectors and values to x
+x append x2 x3 { 2.3 4.5 } x4
+.CE
+The \fBsort\fR operation sorts the vector. If any additional vectors
+are specified, they are rearranged in the same order as the vector.
+For example, you could use it to sort data points represented by x and
+y vectors.
+.CS
+# Sort the data points
+x sort y
+.CE
+The vector \f(CWx\fR is sorted while the components of \f(CWy\fR are
+rearranged so that the original x,y coordinate pairs are retained.
+.PP
+The \fBexpr\fR operation lets you perform arithmetic on vectors.
+The result is stored in the vector.
+.CS
+# Add the two vectors and a scalar
+x expr { x + y }
+x expr { x * 2 }
+.CE
+When a vector is modified, resized, or deleted, it may trigger
+call-backs to notify the clients of the vector. For example, when a
+vector used in the \fBgraph\fR widget is updated, the vector
+automatically notifies the widget that it has changed. The graph can
+then redrawn itself at the next idle point. By default, the
+notification occurs when Tk is next idle. This way you can modify the
+vector many times without incurring the penalty of the graph redrawing
+itself for each change. You can change this behavior using the
+\fBnotify\fR operation.
+.CS
+# Make vector x notify after every change
+x notify always
+ ...
+# Never notify
+x notify never
+ ...
+# Force notification now
+x notify now
+.CE
+To delete a vector, use the \fBvector delete\fR command.
+Both the vector and its corresponding Tcl command are destroyed.
+.CS
+# Remove vector x
+vector destroy x
+.CE
+.SH SYNTAX
+Vectors are created using the \fBvector create\fR operation.
+Th \fBcreate\fR operation can be invoked in one of three forms:
+.TP
+\fBvector create \fIvecName\fR
+This creates a new vector \fIvecName\fR which initially has no components.
+.TP
+\fBvector create \fIvecName\fR(\fIsize\fR)
+This second form creates a new vector which will contain \fIsize\fR
+number of components. The components will be indexed starting from
+zero (0). The default value for the components is \f(CW0.0\fR.
+.TP
+\fBvector create \fIvecName\fR(\fIfirst\fR:\fIlast\fR)
+The last form creates a new vector of indexed \fIfirst\fR through
+\fIlast\fR. \fIFirst\fR and \fIlast\fR can be any integer value
+so long as \fIfirst\fR is less than \fIlast\fR.
+.PP
+Vector names must start with a letter and consist of letters, digits,
+or underscores.
+.CS
+# Error: must start with letter
+vector create 1abc
+.CE
+You can automatically generate vector names using the
+"\f(CW#auto\fR" vector name. The \fBcreate\fR operation will generate a
+unique vector name.
+.CS
+set vec [vector create #auto]
+puts "$vec has [$vec length] components"
+.CE
+.SS VECTOR INDICES
+Vectors are indexed by integers. You can access the individual vector
+components via its array variable or Tcl command. The string
+representing the index can be an integer, a numeric expression, a
+range, or a special keyword.
+.PP
+The index must lie within the current range of the vector, otherwise
+an an error message is returned. Normally the indices of a vector
+are start from 0. But you can use the \fBoffset\fR operation to
+change a vector's indices on-the-fly.
+.CS
+puts $vecName(0)
+vecName offset -5
+puts $vecName(-5)
+.CE
+You can also use numeric expressions as indices. The result
+of the expression must be an integer value.
+.CS
+set n 21
+set vecName($n+3) 50.2
+.CE
+The following special non-numeric indices are available: \f(CWmin\fR, \f(CWmax\fR, \f(CWend\fR, and
+\f(CW++end\fR.
+.CS
+puts "min = $vecName($min)"
+set vecName(end) -1.2
+.CE
+The indices \f(CWmin\fR and \f(CWmax\fR will return the minimum and maximum
+values of the vector. The index \f(CWend\fR returns the value of the
+last component in the vector. The index \f(CW++end\fR is used to append
+new value onto the vector. It automatically extends the vector by
+one component and sets its value.
+.CS
+# Append an new component to the end
+set vecName(++end) 3.2
+.CE
+A range of indices can be indicated by a colon (:).
+.CS
+# Set the first six components to 1.0
+set vecName(0:5) 1.0
+.CE
+If no index is supplied the first or last component is assumed.
+.CS
+# Print the values of all the components
+puts $vecName(:)
+.CE
+.SH VECTOR OPERATIONS
+.TP
+\fBvector create \fIvecName\fR?(\fIsize\fR)?... \fR?\fIswitches\fR?
+The \fBcreate\fR operation creates a new vector \fIvecName\fR. Both a
+Tcl command and array variable \fIvecName\fR are also created. The
+name \fIvecName\fR must be unique, so another Tcl command or array
+variable can not already exist in that scope. You can access the
+components of the vector using its variable. If you change a value in
+the array, or unset an array element, the vector is updated to reflect
+the changes. When the variable \fIvecName\fR is unset, the vector and
+its Tcl command are also destroyed.
+.sp
+The vector has optional switches that affect how the vector is created. They
+are as follows:
+.RS
+.TP
+\fB\-variable \fIvarName\fR
+Specifies the name of a Tcl variable to be mapped to the vector. If
+the variable already exists, it is first deleted, then recreated.
+If \fIvarName\fR is the empty string, then no variable will be mapped.
+You can always map a variable back to the vector using the vector's
+\fBvariable\fR operation.
+.TP
+\fB\-command \fIcmdName\fR
+Maps a Tcl command to the vector. The vector can be accessed using
+\fIcmdName\fR and one of the vector instance operations.
+A Tcl command by that name cannot already exist.
+If \fIcmdName\fR is the empty string, no command mapping
+will be made.
+.TP
+\fB\-watchunset \fIboolean\fR
+Indicates that the vector should automatically delete itself if
+the variable associated with the vector is unset. By default,
+the vector will not be deleted. This is different from previous
+releases. Set \fIboolean\fR to "true" to get the old behavior.
+.RE
+.TP
+\fBvector destroy \fIvecName\fR \fR?\fIvecName...\fR?
+.TP
+\fBvector expr \fIexpression\fR
+.RS
+All binary operators take vectors as operands (remember that numbers
+are treated as one-component vectors). The exact action of binary
+operators depends upon the length of the second operand. If the
+second operand has only one component, then each element of the first
+vector operand is computed by that value. For example, the expression
+"x * 2" multiples all elements of the vector x by 2. If the second
+operand has more than one component, both operands must be the same
+length. Each pair of corresponding elements are computed. So "x + y"
+adds the the first components of x and y together, the second, and so on.
+.sp
+The valid operators are listed below, grouped in decreasing order
+of precedence:
+.TP 20
+\fB\-\0\0!\fR
+Unary minus and logical NOT. The unary minus flips the sign of each
+component in the vector. The logical not operator returns a vector of
+whose values are 0.0 or 1.0. For each non-zero component 1.0 is returned,
+0.0 otherwise.
+.TP 20
+\fB^\fR
+Exponentiation.
+.TP 20
+\fB*\0\0/\0\0%\fR
+Multiply, divide, remainder.
+.TP 20
+\fB+\0\0\-\fR
+Add and subtract.
+.TP 20
+\fB<<\0\0>>\fR
+Left and right shift. Circularly shifts the values of the vector
+(not implemented yet).
+.TP 20
+\fB<\0\0>\0\0<=\0\0>=\fR
+Boolean less, greater, less than or equal, and greater than or equal.
+Each operator returns a vector of ones and zeros. If the condition is true,
+1.0 is the component value, 0.0 otherwise.
+.TP 20
+\fB==\0\0!=\fR
+Boolean equal and not equal.
+Each operator returns a vector of ones and zeros. If the condition is true,
+1.0 is the component value, 0.0 otherwise.
+.TP 20
+\fB|\fR
+Bit-wise OR. (Not implemented).
+.TP 20
+\fB&&\fR
+Logical AND. Produces a 1 result if both operands are non-zero, 0 otherwise.
+.TP 20
+\fB||\fR
+Logical OR. Produces a 0 result if both operands are zero, 1 otherwise.
+.TP 20
+\fIx\fB?\fIy\fB:\fIz\fR
+If-then-else, as in C. (Not implemented yet).
+.LP
+See the C manual for more details on the results produced by each
+operator. All of the binary operators group left-to-right within the
+same precedence level.
+.sp
+Several mathematical functions are supported for vectors. Each of
+the following functions invokes the math library function of the same name;
+see the manual entries for the library functions for details on what
+they do. The operation is applied to all elements of the vector
+returning the results.
+.CS
+.ta 3c 6c 9c
+\fBacos\fR \fBcos\fR \fBhypot\fR \fBsinh\fR
+\fBasin\fR \fBcosh\fR \fBlog\fR \fBsqrt\fR
+\fBatan\fR \fBexp\fR \fBlog10\fR \fBtan\fR
+\fBceil\fR \fBfloor\fR \fBsin\fR \fBtanh\fR
+.CE
+Additional functions are:
+.TP 1i
+\fBabs\fR
+Returns the absolute value of each component.
+.TP 1i
+\fBrandom\fR
+Returns a vector of non-negative values uniformly distributed
+between [0.0, 1.0) using \fIdrand48\fR.
+The seed comes from the internal clock of the machine or may be
+set manual with the srandom function.
+.TP 1i
+\fBround\fR
+Rounds each component of the vector.
+.TP 1i
+\fBsrandom\fR
+Initializes the random number generator using \fIsrand48\fR.
+The high order 32-bits are set using the integral portion of the first
+vector component. All other components are ignored. The low order 16-bits
+are set to an arbitrary value.
+.PP
+The following functions return a single value.
+.TP 1i
+\fBadev\fR
+Returns the average deviation (defined as the sum of the absolute values
+of the differences between component and the mean, divided by the length
+of the vector).
+.TP 1i
+\fBkurtosis\fR
+Returns the degree of peakedness (fourth moment) of the vector.
+.TP 1i
+\fBlength\fR
+Returns the number of components in the vector.
+.TP 1i
+\fBmax\fR
+Returns the vector's maximum value.
+.TP 1i
+\fBmean\fR
+Returns the mean value of the vector.
+.TP 1i
+\fBmedian\fR
+Returns the median of the vector.
+.TP 1i
+\fBmin\fR
+Returns the vector's minimum value.
+.TP 1i
+\fBq1\fR
+Returns the first quartile of the vector.
+.TP 1i
+\fBq3\fR
+Returns the third quartile of the vector.
+.TP 1i
+\fBprod\fR
+Returns the product of the components.
+.TP 1i
+\fBsdev\fR
+Returns the standard deviation (defined as the square root of the variance)
+of the vector.
+.TP 1i
+\fBskew\fR
+Returns the skewness (or third moment) of the vector. This characterizes
+the degree of asymmetry of the vector about the mean.
+.TP 1i
+\fBsum\fR
+Returns the sum of the components.
+.TP 1i
+\fBvar\fR
+Returns the variance of the vector. The sum of the squared differences
+between each component and the mean is computed. The variance is
+the sum divided by the length of the vector minus 1.
+.PP
+The last set returns a vector of the same length as the argument.
+.TP 1i
+\fBnorm\fR
+Scales the values of the vector to lie in the range [0.0..1.0].
+.TP 1i
+\fBsort\fR
+Returns the vector components sorted in ascending order.
+.RE
+.TP
+\fBvector names \fR?\fIpattern\fR?
+.SH INSTANCE OPERATIONS
+You can also use the vector's Tcl command to query or modify it. The
+general form is
+.DS
+\fIvecName \fIoperation\fR \fR?\fIarg\fR?...
+.DE
+Both \fIoperation\fR and its arguments determine the exact behavior of
+the command. The operations available for vectors are listed below.
+.TP
+\fIvecName \fBappend\fR \fIitem\fR ?\fIitem\fR?...
+Appends the component values from \fIitem\fR to \fIvecName\fR.
+\fIItem\fR can be either the name of a vector or a list of numeric
+values.
+.TP
+\fIvecName \fBclear\fR
+Clears the element indices from the array variable associated with
+\fIvecName\fR. This doesn't affect the components of the vector. By
+default, the number of entries in the Tcl array doesn't match the
+number of components in the vector. This is because its too expensive
+to maintain decimal strings for both the index and value for each
+component. Instead, the index and value are saved only when you read
+or write an element with a new index. This command removes the index
+and value strings from the array. This is useful when the vector is
+large.
+.TP
+\fIvecName \fBdelete\fR \fIindex\fR ?\fIindex\fR?...
+Deletes the \fIindex\fRth component from the vector \fIvecName\fR.
+\fIIndex\fR is the index of the element to be deleted. This is the
+same as unsetting the array variable element \fIindex\fR. The vector
+is compacted after all the indices have been deleted.
+.TP
+\fIvecName \fBdup\fR \fIdestName\fR
+Copies \fIvecName\fR to \fIdestName\fR. \fIDestName\fR is the name of a
+destination vector. If a vector \fIdestName\fR already exists, it is
+overwritten with the components of \fIvecName\fR. Otherwise a
+new vector is created.
+.TP
+\fIvecName \fBexpr\fR \fIexpression\fR
+Computes the expression and resets the values of the vector accordingly.
+Both scalar and vector math operations are allowed. All values in
+expressions are either real numbers or names of vectors. All numbers
+are treated as one component vectors.
+.TP
+\fIvecName \fBlength\fR ?\fInewSize\fR?
+Queries or resets the number of components in \fIvecName\fR.
+\fINewSize\fR is a number specifying the new size of the vector. If
+\fInewSize\fR is smaller than the current size of \fIvecName\fR,
+\fIvecName\fR is truncated. If \fInewSize\fR is greater, the vector
+is extended and the new components are initialized to \f(CW0.0\fR. If
+no \fInewSize\fR argument is present, the current length of the vector
+is returned.
+.TP
+\fIvecName \fBmerge\fR \fIsrcName\fR ?\fIsrcName\fR?...
+Merges the named vectors into a single vector. The resulting
+vector is formed by merging the components of each source vector
+one index at a time.
+.TP
+\fIvecName \fBnotify\fR \fIkeyword\fR
+Controls how vector clients are notified of changes to the vector.
+The exact behavior is determined by \fIkeyword\fR.
+.RS
+.TP 0.75i
+\f(CWalways\fR
+Indicates that clients are to be notified immediately whenever the
+vector is updated.
+.TP
+\f(CWnever\fR
+Indicates that no clients are to be notified.
+.TP
+\f(CWwhenidle\fR
+Indicates that clients are to be notified at the next idle point
+whenever the vector is updated.
+.TP
+\f(CWnow\fR
+If any client notifications is currently pending, they are notified
+immediately.
+.TP
+\f(CWcancel\fR
+Cancels pending notifications of clients using the vector.
+.TP
+\f(CWpending\fR
+Returns \f(CW1\fR if a client notification is pending, and \f(CW0\fR otherwise.
+.RE
+.TP
+\fIvecName \fBoffset\fR ?\fIvalue\fR?
+Shifts the indices of the vector by the amount specified by \fIvalue\fR.
+\fIValue\fR is an integer number. If no \fIvalue\fR argument is
+given, the current offset is returned.
+.TP
+\fIvecName \fBpopulate\fR \fIdestName\fR ?\fIdensity\fR?
+Creates a vector \fIdestName\fR which is a superset of \fIvecName\fR.
+\fIDestName\fR will include all the components of \fIvecName\fR, in
+addition the interval between each of the original components will
+contain a \fIdensity\fR number of new components, whose values are
+evenly distributed between the original components values. This is
+useful for generating abscissas to be interpolated along a spline.
+.TP
+\fIvecName \fBrange\fR \fIfirstIndex\fR ?\fIlastIndex\fR?...
+Returns a list of numeric values representing the vector components
+between two indices. Both \fIfirstIndex\fR and \fIlastIndex\fR are
+indices representing the range of components to be returned. If
+\fIlastIndex\fR is less than \fIfirstIndex\fR, the components are
+listed in reverse order.
+.TP
+\fIvecName \fBsearch\fR \fIvalue\fR ?\fIvalue\fR?
+Searches for a value or range of values among the components of
+\fIvecName\fR. If one \fIvalue\fR argument is given, a list of
+indices of the components which equal \fIvalue\fR is returned. If a
+second \fIvalue\fR is also provided, then the indices of all
+components which lie within the range of the two values are returned.
+If no components are found, then \f(CW""\fR is returned.
+.TP
+\fIvecName \fBset\fR \fIitem\fR
+Resets the components of the vector to \fIitem\fR. \fIItem\fR can
+be either a list of numeric expressions or another vector.
+.TP
+\fIvecName \fBseq\fR \fIstart\fR ?\fIfinish\fR? ?\fIstep\fR?
+Generates a sequence of values starting with the value \fIstart\fR.
+\fIFinish\fR indicates the terminating value of the sequence.
+The vector is automatically resized to contain just the sequence.
+If three arguments are present, \fIstep\fR designates the interval.
+.sp
+With only two arguments (no \fIfinish\fR argument), the sequence will
+continue until the vector is filled. With one argument, the interval
+defaults to 1.0.
+.TP
+\fIvecName \fBsort\fR ?\fB-reverse\fR? ?\fIargName\fR?...
+Sorts the vector \fIvecName\fR in increasing order. If the
+\fB-reverse\fR flag is present, the vector is sorted in decreasing
+order. If other arguments \fIargName\fR are present, they are the
+names of vectors which will be rearranged in the same manner as
+\fIvecName\fR. Each vector must be the same length as \fIvecName\fR.
+You could use this to sort the x vector of a graph, while still
+retaining the same x,y coordinate pairs in a y vector.
+.TP
+\fIvecName \fBvariable\fR \fIvarName\fR
+Maps a Tcl variable to the vector, creating another means for
+accessing the vector. The variable \fIvarName\fR can't already
+exist. This overrides any current variable mapping the vector
+may have.
+.RE
+.SH C LANGUAGE API
+You can create, modify, and destroy vectors from C code, using
+library routines.
+You need to include the header file \f(CWblt.h\fR. It contains the
+definition of the structure \fBBlt_Vector\fR, which represents the
+vector. It appears below.
+.CS
+\fRtypedef struct {
+ double *\fIvalueArr\fR;
+ int \fInumValues\fR;
+ int \fIarraySize\fR;
+ double \fImin\fR, \fImax\fR;
+} \fBBlt_Vector\fR;
+.CE
+The field \fIvalueArr\fR points to memory holding the vector
+components. The components are stored in a double precision array,
+whose size size is represented by \fIarraySize\fR. \fINumValues\fR is
+the length of vector. The size of the array is always equal to or
+larger than the length of the vector. \fIMin\fR and \fImax\fR are
+minimum and maximum component values.
+.SH LIBRARY ROUTINES
+The following routines are available from C to manage vectors.
+Vectors are identified by the vector name.
+.PP
+\fBBlt_CreateVector\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_CreateVector\fR (\fIinterp\fR, \fIvecName\fR, \fIlength\fR, \fIvecPtrPtr\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+char *\fIvecName\fR;
+int \fIlength\fR;
+Blt_Vector **\fIvecPtrPtr\fR;
+.RE
+.CE
+.TP
+Description:
+Creates a new vector \fIvecName\fR\fR with a length of \fIlength\fR.
+\fBBlt_CreateVector\fR creates both a new Tcl command and array
+variable \fIvecName\fR. Neither a command nor variable named
+\fIvecName\fR can already exist. A pointer to the vector is
+placed into \fIvecPtrPtr\fR.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully created. If
+\fIlength\fR is negative, a Tcl variable or command \fIvecName\fR
+already exists, or memory cannot be allocated for the vector, then
+\f(CWTCL_ERROR\fR is returned and \fIinterp->result\fR will contain an
+error message.
+.RE
+.sp
+.PP
+\fBBlt_DeleteVectorByName\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_DeleteVectorByName\fR (\fIinterp\fR, \fIvecName\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+char *\fIvecName\fR;
+.RE
+.CE
+.TP 1i
+Description:
+Removes the vector \fIvecName\fR. \fIVecName\fR is the name of a vector
+which must already exist. Both the Tcl command and array variable
+\fIvecName\fR are destroyed. All clients of the vector will be notified
+immediately that the vector has been destroyed.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully deleted. If
+\fIvecName\fR is not the name a vector, then \f(CWTCL_ERROR\fR is returned
+and \fIinterp->result\fR will contain an error message.
+.RE
+.sp
+.PP
+\fBBlt_DeleteVector\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_DeleteVector\fR (\fIvecPtr\fR)
+.RS 1.25i
+Blt_Vector *\fIvecPtr\fR;
+.RE
+.CE
+.TP 1i
+Description:
+Removes the vector pointed to by \fIvecPtr\fR. \fIVecPtr\fR is a
+pointer to a vector, typically set by \fBBlt_GetVector\fR or
+\fBBlt_CreateVector\fR. Both the Tcl command and array variable of
+the vector are destroyed. All clients of the vector will be notified
+immediately that the vector has been destroyed.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully deleted. If
+\fIvecName\fR is not the name a vector, then \f(CWTCL_ERROR\fR is returned
+and \fIinterp->result\fR will contain an error message.
+.RE
+.sp
+.PP
+\fBBlt_GetVector\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_GetVector\fR (\fIinterp\fR, \fIvecName\fR, \fIvecPtrPtr\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+char *\fIvecName\fR;
+Blt_Vector **\fIvecPtrPtr\fR;
+.RE
+.CE
+.TP 1i
+Description:
+Retrieves the vector \fIvecName\fR. \fIVecName\fR is the name of a
+vector which must already exist. \fIVecPtrPtr\fR will point be set to
+the address of the vector.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully retrieved. If
+\fIvecName\fR is not the name of a vector, then \f(CWTCL_ERROR\fR is
+returned and \fIinterp->result\fR will contain an error message.
+.RE
+.sp
+.PP
+\fBBlt_ResetVector\fR
+.PP
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_ResetVector\fR (\fIvecPtr\fR, \fIdataArr\fR,
+ \fInumValues\fR, \fIarraySize\fR, \fIfreeProc\fR)
+.RS 1.25i
+Blt_Vector *\fIvecPtr\fR;
+double *\fIdataArr\fR;
+int *\fInumValues\fR;
+int *\fIarraySize\fR;
+Tcl_FreeProc *\fIfreeProc\fR;
+.RE
+.CE
+.TP
+Description:
+Resets the components of the vector pointed to by \fIvecPtr\fR.
+Calling \fBBlt_ResetVector\fR will trigger the vector to dispatch
+notifications to its clients. \fIDataArr\fR is the array of doubles
+which represents the vector data. \fINumValues\fR is the number of
+elements in the array. \fIArraySize\fR is the actual size of the array
+(the array may be bigger than the number of values stored in
+it). \fIFreeProc\fP indicates how the storage for the vector component
+array (\fIdataArr\fR) was allocated. It is used to determine how to
+reallocate memory when the vector is resized or destroyed. It must be
+\f(CWTCL_DYNAMIC\fR, \f(CWTCL_STATIC\fR, \f(CWTCL_VOLATILE\fR, or a pointer
+to a function to free the memory allocated for the vector array. If
+\fIfreeProc\fR is \f(CWTCL_VOLATILE\fR, it indicates that \fIdataArr\fR
+must be copied and saved. If \fIfreeProc\fR is \f(CWTCL_DYNAMIC\fR, it
+indicates that \fIdataArr\fR was dynamically allocated and that Tcl
+should free \fIdataArr\fR if necessary. \f(CWStatic\fR indicates that
+nothing should be done to release storage for \fIdataArr\fR.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully resized. If
+\fInewSize\fR is negative, a vector \fIvecName\fR does not exist, or
+memory cannot be allocated for the vector, then \f(CWTCL_ERROR\fR is
+returned and \fIinterp->result\fR will contain an error message.
+.RE
+.sp
+.PP
+\fBBlt_ResizeVector\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_ResizeVector\fR (\fIvecPtr\fR, \fInewSize\fR)
+.RS 1.25i
+Blt_Vector *\fIvecPtr\fR;
+int \fInewSize\fR;
+.RE
+.CE
+.TP
+Description:
+Resets the length of the vector pointed to by \fIvecPtr\fR to
+\fInewSize\fR. If \fInewSize\fR is smaller than the current size of
+the vector, it is truncated. If \fInewSize\fR is greater, the vector
+is extended and the new components are initialized to \f(CW0.0\fR.
+Calling \fBBlt_ResetVector\fR will trigger the vector to dispatch
+notifications.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully resized. If
+\fInewSize\fR is negative or memory can not be allocated for the vector,
+then \f(CWTCL_ERROR\fR is returned and \fIinterp->result\fR will contain
+an error message.
+.sp
+.PP
+\fBBlt_VectorExists\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_VectorExists\fR (\fIinterp\fR, \fIvecName\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+char *\fIvecName\fR;
+.RE
+.CE
+.TP
+Description:
+Indicates if a vector named \fIvecName\fR exists in \fIinterp\fR.
+.TP
+Results:
+Returns \f(CW1\fR if a vector \fIvecName\fR exists and \f(CW0\fR otherwise.
+.RE
+.sp
+.PP
+If your application needs to be notified when a vector changes, it can
+allocate a unique \fIclient identifier\fR for itself. Using this
+identifier, you can then register a call-back to be made whenever the
+vector is updated or destroyed. By default, the call-backs are made at
+the next idle point. This can be changed to occur at the time the
+vector is modified. An application can allocate more than one
+identifier for any vector. When the client application is done with
+the vector, it should free the identifier.
+.PP
+The call-back routine must of the following type.
+.CS
+.RS
+.sp
+typedef void (\fBBlt_VectorChangedProc\fR) (Tcl_Interp *\fIinterp\fR,
+.RS .25i
+ClientData \fIclientData\fR, Blt_VectorNotify \fInotify\fR);
+.RE
+.sp
+.RE
+.CE
+.fi
+\fIClientData\fR is passed to this routine whenever it is called. You
+can use this to pass information to the call-back. The \fInotify\fR
+argument indicates whether the vector has been updated of destroyed. It
+is an enumerated type.
+.CS
+.RS
+.sp
+typedef enum {
+ \f(CWBLT_VECTOR_NOTIFY_UPDATE\fR=1,
+ \f(CWBLT_VECTOR_NOTIFY_DESTROY\fR=2
+} \fBBlt_VectorNotify\fR;
+.sp
+.RE
+.CE
+.PP
+\fBBlt_AllocVectorId\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+Blt_VectorId \fBBlt_AllocVectorId\fR (\fIinterp\fR, \fIvecName\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+char *\fIvecName\fR;
+.RE
+.CE
+.TP
+Description:
+Allocates an client identifier for with the vector \fIvecName\fR.
+This identifier can be used to specify a call-back which is triggered
+when the vector is updated or destroyed.
+.TP
+Results:
+Returns a client identifier if successful. If \fIvecName\fR is not
+the name of a vector, then \f(CWNULL\fR is returned and
+\fIinterp->result\fR will contain an error message.
+.RE
+.sp
+.PP
+\fBBlt_GetVectorById\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+int \fBBlt_GetVector\fR (\fIinterp\fR, \fIclientId\fR, \fIvecPtrPtr\fR)
+.RS 1.25i
+Tcl_Interp *\fIinterp\fR;
+Blt_VectorId \fIclientId\fR;
+Blt_Vector **\fIvecPtrPtr\fR;
+.RE
+.CE
+.TP 1i
+Description:
+Retrieves the vector used by \fIclientId\fR. \fIClientId\fR is a valid
+vector client identifier allocated by \fBBlt_AllocVectorId\fR.
+\fIVecPtrPtr\fR will point be set to the address of the vector.
+.TP
+Results:
+Returns \f(CWTCL_OK\fR if the vector is successfully retrieved.
+.RE
+.sp
+.PP
+\fBBlt_SetVectorChangedProc\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+void \fBBlt_SetVectorChangedProc\fR (\fIclientId\fR, \fIproc\fR, \fIclientData\fR);
+.RS 1.25i
+Blt_VectorId \fIclientId\fR;
+Blt_VectorChangedProc *\fIproc\fR;
+ClientData *\fIclientData\fR;
+.RE
+.CE
+.TP
+Description:
+Specifies a call-back routine to be called whenever the vector
+associated with \fIclientId\fR is updated or deleted. \fIProc\fR is a
+pointer to call-back routine and must be of the type
+\fBBlt_VectorChangedProc\fR. \fIClientData\fR is a one-word value to
+be passed to the routine when it is invoked. If \fIproc\fR is
+\f(CWNULL\fR, then the client is not notified.
+.TP
+Results:
+The designated call-back procedure will be invoked when the vector is
+updated or destroyed.
+.RE
+.sp
+.PP
+\fBBlt_FreeVectorId\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+void \fBBlt_FreeVectorId\fR (\fIclientId\fR);
+.RS 1.25i
+Blt_VectorId \fIclientId\fR;
+.RE
+.CE
+.TP
+Description:
+Frees the client identifier. Memory allocated for the identifier
+is released. The client will no longer be notified when the
+vector is modified.
+.TP
+Results:
+The designated call-back procedure will be no longer be invoked when
+the vector is updated or destroyed.
+.RE
+.sp
+.PP
+\fBBlt_NameOfVectorId\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+char *\fBBlt_NameOfVectorId\fR (\fIclientId\fR);
+.RS 1.25i
+Blt_VectorId \fIclientId\fR;
+.RE
+.CE
+.TP
+Description:
+Retrieves the name of the vector associated with the client identifier
+\fIclientId\fR.
+.TP
+Results:
+Returns the name of the vector associated with \fIclientId\fR. If
+\fIclientId\fR is not an identifier or the vector has been destroyed,
+\f(CWNULL\fR is returned.
+.RE
+.sp
+.PP
+\fBBlt_InstallIndexProc\fR
+.RS .25i
+.TP 1i
+Synopsis:
+.CS
+void \fBBlt_InstallIndexProc\fR (\fIindexName\fR, \fIprocPtr\fR)
+.RS 1.25i
+char *\fIindexName\fR;
+Blt_VectorIndexProc *\fIprocPtr\fR;
+.RE
+.CE
+.TP
+Description:
+Registers a function to be called to retrieved the index \fIindexName\fR
+from the vector's array variable.
+.sp
+typedef double Blt_VectorIndexProc(Vector *vecPtr);
+.sp
+The function will be passed a pointer to the vector. The function must
+return a double representing the value at the index.
+.TP
+Results:
+The new index is installed into the vector.
+.RE
+.RE
+.SH C API EXAMPLE
+The following example opens a file of binary data and stores it in an
+array of doubles. The array size is computed from the size of the
+file. If the vector "data" exists, calling \fBBlt_VectorExists\fR,
+\fBBlt_GetVector\fR is called to get the pointer to the vector.
+Otherwise the routine \fBBlt_CreateVector\fR is called to create a new
+vector and returns a pointer to it. Just like the Tcl interface, both
+a new Tcl command and array variable are created when a new vector is
+created. It doesn't make any difference what the initial size of the
+vector is since it will be reset shortly. The vector is updated when
+\fBlt_ResetVector\fR is called. Blt_ResetVector makes the changes
+visible to the Tcl interface and other vector clients (such as a graph
+widget).
+.sp
+.CS
+#include <tcl.h>
+#include <blt.h>
+...
+Blt_Vector *vecPtr;
+double *newArr;
+FILE *f;
+struct stat statBuf;
+int numBytes, numValues;
+
+f = fopen("binary.dat", "r");
+fstat(fileno(f), &statBuf);
+numBytes = (int)statBuf.st_size;
+
+/* Allocate an array big enough to hold all the data */
+newArr = (double *)malloc(numBytes);
+numValues = numBytes / sizeof(double);
+fread((void *)newArr, numValues, sizeof(double), f);
+fclose(f);
+
+if (Blt_VectorExists(interp, "data")) {
+ if (Blt_GetVector(interp, "data", &vecPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+} else {
+ if (Blt_CreateVector(interp, "data", 0, &vecPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+}
+/*
+ * Reset the vector. Clients will be notified when Tk is idle.
+ * TCL_DYNAMIC tells the vector to free the memory allocated
+ * if it needs to reallocate or destroy the vector.
+ */
+if (Blt_ResetVector(vecPtr, newArr, numValues, numValues,
+ TCL_DYNAMIC) != TCL_OK) {
+ return TCL_ERROR;
+}
+.CE
+.SH "INCOMPATIBILITIES"
+In previous versions, if the array variable isn't global
+(i.e. local to a Tcl procedure), the vector is automatically
+destroyed when the procedure returns.
+.CS
+proc doit {} {
+ # Temporary vector x
+ vector x(10)
+ set x(9) 2.0
+ ...
+}
+.CE
+.PP
+This has changed. Variables are not automatically destroyed when
+their variable is unset. You can restore the old behavior by
+setting the "-watchunset" switch.
+.CE
+.SH KEYWORDS
+vector, graph, widget
diff --git a/blt/man/watch.mann b/blt/man/watch.mann
new file mode 100644
index 00000000000..5080943ec75
--- /dev/null
+++ b/blt/man/watch.mann
@@ -0,0 +1,137 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\"
+.so man.macros
+.TH watch n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+.SH NAME
+watch \- call Tcl procedures before and after each command
+.SH SYNOPSIS
+\fBwatch create\fR \fIwatchName\fR ?\fIoptions\fR?
+.sp
+\fBwatch activate\fR \fIwatchName\fR
+.sp
+\fBwatch deactivate\fR \fIwatchName\fR
+.sp
+\fBwatch delete\fR \fIwatchName\fR
+.sp
+\fBwatch configure\fR \fIwatchName\fR ?\fIoptions\fR
+.sp
+\fBwatch info\fR \fIwatchName\fR
+.sp
+\fBwatch names\fR
+.BE
+.SH DESCRIPTION
+The \fBwatch\fR command arranges for Tcl procedures to be invoked
+before and after the execution of each Tcl command.
+.SH INTRODUCTION
+When an error occurs in Tcl, the global variable \fIerrorInfo\fR will
+contain a stack-trace of the active procedures when the error occured.
+Sometimes, however, the stack trace is insufficient. You may need to
+know exactly where in the program's execution the error occured. In
+cases like this, a more general tracing facility would be useful.
+.PP
+The \fBwatch\fR command lets you designate Tcl procedures to be
+invoked before and after the execution of each Tcl command. This
+means you can display the command line and its results for each
+command as it executes. Another use is to profile your Tcl commands.
+You can profile any Tcl command (like \fBif\fR and \fBset\fR), not just
+Tcl procedures.
+.SH EXAMPLE
+The following example use \fBwatch\fR to trace Tcl commands
+(printing to standard error) both before and after they are executed.
+.CS
+proc preCmd { level command argv } {
+ set name [lindex $argv 0]
+ puts stderr "$level $name => $command"
+}
+
+proc postCmd { level command argv retcode results } {
+ set name [lindex $argv 0]
+ puts stderr "$level $name => $argv\n<= ($retcode) $results"
+}
+watch create trace \\
+ -postcmd postCmd -precmd preCmd
+.CE
+.SH "OPERATIONS"
+The following operations are available for the \fBwatch\fR command:
+.TP
+\fBwatch activate \fIwatchName\fR
+Activates the watch, causing Tcl commands the be traced to the
+maximum depth selected.
+.TP
+\fBwatch create \fIwatchName\fR ?\fIoptions\fR?...
+Creates a new watch \fIwatchName\fR. It's an error if another watch
+\fIwatchName\fR already exists and an error message will be returned.
+\fIOptions\fR may have any of the values accepted by the
+\fBwatch configure\fR command.
+This command returns the empty string.
+.TP
+\fBwatch configure \fIwatchName\fR ?\fIoptions...\fR?
+Queries or modifies the configuration options of the watch \fIwatchName\fR.
+\fIWatchName\fR is the name of a watch.
+\fIOptions\fR may have any of the following values:
+.RS
+.TP
+\fB\-active \fIboolean\fR
+Specifies if the watch is active.
+By default, watches are active when created.
+.TP
+\fB\-postcmd \fIstring\fR
+Specifies a Tcl procedure to be called immediately after each Tcl
+command. \fIString\fR is name of a Tcl procedure and any extra
+arguments to be passed to it. Before \fIstring\fR is invoked, five
+more arguments are appended: 1) the current level 2) the current
+command line 3) a list containing the command after substitutions and
+split into words 4) the return code of the command, and 5) the results
+of the command. The return status of the postcmd procedure is always
+ignored.
+.TP
+\fB\-precmd \fIstring\fR
+Specifies a Tcl procedure to be called immediately before each Tcl
+command. \fIString\fR is name of a Tcl procedure and any extra
+arguments to be passed to it. Before \fIstring\fR is invoked, three
+arguments are appended: 1) the current level 2) the current command
+line, and 3) a list containing the command after substitutions and
+split into words. The return status of the \fB\-precmd\fR procedure
+is always ignored.
+.TP
+\fB\-maxlevel \fInumber\fR
+Specifies the maximum evaluation depth to watch Tcl commands.
+The default maximum level is 10000.
+.RE
+.TP
+\fBwatch deactivate \fIwatchName\fR
+Deactivates the watch. The \fB\-precmd\fR and \fB\-postcmd\fR procedures
+will no longer be invoked.
+.TP
+\fBwatch info \fIwatchName\fR
+Returns the configuration information associated with the
+watch \fIwatchName\fR. \fIWatchName\fR is the name of a watch.
+.TP
+\fBwatch names\fR ?\fIstate\fR?
+Lists the names of the watches for a given state. \fIState\fR may be
+one of the following: \f(CWactive\fR, \f(CWidle\fR, or \f(CWignore\fR. If a
+\fIstate\fR argument isn't specified,
+ all watches are
+listed.
+.RE
+.SH KEYWORDS
+debug, profile
diff --git a/blt/man/winop.mann b/blt/man/winop.mann
new file mode 100644
index 00000000000..189680de931
--- /dev/null
+++ b/blt/man/winop.mann
@@ -0,0 +1,131 @@
+'\"
+'\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies.
+'\"
+'\" Permission to use, copy, modify, and distribute this software and its
+'\" documentation for any purpose and without fee is hereby granted, provided
+'\" that the above copyright notice appear in all copies and that both that the
+'\" copyright notice and warranty disclaimer appear in supporting documentation,
+'\" and that the names of Lucent Technologies any of their entities not be used
+'\" in advertising or publicity pertaining to distribution of the software
+'\" without specific, written prior permission.
+'\"
+'\" Lucent Technologies disclaims all warranties with regard to this software,
+'\" including all implied warranties of merchantability and fitness. In no event
+'\" shall Lucent Technologies be liable for any special, indirect or
+'\" consequential damages or any damages whatsoever resulting from loss of use,
+'\" data or profits, whether in an action of contract, negligence or other
+'\" tortuous action, arising out of or in connection with the use or performance
+'\" of this software.
+'\"
+'\" Window command created by George Howlett.
+'\"
+.so man.macros
+.TH winop n BLT_VERSION BLT "BLT Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+winop \- Perform assorted window operations
+.SH SYNOPSIS
+\fBwinop lower\fR ?\fIwindow\fR?...
+.sp
+\fBwinop map\fR ?\fIwindow\fR?...
+.sp
+\fBwinop move \fIwindow x y\fR
+.sp
+\fBwinop raise\fR ?\fIwindow\fR?...
+.sp
+\fBwinop snap \fIwindow photoName\fR
+.sp
+\fBwinop unmap\fR ?\fIwindow\fR?...
+.sp
+\fBwinop warpto\fR ?\fIwindow\fR?
+.BE
+.SH DESCRIPTION
+The \fBwinop\fR command performs various window operations on Tk
+windows using low-level Xlib function calls to work around window
+manager pecularities.
+.SH INTRODUCTION
+Tk has several commands for manipulating its windows: \fBraise\fR,
+\fBlower\fR, \fBwm\fR, etc. These commands ask the window manager to
+perform operations on Tk windows. In some cases, a particular window
+manager won't perform the operation as expected.
+.PP
+For example, if you positioned a toplevel window using \fBwm geometry\fR,
+the window may not actually be at those particular coordinates. The
+position of the window may be offset by dimensions of the title bar added
+by the window manager.
+.PP
+In situations like these, the \fBwinop\fR command can be used to
+workaround these difficulties. Instead, it makes low-level Xlib
+(such \fBXRaiseWindow\fR and \fBXMapWindow\fR) calls to perform these
+operations.
+.CS
+toplevel .top
+wm withdraw .top
+
+# Set the geometry to make the window manager
+# place the window.
+wm geometry .top +100+100
+
+# Move the window to the desired location
+# and "update" to force the window manager
+# to recognize it.
+winop move .top 100 100
+update
+
+wm deiconify .top
+winop move .top 100 100
+.CE
+.SH OPERATIONS
+The following operations are available for the \fBwinop\fR command:
+.TP
+\fBwinop lower\fR ?\fIwindow\fR?...
+Lowers \fIwindow\fR to the bottom of the X window stack. \fIWindow\fR is
+the path name of a Tk window.
+.TP
+\fBwinop map\fR ?\fIwindow\fR?...
+Maps \fIwindow\fR on the screen. \fIWindow\fR
+is the path name of a Tk window. If \fIwindow\fR is already mapped,
+this command has no effect.
+.TP
+\fBwinop move \fIwindow x y\fR
+Move \fIwindow\fR to the screen location specified by \fIx\fR
+and \fIy\fR. \fIWindow\fR is the path name of a Tk window, while
+\fIx\fR and \fIy\fR are screen coordinates. This command returns
+the empty string.
+.TP
+\fBwinop raise\fR ?\fIwindow\fR?...
+Raises \fIwindow\fR to the top of the X window stack. \fIWindow\fR must be
+a valid path name of a Tk window. This command returns the empty string.
+.TP
+\fBwinop snap \fIwindow photoName\fR
+Takes a snapshot of the \fIwindow\fR and stores the contents in the
+photo image \fIphotoName\fR. \fIWindow\fR is the valid path name of a
+Tk window which must be totally visible (unobscured). \fIPhotoName\fR
+is the name of a Tk photo image which must already exist. This command
+can fail if the window is obscured in any fashion, such as covered by
+another window or partially offscreen. In that case, an error message
+is returned.
+.TP
+\fBwinop unmap\fR ?\fIwindow\fR?...
+Unmaps \fIwindow\fR from the screen. \fIWindow\fR is the path name of a Tk
+window.
+.TP
+\fBwinop warpto\fR ?\fIwindow\fR?
+Warps the pointer to \fIwindow\fR. \fIWindow\fR is the path name of a Tk window
+which must be mapped. If \fIwindow\fR is in the form \fI@x,y\fR, where
+\fIx\fR and \fIy\fR are root screen coordinates, the pointer is warped to
+that location on the screen.
+.sp
+[\fII've never heard a good case for warping the pointer in an
+application. It can be useful for testing, but in applications, it's
+always a bad idea. Simply stated, the user owns the pointer, not the
+application. If you have an application that needs it, I'd like to
+hear about it.\fR]
+.sp
+If no \fIwindow\fR argument is present the current location of the
+pointer is returned. The location is returned as a list in the form
+"\fIx y\fR", where \fIx\fR and \fIy\fR are the current coordinates of
+the pointer.
+.SH KEYWORDS
+window, map, raise, lower, pointer, warp
diff --git a/blt/src/Makefile.cyg b/blt/src/Makefile.cyg
new file mode 100644
index 00000000000..b6dba0c0960
--- /dev/null
+++ b/blt/src/Makefile.cyg
@@ -0,0 +1,236 @@
+# ------------------------------------------------------------------------
+# Makefile for static version of BLT library
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+TARGET = i386-mingw32- # for cross-compilations.
+TARGET = # default is native build.
+rc32 = $(TARGET)windres
+#DUMPEXTS = $(TCLDIR)/win/dumpexts.exe
+
+# ------------------------------------------------------------------------
+# C Compiler options
+# ------------------------------------------------------------------------
+
+DEFINES = -D_X86_=1 -DWIN32 -D_DLL -D_MT -DHAVE_JPEGLIB_H=1
+DEBUG_FLAGS = -g
+CFLAGS = -O2
+EXTRA_CFLAGS =
+CC = $(TARGET)gcc.exe
+
+# ------------------------------------------------------------------------
+# Linker flags and options
+# ------------------------------------------------------------------------
+LD = link.exe
+DLLENTRY = @12
+SHLIB_LD = $(DLLWRAP) $(DLLWRAP_FLAGS)
+SHLIB_LD_FLAGS = $(LDFLAGS) --dll
+#LDFLAGS = -mwindows -no-cygwin --target=i386-mingw21
+LDFLAGS = -mwindows -no-cygwin
+
+NORMAL_LIBS = -ltk$(v2) -ltcl$(v2) \
+ -ljpeg \
+ -lwinspool
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) \
+ $(includedir)
+instdirs = $(exec_prefix) $(prefix) $(libdir)
+
+# ------------------------------------------------------------------------
+# Directories containing Tcl and Tk include files and libraries
+# ------------------------------------------------------------------------
+
+JPEGDIR = $(srcdir)/../../jpeg-6b
+TCLDIR = $(srcdir)/../../tcl$(v3)
+TKDIR = $(srcdir)/../../tk$(v3)
+X11DIR = $(TKDIR)
+
+INCLUDES = -I. -I$(srcdir) \
+ -I$(TCLDIR)/generic \
+ -I$(TKDIR)/generic \
+ -I$(X11DIR)/xlib
+
+SHLIB_LD_LIBS = $(NORMAL_LIBS)
+LIBRARIES = $(libname).a $(NORMAL_LIBS)
+
+#SHLIB_LD_LIBS = $(DEBUG_LIBS) $(JPEGDIR)/libjpeg.lib
+#LIBRARIES = $(libname).lib $(DEBUG_LIBS)
+
+# ------------------------------------------------------------------------
+# You don't need to edit anything beyond this point
+# ------------------------------------------------------------------------
+
+N_OBJS = bltTed.o
+V3_OBJS = bltTri.o bltGrMt.o
+
+TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o
+
+GRAPH_OBJS = bltGrAxis.o \
+ bltGrBar.o \
+ bltGrElem.o \
+ bltGrGrid.o \
+ bltGrHairs.o \
+ bltGrLegd.o \
+ bltGrLine.o \
+ bltGrMarker.o \
+ bltGrMisc.o \
+ bltGrPen.o \
+ bltGrPs.o \
+ bltGraph.o
+
+TCL_ONLY_OBJS = bltAlloc.o \
+ bltArrayObj.o \
+ bltBgexec.o \
+ bltChain.o \
+ bltDebug.o \
+ bltHash.o \
+ bltList.o \
+ bltNsUtil.o \
+ bltParse.o \
+ bltPool.o \
+ bltSpline.o \
+ bltSwitch.o \
+ bltTree.o \
+ bltTreeCmd.o \
+ bltWinPipe.o \
+ bltUtil.o \
+ bltVector.o \
+ bltVecMath.o \
+ bltVecCmd.o \
+ bltVecObjCmd.o \
+ bltWatch.o
+
+OBJS = $(GRAPH_OBJS) \
+ $(TCL_ONLY_OBJS) \
+ bltBeep.o \
+ bltBind.o \
+ bltBitmap.o \
+ bltBusy.o \
+ bltCanvEps.o \
+ bltColor.o \
+ bltConfig.o \
+ bltContainer.o \
+ bltCutbuffer.o \
+ bltDragdrop.o \
+ bltHierbox.o \
+ bltHiertable.o bltHtCmd.o bltHtColumn.o bltHtText.o \
+ bltHtext.o \
+ bltImage.o \
+ bltWinImage.o \
+ bltPs.o \
+ bltTable.o \
+ bltTabnotebook.o \
+ bltTabset.o \
+ bltText.o \
+ bltTile.o \
+ bltTreeView.o \
+ bltTreeViewCmd.o \
+ bltTreeViewEdit.o \
+ bltTreeViewColumn.o \
+ bltTreeViewStyle.o \
+ bltWindow.o \
+ bltObjConfig.o \
+ bltWinop.o \
+ $(TK_OBJS) $(N_OBJS)
+
+# GNU Make-specific macro
+HEADERS = blt.h
+SRCS = $(patsubst %.o,$(srcdir)/%.c,$(OBJS))
+exec_name = bltwish
+demo = $(exec_name).exe
+demo2 = $(exec_name)$(version).exe
+libname = BLT$(version)
+version = $(BLT_MAJOR_VERSION)$(BLT_MINOR_VERSION)
+
+CC_SWITCHES = $(CFLAGS) $(EXTRA_CFLAGS) $(DEFINES) $(INCLUDES)
+AR = $(TARGET)ar.exe
+RANLIB = $(TARGET)ranlib.exe
+AS = $(TARGET)as.exe
+DLLTOOL = $(TARGET)dlltool
+DLLTOOL_FLAGS = --as $(AS)
+DLLWRAP = $(TARGET)dllwrap
+DLLWRAP_FLAGS = --driver-name $(CC) --as $(AS) \
+ --dlltool-name $(DLLTOOL)
+LIB = lib.exe
+LINT = lint.exe
+LINTFLAGS = -axhbns
+VPATH = $(srcdir)
+
+all: build-library build-dll build-demo
+
+build-demo: build-library $(demo)
+
+build-library: $(libname)_s.a $(libname).dll
+
+build-dll: $(libname)_s.a build-library
+
+$(demo): build-library $(DEMO_OBJS)
+ $(RM) $(demo)
+ $(CC) -o $@ $(LDFLAGS) $(DEMO_OBJS) $(LIBRARIES)
+
+$(libname)_s.a: $(OBJS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+ $(RANLIB) $@
+
+$(libname).def: $(OBJS)
+ $(DLLTOOL) $(DLLTOOL_FLAGS) --export-all --output-def $@ $(OBJS)
+
+$(libname).a: $(libname).def
+ $(DLLTOOL) $(DLLTOOL_FLAGS) --dllname $(libname).dll \
+ --def $(libname).def --output-lib $@
+
+$(libname).dll: $(libname).a
+ $(RM) $(libname).dll
+ $(DLLWRAP) $(DLLWRAP_FLAGS) -o $@ --def $(libname).def \
+ $(OBJS) $(SHLIB_LD_FLAGS) $(SHLIB_LD_LIBS)
+
+bltHash.h: bltHash.h.in
+ sed -e 's/@SIZEOF_VOID_P@/4/' \
+ -e 's/@SIZEOF_INT@/4/' \
+ -e 's/@SIZEOF_LONG@/4/' \
+ -e 's/@SIZEOF_LONG_LONG@/8/' \
+ -e 's/@HAVE_INTTYPES_H@/0/' \
+ bltHash.h.in > bltHash.h
+
+install: install-dirs install-lib install-headers install-demo
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test ! -d "$$i" ; then \
+ echo " mkdir $$i" ; \
+ mkdir "$$i" ; \
+ fi ; \
+ done
+
+install-demo:
+ $(INSTALL) $(demo) $(bindir)/$(demo)
+ $(INSTALL) $(demo) $(bindir)/$(demo2)
+
+install-lib: $(libname).dll install-dirs
+ $(INSTALL_DATA) $(libname).dll $(bindir)
+
+install-headers:
+ for i in $(HEADERS) ; do \
+ echo "installing $(includedir)/$$i..." ; \
+ $(INSTALL_DATA) "$(srcdir)/$$i" $(includedir) ; \
+ done
+
+lint:
+ $(LINT) $(LINTFLAGS) $(DEFINES) $(INCLUDES) $(SRCS)
+
+clean:
+ $(RM) $(OBJS) $(DEMO_OBJS) $(libname)* $(exec_name)*
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) Makefile
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) -o $*.o $<
diff --git a/blt/src/Makefile.in b/blt/src/Makefile.in
new file mode 100644
index 00000000000..46c1458df12
--- /dev/null
+++ b/blt/src/Makefile.in
@@ -0,0 +1,248 @@
+
+# ------------------------------------------------------------------------
+# Makefile for static version of BLT library
+# ------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------
+# C Compiler options
+# ------------------------------------------------------------------------
+
+BLT_LIBRARY = @BLT_LIBRARY@
+TCLLIBPATH = @TCL_LIB_DIR@/tcl@TCL_VERSION@
+CC = @CC@
+CFLAGS = @CFLAGS@
+DEFINES = @DEFINES@
+EXTRA_CFLAGS = @GCCFLAGS@
+LDFLAGS = @LDFLAGS@ @LD_RUN_PATH@
+version = @BLT_MAJOR_VERSION@@BLT_MINOR_VERSION@
+
+# ------------------------------------------------------------------------
+# Source and targer installation directories
+# ------------------------------------------------------------------------
+
+bindir = $(exec_prefix)/bin
+exec_prefix = @exec_prefix@
+incdir = $(prefix)/include
+libdir = @libdir@
+scriptdir = $(exec_prefix)/lib
+
+prefix = @prefix@
+srcdir = @srcdir@
+
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) $(incdir)
+
+# ------------------------------------------------------------------------
+# Directories containing Tcl and Tk include files and libraries
+# ------------------------------------------------------------------------
+
+INCLUDES = -I. -I$(srcdir) @INCLUDES@
+
+# ------------------------------------------------------------------------
+# Libraries directives for Tcl, Tk, X11, and BLT
+# ------------------------------------------------------------------------
+
+LIBS = @LIB_SPECS@ @EXTRA_LIB_SPECS@
+TCL_ONLY_LIBS = @TCL_ONLY_LIB_SPECS@ @EXTRA_LIB_SPECS@
+
+# SIZEOF_LONG = @SIZEOF_LONG@
+# SIZEOF_VOID_P = @SIZEOF_VOID_P@
+# ------------------------------------------------------------------------
+# You don't need to edit anything beyond this point
+# ------------------------------------------------------------------------
+
+N_OBJS = bltTed.o
+V3_OBJS = bltTri.o bltGrMt.o
+
+TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o
+
+GRAPH_OBJS = bltGrAxis.o \
+ bltGrBar.o \
+ bltGrElem.o \
+ bltGrGrid.o \
+ bltGrHairs.o \
+ bltGrLegd.o \
+ bltGrLine.o \
+ bltGrMarker.o \
+ bltGrMisc.o \
+ bltGrPen.o \
+ bltGrPs.o \
+ bltGraph.o
+
+TCL_ONLY_OBJS = bltAlloc.o \
+ bltArrayObj.o \
+ bltBgexec.o \
+ bltChain.o \
+ bltDebug.o \
+ bltHash.o \
+ bltList.o \
+ bltNsUtil.o \
+ bltParse.o \
+ bltPool.o \
+ bltSpline.o \
+ bltSwitch.o \
+ bltTree.o \
+ bltTreeCmd.o \
+ bltUnixPipe.o \
+ bltUtil.o \
+ bltVector.o \
+ bltVecMath.o \
+ bltVecCmd.o \
+ bltVecObjCmd.o \
+ bltWatch.o
+
+OBJS = $(GRAPH_OBJS) \
+ $(TCL_ONLY_OBJS) \
+ bltBeep.o \
+ bltBind.o \
+ bltBitmap.o \
+ bltBusy.o \
+ bltCanvEps.o \
+ bltColor.o \
+ bltConfig.o \
+ bltContainer.o \
+ bltCutbuffer.o \
+ bltDragdrop.o \
+ bltHierbox.o \
+ bltHtext.o \
+ bltImage.o \
+ bltUnixImage.o \
+ bltPs.o \
+ bltTable.o \
+ bltTabnotebook.o \
+ bltTabset.o \
+ bltText.o \
+ bltTile.o \
+ bltTreeView.o \
+ bltTreeViewCmd.o \
+ bltTreeViewEdit.o \
+ bltTreeViewColumn.o \
+ bltTreeViewStyle.o \
+ bltUnixDnd.o \
+ bltWindow.o \
+ bltObjConfig.o \
+ bltWinop.o \
+ $(TK_OBJS) $(N_OBJS)
+
+# GNU Make-specific macro
+SRCS = $(patsubst %.o,$(srcdir)/%.c,$(OBJS))
+
+bltwish = bltwish
+bltsh = bltsh
+headers = $(srcdir)/blt.h \
+ $(srcdir)/bltBind.h \
+ $(srcdir)/bltChain.h \
+ bltHash.h \
+ $(srcdir)/bltList.h \
+ $(srcdir)/bltPool.h \
+ $(srcdir)/bltTree.h \
+ $(srcdir)/bltVector.h
+
+lib_a = libBLT.a
+libvers_a = libBLT$(version).a
+tcl_only_lib_a = libBLTlite.a
+tcl_only_libvers_a = libBLTlite$(version).a
+
+CC_SWITCHES = $(EXTRA_CFLAGS) $(CFLAGS) $(DEFINES) $(INCLUDES)
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+RANLIB = @RANLIB@
+SHELL = /bin/sh
+AR = ar rc
+RM = rm -f
+LINT = lint
+LINTFLAGS = -axhbns
+XREF = cxref
+XREFFLAGS = -dltR
+LN_S = @LN_S@
+VPATH = $(srcdir)
+
+all: $(bltwish) $(bltsh) @SHLIB_TARGET@
+
+build_shared:
+ (cd shared; $(MAKE) CFLAGS="$(CFLAGS)" all)
+
+$(bltwish): $(lib_a) $(srcdir)/bltUnixMain.c
+ $(RM) $(bltwish)
+ $(CC) $(CC_SWITCHES) $(LDFLAGS) -o $(bltwish) \
+ -DTCLLIBPATH=\"$(TCLLIBPATH)\" \
+ $(srcdir)/bltUnixMain.c $(lib_a) $(LIBS)
+
+$(bltsh): $(tcl_only_lib_a) $(srcdir)/bltUnixMain.c
+ $(RM) $(bltsh)
+ $(CC) $(CC_SWITCHES) $(LDFLAGS) -DTCL_ONLY -o $(bltsh) \
+ -DTCLLIBPATH=\"$(TCLLIBPATH)\" \
+ $(srcdir)/bltUnixMain.c $(tcl_only_lib_a) $(TCL_ONLY_LIBS)
+
+$(lib_a): $(OBJS) $(srcdir)/bltInit.c
+ $(CC) -c $(CC_SWITCHES) -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) $@ $(OBJS) bltInit.o
+ $(RANLIB) $@
+
+$(tcl_only_lib_a): $(TCL_ONLY_OBJS) $(srcdir)/bltInit.c
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) $@ $(TCL_ONLY_OBJS) bltInit.o
+ $(RANLIB) $@
+
+install: mkdirs install-lib install-demo install-headers
+
+install-demo: $(bltwish) $(bltsh)
+ $(INSTALL) -m 0755 $(bltwish) $(INSTALL_ROOT)$(bindir)
+ $(INSTALL) -m 0755 $(bltsh) $(INSTALL_ROOT)$(bindir)
+
+install-lib: $(lib_a) $(tcl_only_lib_a)
+ $(INSTALL_DATA) $(lib_a) $(INSTALL_ROOT)$(libdir)/$(libvers_a)
+ (cd $(INSTALL_ROOT)$(libdir); $(RM) $(lib_a) ; $(LN_S) $(libvers_a) $(lib_a))
+ $(RANLIB) $(INSTALL_ROOT)$(libdir)/$(libvers_a)
+ $(INSTALL_DATA) $(tcl_only_lib_a) $(INSTALL_ROOT)$(libdir)/$(tcl_only_libvers_a)
+ (cd $(INSTALL_ROOT)$(libdir); $(RM) $(tcl_only_lib_a) ; $(LN_S) $(tcl_only_libvers_a) $(tcl_only_lib_a))
+ $(RANLIB) $(INSTALL_ROOT)$(libdir)/$(tcl_only_libvers_a)
+ (cd shared; $(MAKE) install)
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)$$i ; then \
+ : ; \
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)$$i ; \
+ fi ; \
+ done
+
+install-headers:
+ @for i in $(headers) ; do \
+ echo "installing $$i..." ; \
+ $(INSTALL_DATA) -m 0444 $$i $(INSTALL_ROOT)$(incdir) ; \
+ done
+
+lint:
+ $(LINT) $(LINTFLAGS) $(DEFINES) $(INCLUDES) $(SRCS)
+
+xref:
+ $(XREF) $(XREFFLAGS) $(DEFINES) $(INCLUDES) $(SRCS)
+
+clean:
+ $(RM) $(OBJS) bltInit.o $(lib_a) $(tcl_only_lib_a) \
+ $(bltsh)* $(bltwish)* *pure* .pure*
+ (cd shared; $(MAKE) clean)
+
+distclean: clean
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"* Makefile
+ $(RM) bltConfig.h Makefile TAGS
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) $<
+
+PUREFLAGS=
+pure: $(lib_a)
+ $(PURIFYHOME)/purify $(PUREFLAGS) $(CC) $(CC_SWITCHES) \
+ $(srcdir)/bltUnixMain.c -o bltwish $(lib_a) $(LIBS)
+
+QUANTIFYFLAGS=
+quant: $(lib_a)
+ $(QUANTIFYHOME)/quantify $(QUANTIFYFLAGS) $(CC) $(CC_SWITCHES) \
+ $(srcdir)/bltUnixMain.c -o bltwish $(lib_a) $(LIBS)
diff --git a/blt/src/Makefile.vc b/blt/src/Makefile.vc
new file mode 100644
index 00000000000..bf9c12b8acc
--- /dev/null
+++ b/blt/src/Makefile.vc
@@ -0,0 +1,330 @@
+
+# ------------------------------------------------------------------------
+# Makefile for BLT library using VC++.
+# ------------------------------------------------------------------------
+
+include ../win/makedefs
+
+TOOLS32 = c:/Program\ Files/Microsoft\ Visual\ Studio/Vc98
+AR = lib.exe -link50compat
+LD = link.exe
+CC = cl.exe
+rc32 = rc.exe
+
+ifeq ($(WITH_JPEG),0)
+EXTRA_DEFINES =
+endif
+ifeq ($(WITH_JPEG),1)
+EXTRA_DEFINES = -DHAVE_JPEGLIB_H=1
+JPEGDIR = $(srcdir)/../../jpeg-6b
+JPEGLIB = $(JPEGDIR)/libjpeg.lib
+JPEGINC = $(JPEGDIR)
+endif
+ifeq ($(WITH_JPEG),2)
+EXTRA_DEFINES = -DHAVE_IJL_H=1
+JPEGDIR = c:/Program\ Files/Intel/IJL
+JPEGLIB = $(JPEGDIR)/lib/ijl15l.lib
+JPEGINC = $(JPEGDIR)/Include
+endif
+
+# ------------------------------------------------------------------------
+# C Compiler options
+# ------------------------------------------------------------------------
+
+DEFINES = -D_X86_=1 -D__STDC__ -DWIN32 -DCONSOLE -D_MT \
+ $(DEBUG_DEFINES) $(SHLIB_DEFINES) $(EXTRA_DEFINES)
+
+ifeq ($(SHARED),1)
+SHLIB_DEFINES = -D_DLL
+SHLIB_TARGET = build-dll
+LIBS = $(COMMON_LIBS)
+else
+SHLIB_DEFINES = -D_CTYPE_DISABLE_MACROS
+LIBS = $(COMMON_LIBS) $(EXTRA_LIBS)
+endif
+
+ifeq ($(DEBUG),1)
+CFLAGS = -Z7 -Od
+DEBUG_LDFLAGS = -debug:full -debugtype:cv
+DEBUG_DEFINES = -DUSE_TCLALLOC=0
+TK_LIB = $(TKDIR)/win/Debug/tk$(v2)d.lib
+TCL_LIB = $(TCLDIR)/win/Debug/tcl$(v2)d.lib
+MSVCRT = msvcrtd.lib
+else
+CFLAGS = -Ox -GB -GD
+DEBUG_LDFLAGS = -debug:full -debugtype:cv
+TK_LIB = $(TKDIR)/win/Release/tk$(v2).lib
+TCL_LIB = $(TCLDIR)/win/Release/tcl$(v2).lib
+MSVCRT = msvcrt.lib
+endif
+
+EXTRA_CFLAGS = -nologo -W3
+
+# ------------------------------------------------------------------------
+# Linker flags and options
+# ------------------------------------------------------------------------
+
+COMMON_LDFLAGS = -nodefaultlib -release -nologo -warn:3 \
+ -machine:IX86 -align:0x1000 \
+ $(DEBUG_LDFLAGS)
+
+DLLENTRY = @12
+SHLIB_LDFLAGS = $(COMMON_LDFLAGS) \
+ -subsystem:console -entry:mainCRTStartup \
+ -subsystem:windows -entry:WinMainCRTStartup \
+ -entry:_DllMainCRTStartup$(DLLENTRY) -dll
+
+LDFLAGS = $(COMMON_LDFLAGS) \
+ -fixed:NO -stack:2300000
+
+COMMON_LIBS = $(TK_LIB) $(TCL_LIB) \
+ $(MSVCRT) \
+ kernel32.lib user32.lib
+
+EXTRA_LIBS = $(OLELIB) \
+ $(JPEGLIB) \
+ gdi32.lib \
+ oldnames.lib \
+ advapi32.lib \
+ winspool.lib
+
+TCL_ONLY_LIBS = $(TCL_LIB) $(MSVCRT) kernel32.lib user32.lib advapi32.lib
+
+
+# ------------------------------------------------------------------------
+# Source and target installation directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) \
+ $(includedir)
+instdirs = $(exec_prefix) $(prefix) $(libdir)
+
+# ------------------------------------------------------------------------
+# Directories containing Tcl and Tk include files and libraries
+# ------------------------------------------------------------------------
+
+TCLDIR = $(srcdir)/../../tcl$(v3)
+TKDIR = $(srcdir)/../../tk$(v3)
+INCLUDES = -I. -I$(srcdir) \
+ -I$(TOOLS32)/include \
+ -I$(JPEGINC) \
+ -I$(TCLDIR)/win -I$(TCLDIR)/generic \
+ -I$(TKDIR)/win -I$(TKDIR)/generic -I$(TKDIR)/xlib \
+
+#-I$(TCLROOT)/include
+
+SHLIB_LD_LIBS = $(COMMON_LIBS) $(EXTRA_LIBS)
+
+# ------------------------------------------------------------------------
+# You don't need to edit anything beyond this point
+# ------------------------------------------------------------------------
+
+N_OBJS = bltTed.o
+V3_OBJS = bltTri.o bltGrMt.o
+
+TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o
+
+GRAPH_OBJS = bltGrAxis.o \
+ bltGrBar.o \
+ bltGrElem.o \
+ bltGrGrid.o \
+ bltGrHairs.o \
+ bltGrLegd.o \
+ bltGrLine.o \
+ bltGrMarker.o \
+ bltGrMisc.o \
+ bltGrPen.o \
+ bltGrPs.o \
+ bltGraph.o
+
+TCL_ONLY_OBJS = bltAlloc.o \
+ bltArrayObj.o \
+ bltBgexec.o \
+ bltChain.o \
+ bltDebug.o \
+ bltHash.o \
+ bltList.o \
+ bltNsUtil.o \
+ bltParse.o \
+ bltPool.o \
+ bltSpline.o \
+ bltSwitch.o \
+ bltTree.o \
+ bltTreeCmd.o \
+ bltUtil.o \
+ bltVecCmd.o \
+ bltVecMath.o \
+ bltVecObjCmd.o \
+ bltVector.o \
+ bltWatch.o \
+ bltWinPipe.o \
+ bltWinUtil.o \
+ bltWinDde.o \
+ pure_api.o
+
+DEMO_OBJS = tkConsole.o bltWinMain.o
+
+OBJS = $(GRAPH_OBJS) \
+ $(TCL_ONLY_OBJS) \
+ bltBeep.o \
+ bltBind.o \
+ bltBitmap.o \
+ bltBusy.o \
+ bltCanvEps.o \
+ bltConfig.o \
+ bltContainer.o \
+ bltDragdrop.o \
+ bltHierbox.o \
+ bltHtext.o \
+ bltImage.o \
+ bltWinImage.o \
+ bltPs.o \
+ bltTable.o \
+ bltTabnotebook.o \
+ bltTabset.o \
+ bltText.o \
+ bltTile.o \
+ bltTreeView.o \
+ bltTreeViewCmd.o \
+ bltTreeViewEdit.o \
+ bltTreeViewColumn.o \
+ bltTreeViewStyle.o \
+ bltWinDraw.o \
+ bltWinPrnt.o \
+ bltWindow.o \
+ bltObjConfig.o \
+ bltWinop.o \
+ $(TK_OBJS) $(N_OBJS)
+
+NOT_YET = bltContainer.o bltCutBuffer.o bltColor.o
+
+HEADERS = blt.h bltChain.h bltVector.h bltTree.h bltPool.h bltHash.h
+
+# GNU Make-specific macro
+SRCS = $(patsubst %.o,$(srcdir)/%.c,$(OBJS))
+
+shell_name = bltwish
+version = $(BLT_MAJOR_VERSION)$(BLT_MINOR_VERSION)
+bltwish = bltwish.exe
+bltsh = bltsh.exe
+bltwish2 = bltwish$(version).exe
+bltsh2 = bltsh$(version).exe
+
+lib_name = BLT$(version)
+lib_a = BLT$(version).lib
+lib_so = BLT$(version).dll
+tcl_only_lib_a = BLTlite$(version).lib
+tcl_only_lib_so = BLTlite$(version).dll
+
+CC_SWITCHES = $(CFLAGS) $(EXTRA_CFLAGS) $(DEFINES) $(INCLUDES)
+VPATH = $(srcdir)
+
+all: build-library $(SHLIB_TARGET) build-demos
+
+install: all install-dirs install-headers install-binaries install-demos
+
+build-demos: $(SHLIB_TARGET) $(bltwish) $(bltsh)
+
+build-library: $(lib_a) $(tcl_only_lib_a)
+
+build-dll: build-library $(lib_so) $(tcl_only_lib_so)
+
+$(bltwish): $(lib_a) tkConsole.o bltWinMain.c
+ $(RM) $(bltwish)
+ $(CC) -c $(CC_SWITCHES) -DTCLLIBPATH=\"$(TCLLIBPATH)\" \
+ -FobltWinMain.o $(srcdir)/bltWinMain.c
+ LIB=$(TOOLS32)/lib \
+ $(LD) $(LDFLAGS) tkConsole.o bltWinMain.o -out:$(bltwish) \
+ $(lib_a) $(LIBS)
+
+$(bltsh): $(tcl_only_lib_a) bltWinMain.c
+ $(RM) $(bltsh)
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY \
+ -DTCLLIBPATH=\"$(TCLLIBPATH)\" \
+ -FobltWinMain.o $(srcdir)/bltWinMain.c
+ LIB=$(TOOLS32)/lib \
+ $(LD) $(LDFLAGS) bltWinMain.o -out:$(bltsh) \
+ $(tcl_only_lib_a) $(TCL_ONLY_LIBS)
+
+$(lib_a): bltHash.h $(OBJS) bltInit.c
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) -out:$@ bltInit.o $(OBJS)
+
+$(lib_so): $(lib_a) $(OBJS) bltInit.c
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ LIB=$(TOOLS32)/lib \
+ $(LD) $(SHLIB_LDFLAGS) -out:$@ bltInit.o $(OBJS) $(SHLIB_LD_LIBS)
+
+$(tcl_only_lib_a): bltHash.h $(TCL_ONLY_OBJS) bltInit.c
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) -out:$@ bltInit.o $(TCL_ONLY_OBJS)
+
+$(tcl_only_lib_so): $(tcl_only_lib_a) $(TCL_ONLY_OBJS) bltInit.c
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" \
+ -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ LIB=$(TOOLS32)/lib \
+ $(LD) $(SHLIB_LDFLAGS) -out:$@ bltInit.o $(TCL_ONLY_OBJS) \
+ $(TCL_ONLY_LIBS)
+
+bltHash.h: bltHash.h.in
+ sed -e 's/@SIZEOF_VOID_P@/4/' \
+ -e 's/@SIZEOF_INT@/4/' \
+ -e 's/@SIZEOF_LONG@/4/' \
+ -e 's/@SIZEOF_LONG_LONG@/8/' \
+ -e 's/@HAVE_INTTYPES_H@/0/' \
+ bltHash.h.in > bltHash.h
+
+install-dirs:
+ @for i in $(instdirs) ; do \
+ if test ! -d "$$i" ; then \
+ echo " mkdir $$i" ; \
+ mkdir "$$i" ; \
+ fi ; \
+ done
+
+install-binaries: install-lib install-demos
+
+install-demos: build-demos
+ $(INSTALL) $(bltwish) $(bindir)/$(bltwish)
+ $(INSTALL) $(bltwish) $(bindir)/$(bltwish2)
+ $(INSTALL) $(bltsh) $(bindir)/$(bltsh)
+ $(INSTALL) $(bltsh) $(bindir)/$(bltsh2)
+
+install-lib: $(lib_so) $(lib_a)
+ $(INSTALL_DATA) $(lib_so) $(bindir)
+ $(INSTALL_DATA) $(lib_a) $(libdir)
+ $(INSTALL_DATA) $(tcl_only_lib_so) $(bindir)
+ $(INSTALL_DATA) $(tcl_only_lib_a) $(libdir)
+
+install-headers:
+ for i in $(HEADERS) ; do \
+ $(INSTALL_DATA) "$(srcdir)/$$i" $(includedir) ; \
+ done
+
+lint:
+ $(LINT) $(LINTFLAGS) $(DEFINES) $(INCLUDES) $(SRCS)
+
+clean:
+ $(RM) *.o *.pdb *.exp \
+ $(lib_a) $(lib_so) $(tcl_only_lib_a) $(tcl_only_lib_so) \
+ $(bltwish) $(bltsh) $(bltwish2) $(bltsh2)
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+distclean: clean
+ $(RM) Makefile
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) -Fo$*.o $<
+
diff --git a/blt/src/TODO b/blt/src/TODO
new file mode 100644
index 00000000000..bb97835cf76
--- /dev/null
+++ b/blt/src/TODO
@@ -0,0 +1,97 @@
+
+To do:
+
+bgexec (Windows)
+ 1. (BUG) Convert collected data to UTF before passing to the interpreter.
+
+container (Unix)
+ 1. (done) Add timeout option to control how long to search for application
+ window.
+
+debug
+ 1. (done) Recent versions of Tcl swamp Tcl_CommandTrace.
+ Add line cutoff option (default is 6).
+
+dnd (Unix)
+ 1. (DOC) Create manual page for "dnd" command.
+ 2. (Feature) Add Motif drag-and-drop capabilities.
+
+eps
+ 1. (DOC) Update manual page for eps canvas item.
+ 2. (FEATURE) Read Windows EPS files with embedded TIFF images.
+
+graph
+ 1. (done) Fix zooming graph procedure to handle multiple axes.
+ 2. (BUG) Windows printing commands "print1" and "print2" need to use
+ postscript options like -maxpect, -pad, etc. to control graph
+ size.
+ 3. (BUG) No PostScript generated for polygon tiling.
+ 4. (BUG) Clip background polygon for text/bitmap markers.
+ 5. (FEATURE) Add -mask option for bitmap marker.
+ 6. (FEATURE) Allow rotated image markers.
+ 7. (FEATURE) Add oval and rectangle markers.
+ 8. (FEATURE) Add arrowheads to line markers.
+ 9. (BUG) Finish adding error bars.
+ 10. (DOC) Review and update documentation for new typos, new features.
+ 11. (BUG) Store converted screen coordinates in floating point. Can't
+ use integer coordinates for higher resolution PostScript
+ devices. How do Windows' print devices handle this?
+
+hierbox
+ 1. (CHANGE) Hierbox to use tree object for data. The -data option will
+ be a field in the tree.
+ 2. (FEATURE) Add edit bindings for entries.
+ 3. (DOCUMENTATION) Create real hierbox manual page.
+
+hiertable
+ 1. (done) -tree option dumps core.
+ 2. (done) Sorting tree view affects all other hiertables using the tree.
+ Is separate data structure needed for non-flattened sorts? What
+ about moves?
+ 3. (done) Call tree update procedure when tree object is sorted. This
+ is only when a tree is shared between more than one hiertable.
+ It goes out-of-sync with actual tree positions.
+ 4. (CHANGE) "column resize set" should change the width of the active
+ column automatically.
+ 5a.(FEATURE) XOR outline for entry move operation.
+ 5b.(FEATURE) XOR outline for entry resize operation.
+ 6a.(done) XOR outline for column resize operation.
+ 6b.(FEATURE) XOR outline for column move operation.
+ 7. (???) Update procedure isn't called for moved nodes. Call global
+ update routine (like sort) or selected node update procedures?
+ 8. (DOC) Explain selection modes ("single" and "multiple")
+ in manual page.
+ 9. (BUG) Multi-line entry editting is broken.
+ 10. (BUG) Add default bindings for entry editting. Need to set grab
+ on edit window.
+ 11. (BUG) Add standard keyboard bindings.
+ 12. (FEATURE) Images in column title.
+ 13. (PERFORMANCE) Don't redraw entire widget for scrolling. Copy
+ portions of pixmap and redraw only changed areas.
+ This will affect lots of code.
+ 14. (PERFORMANCE) Don't redraw entire widget for selections. Draw
+ only changed entries.
+ 15. (???) Add checkbox column entries.
+ 16. (BUG) "column resize" reports incorrect width of column.
+
+printer (Windows)
+ 1. (DOC) Create manual page for "printer" command.
+ 2. (FEATURE) Add operation to print text and canvas widgets.
+ 3. (FEATURE) Create sample print dialog.
+ 4. (BUG) Needs print job abort handler.
+
+tabset
+ 1. (done) Add perforation gizmo for tearoffs.
+ 2. (FEATURE) Allow alternatative tearout styles.
+
+tree
+ 1. (done) Create Tcl interface.
+ 2. (DOC) Create manuals for both Tcl and C APIs.
+
+vector
+ 1. (FEATURE) Add Tcl-based notification callbacks.
+
+gradient
+ 1. (FEATURE) Create gradient command that interfaces with tiling.
+
+all (Mac)
diff --git a/blt/src/blt.h b/blt/src/blt.h
new file mode 100644
index 00000000000..6465130dc10
--- /dev/null
+++ b/blt/src/blt.h
@@ -0,0 +1,78 @@
+/*
+ * blt.h --
+ *
+ * Copyright 1991-1998 by Bell Labs Innovations for Lucent
+ * Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_H
+#define _BLT_H
+
+#define BLT_MAJOR_VERSION 2
+#define BLT_MINOR_VERSION 4
+#define BLT_VERSION "2.4"
+#define BLT_PATCH_LEVEL "2.4y"
+#define BLT_RELEASE_SERIAL 0
+
+#include <tcl.h>
+
+#ifndef EXPORT
+#define EXPORT
+#endif
+
+#undef EXTERN
+
+#ifdef __cplusplus
+# define EXTERN extern "C" EXPORT
+#else
+# define EXTERN extern EXPORT
+#endif
+
+#ifndef _ANSI_ARGS_
+# define _ANSI_ARGS_(x) ()
+#endif
+
+#include <bltVector.h>
+#include <bltHash.h>
+#ifdef WIN32
+EXTERN int Blt_GetOpenPrinter _ANSI_ARGS_((Tcl_Interp *interp, const char *id,
+ Drawable *drawablePtr));
+
+EXTERN int Blt_StartPrintJob _ANSI_ARGS_((Tcl_Interp *interp, const char *id));
+
+EXTERN int Blt_EndPrintJob _ANSI_ARGS_((Tcl_Interp *interp, const char *id));
+#endif /* WIN32 */
+
+typedef char *Blt_Uid;
+
+EXTERN Blt_Uid Blt_GetUid _ANSI_ARGS_((char *string));
+EXTERN void Blt_FreeUid _ANSI_ARGS_((Blt_Uid uid));
+EXTERN Blt_Uid Blt_FindUid _ANSI_ARGS_((char *string));
+
+#if (TCL_MAJOR_VERSION >= 8)
+EXTERN int Blt_GetArrayFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Blt_HashTable **tablePtrPtr));
+EXTERN Tcl_Obj *Blt_NewArrayObj _ANSI_ARGS_((int objc, Tcl_Obj *objv[]));
+EXTERN void Blt_RegisterArrayObj _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Blt_IsArrayObj _ANSI_ARGS_((Tcl_Obj *obj));
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+#endif /*_BLT_H*/
diff --git a/blt/src/blt.mak b/blt/src/blt.mak
new file mode 100644
index 00000000000..70765b52be7
--- /dev/null
+++ b/blt/src/blt.mak
@@ -0,0 +1,304 @@
+
+# ------------------------------------------------------------------------
+#
+# Nmakefile for BLT library using VC++.
+#
+# Please note this file may or may not be up-to-date.
+#
+# You can compare it with "Makefile.vc" in this directory. That's
+# what I use to build BLT (so it should be current). It builds BLT
+# with VC++ 6.0 and the cygwin32 tool suite from
+#
+# http://sourceware.cygnus.com
+#
+# ------------------------------------------------------------------------
+
+!INCLUDE ../win/makedefs
+
+TOOLS32 = C:/Program Files/Microsoft Visual Studio/Vc98
+prefix = C:/Program Files/Tcl
+
+AR = lib.exe
+LD = link.exe
+CC = cl.exe
+rc32 = rc.exe
+RM = -del
+
+# ------------------------------------------------------------------------
+# C Compiler options
+# ------------------------------------------------------------------------
+
+DEFINES = -D_X86_=1 -D__STDC__ -DWIN32 -DCONSOLE -D_MT \
+ $(DEBUG_DEFINES) $(SHLIB_DEFINES)
+EXTRA_CFLAGS = -nologo -W3
+
+!IF "$(SHARED)" == "1"
+SHLIB_DEFINES = -D_DLL
+SHLIB_TARGET = build-dll
+LIBS = $(COMMON_LIBS)
+!ELSE
+SHLIB_DEFINES = -D_CTYPE_DISABLE_MACROS
+LIBS = $(COMMON_LIBS) $(EXTRA_LIBS)
+!ENDIF
+
+!IF "$(DEBUG)" == "1"
+CFLAGS = -Z7 -Od
+DEBUG_LDFLAGS = -debug:full -debugtype:cv
+D = d
+builddir = .\Debug
+!ELSE
+CFLAGS = -Ox -GB -GD
+DEBUG_LDFLAGS = -debug:full -debugtype:cv
+D =
+builddir = .\Release
+!ENDIF
+
+MSVCRT = msvcrt$(DBG).lib
+TK_LIB = $(TKDIR)/win/$(builddir)/tk$(v2)$(D).lib
+TCL_LIB = $(TCLDIR)/win/$(builddir)/tcl$(v2)$(D).lib
+
+# ------------------------------------------------------------------------
+# Linker flags and options
+# ------------------------------------------------------------------------
+
+JPEGLIB = $(JPEGDIR)/libjpeg.lib
+
+COMMON_LDFLAGS = -nodefaultlib -release -nologo -warn:3 \
+ -machine:IX86 -align:0x1000 \
+ $(DEBUG_LDFLAGS)
+
+DLLENTRY = @12
+SHLIB_LDFLAGS = $(COMMON_LDFLAGS) \
+ -subsystem:console -entry:mainCRTStartup \
+ -subsystem:windows -entry:WinMainCRTStartup \
+ -entry:_DllMainCRTStartup$(DLLENTRY) -dll
+
+LDFLAGS = $(COMMON_LDFLAGS) \
+ -fixed:NO -stack:2300000
+
+COMMON_LIBS = $(TK_LIB) $(TCL_LIB) \
+ $(MSVCRT) \
+ kernel32.lib user32.lib
+
+EXTRA_LIBS = $(OLELIB) \
+ $(JPEGLIB) \
+ gdi32.lib \
+ oldnames.lib \
+ advapi32.lib \
+ winspool.lib
+
+TCL_ONLY_LIBS = $(TCL_LIB) $(MSVCRT) kernel32.lib user32.lib advapi32.lib
+
+# ------------------------------------------------------------------------
+# Source and target directories
+# ------------------------------------------------------------------------
+
+srcdir = .
+instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) \
+ $(includedir)
+instdirs = $(exec_prefix) $(prefix) $(libdir)
+
+# ------------------------------------------------------------------------
+# Directories containing Tcl and Tk include files and libraries
+# ------------------------------------------------------------------------
+
+JPEGDIR = $(srcdir)/../../jpeg-6b
+TCLDIR = $(srcdir)/../../tcl$(v3)
+TKDIR = $(srcdir)/../../tk$(v3)
+INCLUDES = -I. -I$(srcdir) \
+ -I"$(TOOLS32)/include" \
+ -I$(TCLDIR)/win \
+ -I$(TCLDIR)/generic \
+ -I$(TKDIR)/win \
+ -I$(TKDIR)/generic \
+ -I$(TKDIR)/xlib \
+ -I$(JPEGDIR)
+SHLIB_LD_LIBS = $(COMMON_LIBS) $(EXTRA_LIBS)
+
+# ------------------------------------------------------------------------
+# You don't need to edit anything beyond this point
+# ------------------------------------------------------------------------
+
+N_OBJS = bltTed.o
+V3_OBJS = bltTri.o bltGrMt.o
+
+TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o
+
+GRAPH_OBJS = bltGrAxis.o \
+ bltGrBar.o \
+ bltGrElem.o \
+ bltGrGrid.o \
+ bltGrHairs.o \
+ bltGrLegd.o \
+ bltGrLine.o \
+ bltGrMarker.o \
+ bltGrMisc.o \
+ bltGrPen.o \
+ bltGrPs.o \
+ bltGraph.o
+
+TCL_ONLY_OBJS = bltAlloc.o \
+ bltArrayObj.o \
+ bltBgexec.o \
+ bltChain.o \
+ bltDebug.o \
+ bltHash.o \
+ bltList.o \
+ bltNsUtil.o \
+ bltParse.o \
+ bltPool.o \
+ bltSpline.o \
+ bltSwitch.o \
+ bltTree.o \
+ bltTreeCmd.o \
+ bltUtil.o \
+ bltVecCmd.o \
+ bltVecMath.o \
+ bltVecObjCmd.o \
+ bltVector.o \
+ bltWatch.o \
+ bltWinPipe.o \
+ bltWinUtil.o \
+ pure_api.o
+
+DEMO_OBJS = tkConsole.o bltWinMain.o
+
+OBJS = $(GRAPH_OBJS) \
+ $(TCL_ONLY_OBJS) \
+ bltBeep.o \
+ bltBind.o \
+ bltBitmap.o \
+ bltBusy.o \
+ bltCanvEps.o \
+ bltConfig.o \
+ bltContainer.o \
+ bltDragdrop.o \
+ bltHierbox.o \
+ bltHiertable.o \
+ bltHtCmd.o \
+ bltHtColumn.o \
+ bltHtText.o \
+ bltHtext.o \
+ bltImage.o \
+ bltPs.o \
+ bltTable.o \
+ bltTabnotebook.o \
+ bltTabset.o \
+ bltText.o \
+ bltTile.o \
+ bltTreeView.o \
+ bltTreeViewCmd.o \
+ bltTreeViewEdit.o \
+ bltTreeViewColumn.o \
+ bltTreeViewStyle.o \
+ bltWinDraw.o \
+ bltWinPrnt.o \
+ bltWindow.o \
+ bltObjConfig.o \
+ bltWinop.o \
+ $(TK_OBJS) $(N_OBJS)
+
+NOT_YET = bltContainer.o \
+ bltCutBuffer.o bltColor.o
+
+HEADERS = blt.h bltChain.h bltVector.h bltTree.h
+
+shell_name = bltwish
+version = $(BLT_MAJOR_VERSION)$(BLT_MINOR_VERSION)
+bltwish = bltwish.exe
+bltsh = bltsh.exe
+bltwish2 = bltwish$(version).exe
+bltsh2 = bltsh$(version).exe
+
+lib_name = BLT$(version)
+lib_a = BLT$(version).lib
+lib_so = BLT$(version).dll
+tcl_only_lib_a = BLTlite$(version).lib
+tcl_only_lib_so = BLTlite$(version).dll
+
+CC_SWITCHES = $(CFLAGS) $(EXTRA_CFLAGS) $(DEFINES) $(INCLUDES)
+VPATH = $(srcdir)
+
+all: build-library $(SHLIB_TARGET) build-demos
+
+build-demos: $(SHLIB_TARGET) $(bltwish) $(bltsh)
+
+build-library: $(BLT_LIB)
+
+build-library: $(lib_a) $(tcl_only_lib_a)
+
+build-dll: build-library $(lib_so) $(tcl_only_lib_so)
+
+$(bltwish): $(lib_a) tkConsole.o
+ $(RM) $@
+ $(CC) -c $(CC_SWITCHES) -FobltWinMain.o $(srcdir)/bltWinMain.c
+ set LIB="$(TOOLS32)\lib"
+ $(LD) $(LDFLAGS) tkConsole.o bltWinMain.o -out:$(bltwish) \
+ $(lib_a) $(LIBS)
+
+$(bltsh): $(tcl_only_lib_a)
+ $(RM) $(bltsh)
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY \
+ -FobltWinMain.o $(srcdir)/bltWinMain.c
+ set LIB="$(TOOLS32)\lib"
+ $(LD) $(LDFLAGS) bltWinMain.o -out:$(bltsh) \
+ $(tcl_only_lib_a) $(TCL_ONLY_LIBS)
+
+$(lib_a): bltHash.h $(OBJS)
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) -out:$@ bltInit.o $(OBJS)
+
+$(lib_so): $(lib_a)
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ set LIB="$(TOOLS32)\lib"
+ $(LD) $(SHLIB_LDFLAGS) -out:$@ bltInit.o $(OBJS) $(SHLIB_LD_LIBS)
+
+$(tcl_only_lib_a): bltHash.h $(TCL_ONLY_OBJS)
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ $(AR) -out:$@ bltInit.o $(TCL_ONLY_OBJS)
+
+$(tcl_only_lib_so): $(tcl_only_lib_a)
+ $(RM) bltInit.o
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -FobltInit.o $(srcdir)/bltInit.c
+ $(RM) $@
+ set LIB="$(TOOLS32)\lib"
+ $(LD) $(SHLIB_LDFLAGS) -out:$@ bltInit.o $(TCL_ONLY_OBJS) \
+ $(TCL_ONLY_LIBS)
+
+# You probably need to hand edit
+bltHash.h: bltHash.h.win32
+ cp bltHash.h.win32 bltHash.h
+ sed -e 's/@SIZEOF_VOID_P@/4/' \
+ -e 's/@SIZEOF_INT@/4/' \
+ -e 's/@SIZEOF_LONG@/4/' \
+ -e 's/@SIZEOF_LONG_LONG@/8/' \
+ -e 's/@HAVE_INTTYPES_H@/0/' \
+ bltHash.h.in > bltHash.h
+
+clean:
+ -del *.o 2>nul
+ -del *.pdb 2>nul
+ -del *.exp 2>nul
+ -del $(lib_name).* 2>nul
+ -del $(shell_name).* 2>nul
+ -del $(srcdir)\*.bak 2>nul
+ -del $(srcdir)\*~ 2>nul
+ -del $(srcdir)\"#"* 2>nul
+
+{$(srcdir)}.c.o:
+ $(CC) -c $(CC_SWITCHES) -Fo$*.o $<
+
+
+
+
+
+
+
+
+
diff --git a/blt/src/bltAlloc.c b/blt/src/bltAlloc.c
new file mode 100644
index 00000000000..69745f5a7bb
--- /dev/null
+++ b/blt/src/bltAlloc.c
@@ -0,0 +1,98 @@
+#include "bltInt.h"
+
+#ifndef linux
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+#endif
+
+/*
+ * Blt_MallocProcPtr, Blt_FreeProcPtr --
+ *
+ * These global variables allow you to override the default
+ * memory allocation/deallocation routines, simply by setting the
+ * pointers to your own C functions. By default, we try to use
+ * the same memory allocation scheme that Tcl is using: generally
+ * that's Tcl_Alloc and Tcl_Free.
+ */
+#ifdef WIN32
+
+#ifdef __CYGWIN__
+extern char *Tcl_Alloc _ANSI_ARGS_((unsigned int size));
+extern void Tcl_Free _ANSI_ARGS_((char * ptr));
+#endif /*__CYGWIN__*/
+
+Blt_MallocProc *Blt_MallocProcPtr = Tcl_Alloc;
+Blt_FreeProc *Blt_FreeProcPtr = Tcl_Free;
+
+#else
+
+/*
+ * Try to use the same memory allocator/deallocator that Tcl is
+ * using. Before 8.1 it used malloc/free.
+ */
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+/*
+ * We're pointing to the private TclpAlloc/TclpFree instead of public
+ * Tcl_Alloc/Tcl_Free routines because they don't automatically cause
+ * a panic when not enough memory is available. There are cases (such
+ * as allocating a very large vector) where it's recoverable.
+ */
+EXTERN Blt_MallocProc TclpAlloc;
+EXTERN Blt_FreeProc TclpFree;
+
+Blt_MallocProc *Blt_MallocProcPtr = TclpAlloc;
+Blt_FreeProc *Blt_FreeProcPtr = TclpFree;
+
+#else
+
+Blt_MallocProc *Blt_MallocProcPtr = malloc;
+Blt_FreeProc *Blt_FreeProcPtr = free;
+
+#endif /* >= 8.1.0 */
+#endif /* WIN32 */
+
+void *
+Blt_Calloc(nElems, sizeOfElem)
+ unsigned int nElems;
+ size_t sizeOfElem;
+{
+ char *ptr;
+ size_t size;
+
+ size = nElems * sizeOfElem;
+ ptr = Blt_Malloc(size);
+ if (ptr != NULL) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_Strdup --
+ *
+ * Create a copy of the string from heap storage.
+ *
+ * Results:
+ * Returns a pointer to the need string copy.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_Strdup(string)
+ CONST char *string;
+{
+ size_t size;
+ char *ptr;
+
+ size = strlen(string) + 1;
+ ptr = Blt_Malloc(size * sizeof(char));
+ if (ptr != NULL) {
+ strcpy(ptr, string);
+ }
+ return ptr;
+}
+
diff --git a/blt/src/bltArrayObj.c b/blt/src/bltArrayObj.c
new file mode 100644
index 00000000000..b76dd479528
--- /dev/null
+++ b/blt/src/bltArrayObj.c
@@ -0,0 +1,244 @@
+
+/*
+ * bltArrayObj.c --
+ *
+ * Copyright 2000 Silicon Metrics, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The array Tcl object was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_ARRAY
+#include "bltHash.h"
+
+static Tcl_DupInternalRepProc DupArrayInternalRep;
+static Tcl_FreeInternalRepProc FreeArrayInternalRep;
+static Tcl_UpdateStringProc UpdateStringOfArray;
+static Tcl_SetFromAnyProc SetArrayFromAny;
+
+static Tcl_ObjType arrayObjType = {
+ "array",
+ FreeArrayInternalRep, /* Called when an object is freed. */
+ DupArrayInternalRep, /* Copies an internal representation
+ * from one object to another. */
+ UpdateStringOfArray, /* Creates string representation from
+ * an object's internal representation. */
+ SetArrayFromAny, /* Creates valid internal representation
+ * from an object's string representation. */
+};
+
+static int
+SetArrayFromAny(interp, objPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *elemObjPtr;
+ Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+ char **elemArr;
+ char *string;
+ int isNew;
+ int nElem;
+ register int i;
+
+ if (objPtr->typePtr == &arrayObjType) {
+ return TCL_OK;
+ }
+ /*
+ * Get the string representation. Make it up-to-date if necessary.
+ */
+ string = Tcl_GetString(objPtr);
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+ assert(tablePtr);
+ Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
+ for (i = 0; i < nElem; i += 2) {
+ hPtr = Blt_CreateHashEntry(tablePtr, elemArr[i], &isNew);
+ elemObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
+ Blt_SetHashValue(hPtr, elemObjPtr);
+
+ /* Make sure we increment the reference count */
+ Tcl_IncrRefCount(elemObjPtr);
+ }
+
+ if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+ oldTypePtr->freeIntRepProc(objPtr);
+ }
+ objPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
+ objPtr->typePtr = &arrayObjType;
+ Blt_Free(elemArr);
+
+ return TCL_OK;
+}
+
+static void
+DupArrayInternalRep(srcPtr, destPtr)
+ Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
+ Tcl_Obj *destPtr; /* Object with internal rep to set. */
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable *srcTablePtr, *destTablePtr;
+ Tcl_Obj *valueObjPtr;
+ char *key;
+ int isNew;
+
+ srcTablePtr = (Blt_HashTable *)srcPtr->internalRep.otherValuePtr;
+ destTablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+ assert(destTablePtr);
+ Blt_InitHashTable(destTablePtr, BLT_STRING_KEYS);
+ for (hPtr = Blt_FirstHashEntry(srcTablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ key = Blt_GetHashKey(srcTablePtr, hPtr);
+ Blt_CreateHashEntry(destTablePtr, key, &isNew);
+ valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ Blt_SetHashValue(hPtr, valueObjPtr);
+
+ /* Make sure we increment the reference count now that both
+ * array objects are using the same elements. */
+ Tcl_IncrRefCount(valueObjPtr);
+ }
+ Tcl_InvalidateStringRep(destPtr);
+ destPtr->internalRep.otherValuePtr = (VOID *)destTablePtr;
+ destPtr->typePtr = &arrayObjType;
+}
+
+static void
+UpdateStringOfArray(objPtr)
+ Tcl_Obj *objPtr; /* Array object whose string rep to update. */
+{
+ Tcl_DString dString;
+ Blt_HashTable *tablePtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Tcl_Obj *elemObjPtr;
+
+ tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+ Tcl_DStringInit(&dString);
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ Tcl_DStringAppendElement(&dString, Blt_GetHashKey(tablePtr, hPtr));
+ Tcl_DStringAppendElement(&dString, Tcl_GetString(elemObjPtr));
+ }
+ objPtr->bytes = Blt_Strdup(Tcl_DStringValue(&dString));
+ objPtr->length = strlen(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+}
+
+static void
+FreeArrayInternalRep(objPtr)
+ Tcl_Obj *objPtr; /* Array object to release. */
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *elemObjPtr;
+
+ Tcl_InvalidateStringRep(objPtr);
+ tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ Tcl_DecrRefCount(elemObjPtr);
+ }
+ Blt_DeleteHashTable(tablePtr);
+ Blt_Free(tablePtr);
+}
+
+int
+Blt_GetArrayFromObj(interp, objPtr, tablePtrPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+ Blt_HashTable **tablePtrPtr;
+{
+ if (objPtr->typePtr == &arrayObjType) {
+ *tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+ return TCL_OK;
+ }
+ if (SetArrayFromAny(interp, objPtr) == TCL_OK) {
+ *tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+Tcl_Obj *
+Blt_NewArrayObj(objc, objv)
+ int objc;
+ Tcl_Obj *objv[];
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *arrayObjPtr, *objPtr;
+ int isNew;
+ register int i;
+
+ tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+ assert(tablePtr);
+ Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
+
+ for (i = 0; i < objc; i += 2) {
+ hPtr = Blt_CreateHashEntry(tablePtr, Tcl_GetString(objv[i]), &isNew);
+ if ((i + 1) == objc) {
+ objPtr = Tcl_NewStringObj("", -1);
+ } else {
+ objPtr = objv[i+1];
+ }
+ Tcl_IncrRefCount(objPtr);
+ if (!isNew) {
+ Tcl_DecrRefCount((Tcl_Obj *)Blt_GetHashValue(hPtr));
+ }
+ Blt_SetHashValue(hPtr, objPtr);
+ }
+ arrayObjPtr = Tcl_NewObj();
+ /*
+ * Reference counts for entry objects are initialized to 0. They
+ * are incremented as they are inserted into the tree via the
+ * Blt_TreeSetValue call.
+ */
+ arrayObjPtr->refCount = 0;
+ arrayObjPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
+ arrayObjPtr->bytes = NULL;
+ arrayObjPtr->length = 0;
+ arrayObjPtr->typePtr = &arrayObjType;
+ return arrayObjPtr;
+}
+
+int
+Blt_IsArrayObj(objPtr)
+ Tcl_Obj *objPtr;
+{
+ return (objPtr->typePtr == &arrayObjType);
+}
+
+/*ARGSUSED*/
+void
+Blt_RegisterArrayObj(interp)
+ Tcl_Interp *interp; /* Not used. */
+{
+ Tcl_RegisterObjType(&arrayObjType);
+}
+#endif /* NO_ARRAY */
diff --git a/blt/src/bltBeep.c b/blt/src/bltBeep.c
new file mode 100644
index 00000000000..308ff152c93
--- /dev/null
+++ b/blt/src/bltBeep.c
@@ -0,0 +1,92 @@
+/*
+ * bltBeep.c --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software. */
+
+#include "bltInt.h"
+
+#ifndef NO_BEEP
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_BeepCmd --
+ *
+ * This procedure is invoked to process the "bell" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef __STDC__
+static Tcl_CmdProc BeepCmd;
+#endif
+
+/* ARGSUSED */
+static int
+BeepCmd(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 percent;
+
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?volumePercent?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 1) {
+ percent = 50; /* Default setting */
+ } else if (argc == 2) {
+ if (Tcl_GetInt(interp, argv[1], &percent) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((percent < -100) || (percent > 100)) {
+ Tcl_AppendResult(interp, "bad volume percentage value \"",
+ argv[1], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ XBell(Tk_Display(Tk_MainWindow(interp)), percent);
+ return TCL_OK;
+}
+
+int
+Blt_BeepInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"beep", BeepCmd,};
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_BEEP */
diff --git a/blt/src/bltBgexec.c b/blt/src/bltBgexec.c
new file mode 100644
index 00000000000..d274fb58b83
--- /dev/null
+++ b/blt/src/bltBgexec.c
@@ -0,0 +1,2002 @@
+/*
+ * bltBgexec.c --
+ *
+ * This module implements a background "exec" command for the
+ * BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "bgexec" command was created by George Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_BGEXEC
+
+#include <fcntl.h>
+#include <signal.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <sys/types.h>
+
+#include "bltWait.h"
+#include "bltSwitch.h"
+
+#if (TCL_MAJOR_VERSION == 7)
+#define FILEHANDLER_USES_TCLFILES 1
+#else
+typedef int Tcl_File;
+#endif
+
+#ifdef __STDC__
+static Tcl_CmdProc BgexecCmd;
+#endif /* __STDC__ */
+
+#ifdef WIN32
+typedef struct {
+ DWORD pid;
+ HANDLE hProcess;
+} Process;
+#else
+typedef int Process;
+#endif
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+typedef void *Tcl_Encoding; /* Make up dummy type for encoding. */
+#endif
+
+#define ENCODING_ASCII ((Tcl_Encoding)NULL)
+#define ENCODING_BINARY ((Tcl_Encoding)1)
+
+/*
+ * As of Tcl 7.6, we're using our own version of the old
+ * Tcl_CreatePipeline routine. I would have tried to use
+ * Tcl_OpenCommandChannel but you can't get at the array of
+ * process ids, unless of course you pry open the undocumented
+ * structure PipeStatus as clientData. Nor could I figure out
+ * how to set one side of the pipe to be non-blocking. The whole
+ * channel API seems overly complex for what its supposed to
+ * do. [And maybe that's why it keeps changing every release.]
+ */
+extern int Blt_CreatePipeline _ANSI_ARGS_((Tcl_Interp *interp, int argc,
+ char **argv, Process **pidPtrPtr, int *inPipePtr, int *outPipePtr,
+ int *errFilePtr));
+
+#ifdef WIN32
+#define read(fd, buf, size) Blt_AsyncRead((fd),(buf),(size))
+#define close(fd) CloseHandle((HANDLE)fd)
+#define Tcl_CreateFileHandler Blt_CreateFileHandler
+#define Tcl_DeleteFileHandler Blt_DeleteFileHandler
+#define kill KillProcess
+#define waitpid WaitProcess
+#endif
+
+#define READ_AGAIN (0)
+#define READ_EOF (-1)
+#define READ_ERROR (-2)
+
+/* The wait-related definitions are taken from tclUnix.h */
+
+#define TRACE_FLAGS (TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY)
+
+#define BLOCK_SIZE 1024 /* Size of allocation blocks for buffer */
+#define DEF_BUFFER_SIZE (BLOCK_SIZE * 8)
+#define MAX_READS 100 /* Maximum number of successful reads
+ * before stopping to let Tcl catch up
+ * on events */
+
+#ifndef NSIG
+#define NSIG 32 /* Number of signals available */
+#endif /*NSIG*/
+
+#ifndef SIGINT
+#define SIGINT 2
+#endif /* SIGINT */
+
+#ifndef SIGQUIT
+#define SIGQUIT 3
+#endif /* SIGQUIT */
+
+#ifndef SIGKILL
+#define SIGKILL 9
+#endif /* SIGKILL */
+
+#ifndef SIGTERM
+#define SIGTERM 14
+#endif /* SIGTERM */
+
+typedef struct {
+ int number;
+ char *name;
+} SignalId;
+
+static SignalId signalIds[] =
+{
+#ifdef SIGABRT
+ {SIGABRT, "SIGABRT"},
+#endif
+#ifdef SIGALRM
+ {SIGALRM, "SIGALRM"},
+#endif
+#ifdef SIGBUS
+ {SIGBUS, "SIGBUS"},
+#endif
+#ifdef SIGCHLD
+ {SIGCHLD, "SIGCHLD"},
+#endif
+#if defined(SIGCLD) && (!defined(SIGCHLD) || (SIGCLD != SIGCHLD))
+ {SIGCLD, "SIGCLD"},
+#endif
+#ifdef SIGCONT
+ {SIGCONT, "SIGCONT"},
+#endif
+#if defined(SIGEMT) && (!defined(SIGXCPU) || (SIGEMT != SIGXCPU))
+ {SIGEMT, "SIGEMT"},
+#endif
+#ifdef SIGFPE
+ {SIGFPE, "SIGFPE"},
+#endif
+#ifdef SIGHUP
+ {SIGHUP, "SIGHUP"},
+#endif
+#ifdef SIGILL
+ {SIGILL, "SIGILL"},
+#endif
+#ifdef SIGINT
+ {SIGINT, "SIGINT"},
+#endif
+#ifdef SIGIO
+ {SIGIO, "SIGIO"},
+#endif
+#if defined(SIGIOT) && (!defined(SIGABRT) || (SIGIOT != SIGABRT))
+ {SIGIOT, "SIGIOT"},
+#endif
+#ifdef SIGKILL
+ {SIGKILL, "SIGKILL"},
+#endif
+#if defined(SIGLOST) && (!defined(SIGIOT) || (SIGLOST != SIGIOT)) && (!defined(SIGURG) || (SIGLOST != SIGURG))
+ {SIGLOST, "SIGLOST"},
+#endif
+#ifdef SIGPIPE
+ {SIGPIPE, "SIGPIPE"},
+#endif
+#if defined(SIGPOLL) && (!defined(SIGIO) || (SIGPOLL != SIGIO))
+ {SIGPOLL, "SIGPOLL"},
+#endif
+#ifdef SIGPROF
+ {SIGPROF, "SIGPROF"},
+#endif
+#if defined(SIGPWR) && (!defined(SIGXFSZ) || (SIGPWR != SIGXFSZ))
+ {SIGPWR, "SIGPWR"},
+#endif
+#ifdef SIGQUIT
+ {SIGQUIT, "SIGQUIT"},
+#endif
+#ifdef SIGSEGV
+ {SIGSEGV, "SIGSEGV"},
+#endif
+#ifdef SIGSTOP
+ {SIGSTOP, "SIGSTOP"},
+#endif
+#ifdef SIGSYS
+ {SIGSYS, "SIGSYS"},
+#endif
+#ifdef SIGTERM
+ {SIGTERM, "SIGTERM"},
+#endif
+#ifdef SIGTRAP
+ {SIGTRAP, "SIGTRAP"},
+#endif
+#ifdef SIGTSTP
+ {SIGTSTP, "SIGTSTP"},
+#endif
+#ifdef SIGTTIN
+ {SIGTTIN, "SIGTTIN"},
+#endif
+#ifdef SIGTTOU
+ {SIGTTOU, "SIGTTOU"},
+#endif
+#if defined(SIGURG) && (!defined(SIGIO) || (SIGURG != SIGIO))
+ {SIGURG, "SIGURG"},
+#endif
+#if defined(SIGUSR1) && (!defined(SIGIO) || (SIGUSR1 != SIGIO))
+ {SIGUSR1, "SIGUSR1"},
+#endif
+#if defined(SIGUSR2) && (!defined(SIGURG) || (SIGUSR2 != SIGURG))
+ {SIGUSR2, "SIGUSR2"},
+#endif
+#ifdef SIGVTALRM
+ {SIGVTALRM, "SIGVTALRM"},
+#endif
+#ifdef SIGWINCH
+ {SIGWINCH, "SIGWINCH"},
+#endif
+#ifdef SIGXCPU
+ {SIGXCPU, "SIGXCPU"},
+#endif
+#ifdef SIGXFSZ
+ {SIGXFSZ, "SIGXFSZ"},
+#endif
+ {-1, "unknown signal"},
+};
+
+/*
+ * Sink buffer:
+ * ____________________
+ * | | "size" current allocated length of buffer.
+ * | |
+ * |--------------------| "fill" fill point (# characters in buffer).
+ * | Raw |
+ * |--------------------| "mark" Marks end of cooked characters.
+ * | |
+ * | Cooked |
+ * | |
+ * | |
+ * |--------------------| "lastMark" Mark end of processed characters.
+ * | |
+ * | |
+ * | Processed |
+ * | |
+ * |____________________| 0
+ */
+typedef struct {
+ char *name; /* Name of the sink */
+
+ char *doneVar; /* Name of a Tcl variable (malloc'ed)
+ * set to the collected data of the
+ * last UNIX subprocess. */
+
+ char *updateVar; /* Name of a Tcl variable (malloc'ed)
+ * updated as data is read from the
+ * pipe. */
+
+ char **updateCmd; /* Start of a Tcl command executed
+ * whenever data is read from the
+ * pipe. */
+
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Obj **objv; /* */
+ int objc; /* */
+#endif
+
+ int flags;
+
+ Tcl_File file; /* Used for backward compatability
+ * with Tcl 7.5 */
+ Tcl_Encoding encoding;
+ int fd; /* File descriptor of the pipe. */
+ int status;
+
+ int echo; /* Indicates if the pipeline's stderr stream
+ * should be echoed */
+ unsigned char *byteArr; /* Stores command output (malloc-ed):
+ * Initially points to static storage
+ */
+ size_t size; /* Size of dynamically allocated buffer. */
+
+ size_t fill; /* # of bytes read into the buffer. Marks
+ * the current fill point of the buffer. */
+
+ size_t mark; /* # of bytes translated (cooked). */
+ size_t lastMark; /* # of bytes as of the last read.
+ * This indicates the start of the new
+ * data in the buffer since the last
+ * time the "update" variable was
+ * set. */
+
+ unsigned char staticSpace[DEF_BUFFER_SIZE]; /* Static space */
+
+} Sink;
+
+#define SINK_BUFFERED (1<<0)
+#define SINK_KEEP_NL (1<<1)
+#define SINK_NOTIFY (1<<2)
+
+typedef struct {
+ char *statVar; /* Name of a Tcl variable set to the
+ * exit status of the last
+ * process. Setting this variable
+ * triggers the termination of all
+ * subprocesses (regardless whether
+ * they have already completed) */
+
+ int signalNum; /* If non-zero, indicates the signal
+ * to send subprocesses when cleaning
+ * up.*/
+
+ int keepNewline; /* If non-zero, indicates to set Tcl
+ * output variables with trailing
+ * newlines intact */
+
+ int lineBuffered; /* If non-zero, indicates provide data
+ * to update variable and update proc on
+ * a line-by-line basis. */
+
+ int interval; /* Interval to poll for the exiting
+ * processes */
+ char *outputEncodingName; /* Name of decoding scheme to use when
+ * translating output data. */
+ char *errorEncodingName; /* Name of decoding scheme to use when
+ * translating output data. */
+
+ /* Private */
+ Tcl_Interp *interp; /* Interpreter containing variables */
+
+ int nProcs; /* Number of processes in pipeline */
+ Process *procArr; /* Array of process tokens from pipeline.
+ * The token for Unix are pid_t, while
+ * for Win32 they're handles. */
+
+ int traced; /* Indicates that the status variable
+ * is currently being traced. */
+ int detached; /* Indicates that the pipeline is
+ * detached from standard I/O, running
+ * in the background. */
+ Tcl_TimerToken timerToken; /* Token for timer handler which polls
+ * for the exit status of each
+ * sub-process. If zero, there's no
+ * timer handler queued. */
+
+ int *exitCodePtr; /* Pointer to a memory location to
+ * contain the last process' exit
+ * code. */
+ int *donePtr;
+
+ Sink sink1, sink2;
+
+} BackgroundInfo;
+
+
+static Blt_SwitchParseProc StringToSignal;
+static Blt_SwitchCustom killSignalSwitch =
+{
+ StringToSignal, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+static Blt_SwitchSpec switchSpecs[] =
+{
+ {BLT_SWITCH_STRING, "-decodeoutput",
+ Blt_Offset(BackgroundInfo, outputEncodingName), 0},
+ {BLT_SWITCH_STRING, "-decodeerror",
+ Blt_Offset(BackgroundInfo, errorEncodingName), 0},
+ {BLT_SWITCH_BOOLEAN, "-echo",
+ Blt_Offset(BackgroundInfo, sink2.echo), 0},
+ {BLT_SWITCH_STRING, "-error",
+ Blt_Offset(BackgroundInfo, sink2.doneVar), 0},
+ {BLT_SWITCH_STRING, "-update",
+ Blt_Offset(BackgroundInfo, sink1.updateVar), 0},
+ {BLT_SWITCH_STRING, "-output",
+ Blt_Offset(BackgroundInfo, sink1.doneVar), 0},
+ {BLT_SWITCH_STRING, "-lasterror",
+ Blt_Offset(BackgroundInfo, sink2.updateVar), 0},
+ {BLT_SWITCH_STRING, "-lastoutput",
+ Blt_Offset(BackgroundInfo, sink1.updateVar), 0},
+ {BLT_SWITCH_LIST, "-onerror",
+ Blt_Offset(BackgroundInfo, sink2.updateCmd), 0},
+ {BLT_SWITCH_LIST, "-onoutput",
+ Blt_Offset(BackgroundInfo, sink1.updateCmd), 0},
+ {BLT_SWITCH_BOOLEAN, "-keepnewline",
+ Blt_Offset(BackgroundInfo, keepNewline), 0},
+ {BLT_SWITCH_BOOLEAN, "-check",
+ Blt_Offset(BackgroundInfo, interval), 0},
+ {BLT_SWITCH_CUSTOM, "-killsignal",
+ Blt_Offset(BackgroundInfo, signalNum), 0, &killSignalSwitch},
+ {BLT_SWITCH_BOOLEAN, "-linebuffered",
+ Blt_Offset(BackgroundInfo, lineBuffered), 0},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static char *VariableProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *part1, char *part2, int flags));
+static void TimerProc _ANSI_ARGS_((ClientData clientData));
+static void StdoutProc _ANSI_ARGS_((ClientData clientData, int mask));
+static void StderrProc _ANSI_ARGS_((ClientData clientData, int mask));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetSignal --
+ *
+ * Convert a string represent a signal number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSignal(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Contains a pointer to the tabset containing
+ * this image. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ int *signalPtr = (int *)(record + offset);
+ int signalNum;
+
+ if ((string == NULL) || (*string == '\0')) {
+ *signalPtr = 0;
+ return TCL_OK;
+ }
+ if (isdigit(UCHAR(string[0]))) {
+ if (Tcl_GetInt(interp, string, &signalNum) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ char *name;
+ register SignalId *sigPtr;
+
+ name = string;
+
+ /* Clip off any "SIG" prefix from the signal name */
+ if ((name[0] == 'S') && (name[1] == 'I') && (name[2] == 'G')) {
+ name += 3;
+ }
+ signalNum = -1;
+ for (sigPtr = signalIds; sigPtr->number > 0; sigPtr++) {
+ if (strcmp(sigPtr->name + 3, name) == 0) {
+ signalNum = sigPtr->number;
+ break;
+ }
+ }
+ if (signalNum < 0) {
+ Tcl_AppendResult(interp, "unknown signal \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if ((signalNum < 0) || (signalNum > NSIG)) {
+ /* Outside range of signals */
+ Tcl_AppendResult(interp, "signal number \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *signalPtr = signalNum;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetSinkData --
+ *
+ * Returns the data currently saved in the buffer
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetSinkData(sinkPtr, dataPtr, lengthPtr)
+ Sink *sinkPtr;
+ unsigned char **dataPtr;
+ size_t *lengthPtr;
+{
+ size_t length;
+
+ sinkPtr->byteArr[sinkPtr->mark] = '\0';
+ length = sinkPtr->mark;
+ if ((sinkPtr->mark > 0) && (sinkPtr->encoding != ENCODING_BINARY)) {
+ unsigned char *last;
+
+ last = sinkPtr->byteArr + (sinkPtr->mark - 1);
+ if ((!(sinkPtr->flags & SINK_KEEP_NL)) && (*last == '\n')) {
+ length--;
+ }
+ }
+ *dataPtr = sinkPtr->byteArr;
+ *lengthPtr = length;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextBlock --
+ *
+ * Returns the next block of data since the last time this
+ * routine was called.
+ *
+ *----------------------------------------------------------------------
+ */
+static unsigned char *
+NextBlock(sinkPtr, lengthPtr)
+ Sink *sinkPtr;
+ int *lengthPtr;
+{
+ unsigned char *string;
+ int length;
+
+ string = sinkPtr->byteArr + sinkPtr->lastMark;
+ length = sinkPtr->mark - sinkPtr->lastMark;
+ sinkPtr->lastMark = sinkPtr->mark;
+ if (length > 0) {
+ if ((!(sinkPtr->flags & SINK_KEEP_NL)) &&
+ (string[length - 1] == '\n')) {
+ length--;
+ }
+ *lengthPtr = length;
+ return string;
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextLine --
+ *
+ * Returns the next line of data.
+ *
+ *----------------------------------------------------------------------
+ */
+static unsigned char *
+NextLine(sinkPtr, lengthPtr)
+ Sink *sinkPtr;
+ int *lengthPtr;
+{
+ if (sinkPtr->mark > sinkPtr->lastMark) {
+ unsigned char *string;
+ int newBytes;
+ register int i;
+
+ string = sinkPtr->byteArr + sinkPtr->lastMark;
+ newBytes = sinkPtr->mark - sinkPtr->lastMark;
+ for (i = 0; i < newBytes; i++) {
+ if (string[i] == '\n') {
+ int length;
+
+ length = i + 1;
+ sinkPtr->lastMark += length;
+ if (!(sinkPtr->flags & SINK_KEEP_NL)) {
+ length--; /* Backup over the newline. */
+ }
+ *lengthPtr = length;
+ return string;
+ }
+ }
+ /* Newline not found. On errors or EOF, also return a partial line. */
+ if (sinkPtr->status < 0) {
+ *lengthPtr = newBytes;
+ sinkPtr->lastMark = sinkPtr->mark;
+ return string;
+ }
+ }
+ return NULL;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResetSink --
+ *
+ * Removes the bytes already processed from the buffer, possibly
+ * resetting it to empty. This used when we don't care about
+ * keeping all the data collected from the channel (no -output
+ * flag and the process is detached).
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ResetSink(sinkPtr)
+ Sink *sinkPtr;
+{
+ if ((sinkPtr->flags & SINK_BUFFERED) &&
+ (sinkPtr->fill > sinkPtr->lastMark)) {
+ register size_t i, j;
+
+ /* There may be bytes remaining in the buffer, awaiting
+ * another read before we see the next newline. So move the
+ * bytes to the front of the array. */
+
+ for (i = 0, j = sinkPtr->lastMark; j < sinkPtr->fill; i++, j++) {
+ sinkPtr->byteArr[i] = sinkPtr->byteArr[j];
+ }
+ /* Move back the fill point and processed point. */
+ sinkPtr->fill -= sinkPtr->lastMark;
+ sinkPtr->mark -= sinkPtr->lastMark;
+ } else {
+ sinkPtr->mark = sinkPtr->fill = 0;
+ }
+ sinkPtr->lastMark = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitSink --
+ *
+ * Initializes the buffer's storage.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Storage is cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+InitSink(bgPtr, sinkPtr, name, encoding)
+ BackgroundInfo *bgPtr;
+ Sink *sinkPtr;
+ char *name;
+ Tcl_Encoding encoding;
+{
+ sinkPtr->name = name;
+ sinkPtr->echo = FALSE;
+ sinkPtr->fd = -1;
+ sinkPtr->file = (Tcl_File)NULL;
+ sinkPtr->byteArr = sinkPtr->staticSpace;
+ sinkPtr->size = DEF_BUFFER_SIZE;
+ sinkPtr->encoding = encoding;
+ if (bgPtr->keepNewline) {
+ sinkPtr->flags |= SINK_KEEP_NL;
+ }
+ if (bgPtr->lineBuffered) {
+ sinkPtr->flags |= SINK_BUFFERED;
+ }
+ if ((sinkPtr->updateCmd != NULL) ||
+ (sinkPtr->updateVar != NULL) ||
+ (sinkPtr->echo)) {
+ sinkPtr->flags |= SINK_NOTIFY;
+ }
+#if (TCL_MAJOR_VERSION >= 8)
+ if (sinkPtr->updateCmd != NULL) {
+ Tcl_Obj **objArr;
+ char **p;
+ int count;
+ register int i;
+
+ count = 0;
+ for (p = sinkPtr->updateCmd; *p != NULL; p++) {
+ count++;
+ }
+ objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+ for (i = 0; i < count; i++) {
+ objArr[i] = Tcl_NewStringObj(sinkPtr->updateCmd[i], -1);
+ Tcl_IncrRefCount(objArr[i]);
+ }
+ sinkPtr->objv = objArr;
+ sinkPtr->objc = count + 1;
+ }
+#endif
+ ResetSink(sinkPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeSinkBuffer --
+ *
+ * Frees the buffer's storage, freeing any malloc'ed space.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeSinkBuffer(sinkPtr)
+ Sink *sinkPtr;
+{
+ if (sinkPtr->byteArr != sinkPtr->staticSpace) {
+ Blt_Free(sinkPtr->byteArr);
+ }
+ sinkPtr->fd = -1;
+ sinkPtr->file = (Tcl_File)NULL;
+#if (TCL_MAJOR_VERSION >= 8)
+ if (sinkPtr->objv != NULL) {
+ register int i;
+
+ for (i = 0; i < sinkPtr->objc - 1; i++) {
+ Tcl_DecrRefCount(sinkPtr->objv[i]);
+ }
+ Blt_Free(sinkPtr->objv);
+ }
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExtendSinkBuffer --
+ *
+ * Doubles the size of the current buffer.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ExtendSinkBuffer(sinkPtr)
+ Sink *sinkPtr;
+{
+ unsigned char *arrayPtr;
+ register unsigned char *srcPtr, *destPtr, *endPtr;
+ /*
+ * Allocate a new array, double the old size
+ */
+ sinkPtr->size += sinkPtr->size;
+ arrayPtr = Blt_Malloc(sizeof(unsigned char) * sinkPtr->size);
+ if (arrayPtr == NULL) {
+ return -1;
+ }
+ srcPtr = sinkPtr->byteArr;
+ endPtr = sinkPtr->byteArr + sinkPtr->fill;
+ destPtr = arrayPtr;
+ while (srcPtr < endPtr) {
+ *destPtr++ = *srcPtr++;
+ }
+ if (sinkPtr->byteArr != sinkPtr->staticSpace) {
+ Blt_Free(sinkPtr->byteArr);
+ }
+ sinkPtr->byteArr = arrayPtr;
+
+ return (sinkPtr->size - sinkPtr->fill); /* Return bytes left. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadBytes --
+ *
+ * Reads and appends any available data from a given file descriptor
+ * to the buffer.
+ *
+ * Results:
+ * Returns TCL_OK when EOF is found, TCL_RETURN if reading
+ * data would block, and TCL_ERROR if an error occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReadBytes(sinkPtr)
+ Sink *sinkPtr;
+{
+ int nBytes, bytesLeft;
+ register int i;
+ unsigned char *array;
+
+ /*
+ * ------------------------------------------------------------------
+ *
+ * Worry about indefinite postponement.
+ *
+ * Typically we want to stay in the read loop as long as it takes
+ * to collect all the data that's currently available. But if
+ * it's coming in at a constant high rate, we need to arbitrarily
+ * break out at some point. This allows for both setting the
+ * update variable and the Tk program to handle idle events.
+ *
+ * ------------------------------------------------------------------
+ */
+
+ for (i = 0; i < MAX_READS; i++) {
+
+ /* Allocate a larger buffer when the number of remaining bytes
+ * is below the threshold BLOCK_SIZE. */
+
+ bytesLeft = sinkPtr->size - sinkPtr->fill;
+
+ if (bytesLeft < BLOCK_SIZE) {
+ bytesLeft = ExtendSinkBuffer(sinkPtr);
+ if (bytesLeft < 0) {
+ sinkPtr->status = READ_ERROR;
+ return;
+ }
+ }
+ array = sinkPtr->byteArr + sinkPtr->fill;
+
+ /*
+ * Read into a buffer but make sure we leave room for a
+ * trailing NUL byte.
+ */
+ nBytes = read(sinkPtr->fd, array, bytesLeft - 1);
+ if (nBytes == 0) { /* EOF: break out of loop. */
+ sinkPtr->status = READ_EOF;
+ return;
+ }
+ if (nBytes < 0) {
+#ifdef O_NONBLOCK
+#define BLOCKED EAGAIN
+#else
+#define BLOCKED EWOULDBLOCK
+#endif /*O_NONBLOCK*/
+ /* Either an error has occurred or no more data is
+ * currently available to read. */
+ if (errno == BLOCKED) {
+ sinkPtr->status = READ_AGAIN;
+ return;
+ }
+ sinkPtr->byteArr[0] = '\0';
+ sinkPtr->status = READ_ERROR;
+ return;
+ }
+ sinkPtr->fill += nBytes;
+ sinkPtr->byteArr[sinkPtr->fill] = '\0';
+ }
+ sinkPtr->status = nBytes;
+}
+
+#define IsOpenSink(sinkPtr) ((sinkPtr)->fd != -1)
+
+static void
+CloseSink(interp, sinkPtr)
+ Tcl_Interp *interp;
+ Sink *sinkPtr;
+{
+ if (IsOpenSink(sinkPtr)) {
+ close(sinkPtr->fd);
+#ifdef FILEHANDLER_USES_TCLFILES
+ Tcl_DeleteFileHandler(sinkPtr->file);
+ Tcl_FreeFile(sinkPtr->file);
+#else
+ Tcl_DeleteFileHandler(sinkPtr->fd);
+#endif
+ sinkPtr->file = (Tcl_File)NULL;
+ sinkPtr->fd = -1;
+
+#if WINDEBUG
+ PurifyPrintf("CloseSink: set done var %s\n", sinkPtr->name);
+#endif
+ if (sinkPtr->doneVar != NULL) {
+ unsigned char *data;
+ size_t length;
+ /*
+ * If data is to be collected, set the "done" variable
+ * with the contents of the buffer.
+ */
+ GetSinkData(sinkPtr, &data, &length);
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+ data[length] = '\0';
+ if (Tcl_SetVar(interp, sinkPtr->doneVar, data,
+ TCL_GLOBAL_ONLY) == NULL) {
+ Tcl_BackgroundError(interp);
+ }
+#else
+ if (Tcl_SetVar2Ex(interp, sinkPtr->doneVar, NULL,
+ Tcl_NewByteArrayObj(data, length),
+ (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)) == NULL) {
+ Tcl_BackgroundError(interp);
+ }
+#endif
+ }
+#if WINDEBUG
+ PurifyPrintf("CloseSink %s: done\n", sinkPtr->name);
+#endif
+ }
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * CookSink --
+ *
+ * For Windows, translate CR/NL combinations to NL alone.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The size of the byte array may shrink and array contents
+ * shifted as carriage returns are found and removed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CookSink(interp, sinkPtr)
+ Tcl_Interp *interp;
+ Sink *sinkPtr;
+{
+ unsigned char *srcPtr, *destPtr, *endPtr;
+ size_t oldMark;
+
+ oldMark = sinkPtr->mark;
+ if (sinkPtr->encoding == ENCODING_BINARY) { /* binary */
+ /* No translation needed. */
+ sinkPtr->mark = sinkPtr->fill;
+ } else if (sinkPtr->encoding == ENCODING_ASCII) { /* ascii */
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+ /* Clean out NUL bytes, make spaces */
+ srcPtr = sinkPtr->byteArr + sinkPtr->mark;
+ endPtr = sinkPtr->byteArr + sinkPtr->fill;
+ while (srcPtr < endPtr) {
+ if (*srcPtr == '\0') {
+ *srcPtr = '?';
+ }
+ srcPtr++;
+ }
+#endif /* < 8.1.0 */
+ /* One-to-one translation. mark == fill. */
+ sinkPtr->mark = sinkPtr->fill;
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ } else { /* unicode. */
+ size_t nRaw, nLeftOver;
+ int nSrcCooked, nCooked;
+ size_t cookedSize, spaceLeft, needed;
+ int result;
+ unsigned char leftover[100];
+ unsigned char *raw, *cooked;
+
+ raw = sinkPtr->byteArr + sinkPtr->mark;
+ nRaw = sinkPtr->fill - sinkPtr->mark;
+ /* Ideally, the cooked buffer size should be smaller */
+ cookedSize = nRaw * TCL_UTF_MAX + 1;
+ cooked = Blt_Malloc(cookedSize);
+ result = Tcl_ExternalToUtf(interp, sinkPtr->encoding,
+ (char *)raw, nRaw, 0, NULL, (char *)cooked,
+ cookedSize, &nSrcCooked, &nCooked, NULL);
+ nLeftOver = 0;
+ if (result == TCL_CONVERT_MULTIBYTE) {
+ /*
+ * Last multibyte sequence wasn't completed. Save the
+ * extra characters in a temporary buffer.
+ */
+ nLeftOver = (nRaw - nSrcCooked);
+ srcPtr = sinkPtr->byteArr + (sinkPtr->mark + nSrcCooked);
+ endPtr = srcPtr + nLeftOver;
+ destPtr = leftover;
+ while (srcPtr < endPtr) {
+ *destPtr++ = *srcPtr++;
+ }
+ }
+ /*
+ * Create a bigger
+ */
+
+ needed = nLeftOver + nCooked;
+ spaceLeft = sinkPtr->size - sinkPtr->mark;
+ if (spaceLeft >= needed) {
+ spaceLeft = ExtendSinkBuffer(sinkPtr);
+ }
+ assert(spaceLeft > needed);
+ /*
+ * Replace the characters from the mark with the translated
+ * characters.
+ */
+ srcPtr = cooked;
+ endPtr = cooked + nCooked;
+ destPtr = sinkPtr->byteArr + sinkPtr->mark;
+ while (srcPtr < endPtr) {
+ *destPtr++ = *srcPtr++;
+ }
+ /* Add the number of newly translated characters to the mark */
+ sinkPtr->mark += nCooked;
+
+ srcPtr = leftover;
+ endPtr = leftover + nLeftOver;
+ while (srcPtr < endPtr) {
+ *destPtr++ = *srcPtr++;
+ }
+ sinkPtr->fill = sinkPtr->mark + nLeftOver;
+#endif /* >= 8.1.0 */
+ }
+#ifdef WIN32
+ /*
+ * Translate CRLF character sequences to LF characters. We have to
+ * do this after converting the string to UTF from UNICODE.
+ */
+ if (sinkPtr->encoding != ENCODING_BINARY) {
+ int count;
+
+ destPtr = srcPtr = sinkPtr->byteArr + oldMark;
+ endPtr = sinkPtr->byteArr + sinkPtr->fill;
+ *endPtr = '\0';
+ count = 0;
+ for (endPtr--; srcPtr < endPtr; srcPtr++) {
+ if ((*srcPtr == '\r') && (*(srcPtr + 1) == '\n')) {
+ count++;
+ continue; /* Skip the CR in CR/LF sequences. */
+ }
+ if (srcPtr != destPtr) {
+ *destPtr = *srcPtr; /* Collapse the string, overwriting
+ * the \r's encountered. */
+ }
+ destPtr++;
+ }
+ sinkPtr->mark -= count;
+ sinkPtr->fill -= count;
+ *destPtr = *srcPtr; /* Copy the last byte */
+ if (*destPtr == '\r') {
+ sinkPtr->mark--;
+ }
+ }
+#endif /* WIN32 */
+}
+
+#ifdef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitProcess --
+ *
+ * Emulates the waitpid system call under the Win32 API.
+ *
+ * Results:
+ * Returns 0 if the process is still alive, -1 on an error, or
+ * the pid on a clean close.
+ *
+ * Side effects:
+ * Unless WNOHANG is set and the wait times out, the process
+ * information record will be deleted and the process handle
+ * will be closed.
+ *
+ *----------------------------------------------------------------------
+ */
+#define WINDEBUG 0
+static int
+WaitProcess(
+ Process child,
+ int *statusPtr,
+ int flags)
+{
+ int result;
+ DWORD status, exitCode;
+ int timeout;
+
+#if WINDEBUG
+ PurifyPrintf("WAITPID(%x)\n", child.hProcess);
+#endif
+ *statusPtr = 0;
+ if (child.hProcess == INVALID_HANDLE_VALUE) {
+ errno = EINVAL;
+ return -1;
+ }
+#if WINDEBUG
+ PurifyPrintf("WAITPID: waiting for 0x%x\n", child.hProcess);
+#endif
+ timeout = (flags & WNOHANG) ? 0 : INFINITE;
+ status = WaitForSingleObject(child.hProcess, timeout);
+
+#if WINDEBUG
+ PurifyPrintf("WAITPID: wait status is %d\n", status);
+#endif
+ switch (status) {
+ case WAIT_FAILED:
+ errno = ECHILD;
+ *statusPtr = ECHILD;
+ result = -1;
+ break;
+
+ case WAIT_TIMEOUT:
+ if (timeout == 0) {
+ return 0; /* Try again */
+ }
+ result = 0;
+ break;
+
+ default:
+ case WAIT_ABANDONED:
+ case WAIT_OBJECT_0:
+ GetExitCodeProcess(child.hProcess, &exitCode);
+ *statusPtr = ((exitCode << 8) & 0xff00);
+#if WINDEBUG
+ PurifyPrintf("WAITPID: exit code of %d is %d (%x)\n", child.hProcess,
+ *statusPtr, exitCode);
+#endif
+ result = child.pid;
+ assert(result != -1);
+ break;
+ }
+ CloseHandle(child.hProcess);
+ return result;
+}
+
+static BOOL CALLBACK
+EnumWindowsProc(HWND hWnd, LPARAM lParam)
+{
+ DWORD pid = 0;
+ Process *procPtr = (Process *)lParam;
+
+ GetWindowThreadProcessId(hWnd, &pid);
+ if (pid == procPtr->pid) {
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ }
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * KillProcess --
+ *
+ * Emulates the UNIX kill system call under Win32 API.
+ *
+ * Results:
+ * Returns 0 if the process is killed, -1 on an error.
+ *
+ * Side effects:
+ * Process is terminated.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+KillProcess(Process proc, int signal)
+{
+ DWORD status;
+
+ if ((proc.hProcess == NULL) || (proc.hProcess == INVALID_HANDLE_VALUE)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ EnumWindows(EnumWindowsProc, (LPARAM)&proc);
+
+ /*
+ * Wait on the handle. If it signals, great. If it times out,
+ * then call TerminateProcess on it.
+ *
+ * On Windows 95/98 this also has the added benefit of stopping
+ * KERNEL32.dll from dumping. The 2 second number is arbitrary.
+ * (1 second seems to fail intermittently).
+ */
+ status = WaitForSingleObject(proc.hProcess, 2000);
+ if (status == WAIT_OBJECT_0) {
+ return 0;
+ }
+ if (!TerminateProcess(proc.hProcess, 1)) {
+#if WINDEBUG
+ PurifyPrintf("can't terminate process (handle=%d): %s\n",
+ proc.hProcess, Blt_LastError());
+#endif /* WINDEBUG */
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* WIN32 */
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+
+static void
+NotifyOnUpdate(interp, sinkPtr, data, nBytes)
+ Tcl_Interp *interp;
+ Sink *sinkPtr;
+ unsigned char *data;
+ int nBytes;
+{
+ char save;
+
+#if WINDEBUG_0
+ PurifyPrintf("read %s", data);
+#endif
+ if (data[0] == '\0') {
+ return;
+ }
+ save = data[nBytes];
+ data[nBytes] = '\0';
+ if (sinkPtr->echo) {
+ Tcl_Channel channel;
+
+ channel = Tcl_GetStdChannel(TCL_STDERR);
+ if (channel == NULL) {
+ Tcl_AppendResult(interp, "can't get stderr channel", (char *)NULL);
+ Tcl_BackgroundError(interp);
+ sinkPtr->echo = FALSE;
+ } else {
+ Tcl_Write(channel, data, nBytes);
+ if (save == '\n') {
+ Tcl_Write(channel, "\n", 1);
+ }
+ Tcl_Flush(channel);
+ }
+ }
+ if (sinkPtr->updateCmd != NULL) {
+ Tcl_DString dString;
+ int result;
+ register char **p;
+
+ Tcl_DStringInit(&dString);
+ for (p = sinkPtr->updateCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, data);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ if (sinkPtr->updateVar != NULL) {
+ int flags;
+ char *result;
+
+ flags = (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
+ result = Tcl_SetVar(interp, sinkPtr->updateVar, data, flags);
+ if (result == NULL) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ data[nBytes] = save;
+}
+
+#else
+
+static void
+NotifyOnUpdate(interp, sinkPtr, data, nBytes)
+ Tcl_Interp *interp;
+ Sink *sinkPtr;
+ unsigned char *data;
+ int nBytes;
+{
+ Tcl_Obj *objPtr;
+
+#if WINDEBUG_0
+ PurifyPrintf("read %s", data);
+#endif
+ if ((nBytes == 0) || (data[0] == '\0')) {
+ return;
+ }
+ if (sinkPtr->echo) {
+ Tcl_Channel channel;
+
+ channel = Tcl_GetStdChannel(TCL_STDERR);
+ if (channel == NULL) {
+ Tcl_AppendResult(interp, "can't get stderr channel", (char *)NULL);
+ Tcl_BackgroundError(interp);
+ sinkPtr->echo = FALSE;
+ } else {
+ if (data[nBytes] == '\n') {
+ objPtr = Tcl_NewByteArrayObj(data, nBytes + 1);
+ } else {
+ objPtr = Tcl_NewByteArrayObj(data, nBytes);
+ }
+ Tcl_WriteObj(channel, objPtr);
+ Tcl_Flush(channel);
+ }
+ }
+
+ objPtr = Tcl_NewByteArrayObj(data, nBytes);
+ Tcl_IncrRefCount(objPtr);
+ if (sinkPtr->objv != NULL) {
+ int result;
+
+ sinkPtr->objv[sinkPtr->objc - 1] = objPtr;
+ result = Tcl_EvalObjv(interp, sinkPtr->objc, sinkPtr->objv, 0);
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ if (sinkPtr->updateVar != NULL) {
+ Tcl_Obj *result;
+
+ result = Tcl_SetVar2Ex(interp, sinkPtr->updateVar, NULL, objPtr,
+ (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
+ if (result == NULL) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ Tcl_DecrRefCount(objPtr);
+}
+
+#endif /* < 8.1.0 */
+
+static int
+CollectData(bgPtr, sinkPtr)
+ BackgroundInfo *bgPtr;
+ Sink *sinkPtr;
+{
+ if ((bgPtr->detached) && (sinkPtr->doneVar == NULL)) {
+ ResetSink(sinkPtr);
+ }
+ ReadBytes(sinkPtr);
+ CookSink(bgPtr->interp, sinkPtr);
+ if ((sinkPtr->mark > sinkPtr->lastMark) &&
+ (sinkPtr->flags & SINK_NOTIFY)) {
+ unsigned char *data;
+ int length;
+
+ if (sinkPtr->flags & SINK_BUFFERED) {
+ /* For line-by-line updates, call NotifyOnUpdate for each
+ * new complete line. */
+ while ((data = NextLine(sinkPtr, &length)) != NULL) {
+ NotifyOnUpdate(bgPtr->interp, sinkPtr, data, length);
+ }
+ } else {
+ data = NextBlock(sinkPtr, &length);
+ NotifyOnUpdate(bgPtr->interp, sinkPtr, data, length);
+ }
+ }
+ if (sinkPtr->status >= 0) {
+ return TCL_OK;
+ }
+ if (sinkPtr->status == READ_ERROR) {
+ Tcl_AppendResult(bgPtr->interp, "can't read data from ", sinkPtr->name,
+ ": ", Tcl_PosixError(bgPtr->interp), (char *)NULL);
+ Tcl_BackgroundError(bgPtr->interp);
+ return TCL_ERROR;
+ }
+#if WINDEBUG
+ PurifyPrintf("CollectData %s: done\n", sinkPtr->name);
+#endif
+ return TCL_RETURN;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateSinkHandler --
+ *
+ * Creates a file handler for the given sink. The file
+ * descriptor is also set for non-blocking I/O.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The memory allocated to the BackgroundInfo structure released.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateSinkHandler(bgPtr, sinkPtr, proc)
+ BackgroundInfo *bgPtr;
+ Sink *sinkPtr;
+ Tcl_FileProc *proc;
+{
+#ifndef WIN32
+ int flags;
+
+ flags = fcntl(sinkPtr->fd, F_GETFL);
+#ifdef O_NONBLOCK
+ flags |= O_NONBLOCK;
+#else
+ flags |= O_NDELAY;
+#endif
+ if (fcntl(sinkPtr->fd, F_SETFL, flags) < 0) {
+ Tcl_AppendResult(bgPtr->interp, "can't set file descriptor ",
+ Blt_Itoa(sinkPtr->fd), " to non-blocking:",
+ Tcl_PosixError(bgPtr->interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+#endif /* WIN32 */
+#ifdef FILEHANDLER_USES_TCLFILES
+ sinkPtr->file = Tcl_GetFile((ClientData)sinkPtr->fd, TCL_UNIX_FD);
+ Tcl_CreateFileHandler(sinkPtr->file, TCL_READABLE, proc, bgPtr);
+#else
+ Tcl_CreateFileHandler(sinkPtr->fd, TCL_READABLE, proc, bgPtr);
+#endif /* FILEHANDLER_USES_TCLFILES */
+ return TCL_OK;
+}
+
+static void
+DisableTriggers(bgPtr)
+ BackgroundInfo *bgPtr; /* Background info record. */
+{
+
+ if (bgPtr->traced) {
+ Tcl_UntraceVar(bgPtr->interp, bgPtr->statVar, TRACE_FLAGS,
+ VariableProc, bgPtr);
+ bgPtr->traced = FALSE;
+ }
+ if (IsOpenSink(&(bgPtr->sink1))) {
+ CloseSink(bgPtr->interp, &(bgPtr->sink1));
+ }
+ if (IsOpenSink(&(bgPtr->sink2))) {
+ CloseSink(bgPtr->interp, &(bgPtr->sink2));
+ }
+ if (bgPtr->timerToken != (Tcl_TimerToken) 0) {
+ Tcl_DeleteTimerHandler(bgPtr->timerToken);
+ bgPtr->timerToken = 0;
+ }
+ if (bgPtr->donePtr != NULL) {
+ *bgPtr->donePtr = TRUE;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeBackgroundInfo --
+ *
+ * Releases the memory allocated for the backgrounded process.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeBackgroundInfo(bgPtr)
+ BackgroundInfo *bgPtr;
+{
+ Blt_FreeSwitches(switchSpecs, (char *)bgPtr, 0);
+ if (bgPtr->statVar != NULL) {
+ Blt_Free(bgPtr->statVar);
+ }
+ if (bgPtr->procArr != NULL) {
+ Blt_Free(bgPtr->procArr);
+ }
+ Blt_Free(bgPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyBackgroundInfo --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure (BackgroundInfo) at a safe
+ * time (when no one is using it anymore).
+ *
+ * Results:
+ * None.b
+ *
+ * Side effects:
+ * The memory allocated to the BackgroundInfo structure released.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+DestroyBackgroundInfo(bgPtr)
+ BackgroundInfo *bgPtr; /* Background info record. */
+{
+ DisableTriggers(bgPtr);
+ FreeSinkBuffer(&(bgPtr->sink2));
+ FreeSinkBuffer(&(bgPtr->sink1));
+ if (bgPtr->procArr != NULL) {
+ register int i;
+
+ for (i = 0; i < bgPtr->nProcs; i++) {
+ if (bgPtr->signalNum > 0) {
+ kill(bgPtr->procArr[i], bgPtr->signalNum);
+ }
+#ifdef WIN32
+ Tcl_DetachPids(1, (Tcl_Pid *)&(bgPtr->procArr[i].pid));
+#else
+#if (TCL_MAJOR_VERSION == 7)
+ Tcl_DetachPids(1, &(bgPtr->procArr[i]));
+#else
+ Tcl_DetachPids(1, (Tcl_Pid *)bgPtr->procArr[i]);
+#endif /* TCL_MAJOR_VERSION == 7 */
+#endif /* WIN32 */
+ }
+ }
+ FreeBackgroundInfo(bgPtr);
+ Tcl_ReapDetachedProcs();
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VariableProc --
+ *
+ * Kills all currently running subprocesses (given the specified
+ * signal). This procedure is called when the user sets the status
+ * variable associated with this group of child subprocesses.
+ *
+ * Results:
+ * Always returns NULL. Only called from a variable trace.
+ *
+ * Side effects:
+ * The subprocesses are signaled for termination using the
+ * specified kill signal. Additionally, any resources allocated
+ * to track the subprocesses is released.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static char *
+VariableProc(clientData, interp, part1, part2, flags)
+ ClientData clientData; /* File output information. */
+ Tcl_Interp *interp;
+ char *part1, *part2; /* Not Used. */
+ int flags;
+{
+ if (flags & TRACE_FLAGS) {
+ BackgroundInfo *bgPtr = clientData;
+
+ /* Kill all child processes that remain alive. */
+ if ((bgPtr->procArr != NULL) && (bgPtr->signalNum > 0)) {
+ register int i;
+
+ for (i = 0; i < bgPtr->nProcs; i++) {
+ kill(bgPtr->procArr[i], bgPtr->signalNum);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TimerProc --
+ *
+ * This is a timer handler procedure which gets called
+ * periodically to reap any of the sub-processes if they have
+ * terminated. After the last process has terminated, the
+ * contents of standard output are stored
+ * in the output variable, which triggers the cleanup proc (using
+ * a variable trace). The status the last process to exit is
+ * written to the status variable.
+ *
+ * Results:
+ * None. Called from the Tcl event loop.
+ *
+ * Side effects:
+ * Many. The contents of procArr is shifted, leaving only those
+ * sub-processes which have not yet terminated. If there are
+ * still subprocesses left, this procedure is placed in the timer
+ * queue again. Otherwise the output and possibly the status
+ * variables are updated. The former triggers the cleanup
+ * routine which will destroy the information and resources
+ * associated with these background processes.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TimerProc(clientData)
+ ClientData clientData;
+{
+ BackgroundInfo *bgPtr = clientData;
+ register int i;
+ unsigned int lastPid;
+ int pid;
+ enum PROCESS_STATUS {
+ PROCESS_EXITED, PROCESS_STOPPED, PROCESS_KILLED, PROCESS_UNKNOWN
+ } pcode;
+ WAIT_STATUS_TYPE waitStatus, lastStatus;
+ int nLeft; /* Number of processes still not reaped */
+ char string[200];
+ Tcl_DString dString;
+ int code;
+ char *result;
+
+ lastPid = (unsigned int)-1;
+ *((int *)&waitStatus) = 0;
+ *((int *)&lastStatus) = 0;
+
+ nLeft = 0;
+ for (i = 0; i < bgPtr->nProcs; i++) {
+#ifdef WIN32
+ pid = WaitProcess(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG);
+#else
+ pid = waitpid(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG);
+#endif
+ if (pid == 0) { /* Process has not terminated yet */
+ if (nLeft < i) {
+ bgPtr->procArr[nLeft] = bgPtr->procArr[i];
+ }
+ nLeft++; /* Count the number of processes left */
+ } else if (pid != -1) {
+ /*
+ * Save the status information associated with the subprocess.
+ * We'll use it only if this is the last subprocess to be reaped.
+ */
+ lastStatus = waitStatus;
+ lastPid = (unsigned int)pid;
+ }
+ }
+ bgPtr->nProcs = nLeft;
+
+ if ((nLeft > 0) ||
+ (IsOpenSink(&(bgPtr->sink1))) ||
+ (IsOpenSink(&(bgPtr->sink2)))) {
+ /* Keep polling for the status of the children that are left */
+ bgPtr->timerToken = Tcl_CreateTimerHandler(bgPtr->interval, TimerProc,
+ bgPtr);
+#if WINDEBUG
+ PurifyPrintf("schedule TimerProc(nProcs=%d)\n", nLeft);
+#endif
+ return;
+ }
+
+ /*
+ * All child processes have completed. Set the status variable
+ * with the status of the last process reaped. The status is a
+ * list of an error token, the exit status, and a message.
+ */
+
+ code = WEXITSTATUS(lastStatus);
+ Tcl_DStringInit(&dString);
+ if (WIFEXITED(lastStatus)) {
+ Tcl_DStringAppendElement(&dString, "EXITED");
+ pcode = PROCESS_EXITED;
+ } else if (WIFSIGNALED(lastStatus)) {
+ Tcl_DStringAppendElement(&dString, "KILLED");
+ pcode = PROCESS_KILLED;
+ code = -1;
+ } else if (WIFSTOPPED(lastStatus)) {
+ Tcl_DStringAppendElement(&dString, "STOPPED");
+ pcode = PROCESS_STOPPED;
+ code = -1;
+ } else {
+ Tcl_DStringAppendElement(&dString, "UNKNOWN");
+ pcode = PROCESS_UNKNOWN;
+ }
+#ifdef WIN32
+ sprintf(string, "%u", lastPid);
+ Tcl_DStringAppendElement(&dString, string);
+#else
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(lastPid));
+#endif
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(code));
+ switch(pcode) {
+ case PROCESS_EXITED:
+ Tcl_DStringAppendElement(&dString, "child completed normally");
+ break;
+ case PROCESS_KILLED:
+ Tcl_DStringAppendElement(&dString,
+ Tcl_SignalMsg((int)(WTERMSIG(lastStatus))));
+ break;
+ case PROCESS_STOPPED:
+ Tcl_DStringAppendElement(&dString,
+ Tcl_SignalMsg((int)(WSTOPSIG(lastStatus))));
+ break;
+ case PROCESS_UNKNOWN:
+ sprintf(string, "child completed with unknown status 0x%x",
+ *((int *)&lastStatus));
+ Tcl_DStringAppendElement(&dString, string);
+ break;
+ }
+ if (bgPtr->exitCodePtr != NULL) {
+ *bgPtr->exitCodePtr = code;
+ }
+ DisableTriggers(bgPtr);
+ result = Tcl_SetVar(bgPtr->interp, bgPtr->statVar,
+ Tcl_DStringValue(&dString), TCL_GLOBAL_ONLY);
+ Tcl_DStringFree(&dString);
+ if (result == NULL) {
+ Tcl_BackgroundError(bgPtr->interp);
+ }
+ if (bgPtr->detached) {
+ DestroyBackgroundInfo(bgPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Stdoutproc --
+ *
+ * This procedure is called when output from the detached command
+ * is available. The output is read and saved in a buffer in the
+ * BackgroundInfo structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Data is stored in the buffer. This character array may
+ * be increased as more space is required to contain the output
+ * of the command.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+StdoutProc(clientData, mask)
+ ClientData clientData; /* File output information. */
+ int mask; /* Not used. */
+{
+ BackgroundInfo *bgPtr = clientData;
+
+ if (CollectData(bgPtr, &(bgPtr->sink1)) == TCL_OK) {
+ return;
+ }
+ /*
+ * Either EOF or an error has occurred. In either case, close the
+ * sink. Note that closing the sink will also remove the file
+ * handler, so this routine will not be called again.
+ */
+ CloseSink(bgPtr->interp, &(bgPtr->sink1));
+
+ /*
+ * If both sinks (stdout and stderr) are closed, this doesn't
+ * necessarily mean that the process has terminated. Set up a
+ * timer handler to periodically poll for the exit status of each
+ * process. Initially check at the next idle interval.
+ */
+ if (!IsOpenSink(&(bgPtr->sink2))) {
+ bgPtr->timerToken = Tcl_CreateTimerHandler(0, TimerProc, clientData);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StderrProc --
+ *
+ * This procedure is called when error from the detached command
+ * is available. The error is read and saved in a buffer in the
+ * BackgroundInfo structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Data is stored in the buffer. This character array may
+ * be increased as more space is required to contain the stderr
+ * of the command.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+StderrProc(clientData, mask)
+ ClientData clientData; /* File output information. */
+ int mask; /* Not used. */
+{
+ BackgroundInfo *bgPtr = clientData;
+
+ if (CollectData(bgPtr, &(bgPtr->sink2)) == TCL_OK) {
+ return;
+ }
+ /*
+ * Either EOF or an error has occurred. In either case, close the
+ * sink. Note that closing the sink will also remove the file
+ * handler, so this routine will not be called again.
+ */
+ CloseSink(bgPtr->interp, &(bgPtr->sink2));
+
+ /*
+ * If both sinks (stdout and stderr) are closed, this doesn't
+ * necessarily mean that the process has terminated. Set up a
+ * timer handler to periodically poll for the exit status of each
+ * process. Initially check at the next idle interval.
+ */
+ if (!IsOpenSink(&(bgPtr->sink1))) {
+ bgPtr->timerToken = Tcl_CreateTimerHandler(0, TimerProc, clientData);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BgexecCmd --
+ *
+ * This procedure is invoked to process the "bgexec" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+BgexecCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int *outFdPtr, *errFdPtr;
+ int nProcs;
+ Process *pidPtr;
+ char *lastArg;
+ BackgroundInfo *bgPtr;
+ int i;
+ int detached;
+ Tcl_Encoding encoding;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " varName ?options? command ?arg...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /* Check if the command line is to be run detached (the last
+ * argument is "&") */
+ lastArg = argv[argc - 1];
+ detached = ((lastArg[0] == '&') && (lastArg[1] == '\0'));
+ if (detached) {
+ argc--;
+ argv[argc] = NULL; /* Remove the '&' argument */
+ }
+ bgPtr = Blt_Calloc(1, sizeof(BackgroundInfo));
+ assert(bgPtr);
+
+ /* Initialize the background information record */
+ bgPtr->interp = interp;
+ bgPtr->signalNum = SIGKILL;
+ bgPtr->nProcs = -1;
+ bgPtr->interval = 1000;
+ bgPtr->detached = detached;
+ bgPtr->keepNewline = FALSE;
+ bgPtr->statVar = Blt_Strdup(argv[1]);
+
+ /* Try to clean up any detached processes */
+ Tcl_ReapDetachedProcs();
+
+ i = Blt_ProcessSwitches(interp, switchSpecs, argc - 2, argv + 2,
+ (char *)bgPtr, BLT_SWITCH_ARGV_PARTIAL);
+ if (i < 0) {
+ FreeBackgroundInfo(bgPtr);
+ return TCL_ERROR;
+ }
+ i += 2;
+ /* Must be at least one argument left as the command to execute. */
+ if (argc <= i) {
+ Tcl_AppendResult(interp, "missing command to execute: should be \"",
+ argv[0], " varName ?options? command ?arg...?\"", (char *)NULL);
+ FreeBackgroundInfo(bgPtr);
+ return TCL_ERROR;
+ }
+
+ /* Put a trace on the exit status variable. The will also allow
+ * the user to prematurely terminate the pipeline by simply
+ * setting it. */
+ Tcl_TraceVar(interp, bgPtr->statVar, TRACE_FLAGS, VariableProc, bgPtr);
+ bgPtr->traced = TRUE;
+
+ encoding = ENCODING_ASCII;
+ if (bgPtr->outputEncodingName != NULL) {
+ if (strcmp(bgPtr->outputEncodingName, "binary") == 0) {
+ encoding = ENCODING_BINARY;
+ } else {
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ encoding = Tcl_GetEncoding(interp, bgPtr->outputEncodingName);
+ if (encoding == NULL) {
+ goto error;
+ }
+#endif
+ }
+ }
+ InitSink(bgPtr, &(bgPtr->sink1), "stdout", encoding);
+ if (bgPtr->errorEncodingName != NULL) {
+ if (strcmp(bgPtr->errorEncodingName, "binary") == 0) {
+ encoding = ENCODING_BINARY;
+ } else {
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ encoding = Tcl_GetEncoding(interp, bgPtr->errorEncodingName);
+ if (encoding == NULL) {
+ goto error;
+ }
+#endif
+ }
+ }
+ InitSink(bgPtr, &(bgPtr->sink2), "stderr", encoding);
+
+ outFdPtr = errFdPtr = (int *)NULL;
+#ifdef WIN32
+ if ((!bgPtr->detached) ||
+ (bgPtr->sink1.doneVar != NULL) ||
+ (bgPtr->sink1.updateVar != NULL) ||
+ (bgPtr->sink1.updateCmd != NULL)) {
+ outFdPtr = &(bgPtr->sink1.fd);
+ }
+#else
+ outFdPtr = &(bgPtr->sink1.fd);
+#endif
+ if ((bgPtr->sink2.doneVar != NULL) ||
+ (bgPtr->sink2.updateVar != NULL) ||
+ (bgPtr->sink2.updateCmd != NULL) ||
+ (bgPtr->sink2.echo)) {
+ errFdPtr = &(bgPtr->sink2.fd);
+ }
+ nProcs = Blt_CreatePipeline(interp, argc - i, argv + i, &pidPtr,
+ (int *)NULL, outFdPtr, errFdPtr);
+ if (nProcs < 0) {
+ goto error;
+ }
+ bgPtr->procArr = pidPtr;
+ bgPtr->nProcs = nProcs;
+
+ if (bgPtr->sink1.fd == -1) {
+
+ /* If output has been redirected, start polling immediately
+ * for the exit status of each process. Normally, this is
+ * done only after stdout has been closed by the last process,
+ * but here stdout has been redirected. The default polling
+ * interval is every 1 second. */
+
+ bgPtr->timerToken = Tcl_CreateTimerHandler(bgPtr->interval, TimerProc,
+ bgPtr);
+
+ } else if (CreateSinkHandler(bgPtr, &(bgPtr->sink1), StdoutProc)
+ != TCL_OK) {
+ goto error;
+ }
+ if ((bgPtr->sink2.fd != -1) &&
+ (CreateSinkHandler(bgPtr, &(bgPtr->sink2), StderrProc) != TCL_OK)) {
+ goto error;
+ }
+ if (bgPtr->detached) {
+ char string[200];
+
+ /* If detached, return a list of the child process ids instead
+ * of the output of the command. */
+ for (i = 0; i < nProcs; i++) {
+#ifdef WIN32
+ sprintf(string, "%u", bgPtr->procArr[i].pid);
+#else
+ sprintf(string, "%d", bgPtr->procArr[i]);
+#endif
+ Tcl_AppendElement(interp, string);
+ }
+ } else {
+ int exitCode;
+ int done;
+
+ bgPtr->exitCodePtr = &exitCode;
+ bgPtr->donePtr = &done;
+
+ exitCode = done = 0;
+ while (!done) {
+ Tcl_DoOneEvent(0);
+ }
+ DisableTriggers(bgPtr);
+ if ((exitCode == 0) && (bgPtr->sink1.doneVar == NULL)) {
+ unsigned char *data;
+ size_t length;
+
+ /* Return the output of the command */
+ GetSinkData(&(bgPtr->sink1), &data, &length);
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+ data[length] = '\0';
+ Tcl_SetResult(interp, data, TCL_VOLATILE);
+#else
+ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(data, length));
+#endif
+ }
+ /* Clean up resources used. */
+ DestroyBackgroundInfo(bgPtr);
+ if (exitCode != 0) {
+ Tcl_AppendResult(interp, "child process exited abnormally",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+ error:
+ DisableTriggers(bgPtr);
+ DestroyBackgroundInfo(bgPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_BgexecInit --
+ *
+ * This procedure is invoked to initialize the "bgexec" Tcl
+ * command. See the user documentation for details on what it
+ * does.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_BgexecInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec = {"bgexec", BgexecCmd, };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+#endif /* NO_BGEXEC */
diff --git a/blt/src/bltBind.c b/blt/src/bltBind.c
new file mode 100644
index 00000000000..57047974b5c
--- /dev/null
+++ b/blt/src/bltBind.c
@@ -0,0 +1,644 @@
+/*
+ * bltBind.c --
+ *
+ * This module implements object binding procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#include "bltBind.h"
+
+#if defined(__STDC__)
+static Tk_EventProc BindProc;
+#endif
+
+/*
+ * Binding table procedures.
+ */
+#define REPICK_IN_PROGRESS (1<<0)
+#define LEFT_GRABBED_ITEM (1<<1)
+
+#define ALL_BUTTONS_MASK \
+ (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)
+
+#ifndef VirtualEventMask
+#define VirtualEventMask (1L << 30)
+#endif
+
+#define ALL_VALID_EVENTS_MASK \
+ (ButtonMotionMask | Button1MotionMask | Button2MotionMask | \
+ Button3MotionMask | Button4MotionMask | Button5MotionMask | \
+ ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
+ LeaveWindowMask | KeyPressMask | KeyReleaseMask | \
+ PointerMotionMask | VirtualEventMask)
+
+static int buttonMasks[] =
+{
+ 0, /* No buttons pressed */
+ Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
+};
+
+/*
+ * How to make drag&drop work?
+ *
+ * Right now we generate pseudo <Enter> <Leave> events within
+ * button grab on an object. They're marked NotifyVirtual instead
+ * of NotifyAncestor. A better solution: generate new-style
+ * virtual <<DragEnter>> <<DragMotion>> <<DragLeave>> events.
+ * These virtual events don't have to exist as "real" event
+ * sequences, like virtual events do now.
+ */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoEvent --
+ *
+ * This procedure is called to invoke binding processing
+ * for a new event that is associated with the current item
+ * for a legend.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the bindings for the legend. A binding script
+ * could delete an entry, so callers should protect themselves
+ * with Tcl_Preserve and Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+DoEvent(bindPtr, eventPtr, item)
+ struct Blt_BindTableStruct *bindPtr; /* Binding information for widget in
+ * which event occurred. */
+ XEvent *eventPtr; /* Real or simulated X event that
+ * is to be processed. */
+ ClientData item;
+{
+
+ if (bindPtr->bindingTable == NULL) {
+ return;
+ }
+ if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
+ item = bindPtr->focusItem;
+ }
+ if (item == NULL) {
+ return;
+ }
+ /*
+ * Invoke the binding system.
+ */
+ if (bindPtr->tkwin != NULL) {
+ Blt_List list;
+ ClientData *arrayPtr;
+ int nTags;
+ ClientData tags[32];
+ register Blt_ListNode node;
+
+ list = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ if (bindPtr->tagProc == NULL) {
+ Blt_ListAppend(list, Tk_GetUid("all"), 0);
+ Blt_ListAppend(list, (char *)item, 0);
+ } else {
+ (*bindPtr->tagProc) (bindPtr, item, list);
+ }
+ nTags = Blt_ListGetLength(list);
+ arrayPtr = tags;
+ if (nTags >= 32) {
+ arrayPtr = Blt_Malloc(sizeof(ClientData) * nTags);
+
+ }
+ nTags = 0;
+ for (node = Blt_ListFirstNode(list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ arrayPtr[nTags++] = Blt_ListGetKey(node);
+ }
+ Tk_BindEvent(bindPtr->bindingTable, eventPtr, bindPtr->tkwin, nTags,
+ arrayPtr);
+ if (nTags >= 32) {
+ Blt_Free(arrayPtr);
+ }
+ Blt_ListDestroy(list);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PickCurrentItem --
+ *
+ * Find the topmost item in a legend that contains a given
+ * location and mark the the current item. If the current
+ * item has changed, generate a fake exit event on the old
+ * current item and a fake enter event on the new current
+ * item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The current item may change. If it does, then the commands
+ * associated with item entry and exit could do just about
+ * anything. A binding script could delete the legend, so
+ * callers should protect themselves with Tcl_Preserve and
+ * Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+PickCurrentItem(bindPtr, eventPtr)
+ struct Blt_BindTableStruct *bindPtr; /* Binding table information. */
+ XEvent *eventPtr; /* Event describing location of
+ * mouse cursor. Must be EnterWindow,
+ * LeaveWindow, ButtonRelease, or
+ * MotionNotify. */
+{
+ int buttonDown;
+ ClientData newItem;
+
+ /*
+ * Check whether or not a button is down. If so, we'll log entry
+ * and exit into and out of the current item, but not entry into
+ * any other item. This implements a form of grabbing equivalent
+ * to what the X server does for windows.
+ */
+ buttonDown = (bindPtr->state & ALL_BUTTONS_MASK);
+ if (!buttonDown) {
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ }
+ /*
+ * Save information about this event in the widget. The event in
+ * the widget is used for two purposes:
+ *
+ * 1. Event bindings: if the current item changes, fake events are
+ * generated to allow item-enter and item-leave bindings to trigger.
+ * 2. Reselection: if the current item gets deleted, can use the
+ * saved event to find a new current item.
+ * Translate MotionNotify events into EnterNotify events, since that's
+ * what gets reported to item handlers.
+ */
+
+ if (eventPtr != &bindPtr->pickEvent) {
+ if ((eventPtr->type == MotionNotify) ||
+ (eventPtr->type == ButtonRelease)) {
+ bindPtr->pickEvent.xcrossing.type = EnterNotify;
+ bindPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
+ bindPtr->pickEvent.xcrossing.send_event =
+ eventPtr->xmotion.send_event;
+ bindPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
+ bindPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
+ bindPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
+ bindPtr->pickEvent.xcrossing.subwindow = None;
+ bindPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
+ bindPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
+ bindPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
+ bindPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
+ bindPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
+ bindPtr->pickEvent.xcrossing.mode = NotifyNormal;
+ bindPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
+ bindPtr->pickEvent.xcrossing.same_screen
+ = eventPtr->xmotion.same_screen;
+ bindPtr->pickEvent.xcrossing.focus = False;
+ bindPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
+ } else {
+ bindPtr->pickEvent = *eventPtr;
+ }
+ }
+ bindPtr->activePick = TRUE;
+
+ /*
+ * If this is a recursive call (there's already a partially completed
+ * call pending on the stack; it's in the middle of processing a
+ * Leave event handler for the old current item) then just return;
+ * the pending call will do everything that's needed.
+ */
+ if (bindPtr->flags & REPICK_IN_PROGRESS) {
+ return;
+ }
+ /*
+ * A LeaveNotify event automatically means that there's no current
+ * item, so the check for closest item can be skipped.
+ */
+ if (bindPtr->pickEvent.type != LeaveNotify) {
+ int x, y;
+
+ x = bindPtr->pickEvent.xcrossing.x;
+ y = bindPtr->pickEvent.xcrossing.y;
+ newItem = (*bindPtr->pickProc) (bindPtr->clientData, x, y);
+ } else {
+ newItem = NULL;
+ }
+ if ((newItem == bindPtr->currentItem) &&
+ !(bindPtr->flags & LEFT_GRABBED_ITEM)) {
+ /*
+ * Nothing to do: the current item hasn't changed.
+ */
+ return;
+ }
+#ifndef FULLY_SIMULATE_GRAB
+ if ((newItem != bindPtr->currentItem) && (buttonDown)) {
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+ return;
+ }
+#endif
+ /*
+ * Simulate a LeaveNotify event on the previous current item and
+ * an EnterNotify event on the new current item. Remove the "current"
+ * tag from the previous current item and place it on the new current
+ * item.
+ */
+ if ((newItem != bindPtr->currentItem) && (bindPtr->currentItem != NULL) &&
+ !(bindPtr->flags & LEFT_GRABBED_ITEM)) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = LeaveNotify;
+ /*
+ * If the event's detail happens to be NotifyInferior the
+ * binding mechanism will discard the event. To be consistent,
+ * always use NotifyAncestor.
+ */
+ event.xcrossing.detail = NotifyAncestor;
+
+ bindPtr->flags |= REPICK_IN_PROGRESS;
+ DoEvent(bindPtr, &event, bindPtr->currentItem);
+ bindPtr->flags &= ~REPICK_IN_PROGRESS;
+
+ /*
+ * Note: during DoEvent above, it's possible that
+ * bindPtr->newItem got reset to NULL because the
+ * item was deleted.
+ */
+ }
+ if ((newItem != bindPtr->currentItem) && (buttonDown)) {
+ XEvent event;
+
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+ event = bindPtr->pickEvent;
+ if (newItem != bindPtr->newItem) {
+ ClientData saved;
+
+ /*
+ * Generate <Enter> and <Leave> events for objects during
+ * button grabs. This isn't standard. But for example, it
+ * allows one to provide balloon help on the individual
+ * entries of the Hierbox widget.
+ */
+ saved = bindPtr->currentItem;
+ if (bindPtr->newItem != NULL) {
+ event.type = LeaveNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = bindPtr->newItem;
+ DoEvent(bindPtr, &event, bindPtr->newItem);
+ }
+ bindPtr->newItem = newItem;
+ if (newItem != NULL) {
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = newItem;
+ DoEvent(bindPtr, &event, newItem);
+ }
+ bindPtr->currentItem = saved;
+ }
+ return;
+ }
+ /*
+ * Special note: it's possible that
+ * bindPtr->newItem == bindPtr->currentItem
+ * here. This can happen, for example, if LEFT_GRABBED_ITEM was set.
+ */
+
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ bindPtr->currentItem = bindPtr->newItem = newItem;
+ if (bindPtr->currentItem != NULL) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyAncestor;
+ DoEvent(bindPtr, &event, newItem);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BindProc --
+ *
+ * This procedure is invoked by the Tk dispatcher to handle
+ * events associated with bindings on items.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command invoked as part of the binding
+ * (if there was any).
+ *
+ *--------------------------------------------------------------
+ */
+static void
+BindProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to widget structure. */
+ XEvent *eventPtr; /* Pointer to X event that just
+ * happened. */
+{
+ struct Blt_BindTableStruct *bindPtr = (struct Blt_BindTableStruct *)clientData;
+ int mask;
+
+ Tcl_Preserve(bindPtr->clientData);
+
+ /*
+ * This code below keeps track of the current modifier state in
+ * bindPtr->state. This information is used to defer repicks of
+ * the current item while buttons are down.
+ */
+ switch (eventPtr->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ mask = 0;
+ if ((eventPtr->xbutton.button >= Button1) &&
+ (eventPtr->xbutton.button <= Button5)) {
+ mask = buttonMasks[eventPtr->xbutton.button];
+ }
+ /*
+ * For button press events, repick the current item using the
+ * button state before the event, then process the event. For
+ * button release events, first process the event, then repick
+ * the current item using the button state *after* the event
+ * (the button has logically gone up before we change the
+ * current item).
+ */
+
+ if (eventPtr->type == ButtonPress) {
+
+ /*
+ * On a button press, first repick the current item using
+ * the button state before the event, the process the event.
+ */
+
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ bindPtr->state ^= mask;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+
+ } else {
+
+ /*
+ * Button release: first process the event, with the button
+ * still considered to be down. Then repick the current
+ * item under the assumption that the button is no longer down.
+ */
+ bindPtr->state = eventPtr->xbutton.state;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ eventPtr->xbutton.state ^= mask;
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ eventPtr->xbutton.state ^= mask;
+ }
+ break;
+
+ case EnterNotify:
+ case LeaveNotify:
+ bindPtr->state = eventPtr->xcrossing.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ break;
+
+ case MotionNotify:
+ bindPtr->state = eventPtr->xmotion.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ bindPtr->state = eventPtr->xkey.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ break;
+ }
+ Tcl_Release(bindPtr->clientData);
+}
+
+int
+Blt_ConfigureBindings(interp, bindPtr, item, argc, argv)
+ Tcl_Interp *interp;
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData item;
+ int argc;
+ char **argv;
+{
+ char *command;
+ unsigned long mask;
+ char *seq;
+
+ if (argc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ if (argc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, argv[0]);
+ if (command == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, command, TCL_VOLATILE);
+ return TCL_OK;
+ }
+
+ seq = argv[0];
+ command = argv[1];
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+#if (TCL_MAJOR_VERSION >= 8)
+
+int
+Blt_ConfigureBindingsFromObj(interp, bindPtr, item, objc, objv)
+ Tcl_Interp *interp;
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData item;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ char *command;
+ unsigned long mask;
+ char *seq;
+ char *string;
+
+ if (objc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ string = Tcl_GetString(objv[0]);
+ if (objc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, string);
+ if (command == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid binding event \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, command, TCL_VOLATILE);
+ return TCL_OK;
+ }
+
+ seq = string;
+ command = Tcl_GetString(objv[1]);
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+#endif
+
+Blt_BindTable
+Blt_CreateBindingTable(interp, tkwin, clientData, pickProc, tagProc)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ ClientData clientData;
+ Blt_BindPickProc *pickProc;
+ Blt_BindTagProc *tagProc;
+{
+ unsigned int mask;
+ struct Blt_BindTableStruct *bindPtr;
+
+ bindPtr = Blt_Calloc(1, sizeof(struct Blt_BindTableStruct));
+ assert(bindPtr);
+ bindPtr->clientData = clientData;
+ bindPtr->pickProc = pickProc;
+ bindPtr->tagProc = tagProc;
+ bindPtr->tkwin = tkwin;
+ bindPtr->bindingTable = Tk_CreateBindingTable(interp);
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ return bindPtr;
+}
+
+void
+Blt_DestroyBindingTable(bindPtr)
+ struct Blt_BindTableStruct *bindPtr;
+{
+ unsigned int mask;
+
+ Tk_DeleteBindingTable(bindPtr->bindingTable);
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ Blt_Free(bindPtr);
+}
+
+void
+Blt_PickCurrentItem(bindPtr)
+ struct Blt_BindTableStruct *bindPtr;
+{
+ if (bindPtr->activePick) {
+ PickCurrentItem(bindPtr, &(bindPtr->pickEvent));
+ }
+}
+
+void
+Blt_DeleteBindings(bindPtr, object)
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData object;
+{
+ Tk_DeleteAllBindings(bindPtr->bindingTable, object);
+
+ /*
+ * If this is the object currently picked, we need to repick one.
+ */
+ if (bindPtr->currentItem == object) {
+ bindPtr->currentItem = NULL;
+ }
+ if (bindPtr->newItem == object) {
+ bindPtr->newItem = NULL;
+ }
+ if (bindPtr->focusItem == object) {
+ bindPtr->focusItem = NULL;
+ }
+}
+
+void
+Blt_MoveBindingTable(bindPtr, tkwin)
+ struct Blt_BindTableStruct *bindPtr;
+ Tk_Window tkwin;
+{
+ unsigned int mask;
+
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ if (bindPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ }
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ bindPtr->tkwin = tkwin;
+}
diff --git a/blt/src/bltBind.h b/blt/src/bltBind.h
new file mode 100644
index 00000000000..25282c4b830
--- /dev/null
+++ b/blt/src/bltBind.h
@@ -0,0 +1,114 @@
+/*
+ * bltBind.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_BIND_H
+#define _BLT_BIND_H
+
+#include <bltList.h>
+
+typedef struct Blt_BindTableStruct *Blt_BindTable;
+
+typedef ClientData (Blt_BindPickProc) _ANSI_ARGS_((ClientData clientData,
+ int x, int y));
+
+typedef void (Blt_BindTagProc) _ANSI_ARGS_((Blt_BindTable bindTable,
+ ClientData object, Blt_List list));
+
+/*
+ * Binding structure information:
+ */
+
+struct Blt_BindTableStruct {
+ unsigned int flags;
+ Tk_BindingTable bindingTable;
+ /* Table of all bindings currently defined.
+ * NULL means that no bindings exist, so the
+ * table hasn't been created. Each "object"
+ * used for this table is either a Tk_Uid for
+ * a tag or the address of an item named by
+ * id. */
+
+ ClientData currentItem; /* The item currently containing the mouse
+ * pointer, or NULL if none. */
+
+ ClientData newItem; /* The item that is about to become the
+ * current one, or NULL. This field is
+ * used to detect deletions of the new
+ * current item pointer that occur during
+ * Leave processing of the previous current
+ * tab. */
+
+ ClientData focusItem;
+
+ XEvent pickEvent; /* The event upon which the current choice
+ * of the current tab is based. Must be saved
+ * so that if the current item is deleted,
+ * we can pick another. */
+ int activePick; /* The pick event has been initialized so
+ * that we can repick it */
+
+ int state; /* Last known modifier state. Used to
+ * defer picking a new current object
+ * while buttons are down. */
+
+ ClientData clientData;
+ Tk_Window tkwin;
+ Blt_BindPickProc *pickProc; /* Routine to report the item the mouse is
+ * currently over. */
+ Blt_BindTagProc *tagProc; /* Routine to report tags picked items. */
+};
+
+EXTERN void Blt_DestroyBindingTable _ANSI_ARGS_((Blt_BindTable table));
+
+EXTERN Blt_BindTable Blt_CreateBindingTable _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, ClientData clientData, Blt_BindPickProc *pickProc,
+ Blt_BindTagProc *tagProc));
+
+EXTERN int Blt_ConfigureBindings _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_BindTable table, ClientData item, int argc, char **argv));
+
+#if (TCL_MAJOR_VERSION >= 8)
+EXTERN int Blt_ConfigureBindingsFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_BindTable table, ClientData item, int objc, Tcl_Obj *CONST *objv));
+#endif
+
+EXTERN void Blt_PickCurrentItem _ANSI_ARGS_((Blt_BindTable table));
+
+EXTERN void Blt_DeleteBindings _ANSI_ARGS_((Blt_BindTable table,
+ ClientData object));
+
+EXTERN void Blt_MoveBindingTable _ANSI_ARGS_((Blt_BindTable table,
+ Tk_Window tkwin));
+
+#define Blt_SetFocusItem(bindPtr, object) \
+ ((bindPtr)->focusItem = (ClientData)(object))
+#define Blt_SetCurrentItem(bindPtr, object) \
+ ((bindPtr)->currentItem = (ClientData)(object))
+
+#define Blt_GetCurrentItem(bindPtr) ((bindPtr)->currentItem)
+#define Blt_GetLatestItem(bindPtr) ((bindPtr)->newItem)
+
+#define Blt_GetBindingData(bindPtr) ((bindPtr)->clientData)
+
+#endif /*_BLT_BIND_H*/
diff --git a/blt/src/bltBitmap.c b/blt/src/bltBitmap.c
new file mode 100644
index 00000000000..12e39a5f74c
--- /dev/null
+++ b/blt/src/bltBitmap.c
@@ -0,0 +1,1504 @@
+
+/*
+ * bltBitmap.c --
+ *
+ * This module implements Tcl bitmaps for the Tk toolkit.
+ *
+ * Much of the code is taken from XRdBitF.c and XWrBitF.c
+ * from the MIT X11R5 distribution.
+ *
+ * Copyright, 1987, Massachusetts Institute of Technology Permission
+ * to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of M.I.T. not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "bitmap" command created by George Howlett. */
+
+/*
+ Predefined table holds bitmap info (source width, height)
+ Name table holds bitmap names
+ Id table hold bitmap ids
+ Both id and name tables get you the actual bitmap.
+ */
+#include "bltInt.h"
+
+#ifndef NO_BITMAP
+#include "bltHash.h"
+#include <X11/Xutil.h>
+
+#define BITMAP_THREAD_KEY "BLT Bitmap Data"
+
+/*
+ * BitmapInterpData --
+ *
+ * Tk's routine to create a bitmap, Tk_DefineBitmap, assumes that
+ * the source (bit array) is always statically allocated. This
+ * isn't true here (we dynamically allocate the arrays), so we have
+ * to save them in a hashtable and cleanup after the interpreter
+ * is deleted.
+ */
+typedef struct {
+ Blt_HashTable bitmapTable; /* Hash table of bitmap data keyed by
+ * the name of the bitmap. */
+ Tcl_Interp *interp;
+ Display *display; /* Display of interpreter. */
+ Tk_Window tkwin; /* Main window of interpreter. */
+} BitmapInterpData;
+
+#define MAX_SIZE 255
+
+/*
+ * BitmapInfo --
+ */
+typedef struct {
+ double rotate; /* Rotation of text string */
+ double scale; /* Scaling factor */
+ Tk_Font font; /* Font pointer */
+ Tk_Justify justify; /* Justify text */
+ Blt_Pad padX, padY; /* Padding around the text */
+} BitmapInfo;
+
+/*
+ * BitmapData --
+ */
+typedef struct {
+ int width, height; /* Dimension of image */
+ unsigned char *bits; /* Data array for bitmap image */
+ int arraySize; /* Number of bytes in data array */
+} BitmapData;
+
+#define DEF_BITMAP_FONT STD_FONT
+#define DEF_BITMAP_PAD "4"
+#define DEF_BITMAP_ROTATE "0.0"
+#define DEF_BITMAP_SCALE "1.0"
+#define DEF_BITMAP_JUSTIFY "center"
+
+extern Tk_CustomOption bltPadOption;
+
+static Tk_ConfigSpec composeConfigSpecs[] =
+{
+ {TK_CONFIG_FONT, "-font", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_FONT, Tk_Offset(BitmapInfo, font), 0},
+ {TK_CONFIG_JUSTIFY, "-justify", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_JUSTIFY, Tk_Offset(BitmapInfo, justify),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_PAD, Tk_Offset(BitmapInfo, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_PAD, Tk_Offset(BitmapInfo, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_DOUBLE, "-rotate", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_ROTATE, Tk_Offset(BitmapInfo, rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-scale", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_SCALE, Tk_Offset(BitmapInfo, scale),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static Tk_ConfigSpec defineConfigSpecs[] =
+{
+ {TK_CONFIG_DOUBLE, "-rotate", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_ROTATE, Tk_Offset(BitmapInfo, rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-scale", (char *)NULL, (char *)NULL,
+ DEF_BITMAP_SCALE, Tk_Offset(BitmapInfo, scale),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/* Shared data for the image read/parse logic */
+static char hexTable[256]; /* conversion value */
+static int initialized = 0; /* easier to fill in at run time */
+
+#define blt_width 40
+#define blt_height 40
+static unsigned char blt_bits[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x04,
+ 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0xe4, 0x33, 0x3f,
+ 0x01, 0x00, 0x64, 0x36, 0x0c, 0x01, 0x00, 0x64, 0x36, 0x8c, 0x00, 0x00,
+ 0xe4, 0x33, 0x8c, 0x00, 0x00, 0x64, 0x36, 0x8c, 0x00, 0x00, 0x64, 0x36,
+ 0x0c, 0x01, 0x00, 0xe4, 0xf3, 0x0d, 0x01, 0x00, 0x04, 0x00, 0x00, 0x02,
+ 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xf8, 0xff,
+ 0x03, 0x80, 0xed, 0x07, 0x00, 0x04, 0xe0, 0x0c, 0x00, 0x20, 0x09, 0x10,
+ 0x0c, 0x00, 0x00, 0x12, 0x10, 0x0c, 0x00, 0x00, 0x10, 0x30, 0x00, 0x00,
+ 0x00, 0x19, 0xd0, 0x03, 0x00, 0x00, 0x14, 0xb0, 0xfe, 0xff, 0xff, 0x1b,
+ 0x50, 0x55, 0x55, 0x55, 0x0d, 0xe8, 0xaa, 0xaa, 0xaa, 0x16, 0xe4, 0xff,
+ 0xff, 0xff, 0x2f, 0xf4, 0xff, 0xff, 0xff, 0x27, 0xd8, 0xae, 0xaa, 0xbd,
+ 0x2d, 0x6c, 0x5f, 0xd5, 0x67, 0x1b, 0xbc, 0xf3, 0x7f, 0xd0, 0x36, 0xf8,
+ 0x01, 0x10, 0xcc, 0x1f, 0xe0, 0x45, 0x8e, 0x92, 0x0f, 0xb0, 0x32, 0x41,
+ 0x43, 0x0b, 0xd0, 0xcf, 0x3c, 0x7c, 0x0d, 0xb0, 0xaa, 0xc2, 0xab, 0x0a,
+ 0x60, 0x55, 0x55, 0x55, 0x05, 0xc0, 0xff, 0xab, 0xaa, 0x03, 0x00, 0x00,
+ 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define bigblt_width 64
+#define bigblt_height 64
+static unsigned char bigblt_bits[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0xe2, 0x0f, 0xc7, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x1f,
+ 0xc7, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00,
+ 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x38,
+ 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x1f, 0x07, 0x1c, 0x04, 0x00,
+ 0x00, 0x00, 0xe2, 0x1f, 0x07, 0x1c, 0x04, 0x00, 0x00, 0x00, 0xe2, 0x38,
+ 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00,
+ 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x1f,
+ 0xff, 0x1c, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x0f, 0xff, 0x1c, 0x10, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x00, 0xe0, 0xf6, 0x3f, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1c, 0x06, 0x00,
+ 0x00, 0x00, 0xc0, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0xc0, 0x08, 0x03,
+ 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x40, 0x04, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x06, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x06, 0x40, 0x55, 0xff, 0xff,
+ 0xff, 0xff, 0x7f, 0x05, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x06,
+ 0x80, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x03, 0x40, 0xab, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0x01, 0x70, 0x57, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x04,
+ 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0b, 0xd8, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x14, 0xd0, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13,
+ 0xf0, 0xda, 0xbf, 0xaa, 0xba, 0xfd, 0xd6, 0x0b, 0x70, 0xed, 0x77, 0x55,
+ 0x57, 0xe5, 0xad, 0x07, 0xb8, 0xf7, 0xab, 0xaa, 0xaa, 0xd2, 0x5b, 0x0f,
+ 0xf8, 0xfb, 0x54, 0x55, 0x75, 0x94, 0xf7, 0x1e, 0xf0, 0x7b, 0xfa, 0xff,
+ 0x9f, 0xa9, 0xef, 0x1f, 0xc0, 0xbf, 0x00, 0x20, 0x40, 0x54, 0xfe, 0x0f,
+ 0x00, 0x1f, 0x92, 0x00, 0x04, 0xa9, 0xfc, 0x01, 0xc0, 0x5f, 0x41, 0xf9,
+ 0x04, 0x21, 0xfd, 0x00, 0xc0, 0x9b, 0x28, 0x04, 0xd8, 0x0a, 0x9a, 0x03,
+ 0x40, 0x5d, 0x08, 0x40, 0x44, 0x44, 0x62, 0x03, 0xc0, 0xaa, 0x67, 0xe2,
+ 0x03, 0x64, 0xba, 0x02, 0x40, 0x55, 0xd5, 0x55, 0xfd, 0xdb, 0x55, 0x03,
+ 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x01, 0x00, 0x57, 0x55, 0x55,
+ 0x55, 0x55, 0xd5, 0x00, 0x00, 0xac, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00,
+ 0x00, 0xf0, 0xff, 0x57, 0x55, 0x55, 0x1d, 0x00, 0x00, 0x00, 0x00, 0xf8,
+ 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#ifdef __STDC__
+static Tcl_CmdProc BitmapCmd;
+static Tcl_InterpDeleteProc BitmapInterpDeleteProc;
+#endif
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetHexValue --
+ *
+ * Converts the hexadecimal string into an unsigned integer
+ * value. The hexadecimal string need not have a leading "0x".
+ *
+ * Results:
+ * Returns a standard TCL result. If the conversion was
+ * successful, TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side Effects:
+ * If the conversion fails, interp->result is filled with an
+ * error message.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+GetHexValue(interp, string, valuePtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *valuePtr;
+{
+ register int c;
+ register char *s;
+ register int value;
+
+ s = string;
+ if ((s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) {
+ s += 2;
+ }
+ if (s[0] == '\0') {
+ Tcl_AppendResult(interp, "expecting hex value: got \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Only found "0x" */
+ }
+ value = 0;
+ for ( /*empty*/ ; *s != '\0'; s++) {
+ /* Trim high bits, check type and accumulate */
+ c = *s & 0xff;
+ if (!isxdigit(c)) {
+ Tcl_AppendResult(interp, "expecting hex value: got \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR; /* Not a hexadecimal number */
+ }
+ value = (value << 4) + hexTable[c];
+ }
+ *valuePtr = value;
+ return TCL_OK;
+}
+
+#ifdef WIN32
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapToData --
+ *
+ * Converts a bitmap into an data array.
+ *
+ * Results:
+ * Returns the number of bytes in an data array representing the bitmap.
+ *
+ * Side Effects:
+ * Memory is allocated for the data array. Caller must free
+ * array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+BitmapToData(
+ Tk_Window tkwin, /* Main window of interpreter */
+ Pixmap bitmap, /* Bitmap to be queried */
+ int width, int height, /* Dimensions of the bitmap */
+ unsigned char **bitsPtr) /* Pointer to converted array of data */
+{
+ int value, bitMask;
+ unsigned long pixel;
+ register int x, y;
+ int count;
+ int arraySize, bytes_per_line;
+ unsigned char *bits;
+ unsigned char *srcPtr, *srcBits;
+ int bytesPerRow;
+
+ *bitsPtr = NULL;
+ srcBits = Blt_GetBitmapData(Tk_Display(tkwin), bitmap, width, height,
+ &bytesPerRow);
+ if (srcBits == NULL) {
+ OutputDebugString("BitmapToData: Can't get bitmap data");
+ return 0;
+ }
+ bytes_per_line = (width + 7) / 8;
+ arraySize = height * bytes_per_line;
+ bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+ assert(bits);
+ count = 0;
+ for (y = height - 1; y >= 0; y--) {
+ srcPtr = srcBits + (bytesPerRow * y);
+ value = 0, bitMask = 1;
+ for (x = 0; x < width; /* empty */ ) {
+ pixel = (*srcPtr & (0x80 >> (x % 8)));
+ if (pixel) {
+ value |= bitMask;
+ }
+ bitMask <<= 1;
+ x++;
+ if (!(x & 7)) {
+ bits[count++] = (unsigned char)value;
+ value = 0, bitMask = 1;
+ srcPtr++;
+ }
+ }
+ if (x & 7) {
+ bits[count++] = (unsigned char)value;
+ }
+ }
+ *bitsPtr = bits;
+ return count;
+}
+
+#else
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapToData --
+ *
+ * Converts a bitmap into an data array.
+ *
+ * Results:
+ * Returns the number of bytes in an data array representing the bitmap.
+ *
+ * Side Effects:
+ * Memory is allocated for the data array. Caller must free
+ * array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+BitmapToData(tkwin, bitmap, width, height, bitsPtr)
+ Tk_Window tkwin; /* Main window of interpreter */
+ Pixmap bitmap; /* Bitmap to be queried */
+ int width, height; /* Dimensions of the bitmap */
+ unsigned char **bitsPtr; /* Pointer to converted array of data */
+{
+ int value, bitMask;
+ unsigned long pixel;
+ register int x, y;
+ int count;
+ int arraySize, bytes_per_line;
+ Display *display;
+ XImage *imagePtr;
+ unsigned char *bits;
+
+ display = Tk_Display(tkwin);
+ /* Convert the bitmap to an image */
+ imagePtr = XGetImage(display, bitmap, 0, 0, width, height, 1L, XYPixmap);
+ /*
+ * The slow but robust brute force method of converting an image:
+ */
+ bytes_per_line = (width + 7) / 8;
+ arraySize = height * bytes_per_line;
+ bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+ assert(bits);
+ count = 0;
+ for (y = 0; y < height; y++) {
+ value = 0, bitMask = 1;
+ for (x = 0; x < width; /*empty*/ ) {
+ pixel = XGetPixel(imagePtr, x, y);
+ if (pixel) {
+ value |= bitMask;
+ }
+ bitMask <<= 1;
+ x++;
+ if (!(x & 7)) {
+ bits[count++] = (unsigned char)value;
+ value = 0, bitMask = 1;
+ }
+ }
+ if (x & 7) {
+ bits[count++] = (unsigned char)value;
+ }
+ }
+ XDestroyImage(imagePtr);
+ *bitsPtr = bits;
+ return count;
+}
+
+#endif
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AsciiToData --
+ *
+ * Converts a Tcl list of ASCII values into a data array.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * If an error occurs while processing the data, interp->result
+ * is filled with a corresponding error message.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AsciiToData(interp, elemList, width, height, bitsPtr)
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ char *elemList; /* List of of hex numbers representing
+ * bitmap data */
+ int width, height; /* Height and width */
+ unsigned char **bitsPtr; /* data array (output) */
+{
+ int arraySize; /* Number of bytes of data */
+ int value; /* from an input line */
+ int padding; /* to handle alignment */
+ int bytesPerLine; /* per scanline of data */
+ unsigned char *bits;
+ register int count;
+ enum Formats {
+ V10, V11
+ } format;
+ register int i; /* */
+ char **valueArr;
+ int nValues;
+
+ /* First time through initialize the ascii->hex translation table */
+ if (!initialized) {
+ Blt_InitHexTable(hexTable);
+ initialized = 1;
+ }
+ if (Tcl_SplitList(interp, elemList, &nValues, &valueArr) != TCL_OK) {
+ return -1;
+ }
+ bytesPerLine = (width + 7) / 8;
+ arraySize = bytesPerLine * height;
+ if (nValues == arraySize) {
+ format = V11;
+ } else if (nValues == (arraySize / 2)) {
+ format = V10;
+ } else {
+ Tcl_AppendResult(interp, "bitmap has wrong # of data values",
+ (char *)NULL);
+ goto error;
+ }
+ padding = 0;
+ if (format == V10) {
+ padding = ((width % 16) && ((width % 16) < 9));
+ if (padding) {
+ bytesPerLine = (width + 7) / 8 + padding;
+ arraySize = bytesPerLine * height;
+ }
+ }
+ bits = Blt_Calloc(sizeof(unsigned char), arraySize);
+ if (bits == NULL) {
+ Tcl_AppendResult(interp, "can't allocate memory for bitmap",
+ (char *)NULL);
+ goto error;
+ }
+ count = 0;
+ for (i = 0; i < nValues; i++) {
+ if (GetHexValue(interp, valueArr[i], &value) != TCL_OK) {
+ Blt_Free(bits);
+ goto error;
+ }
+ bits[count++] = (unsigned char)value;
+ if (format == V10) {
+ if ((!padding) || (((i * 2) + 2) % bytesPerLine)) {
+ bits[count++] = value >> 8;
+ }
+ }
+ }
+ Blt_Free(valueArr);
+ *bitsPtr = bits;
+ return count;
+ error:
+ Blt_Free(valueArr);
+ return -1;
+}
+
+
+static int
+ParseListData(interp, string, widthPtr, heightPtr, bitsPtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *widthPtr;
+ int *heightPtr;
+ unsigned char **bitsPtr;
+{
+ register char *p;
+ char **elemArr;
+ int nElem;
+ int width, height;
+ int result;
+ int arraySize;
+
+ arraySize = -1;
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return -1;
+ }
+ if (nElem == 2) {
+ char **dimArr;
+ int nDim;
+
+ if (Tcl_SplitList(interp, elemArr[0], &nDim, &dimArr) != TCL_OK) {
+ goto error;
+ }
+ if (nDim != 2) {
+ Tcl_AppendResult(interp, "wrong # of bitmap dimensions: ",
+ "should be \"width height\"", (char *)NULL);
+ result = TCL_ERROR;
+ } else {
+ result = ((Tcl_GetInt(interp, dimArr[0], &width) == TCL_OK) &&
+ (Tcl_GetInt(interp, dimArr[1], &height) == TCL_OK));
+ }
+ Blt_Free(dimArr);
+ if (!result) {
+ goto error;
+ }
+ string = elemArr[1];
+ } else if (nElem == 3) {
+ if ((Tcl_GetInt(interp, elemArr[0], &width) != TCL_OK) ||
+ (Tcl_GetInt(interp, elemArr[1], &height) != TCL_OK)) {
+ goto error;
+ }
+ string = elemArr[2];
+ } else {
+ Tcl_AppendResult(interp, "wrong # of bitmap data components: ",
+ "should be \"dimensions sourceData\"", (char *)NULL);
+ goto error;
+ }
+ if ((width < 1) || (height < 1)) {
+ Tcl_AppendResult(interp, "bad bitmap dimensions", (char *)NULL);
+ goto error;
+ }
+ /* Convert commas to blank spaces */
+
+ for (p = string; *p != '\0'; p++) {
+ if (*p == ',') {
+ *p = ' ';
+ }
+ }
+ arraySize = AsciiToData(interp, string, width, height, bitsPtr);
+ *widthPtr = width;
+ *heightPtr = height;
+ error:
+ Blt_Free(elemArr);
+ return arraySize;
+}
+
+/*
+ * Parse the lines that define the dimensions of the bitmap,
+ * plus the first line that defines the bitmap data (it declares
+ * the name of a data variable but doesn't include any actual
+ * data). These lines look something like the following:
+ *
+ * #define foo_width 16
+ * #define foo_height 16
+ * #define foo_x_hot 3
+ * #define foo_y_hot 3
+ * static char foo_bits[] = {
+ *
+ * The x_hot and y_hot lines may or may not be present. It's
+ * important to check for "char" in the last line, in order to
+ * reject old X10-style bitmaps that used shorts.
+ */
+
+static int
+ParseStructData(interp, string, widthPtr, heightPtr, bitsPtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *widthPtr;
+ int *heightPtr;
+ unsigned char **bitsPtr;
+{
+ int width, height;
+ int hotX, hotY;
+ char *line, *nextline;
+ register char *p;
+ Tcl_RegExp re;
+ char *name, *value, *data;
+ int len;
+ int arraySize;
+
+ width = height = 0;
+ hotX = hotY = -1;
+ data = NULL;
+ nextline = string;
+ for (line = string; nextline != NULL; line = nextline + 1) {
+ nextline = strchr(line, '\n');
+ if ((nextline == NULL) || (line == nextline)) {
+ continue; /* Empty line */
+ }
+ *nextline = '\0';
+ re = Tcl_RegExpCompile(interp, " *# *define +");
+ if (Tcl_RegExpExec(interp, re, line, line)) {
+ char *start, *end;
+
+ Tcl_RegExpRange(re, 0, &start, &end);
+ name = strtok(end, " \t");
+ value = strtok(NULL, " \t");
+ if ((name == NULL) || (value == NULL)) {
+ return TCL_ERROR;
+ }
+ len = strlen(name);
+ if ((len >= 6) && (name[len-6] == '_') &&
+ (strcmp(name+len-6, "_width") == 0)) {
+ if (Tcl_GetInt(interp, value, &width) != TCL_OK) {
+ return -1;
+ }
+ } else if ((len >= 7) && (name[len-7] == '_') &&
+ (strcmp(name+len-7, "_height") == 0)) {
+ if (Tcl_GetInt(interp, value, &height) != TCL_OK) {
+ return -1;
+ }
+ } else if ((len >= 6) && (name[len-6] == '_') &&
+ (strcmp(name+len-6, "_x_hot") == 0)) {
+ if (Tcl_GetInt(interp, value, &hotX) != TCL_OK) {
+ return -1;
+ }
+ } else if ((len >= 6) && (name[len-6] == '_') &&
+ (strcmp(name+len-6, "_y_hot") == 0)) {
+ if (Tcl_GetInt(interp, value, &hotY) != TCL_OK) {
+ return -1;
+ }
+ }
+ } else {
+ re = Tcl_RegExpCompile(interp, " *static +.*char +");
+ if (Tcl_RegExpExec(interp, re, line, line)) {
+ /* Find the { */
+ /* Repair the string so we can search the entire string. */
+ *nextline = ' ';
+ p = strchr(line, '{');
+ if (p == NULL) {
+ return -1;
+ }
+ data = p + 1;
+ break;
+ } else {
+ Tcl_AppendResult(interp, "unknown bitmap format: ",
+ "obsolete X10 bitmap file?", (char *) NULL);
+ return -1;
+ }
+ }
+ }
+ /*
+ * Now we've read everything but the data. Allocate an array
+ * and read in the data.
+ */
+ if ((width <= 0) || (height <= 0)) {
+ Tcl_AppendResult(interp, "invalid bitmap dimensions \"", (char *)NULL);
+ Tcl_AppendResult(interp, Blt_Itoa(width), " x ", (char *)NULL);
+ Tcl_AppendResult(interp, Blt_Itoa(height), "\"", (char *)NULL);
+ return -1;
+ }
+ *widthPtr = width;
+ *heightPtr = height;
+ for (p = data; *p != '\0'; p++) {
+ if ((*p == ',') || (*p == ';') || (*p == '}')) {
+ *p = ' ';
+ }
+ }
+ arraySize = AsciiToData(interp, data, width, height, bitsPtr);
+ return arraySize;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RotateData --
+ *
+ * Creates a new data array of the rotated image.
+ *
+ * Results:
+ * A standard Tcl result. If the bitmap data is rotated
+ * successfully, TCL_OK is returned. But if memory could not be
+ * allocated for the new data array, TCL_ERROR is returned and an
+ * error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is allocated for rotated data array. Caller must
+ * free array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+RotateData(interp, srcPtr, theta, destPtr)
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ BitmapData *srcPtr;
+ double theta; /* Rotate bitmap this many degrees */
+ BitmapData *destPtr;
+{
+ register int dx, dy, sx, sy;
+ double srcX, srcY, destX, destY; /* Origins of source and destination
+ * bitmaps */
+ double sinTheta, cosTheta;
+ double transX, transY, rotX, rotY;
+ double radians;
+ unsigned char *bits;
+ int arraySize;
+ int pixel, ipixel;
+ int srcBytesPerLine, destBytesPerLine;
+
+ srcBytesPerLine = (srcPtr->width + 7) / 8;
+ Blt_GetBoundingBox(srcPtr->width, srcPtr->height, theta,
+ &(destPtr->width), &(destPtr->height), (Point2D *)NULL);
+
+ destBytesPerLine = (destPtr->width + 7) / 8;
+ arraySize = destPtr->height * destBytesPerLine;
+ bits = Blt_Calloc(arraySize, sizeof(unsigned char));
+ if (bits == NULL) {
+ Tcl_AppendResult(interp, "can't allocate bitmap data array",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPtr->bits = bits;
+ destPtr->arraySize = arraySize;
+
+ radians = (theta / 180.0) * M_PI;
+ sinTheta = sin(radians);
+ cosTheta = cos(radians);
+
+ /*
+ * Coordinates of the centers of the source and destination rectangles
+ */
+ srcX = srcPtr->width * 0.5;
+ srcY = srcPtr->height * 0.5;
+ destX = destPtr->width * 0.5;
+ destY = destPtr->height * 0.5;
+
+ /*
+ * Rotate each pixel of dest image, placing results in source image
+ */
+ for (dx = 0; dx < destPtr->width; dx++) {
+ for (dy = 0; dy < destPtr->height; dy++) {
+ if (theta == 270.0) {
+ sx = dy, sy = destPtr->width - dx - 1;
+ } else if (theta == 180.0) {
+ sx = destPtr->width - dx - 1, sy = destPtr->height - dy - 1;
+ } else if (theta == 90.0) {
+ sx = destPtr->height - dy - 1, sy = dx;
+ } else if (theta == 0.0) {
+ sx = dx, sy = dy;
+ } else {
+ /* Translate origin to center of destination image */
+
+ transX = dx - destX;
+ transY = dy - destY;
+
+ /* Rotate the coordinates about the origin */
+
+ rotX = (transX * cosTheta) - (transY * sinTheta);
+ rotY = (transX * sinTheta) + (transY * cosTheta);
+
+ /* Translate back to the center of the source image */
+ rotX += srcX;
+ rotY += srcY;
+
+ sx = ROUND(rotX);
+ sy = ROUND(rotY);
+
+ /*
+ * Verify the coordinates, since the destination image
+ * can be bigger than the source.
+ */
+
+ if ((sx >= srcPtr->width) || (sx < 0) ||
+ (sy >= srcPtr->height) || (sy < 0)) {
+ continue;
+ }
+ }
+ ipixel = (srcBytesPerLine * sy) + (sx / 8);
+ pixel = srcPtr->bits[ipixel] & (1 << (sx % 8));
+ if (pixel) {
+ ipixel = (destBytesPerLine * dy) + (dx / 8);
+ bits[ipixel] |= (1 << (dx % 8));
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ScaleData --
+ *
+ * Scale the data of the bitmap, creating a new data array of the
+ * scaled bitmap.
+ *
+ * Results:
+ * A standard Tcl result. If the bitmap data is scaled
+ * successfully, TCL_OK is returned. But if memory could not be
+ * allocated for the new data array, TCL_ERROR is returned and an
+ * error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is allocated for scaled data array. Caller must
+ * dispose of this array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+ScaleData(interp, srcPtr, scale, destPtr)
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ BitmapData *srcPtr;
+ double scale; /* Scale bitmap by this factor */
+ BitmapData *destPtr;
+{
+ register int dx, dy, sx, sy;
+ double scaleX, scaleY;
+ unsigned char *bits;
+ int arraySize;
+ int pixel, ipixel;
+ int srcBytesPerLine, destBytesPerLine;
+
+ destPtr->width = (int)((srcPtr->width * scale) + 0.5);
+ destPtr->height = (int)((srcPtr->height * scale) + 0.5);
+ srcBytesPerLine = (srcPtr->width + 7) / 8;
+ destBytesPerLine = (destPtr->width + 7) / 8;
+
+ arraySize = destPtr->height * destBytesPerLine;
+ bits = Blt_Calloc(arraySize, sizeof(unsigned char));
+ if (bits == NULL) {
+ Tcl_AppendResult(interp, "can't allocate bitmap data array",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPtr->bits = bits;
+ destPtr->arraySize = arraySize;
+
+ /*
+ * Scale each pixel of destination image from results of source image
+ */
+ for (dy = 0; dy < destPtr->height; dy++) {
+ scaleY = (dy / scale);
+ sy = ROUND(scaleY);
+ for (dx = 0; dx < destPtr->width; dx++) {
+ scaleX = (dx / scale);
+ sx = ROUND(scaleX);
+
+ /*
+ * Verify the coordinates, since the destination image can be
+ * bigger than the source
+ */
+ if ((sx >= srcPtr->width) || (sx < 0) ||
+ (sy >= srcPtr->height) || (sy < 0)) {
+ continue;
+ }
+ ipixel = (srcBytesPerLine * sy) + (sx / 8);
+ pixel = srcPtr->bits[ipixel] & (1 << (sx % 8));
+ if (pixel) {
+ ipixel = (destBytesPerLine * dy) + (dx / 8);
+ bits[ipixel] |= (1 << (dx % 8));
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapDataToString --
+ *
+ * Returns a list of hex values corresponding to the data
+ * bits of the bitmap given.
+ *
+ * Converts the unsigned character value into a two character
+ * hexadecimal string. A separator is also added, which may
+ * either a newline or space according the the number of bytes
+ * already output.
+ *
+ * Results:
+ * Returns TCL_ERROR if a data array can't be generated
+ * from the bitmap (memory allocation failure), otherwise TCL_OK.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+BitmapDataToString(tkwin, bitmap, resultPtr)
+ Tk_Window tkwin; /* Main window of interpreter */
+ Pixmap bitmap; /* Bitmap to be queried */
+ Tcl_DString *resultPtr; /* Dynamic string to output results to */
+{
+ unsigned char *bits;
+ char *separator;
+ int arraySize;
+ register int i;
+ char string[200];
+ int width, height;
+
+ /* Get the dimensions of the bitmap */
+ Tk_SizeOfBitmap(Tk_Display(tkwin), bitmap, &width, &height);
+ arraySize = BitmapToData(tkwin, bitmap, width, height, &bits);
+#define BYTES_PER_OUTPUT_LINE 24
+ for (i = 0; i < arraySize; i++) {
+ separator = (i % BYTES_PER_OUTPUT_LINE) ? " " : "\n ";
+ sprintf(string, "%s%02x", separator, bits[i]);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if (bits != NULL) {
+ Blt_Free(bits);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ComposeOp --
+ *
+ * Converts the text string into an internal bitmap.
+ *
+ * There's a lot of extra (read unnecessary) work going on here,
+ * but I don't (right now) think that it matters much. The
+ * rotated bitmap (formerly an image) is converted back to an
+ * image just so we can convert it to a data array for
+ * Tk_DefineBitmap.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * If an error occurs while processing the data, interp->result
+ * is filled with a corresponding error message.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+ComposeOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Number of arguments */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ int width, height; /* Dimensions of bitmap */
+ Pixmap bitmap; /* Text bitmap */
+ unsigned char *bits; /* Data array derived from text bitmap */
+ int arraySize;
+ BitmapInfo info; /* Text rotation and font information */
+ int result;
+ double theta;
+ TextStyle ts;
+ TextLayout *textPtr;
+ Tk_Window tkwin; /* Main window of interpreter */
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ tkwin = dataPtr->tkwin;
+ bitmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[2]));
+ Tcl_ResetResult(interp);
+ if (bitmap != None) {
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ return TCL_OK;
+ }
+ /* Initialize info and process flags */
+ info.justify = TK_JUSTIFY_CENTER;
+ info.rotate = 0.0; /* No rotation or scaling by default */
+ info.scale = 1.0;
+ info.padLeft = info.padRight = 0;
+ info.padTop = info.padBottom = 0;
+ info.font = (Tk_Font)NULL; /* Initialized by Tk_ConfigureWidget */
+ if (Tk_ConfigureWidget(interp, tkwin, composeConfigSpecs,
+ argc - 4, argv + 4, (char *)&info, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ theta = FMOD(info.rotate, (double)360.0);
+ if (theta < 0.0) {
+ theta += 360.0;
+ }
+ Blt_InitTextStyle(&ts);
+ ts.font = info.font;
+ ts.theta = 0.0;
+ ts.justify = info.justify;
+ ts.padX = info.padX;
+ ts.padY = info.padY;
+ ts.leader = 0;
+ ts.anchor = TK_ANCHOR_CENTER;
+
+ textPtr = Blt_GetTextLayout(argv[3], &ts);
+ bitmap = Blt_CreateTextBitmap(tkwin, textPtr, &ts, &width, &height);
+ Blt_Free(textPtr);
+ if (bitmap == None) {
+ Tcl_AppendResult(interp, "can't create bitmap", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Free the font structure, since we don't need it anymore */
+ Tk_FreeOptions(composeConfigSpecs, (char *)&info, dataPtr->display, 0);
+
+ /* Convert bitmap back to a data array */
+ arraySize = BitmapToData(tkwin, bitmap, width, height, &bits);
+ Tk_FreePixmap(dataPtr->display, bitmap);
+ if (arraySize == 0) {
+ Tcl_AppendResult(interp, "can't get bitmap data", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* If bitmap is to be rotated or scale, do it here */
+ if (theta != 0.0) {
+ BitmapData srcData, destData;
+
+ srcData.bits = bits;
+ srcData.width = width;
+ srcData.height = height;
+ srcData.arraySize = arraySize;
+
+ result = RotateData(interp, &srcData, theta, &destData);
+ Blt_Free(bits); /* Free the un-rotated data array */
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bits = destData.bits;
+ width = destData.width;
+ height = destData.height;
+ }
+ if (info.scale != 1.0) {
+ BitmapData srcData, destData;
+
+ srcData.bits = bits;
+ srcData.width = width;
+ srcData.height = height;
+ srcData.arraySize = arraySize;
+
+ result = ScaleData(interp, &srcData, info.scale, &destData);
+ Blt_Free(bits); /* Free the un-scaled data array */
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bits = destData.bits;
+ width = destData.width;
+ height = destData.height;
+ }
+ /* Create the bitmap again, this time using Tk's bitmap facilities */
+ result = Tk_DefineBitmap(interp, Tk_GetUid(argv[2]), (char *)bits,
+ width, height);
+ if (result != TCL_OK) {
+ Blt_Free(bits);
+ }
+ hPtr = Blt_CreateHashEntry(&(dataPtr->bitmapTable), argv[2], &isNew);
+ Blt_SetHashValue(hPtr, bits);
+ return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DefineOp --
+ *
+ * Converts the dataList into an internal bitmap.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * If an error occurs while processing the data, interp->result
+ * is filled with a corresponding error message.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+DefineOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Number of arguments */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ int width, height; /* Dimensions of bitmap */
+ unsigned char *bits; /* working variable */
+ register char *p;
+ char *defn; /* Definition of bitmap. */
+ BitmapInfo info; /* Not used. */
+ int arraySize;
+ int result;
+ double theta;
+ Pixmap bitmap;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ Tcl_ResetResult(interp);
+ if (bitmap != None) {
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ return TCL_OK;
+ }
+ /* Initialize info and then process flags */
+ info.rotate = 0.0; /* No rotation by default */
+ info.scale = 1.0; /* No scaling by default */
+ if (Tk_ConfigureWidget(interp, dataPtr->tkwin, defineConfigSpecs,
+ argc - 4, argv + 4, (char *)&info, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Skip leading spaces. */
+ for (p = argv[3]; isspace(UCHAR(*p)); p++) {
+ /*empty*/
+ }
+ defn = Blt_Strdup(p);
+ bits = NULL;
+ if (*p == '#') {
+ arraySize = ParseStructData(interp, defn, &width, &height, &bits);
+ } else {
+ arraySize = ParseListData(interp, defn, &width, &height, &bits);
+ }
+ Blt_Free(defn);
+ if (arraySize <= 0) {
+ return TCL_ERROR;
+ }
+ theta = FMOD(info.rotate, 360.0);
+ if (theta < 0.0) {
+ theta += 360.0;
+ }
+ /* If bitmap is to be rotated or scale, do it here */
+ if (theta != 0.0) {
+ BitmapData srcData, destData;
+
+ srcData.bits = bits;
+ srcData.width = width;
+ srcData.height = height;
+ srcData.arraySize = arraySize;
+
+ result = RotateData(interp, &srcData, theta, &destData);
+ Blt_Free(bits); /* Free the un-rotated data array */
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bits = destData.bits;
+ width = destData.width;
+ height = destData.height;
+ }
+ if (info.scale != 1.0) {
+ BitmapData srcData, destData;
+
+ srcData.bits = bits;
+ srcData.width = width;
+ srcData.height = height;
+ srcData.arraySize = arraySize;
+
+ result = ScaleData(interp, &srcData, info.scale, &destData);
+ Blt_Free(bits); /* Free the un-scaled data array */
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bits = destData.bits;
+ width = destData.width;
+ height = destData.height;
+ }
+ result = Tk_DefineBitmap(interp, Tk_GetUid(argv[2]), (char *)bits,
+ width, height);
+ if (result != TCL_OK) {
+ Blt_Free(bits);
+ }
+ hPtr = Blt_CreateHashEntry(&(dataPtr->bitmapTable), argv[2], &isNew);
+ Blt_SetHashValue(hPtr, bits);
+ return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExistOp --
+ *
+ * Indicates if the named bitmap exists.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Not used. */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ Pixmap bitmap;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ Tcl_ResetResult(interp);
+ if (bitmap != None) {
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ }
+ Blt_SetBooleanResult(interp, (bitmap != None));
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * HeightOp --
+ *
+ * Returns the height of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+HeightOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Not used. */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ int width, height;
+ Pixmap bitmap;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+ Tcl_SetResult(interp, Blt_Itoa(height), TCL_VOLATILE);
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * WidthOp --
+ *
+ * Returns the width of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+WidthOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Not used. */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ int width, height;
+ Pixmap bitmap;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+ Tcl_SetResult(interp, Blt_Itoa(width), TCL_VOLATILE);
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SourceOp --
+ *
+ * Returns the data array (excluding width and height)
+ * of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SourceOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Not used. */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ Pixmap bitmap;
+ Tcl_DString dString;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ BitmapDataToString(dataPtr->tkwin, bitmap, &dString);
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DataOp --
+ *
+ * Returns the data array, including width and height,
+ * of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DataOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Not used. */
+ char **argv; /* Argument list */
+{
+ BitmapInterpData *dataPtr = clientData;
+ Pixmap bitmap;
+ int width, height;
+ Tcl_DString dString;
+
+ bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(width));
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(height));
+ Tcl_DStringStartSublist(&dString);
+ BitmapDataToString(dataPtr->tkwin, bitmap, &dString);
+ Tcl_DStringEndSublist(&dString);
+ Tk_FreeBitmap(dataPtr->display, bitmap);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BLT Sub-command specification:
+ *
+ * - Name of the sub-command.
+ * - Minimum number of characters needed to unambiguously
+ * recognize the sub-command.
+ * - Pointer to the function to be called for the sub-command.
+ * - Minimum number of arguments accepted.
+ * - Maximum number of arguments accepted.
+ * - String to be displayed for usage.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec bitmapOps[] =
+{
+ {"compose", 1, (Blt_Op)ComposeOp, 4, 0, "bitmapName text ?flags?",},
+ {"data", 2, (Blt_Op)DataOp, 3, 3, "bitmapName",},
+ {"define", 2, (Blt_Op)DefineOp, 4, 0, "bitmapName data ?flags?",},
+ {"exists", 1, (Blt_Op)ExistsOp, 3, 3, "bitmapName",},
+ {"height", 1, (Blt_Op)HeightOp, 3, 3, "bitmapName",},
+ {"source", 1, (Blt_Op)SourceOp, 3, 3, "bitmapName",},
+ {"width", 1, (Blt_Op)WidthOp, 3, 3, "bitmapName",},
+};
+static int nBitmapOps = sizeof(bitmapOps) / sizeof(Blt_OpSpec);
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BitmapCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to bitmaps managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BitmapCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data for bitmaps. */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nBitmapOps, bitmapOps, BLT_OP_ARG1, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapInterpDeleteProc --
+ *
+ * This is called when the interpreter is deleted. All the tiles
+ * are specific to that interpreter are destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys the tile table.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BitmapInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+{
+ BitmapInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ unsigned char *bits;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->bitmapTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ bits = (unsigned char *)Blt_GetHashValue(hPtr);
+ Blt_Free(bits);
+ }
+ Blt_DeleteHashTable(&(dataPtr->bitmapTable));
+ Tcl_DeleteAssocData(interp, BITMAP_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+static BitmapInterpData *
+GetBitmapInterpData(interp)
+ Tcl_Interp *interp;
+{
+ BitmapInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (BitmapInterpData *)
+ Tcl_GetAssocData(interp, BITMAP_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(BitmapInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ dataPtr->tkwin = Tk_MainWindow(interp);
+ dataPtr->display = Tk_Display(dataPtr->tkwin);
+ Tcl_SetAssocData(interp, BITMAP_THREAD_KEY, BitmapInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->bitmapTable), BLT_STRING_KEYS);
+ }
+ return dataPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_BitmapInit --
+ *
+ * This procedure is invoked to initialize the bitmap command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Adds the command to the interpreter and sets an array variable
+ * which its version number.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_BitmapInit(interp)
+ Tcl_Interp *interp;
+{
+ BitmapInterpData *dataPtr;
+ static Blt_CmdSpec cmdSpec =
+ {"bitmap", BitmapCmd};
+
+ /* Define the BLT logo bitmaps */
+
+ dataPtr = GetBitmapInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ Tk_DefineBitmap(interp, Tk_GetUid("bigBLT"), (char *)bigblt_bits,
+ bigblt_width, bigblt_height);
+ Tk_DefineBitmap(interp, Tk_GetUid("BLT"), (char *)blt_bits,
+ blt_width, blt_height);
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+#endif /* NO_BITMAP */
diff --git a/blt/src/bltBusy.c b/blt/src/bltBusy.c
new file mode 100644
index 00000000000..be4d5eed284
--- /dev/null
+++ b/blt/src/bltBusy.c
@@ -0,0 +1,1196 @@
+/*
+ * bltBusy.c --
+ *
+ * This module implements busy windows for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "busy" command was created by George Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_BUSY
+#include "bltHash.h"
+
+#define BUSYDEBUG 0
+
+#ifndef TK_REPARENTED
+#define TK_REPARENTED 0
+#endif
+
+#define BUSY_THREAD_KEY "BLT Busy Data"
+
+typedef struct {
+ Display *display; /* Display of busy window */
+ Tcl_Interp *interp; /* Interpreter where "busy" command was
+ * created. It's used to key the
+ * searches in the window hierarchy. See the
+ * "windows" command. */
+
+ Tk_Window tkBusy; /* Busy window: Transparent window used
+ * to block delivery of events to windows
+ * underneath it. */
+
+ Tk_Window tkParent; /* Parent window of the busy
+ * window. It may be the reference
+ * window (if the reference is a
+ * toplevel) or a mutual ancestor of
+ * the reference window */
+
+ Tk_Window tkRef; /* Reference window of the busy window.
+ * It's is used to manage the size and
+ * position of the busy window. */
+
+
+ int x, y; /* Position of the reference window */
+
+ int width, height; /* Size of the reference window. Retained to
+ * know if the reference window has been
+ * reconfigured to a new size. */
+
+ int isBusy; /* Indicates whether the transparent
+ * window should be displayed. This
+ * can be different from what
+ * Tk_IsMapped says because the a
+ * sibling reference window may be
+ * unmapped, forcing the busy window
+ * to be also hidden. */
+
+ int menuBar; /* Menu bar flag. */
+ Tk_Cursor cursor; /* Cursor for the busy window. */
+
+ Blt_HashEntry *hashPtr; /* Used the delete the busy window entry
+ * out of the global hash table. */
+ Blt_HashTable *tablePtr;
+} Busy;
+
+#ifdef WIN32
+#define DEF_BUSY_CURSOR "wait"
+#else
+#define DEF_BUSY_CURSOR "watch"
+#endif
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor",
+ DEF_BUSY_CURSOR, Tk_Offset(Busy, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef struct {
+ Blt_HashTable busyTable; /* Hash table of busy window
+ * structures keyed by the address of
+ * the reference Tk window */
+} BusyInterpData;
+
+static void BusyGeometryProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void BusyCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin));
+
+static Tk_GeomMgr busyMgrInfo =
+{
+ "busy", /* Name of geometry manager used by winfo */
+ BusyGeometryProc, /* Procedure to for new geometry requests */
+ BusyCustodyProc, /* Procedure when window is taken away */
+};
+
+/* Forward declarations */
+static void DestroyBusy _ANSI_ARGS_((DestroyData dataPtr));
+static void BusyEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+
+#ifdef __STDC__
+static Tk_EventProc RefWinEventProc;
+static Tcl_CmdProc BusyCmd;
+static Tcl_InterpDeleteProc BusyInterpDeleteProc;
+#endif
+
+static void
+ShowBusyWindow(busyPtr)
+ Busy *busyPtr;
+{
+ if (busyPtr->tkBusy != NULL) {
+ Tk_MapWindow(busyPtr->tkBusy);
+ /*
+ * Always restack the busy window, just in case new sibling
+ * windows have been created. Can't use Tk_RestackWindow
+ * because it doesn't work under Win32.
+ */
+ XRaiseWindow(Tk_Display(busyPtr->tkBusy),
+ Tk_WindowId(busyPtr->tkBusy));
+ }
+#ifdef WIN32
+ {
+ POINT point;
+ /*
+ * Under Win32, cursors aren't associated with windows. Tk
+ * fakes this by watching Motion events on its windows. So Tk
+ * will automatically change the cursor when the pointer
+ * enters the Busy window. But Windows doesn't immediately
+ * change the cursor; it waits for the cursor position to
+ * change or a system call. We need to change the cursor
+ * before the application starts processing, so set the cursor
+ * position redundantly back to the current position.
+ */
+ GetCursorPos(&point);
+ SetCursorPos(point.x, point.y);
+ }
+#endif /* WIN32 */
+}
+
+static void
+HideBusyWindow(busyPtr)
+ Busy *busyPtr;
+{
+ if (busyPtr->tkBusy != NULL) {
+ Tk_UnmapWindow(busyPtr->tkBusy);
+ }
+#ifdef WIN32
+ {
+ POINT point;
+ /*
+ * Under Win32, cursors aren't associated with windows. Tk
+ * fakes this by watching Motion events on its windows. So Tk
+ * will automatically change the cursor when the pointer
+ * enters the Busy window. But Windows doesn't immediately
+ * change the cursor; it waits for the cursor position to
+ * change or a system call. We need to change the cursor
+ * before the application starts processing, so set the cursor
+ * position redundantly back to the current position.
+ */
+ GetCursorPos(&point);
+ SetCursorPos(point.x, point.y);
+ }
+#endif /* WIN32 */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BusyEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for events on
+ * the busy window itself. We're only concerned with destroy
+ * events.
+ *
+ * It might be necessary (someday) to watch resize events. Right
+ * now, I don't think there's any point in it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When a busy window is destroyed, all internal structures
+ * associated with it released at the next idle point.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+BusyEventProc(clientData, eventPtr)
+ ClientData clientData; /* Busy window record */
+ XEvent *eventPtr; /* Event which triggered call to routine */
+{
+ Busy *busyPtr = clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ busyPtr->tkBusy = NULL;
+ Tcl_EventuallyFree(busyPtr, DestroyBusy);
+ }
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BusyCustodyProc --
+ *
+ * This procedure is invoked when the busy window has been stolen
+ * by another geometry manager. The information and memory
+ * associated with the busy window is released. I don't know why
+ * anyone would try to pack a busy window, but this should keep
+ * everything sane, if it is.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The Busy structure is freed at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Information about the busy window. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Busy *busyPtr = clientData;
+
+ Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc,
+ busyPtr);
+ HideBusyWindow(busyPtr);
+ busyPtr->tkBusy = NULL;
+ Tcl_EventuallyFree(busyPtr, DestroyBusy);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BusyGeometryProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for busy
+ * windows. Busy windows never request geometry, so it's
+ * unlikely that this routine will ever be called. The routine
+ * exists simply as a place holder for the GeomProc in the
+ * Geometry Manager structure.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Information about window that got new
+ * preferred geometry. */
+ Tk_Window tkwin; /* Other Tk-related information about the
+ * window. */
+{
+ /* Should never get here */
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * RefWinEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for the
+ * following events on the reference window. If the reference and
+ * parent windows are the same, only the first event is
+ * important.
+ *
+ * 1) ConfigureNotify - The reference window has been resized or
+ * moved. Move and resize the busy window
+ * to be the same size and position of the
+ * reference window.
+ *
+ * 2) DestroyNotify - The reference window was destroyed. Destroy
+ * the busy window and the free resources
+ * used.
+ *
+ * 3) MapNotify - The reference window was (re)shown. Map the
+ * busy window again.
+ *
+ * 4) UnmapNotify - The reference window was hidden. Unmap the
+ * busy window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the reference window gets deleted, internal structures get
+ * cleaned up. When it gets resized, the busy window is resized
+ * accordingly. If it's displayed, the busy window is displayed. And
+ * when it's hidden, the busy window is unmapped.
+ *
+ * -------------------------------------------------------------------
+ */
+static void
+RefWinEventProc(clientData, eventPtr)
+ ClientData clientData; /* Busy window record */
+ register XEvent *eventPtr; /* Event which triggered call to routine */
+{
+ register Busy *busyPtr = clientData;
+
+ switch (eventPtr->type) {
+ case ReparentNotify:
+ case DestroyNotify:
+
+ /*
+ * Arrange for the busy structure to be removed at a proper time.
+ */
+
+ Tcl_EventuallyFree(busyPtr, DestroyBusy);
+ break;
+
+ case ConfigureNotify:
+ if ((busyPtr->width != Tk_Width(busyPtr->tkRef)) ||
+ (busyPtr->height != Tk_Height(busyPtr->tkRef)) ||
+ (busyPtr->x != Tk_X(busyPtr->tkRef)) ||
+ (busyPtr->y != Tk_Y(busyPtr->tkRef))) {
+ int x, y;
+
+ busyPtr->width = Tk_Width(busyPtr->tkRef);
+ busyPtr->height = Tk_Height(busyPtr->tkRef);
+ busyPtr->x = Tk_X(busyPtr->tkRef);
+ busyPtr->y = Tk_Y(busyPtr->tkRef);
+
+ x = y = 0;
+
+ if (busyPtr->tkParent != busyPtr->tkRef) {
+ Tk_Window tkwin;
+
+ for (tkwin = busyPtr->tkRef; (tkwin != NULL) &&
+ (!Tk_IsTopLevel(tkwin)); tkwin = Tk_Parent(tkwin)) {
+ if (tkwin == busyPtr->tkParent) {
+ break;
+ }
+ x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+ y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+ }
+ }
+#if BUSYDEBUG
+ PurifyPrintf("menubar2: width=%d, height=%d\n",
+ busyPtr->width, busyPtr->height);
+#endif
+ if (busyPtr->tkBusy != NULL) {
+#if BUSYDEBUG
+ fprintf(stderr, "busy window %s is at %d,%d %dx%d\n",
+ Tk_PathName(busyPtr->tkBusy),
+ x, y, busyPtr->width, busyPtr->height);
+#endif
+ Tk_MoveResizeWindow(busyPtr->tkBusy, x, y, busyPtr->width,
+ busyPtr->height);
+ if (busyPtr->isBusy) {
+ ShowBusyWindow(busyPtr);
+ }
+ }
+ }
+ break;
+
+ case MapNotify:
+ if ((busyPtr->tkParent != busyPtr->tkRef) && (busyPtr->isBusy)) {
+ ShowBusyWindow(busyPtr);
+ }
+ break;
+
+ case UnmapNotify:
+ if (busyPtr->tkParent != busyPtr->tkRef) {
+ HideBusyWindow(busyPtr);
+ }
+ break;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ConfigureBusy --
+ *
+ * This procedure is called from the Tk event dispatcher. It
+ * releases X resources and memory used by the busy window and
+ * updates the internal hash table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and resources are released and the Tk event handler
+ * is removed.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ConfigureBusy(interp, busyPtr, argc, argv)
+ Tcl_Interp *interp;
+ Busy *busyPtr;
+ int argc;
+ char **argv;
+{
+ Tk_Cursor oldCursor;
+
+ oldCursor = busyPtr->cursor;
+ if (Tk_ConfigureWidget(interp, busyPtr->tkRef, configSpecs, argc, argv,
+ (char *)busyPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (busyPtr->cursor != oldCursor) {
+ if (busyPtr->cursor == None) {
+ Tk_UndefineCursor(busyPtr->tkBusy);
+ } else {
+ Tk_DefineCursor(busyPtr->tkBusy, busyPtr->cursor);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * CreateBusy --
+ *
+ * Creates a child transparent window that obscures its parent
+ * window thereby effectively blocking device events. The size
+ * and position of the busy window is exactly that of the reference
+ * window.
+ *
+ * We want to create sibling to the window to be blocked. If the
+ * busy window is a child of the window to be blocked, Enter/Leave
+ * events can sneak through. Futhermore under WIN32, messages of
+ * transparent windows are sent directly to the parent. The only
+ * exception to this are toplevels, since we can't make a sibling.
+ * Fortunately, toplevel windows rarely receive events that need
+ * blocking.
+ *
+ * Results:
+ * Returns a pointer to the new busy window structure.
+ *
+ * Side effects:
+ * When the busy window is eventually displayed, it will screen
+ * device events (in the area of the reference window) from reaching
+ * its parent window and its children. User feed back can be
+ * achieved by changing the cursor.
+ *
+ * -------------------------------------------------------------------
+ */
+static Busy *
+CreateBusy(interp, tkRef)
+ Tcl_Interp *interp; /* Interpreter to report error to */
+ Tk_Window tkRef; /* Window hosting the busy window */
+{
+ Busy *busyPtr;
+ int length;
+ char *fmt, *name;
+ Tk_Window tkBusy;
+ Window parent;
+ Tk_Window tkChild, tkParent;
+ Tk_FakeWin *winPtr;
+ int x, y;
+
+ busyPtr = Blt_Calloc(1, sizeof(Busy));
+ assert(busyPtr);
+ x = y = 0;
+ length = strlen(Tk_Name(tkRef));
+ name = Blt_Malloc(length + 6);
+ if (Tk_IsTopLevel(tkRef)) {
+ fmt = "_Busy"; /* Child */
+ tkParent = tkRef;
+ } else {
+ Tk_Window tkwin;
+
+ fmt = "%s_Busy"; /* Sibling */
+ tkParent = Tk_Parent(tkRef);
+ for (tkwin = tkRef; (tkwin != NULL) && (!Tk_IsTopLevel(tkwin));
+ tkwin = Tk_Parent(tkwin)) {
+ if (tkwin == tkParent) {
+ break;
+ }
+ x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+ y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+ }
+ }
+ for (tkChild = Blt_FirstChild(tkParent); tkChild != NULL;
+ tkChild = Blt_NextChild(tkChild)) {
+ Tk_MakeWindowExist(tkChild);
+ }
+ sprintf(name, fmt, Tk_Name(tkRef));
+ tkBusy = Tk_CreateWindow(interp, tkParent, name, (char *)NULL);
+ Blt_Free(name);
+
+ if (tkBusy == NULL) {
+ return NULL;
+ }
+ Tk_MakeWindowExist(tkRef);
+ busyPtr->display = Tk_Display(tkRef);
+ busyPtr->interp = interp;
+ busyPtr->tkRef = tkRef;
+ busyPtr->tkParent = tkParent;
+ busyPtr->tkBusy = tkBusy;
+ busyPtr->width = Tk_Width(tkRef);
+ busyPtr->height = Tk_Height(tkRef);
+ busyPtr->x = Tk_X(tkRef);
+ busyPtr->y = Tk_Y(tkRef);
+ busyPtr->cursor = None;
+ busyPtr->isBusy = FALSE;
+ Tk_SetClass(tkBusy, "Busy");
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkBusy, busyPtr);
+#endif
+ winPtr = (Tk_FakeWin *) tkRef;
+ if (winPtr->flags & TK_REPARENTED) {
+ /*
+ * This works around a bug in the implementation of menubars
+ * for non-MacIntosh window systems (Win32 and X11). Tk
+ * doesn't reset the pointers to the parent window when the
+ * menu is reparented (winPtr->parentPtr points to the
+ * wrong window). We get around this by determining the parent
+ * via the native API calls.
+ */
+#ifdef WIN32
+ {
+ HWND hWnd;
+ RECT rect;
+
+ hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkRef)));
+ parent = (Window) hWnd;
+ if (GetWindowRect(hWnd, &rect)) {
+ busyPtr->width = rect.right - rect.left;
+ busyPtr->height = rect.bottom - rect.top;
+#if BUSYDEBUG
+ PurifyPrintf("menubar: width=%d, height=%d\n",
+ busyPtr->width, busyPtr->height);
+#endif
+ }
+ }
+#else
+ parent = Blt_GetParent(Tk_Display(tkRef), Tk_WindowId(tkRef));
+#endif
+ } else {
+ parent = Tk_WindowId(tkParent);
+#ifdef WIN32
+ parent = (Window) Tk_GetHWND(parent);
+#endif
+ }
+ Blt_MakeTransparentWindowExist(tkBusy, parent, TRUE);
+
+#if BUSYDEBUG
+ PurifyPrintf("menubar1: width=%d, height=%d\n", busyPtr->width,
+ busyPtr->height);
+ fprintf(stderr, "busy window %s is at %d,%d %dx%d\n", Tk_PathName(tkBusy),
+ x, y, busyPtr->width, busyPtr->height);
+#endif
+ Tk_MoveResizeWindow(tkBusy, x, y, busyPtr->width, busyPtr->height);
+
+ /* Only worry if the busy window is destroyed. */
+ Tk_CreateEventHandler(tkBusy, StructureNotifyMask, BusyEventProc, busyPtr);
+ /*
+ * Indicate that the busy window's geometry is being managed.
+ * This will also notify us if the busy window is ever packed.
+ */
+ Tk_ManageGeometry(tkBusy, &busyMgrInfo, busyPtr);
+ if (busyPtr->cursor != None) {
+ Tk_DefineCursor(tkBusy, busyPtr->cursor);
+ }
+ /* Track the reference window to see if it is resized or destroyed. */
+ Tk_CreateEventHandler(tkRef, StructureNotifyMask, RefWinEventProc, busyPtr);
+ return busyPtr;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * DestroyBusy --
+ *
+ * This procedure is called from the Tk event dispatcher. It
+ * releases X resources and memory used by the busy window and
+ * updates the internal hash table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and resources are released and the Tk event handler
+ * is removed.
+ *
+ * -------------------------------------------------------------------
+ */
+static void
+DestroyBusy(data)
+ DestroyData data; /* Busy window structure record */
+{
+ Busy *busyPtr = (Busy *)data;
+
+ Tk_FreeOptions(configSpecs, (char *)busyPtr, busyPtr->display, 0);
+ if (busyPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(busyPtr->tablePtr, busyPtr->hashPtr);
+ }
+ Tk_DeleteEventHandler(busyPtr->tkRef, StructureNotifyMask,
+ RefWinEventProc, busyPtr);
+ if (busyPtr->tkBusy != NULL) {
+ Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask,
+ BusyEventProc, busyPtr);
+ Tk_ManageGeometry(busyPtr->tkBusy, NULL, busyPtr);
+ Tk_DestroyWindow(busyPtr->tkBusy);
+ }
+ Blt_Free(busyPtr);
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * GetBusy --
+ *
+ * Returns the busy window structure associated with the reference
+ * window, keyed by its path name. The clientData argument is
+ * the main window of the interpreter, used to search for the
+ * reference window in its own window hierarchy.
+ *
+ * Results:
+ * If path name represents a reference window with a busy window, a
+ * pointer to the busy window structure is returned. Otherwise,
+ * NULL is returned and an error message is left in
+ * interp->result.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+GetBusy(dataPtr, interp, pathName, busyPtrPtr)
+ BusyInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ char *pathName; /* Path name of parent window */
+ Busy **busyPtrPtr; /* Will contain address of busy window if
+ * found. */
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(dataPtr->busyTable), (char *)tkwin);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *busyPtrPtr = ((Busy *)Blt_GetHashValue(hPtr));
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * HoldBusy --
+ *
+ * Creates (if necessary) and maps a busy window, thereby
+ * preventing device events from being be received by the parent
+ * window and its children.
+ *
+ * Results:
+ * Returns a standard TCL result. If path name represents a busy
+ * window, it is unmapped and TCL_OK is returned. Otherwise,
+ * TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * The busy window is created and displayed, blocking events from
+ * the parent window and its children.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+HoldBusy(dataPtr, interp, argc, argv)
+ BusyInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv; /* Window name and option pairs */
+{
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ Busy *busyPtr;
+ int isNew;
+ int result;
+
+ tkwin = Tk_NameToWindow(interp, argv[0], Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&(dataPtr->busyTable), (char *)tkwin, &isNew);
+ if (isNew) {
+ busyPtr = (Busy *)CreateBusy(interp, tkwin);
+ if (busyPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_SetHashValue(hPtr, (char *)busyPtr);
+ busyPtr->hashPtr = hPtr;
+ } else {
+ busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+ }
+ busyPtr->tablePtr = &(dataPtr->busyTable);
+ result = ConfigureBusy(interp, busyPtr, argc - 1, argv + 1);
+
+ /*
+ * Don't map the busy window unless the reference window is also
+ * currently displayed.
+ */
+ if (Tk_IsMapped(busyPtr->tkRef)) {
+ ShowBusyWindow(busyPtr);
+ } else {
+ HideBusyWindow(busyPtr);
+ }
+ busyPtr->isBusy = TRUE;
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * StatusOp --
+ *
+ * Returns the status of the busy window; whether it's blocking
+ * events or not.
+ *
+ * Results:
+ * Returns a standard TCL result. If path name represents a busy
+ * window, the status is returned via interp->result and TCL_OK
+ * is returned. Otherwise, TCL_ERROR is returned and an error
+ * message is left in interp->result.
+ *
+ * -------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StatusOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report error to */
+ int argc; /* Not used. */
+ char **argv;
+{
+ BusyInterpData *dataPtr = clientData;
+ Busy *busyPtr;
+
+ if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(busyPtr);
+ Blt_SetBooleanResult(interp, busyPtr->isBusy);
+ Tcl_Release(busyPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ForgetOp --
+ *
+ * Destroys the busy window associated with the reference window and
+ * arranges for internal resources to the released when they're
+ * not being used anymore.
+ *
+ * Results:
+ * Returns a standard TCL result. If path name represents a busy
+ * window, it is destroyed and TCL_OK is returned. Otherwise,
+ * TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * The busy window is removed. Other related memory and resources
+ * are eventually released by the Tk dispatcher.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ForgetOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv;
+{
+ BusyInterpData *dataPtr = clientData;
+ Busy *busyPtr;
+ register int i;
+
+ for (i = 2; i < argc; i++) {
+ if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Unmap the window even though it will be soon destroyed */
+ HideBusyWindow(busyPtr);
+ Tcl_EventuallyFree(busyPtr, DestroyBusy);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ReleaseOp --
+ *
+ * Unmaps the busy window, thereby permitting device events
+ * to be received by the parent window and its children.
+ *
+ * Results:
+ * Returns a standard TCL result. If path name represents a busy
+ * window, it is unmapped and TCL_OK is returned. Otherwise,
+ * TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * The busy window is hidden, allowing the parent window and
+ * its children to receive events again.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ReleaseOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv;
+{
+ BusyInterpData *dataPtr = clientData;
+ Busy *busyPtr;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ HideBusyWindow(busyPtr);
+ busyPtr->isBusy = FALSE;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Reports the names of all widgets with busy windows attached to
+ * them, matching a given pattern. If no pattern is given, all
+ * busy widgets are listed.
+ *
+ * Results:
+ * Returns a TCL list of the names of the widget with busy windows
+ * attached to them, regardless if the widget is currently busy
+ * or not.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+NamesOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv;
+{
+ BusyInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Busy *busyPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->busyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+ if ((argc == 2) ||
+ (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
+ Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * BusyOp --
+ *
+ * Reports the names of all widgets with busy windows attached to
+ * them, matching a given pattern. If no pattern is given, all
+ * busy widgets are listed.
+ *
+ * Results:
+ * Returns a TCL list of the names of the widget with busy windows
+ * attached to them, regardless if the widget is currently busy
+ * or not.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+BusyOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv;
+{
+ BusyInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Busy *busyPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->busyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+ if (!busyPtr->isBusy) {
+ continue;
+ }
+ if ((argc == 2) ||
+ (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
+ Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * HoldOp --
+ *
+ * Creates (if necessary) and maps a busy window, thereby
+ * preventing device events from being be received by the parent
+ * window and its children. The argument vector may contain
+ * option-value pairs of configuration options to be set.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ * Side effects:
+ * The busy window is created and displayed, blocking events from the
+ * parent window and its children.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+HoldOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv; /* Window name and option pairs */
+{
+ BusyInterpData *dataPtr = clientData;
+ register int i, count;
+
+ if ((argv[1][0] == 'h') && (strcmp(argv[1], "hold") == 0)) {
+ argc--, argv++; /* Command used "hold" keyword */
+ }
+ for (i = 1; i < argc; i++) {
+ /*
+ * Find the end of the option-value pairs for this window.
+ */
+ for (count = i + 1; count < argc; count += 2) {
+ if (argv[count][0] != '-') {
+ break;
+ }
+ }
+ if (count > argc) {
+ count = argc;
+ }
+ if (HoldBusy(dataPtr, interp, count - i, argv + i) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ i = count;
+ }
+ return TCL_OK;
+}
+
+/* ARGSUSED*/
+static int
+CgetOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv; /* Widget pathname and option switch */
+{
+ BusyInterpData *dataPtr = clientData;
+ Busy *busyPtr;
+ int result;
+
+ if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(busyPtr);
+ result = Tk_ConfigureValue(interp, busyPtr->tkRef, configSpecs,
+ (char *)busyPtr, argv[3], 0);
+ Tcl_Release(busyPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list in order
+ * to configure (or reconfigure) a busy window.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information get set for busyPtr; old resources
+ * get freed, if there were any. The busy window destroyed and
+ * recreated in a new parent window.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv; /* Reference window path name and options */
+{
+ BusyInterpData *dataPtr = clientData;
+ Busy *busyPtr;
+ int result;
+
+ if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
+ (char *)busyPtr, (char *)NULL, 0);
+ } else if (argc == 4) {
+ result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
+ (char *)busyPtr, argv[3], 0);
+ } else {
+ Tcl_Preserve(busyPtr);
+ result = ConfigureBusy(interp, busyPtr, argc - 3, argv + 3);
+ Tcl_Release(busyPtr);
+ }
+ return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BusyInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the "busy" command
+ * is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys all the hash table managing the busy windows.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ BusyInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Busy *busyPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->busyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+ busyPtr->hashPtr = NULL;
+ DestroyBusy((DestroyData)busyPtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->busyTable));
+ Tcl_DeleteAssocData(interp, BUSY_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Busy Sub-command specification:
+ *
+ * - Name of the sub-command.
+ * - Minimum number of characters needed to unambiguously
+ * recognize the sub-command.
+ * - Pointer to the function to be called for the sub-command.
+ * - Minimum number of arguments accepted.
+ * - Maximum number of arguments accepted.
+ * - String to be displayed for usage (arguments only).
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec busyOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "window option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "window ?options?...",},
+ {"forget", 1, (Blt_Op)ForgetOp, 2, 0, "?window?...",},
+ {"hold", 3, (Blt_Op)HoldOp, 3, 0,
+ "window ?options?... ?window options?...",},
+ {"isbusy", 1, (Blt_Op)BusyOp, 2, 3, "?pattern?",},
+ {"names", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+ {"release", 1, (Blt_Op)ReleaseOp, 2, 0, "?window?...",},
+ {"status", 1, (Blt_Op)StatusOp, 3, 3, "window",},
+ {"windows", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+};
+static int nBusyOps = sizeof(busyOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BusyCmd --
+ *
+ * This procedure is invoked to process the "busy" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BusyCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter associated with command */
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ if ((argc > 1) && (argv[1][0] == '.')) {
+ return HoldOp(clientData, interp, argc, argv);
+ }
+ proc = Blt_GetOp(interp, nBusyOps, busyOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+static BusyInterpData *
+GetBusyInterpData(interp)
+ Tcl_Interp *interp;
+{
+ BusyInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (BusyInterpData *)
+ Tcl_GetAssocData(interp, BUSY_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(BusyInterpData));
+ assert(dataPtr);
+ Tcl_SetAssocData(interp, BUSY_THREAD_KEY, BusyInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->busyTable), BLT_ONE_WORD_KEYS);
+ }
+ return dataPtr;
+}
+
+int
+Blt_BusyInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec = {"busy", BusyCmd, };
+ BusyInterpData *dataPtr;
+
+ dataPtr = GetBusyInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+#endif /* NO_BUSY */
diff --git a/blt/src/bltCanvEps.c b/blt/src/bltCanvEps.c
new file mode 100644
index 00000000000..319f1d0b829
--- /dev/null
+++ b/blt/src/bltCanvEps.c
@@ -0,0 +1,1742 @@
+/*
+ * bltCanvEps.c --
+ *
+ * This file implements Encapsulated PostScript items for canvas widgets.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * EPS canvas item created by George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 1. Add -rotate option. Allow arbitrary rotation of image and EPS.
+ * 2. Draw color images instead of photos. This will eliminate the need
+ * to create hidden photo images.
+ * 3. Create a spiffy demo that lets you edit your page description.
+ */
+#include "bltInt.h"
+#include "bltPs.h"
+#include "bltImage.h"
+
+#ifdef HAVE_TIFF_H
+#include "tiff.h"
+#endif
+#include <fcntl.h>
+
+#ifdef WIN32
+#include <io.h>
+#define open _open
+#define close _close
+#define write _write
+#define unlink _unlink
+#define lseek _lseek
+#define fdopen _fdopen
+#define fcntl _fcntl
+#define O_RDWR _O_RDWR
+#define O_CREAT _O_CREAT
+#define O_TRUNC _O_TRUNC
+#define O_EXCL _O_EXCL
+#endif
+
+#define DEBUG_READER 0
+#define PS_PREVIEW_EPSI 0
+#define PS_PREVIEW_WMF 1
+#define PS_PREVIEW_TIFF 2
+
+#define xLeft header.x1
+#define xRight header.x2
+#define yTop header.y1
+#define yBottom header.y2
+
+
+#define MAX_EPS_LINE_LENGTH 255 /* Maximum line length for a EPS file */
+
+/*
+ * ParseInfo --
+ *
+ * This structure is used to pass PostScript file information
+ * around to various routines while parsing the EPS file.
+ */
+typedef struct {
+ int maxBytes; /* Maximum length of PostScript code. */
+ int lineNumber; /* Current line number of EPS file */
+ char line[MAX_EPS_LINE_LENGTH + 1];
+ /* Buffer to contain a single line from
+ * the PostScript file. */
+ char hexTable[256]; /* Table for converting ASCII hex digits to
+ * values */
+
+ char *nextPtr; /* Pointer to the next character to process on
+ * the current line. If NULL (or if nextPtr
+ * points a NULL byte), this indicates the
+ * the next line needs to be read. */
+ FILE *f; /* */
+} ParseInfo;
+
+#define DEF_EPS_ANCHOR "nw"
+#define DEF_EPS_OUTLINE_COLOR RGB_BLACK
+#define DEF_EPS_OUTLINE_MONO RGB_BLACK
+#define DEF_EPS_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_EPS_FILE_NAME (char *)NULL
+#define DEF_EPS_FONT STD_FONT
+#define DEF_EPS_FILL_COLOR STD_COLOR_NORMAL_FG
+#define DEF_EPS_FILL_MONO STD_MONO_NORMAL_FG
+#define DEF_EPS_HEIGHT "0"
+#define DEF_EPS_IMAGE_NAME (char *)NULL
+#define DEF_EPS_JUSTIFY "center"
+#define DEF_EPS_QUICK_RESIZE "no"
+#define DEF_EPS_RELIEF "sunken"
+#define DEF_EPS_SHADOW_COLOR (char *)NULL
+#define DEF_EPS_SHADOW_MONO (char *)NULL
+#define DEF_EPS_SHOW_IMAGE "yes"
+#define DEF_EPS_STIPPLE (char *)NULL
+#define DEF_EPS_TAGS (char *)NULL
+#define DEF_EPS_TITLE (char *)NULL
+#define DEF_EPS_TITLE_ANCHOR "center"
+#define DEF_EPS_TITLE_COLOR RGB_BLACK
+#define DEF_EPS_TITLE_ROTATE "0"
+#define DEF_EPS_WIDTH "0"
+
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Tk_CustomOption tagsOption;
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+
+/*
+ * The structure below defines the record for each EPS item.
+ */
+typedef struct {
+ Tk_Item header; /* Generic stuff that's the same for all
+ * types. MUST BE FIRST IN STRUCTURE. */
+ Tk_Canvas canvas; /* Canvas containing the EPS item. */
+
+ int canvasX, canvasY; /* Translated (by the anchor) canvas
+ * coordinates of the EPS item. */
+
+ int lastWidth, lastHeight; /* Last known dimensions of the EPS item.
+ * This is used to know if the color image
+ * preview needs to be resized. */
+
+ Tcl_Interp *interp;
+
+ FILE *psFile; /* File pointer to Encapsulated
+ * PostScript file. We'll hold this as
+ * long as the EPS item is using this
+ * file. */
+ size_t psStart; /* File offset of PostScript code. */
+ size_t psLength; /* Length of PostScript code. If zero,
+ * indicates to read to EOF. */
+ size_t wmfStart; /* File offset of Windows Metafile preview. */
+ size_t wmfLength; /* Length of WMF portion in bytes. If zero,
+ * indicates there is no WMF preview. */
+ size_t tiffStart; /* File offset of TIFF preview. */
+ size_t tiffLength; /* Length of TIFF portion in bytes. If zero,
+ * indicates there is no TIFF preview. */
+ char *previewName;
+ int previewFormat;
+
+ Tk_Image preview; /* A Tk photo image provided as a
+ * preview of the EPS contents. This
+ * image supersedes any EPS preview
+ * embedded PostScript preview (EPSI). */
+
+ Tk_Image tmpImage; /* Used to display the resized preview image.
+ * Created and deleted internally. */
+
+
+ Pixmap pixmap; /* Pixmap representing scaled preview. This
+ * isn't currently used. For now we're
+ * overwriting the Tk image everytime the
+ * EPS item is resized. In the future
+ * we'll use our own image routines. */
+
+ ColorTable colorTable; /* Pointer to color table */
+
+ Blt_Colorimage colorImage; /* The original photo or PostScript
+ * preview image converted to a color
+ * image. This is kept around for
+ * resampling or resizing the image. */
+
+ unsigned int firstLine, lastLine;
+ /* First and last line numbers of the
+ * PostScript preview. They are used
+ * to skip over the preview when
+ * encapsulating PostScript for the
+ * canvas item. */
+
+ GC fillGC; /* Graphics context to fill background
+ * of image outline if no preview image
+ * was present. */
+
+ int llx, lly, urx, ury; /* Lower left and upper right coordinates
+ * of PostScript bounding box, retrieved
+ * from file's "BoundingBox:" field. */
+
+ char *title; /* Title, retrieved from the file's "Title:"
+ * field, to be displayed over the top of
+ * the EPS preview (malloc-ed). */
+
+ Tcl_DString dString; /* Contains the encapsulate PostScript. */
+
+ /* User configurable fields */
+
+ double x, y; /* Canvas coordinates of the item */
+ Tk_Anchor anchor;
+
+ char *fileName; /* Name of the encapsulated PostScript file.
+ * If NULL, indicates that no EPS file
+ * has be successfully loaded yet. */
+
+ char *reqTitle; /* Title to be displayed in the EPS item.
+ * Supersedes the title found in the EPS
+ * file. If NULL, indicates that the title
+ * found in the EPS file should be used. */
+
+ int width, height; /* Dimensions of EPS item. If set to zero,
+ * the dimension found in the "%%BoundingBox:"
+ * specification from the EPS file are
+ * used. */
+
+ int showImage; /* Indicates if the image or the outline
+ * rectangle should be displayed */
+
+ int quick;
+
+ XColor *fillColor; /* Fill color of the image outline. */
+
+ Tk_3DBorder border; /* Outline color */
+
+ int borderWidth;
+ int relief;
+
+ TextStyle titleStyle; /* Font, color, etc. for title */
+
+ Pixmap stipple; /* Stipple for image fill */
+
+ ClientData tiffPtr;
+#ifdef WIN32
+ HENHMETAFILE *hMetaFile; /* Windows metafile. */
+#endif
+} EpsItem;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
+ DEF_EPS_ANCHOR, Tk_Offset(EpsItem, anchor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", (char *)NULL,
+ DEF_EPS_BORDER_WIDTH, Tk_Offset(EpsItem, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-file", (char *)NULL, (char *)NULL,
+ DEF_EPS_FILE_NAME, Tk_Offset(EpsItem, fileName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_EPS_FONT, Tk_Offset(EpsItem, titleStyle.font), 0},
+ {TK_CONFIG_COLOR, "-fill", "fill", (char *)NULL,
+ DEF_EPS_FILL_COLOR, Tk_Offset(EpsItem, fillColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-fill", "fill", (char *)NULL,
+ DEF_EPS_FILL_MONO, Tk_Offset(EpsItem, fillColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
+ DEF_EPS_HEIGHT, Tk_Offset(EpsItem, height),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-image", (char *)NULL, (char *)NULL,
+ DEF_EPS_IMAGE_NAME, Tk_Offset(EpsItem, previewName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_EPS_JUSTIFY, Tk_Offset(EpsItem, titleStyle.justify),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-outline", "outline", (char *)NULL,
+ DEF_EPS_OUTLINE_COLOR, Tk_Offset(EpsItem, border),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-outline", "outline", (char *)NULL,
+ DEF_EPS_OUTLINE_MONO, Tk_Offset(EpsItem, border),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-quick", "quick", "Quick",
+ DEF_EPS_QUICK_RESIZE, Tk_Offset(EpsItem, quick),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_RELIEF, "-relief", (char *)NULL, (char *)NULL,
+ DEF_EPS_RELIEF, Tk_Offset(EpsItem, relief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_EPS_SHADOW_COLOR, Tk_Offset(EpsItem, titleStyle.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_EPS_SHADOW_MONO, Tk_Offset(EpsItem, titleStyle.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_BOOLEAN, "-showimage", "showImage", "ShowImage",
+ DEF_EPS_SHOW_IMAGE, Tk_Offset(EpsItem, showImage),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BITMAP, "-stipple", (char *)NULL, (char *)NULL,
+ DEF_EPS_STIPPLE, Tk_Offset(EpsItem, stipple), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tags", (char *)NULL, (char *)NULL,
+ DEF_EPS_TAGS, 0, TK_CONFIG_NULL_OK, &tagsOption},
+ {TK_CONFIG_STRING, "-title", (char *)NULL, (char *)NULL,
+ DEF_EPS_TITLE, Tk_Offset(EpsItem, reqTitle), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-titleanchor", (char *)NULL, (char *)NULL,
+ DEF_EPS_TITLE_ANCHOR, Tk_Offset(EpsItem, titleStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-titlecolor", (char *)NULL, (char *)NULL,
+ DEF_EPS_TITLE_COLOR, Tk_Offset(EpsItem, titleStyle.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_DOUBLE, "-titlerotate", "titleRotate", "TitleRotate",
+ DEF_EPS_TITLE_ROTATE, Tk_Offset(EpsItem, titleStyle.theta),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
+ DEF_EPS_WIDTH, Tk_Offset(EpsItem, width),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * Prototypes for procedures defined in this file:
+ */
+
+static void ImageChangedProc _ANSI_ARGS_((ClientData clientData, int x, int y,
+ int width, int height, int imgWidth, int imgHeight));
+static int EpsCoords _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
+ Tk_Item * itemPtr, int argc, char **argv));
+static int EpsToArea _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ double *rectPtr));
+static double EpsToPoint _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ double *coordPtr));
+static void ComputeEpsBbox _ANSI_ARGS_((Tk_Canvas canvas, EpsItem *imgPtr));
+static int ConfigureEps _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
+ Tk_Item * itemPtr, int argc, char **argv, int flags));
+static int CreateEps _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
+ struct Tk_Item * itemPtr, int argc, char **argv));
+static void DeleteEps _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ Display *display));
+static void DisplayEps _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ Display *display, Drawable dst, int x, int y, int width, int height));
+static void ScaleEps _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ double originX, double originY, double scaleX, double scaleY));
+static void TranslateEps _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item * itemPtr,
+ double deltaX, double deltaY));
+static int EpsToPostScript _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
+ Tk_Item * itemPtr, int prepass));
+static int ReadPostScript _ANSI_ARGS_((Tcl_Interp *interp, EpsItem *epsPtr));
+
+
+static char *
+SkipBlanks(piPtr)
+ ParseInfo *piPtr;
+{
+ char *s;
+
+ for (s = piPtr->line; isspace(UCHAR(*s)); s++) {
+ /*empty*/
+ }
+ return s;
+}
+
+static int
+ReadPsLine(piPtr)
+ ParseInfo *piPtr;
+{
+ if (ftell(piPtr->f) < piPtr->maxBytes) {
+ if (fgets(piPtr->line, MAX_EPS_LINE_LENGTH, piPtr->f) != NULL) {
+ piPtr->lineNumber++;
+#if DEBUG_READER0
+ PurifyPrintf("%d: %s\n", piPtr->lineNumber, piPtr->line);
+#endif
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReverseBits --
+ *
+ * Convert a byte from a X image into PostScript image order.
+ * This requires not only the nybbles to be reversed but also
+ * their bit values.
+ *
+ * Results:
+ * The converted byte is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+INLINE static unsigned char
+ReverseBits(byte)
+ register unsigned char byte;
+{
+ byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
+ byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
+ byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
+ return byte;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetHexValue --
+ *
+ * Reads the next ASCII hex value from EPS preview image and
+ * converts it.
+ *
+ * Results:
+ * One of three Tcl return values is possible.
+ *
+ * TCL_OK the next byte was successfully parsed.
+ * TCL_ERROR an error occurred processing the next hex value.
+ * TCL_RETURN "%%EndPreview" line was detected.
+ *
+ * The converted hex value is returned via "bytePtr".
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetHexValue(piPtr, bytePtr)
+ ParseInfo *piPtr;
+ unsigned char *bytePtr;
+{
+ register char *p;
+ unsigned int byte;
+
+ p = piPtr->nextPtr;
+ if (p == NULL) {
+ nextLine:
+ if (!ReadPsLine(piPtr)) {
+#if DEBUG_READER
+ PurifyPrintf("short file\n");
+#endif
+ return TCL_ERROR; /* Short file */
+ }
+ if (piPtr->line[0] != '%') {
+#if DEBUG_READER
+ PurifyPrintf("line doesn't start with %% (%s)\n", piPtr->line);
+#endif
+ return TCL_ERROR;
+ }
+ if ((piPtr->line[1] == '%') &&
+ (strncmp(piPtr->line + 2, "EndPreview", 10) == 0)) {
+#if DEBUG_READER
+ PurifyPrintf("end of preview (%s)\n", piPtr->line);
+#endif
+ return TCL_RETURN;
+ }
+ p = piPtr->line + 1;
+ }
+ while (isspace((int)*p)) {
+ p++; /* Skip spaces */
+ }
+ if (*p == '\0') {
+ goto nextLine;
+ }
+ if ((!isxdigit((int)p[0])) || (!isxdigit((int)p[1]))) {
+#if DEBUG_READER
+ PurifyPrintf("not a hex digit (%s)\n", piPtr->line);
+#endif
+ return TCL_ERROR;
+ }
+ byte = (piPtr->hexTable[(int)p[0]] << 4) | piPtr->hexTable[(int)p[1]];
+ p += 2;
+ piPtr->nextPtr = p;
+ *bytePtr = byte;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadEPSI --
+ *
+ * Reads the EPS preview image from the PostScript file, converting
+ * the image into a Blt_Colorimage. If an error occurs when parsing
+ * the preview, the preview is silently ignored.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReadEPSI(epsPtr, piPtr)
+ EpsItem *epsPtr;
+ ParseInfo *piPtr;
+{
+ Blt_Colorimage image;
+ int width, height, bitsPerPixel, nLines;
+ char *dscBeginPreview;
+
+ dscBeginPreview = piPtr->line + 16;
+ if (sscanf(dscBeginPreview, "%d %d %d %d", &width, &height, &bitsPerPixel,
+ &nLines) != 4) {
+#if DEBUG_READER
+ PurifyPrintf("bad %%BeginPreview (%s) format\n", dscBeginPreview);
+#endif
+ return;
+ }
+ if (((bitsPerPixel != 1) && (bitsPerPixel != 8)) || (width < 1) ||
+ (width > SHRT_MAX) || (height < 1) || (height > SHRT_MAX)) {
+#if DEBUG_READER
+ PurifyPrintf("Bad %%BeginPreview (%s) values\n", dscBeginPreview);
+#endif
+ return; /* Bad "%%BeginPreview:" information */
+ }
+ epsPtr->firstLine = piPtr->lineNumber;
+ Blt_InitHexTable(piPtr->hexTable);
+ piPtr->nextPtr = NULL;
+ image = Blt_CreateColorimage(width, height);
+
+ if (bitsPerPixel == 8) {
+ int result;
+ register Pix32 *destPtr;
+ register int x, y;
+ unsigned char byte;
+
+ for (y = height - 1; y >= 0; y--) {
+ destPtr = Blt_ColorimageBits(image) + (y * width);
+ for (x = 0; x < width; x++, destPtr++) {
+ result = GetHexValue(piPtr, &byte);
+ if (result == TCL_ERROR) {
+ goto error;
+ }
+ if (result == TCL_RETURN) {
+ goto done;
+ }
+ destPtr->Red = destPtr->Green = destPtr->Blue = ~byte;
+ destPtr->Alpha = 0xFF;
+ }
+ }
+ } else if (bitsPerPixel == 1) {
+ int result;
+ register Pix32 *destPtr;
+ register int x, y;
+ unsigned char byte;
+ register int bit;
+
+ destPtr = Blt_ColorimageBits(image);
+ for (y = 0; y < height; y++) {
+ bit = 8;
+ for (x = 0; x < width; x++, destPtr++) {
+ if (bit == 8) {
+ result = GetHexValue(piPtr, &byte);
+ if (result == TCL_ERROR) {
+ goto error;
+ }
+ if (result == TCL_RETURN) {
+ goto done;
+ }
+ byte = ReverseBits(byte);
+ bit = 0;
+ }
+ if (((byte >> bit) & 0x01) == 0) {
+ destPtr->value = 0xFFFFFFFF;
+ }
+ bit++;
+ }
+ }
+ } else {
+ fprintf(stderr, "unknown EPSI bitsPerPixel (%d)\n", bitsPerPixel);
+ }
+ done:
+ epsPtr->colorImage = image;
+ epsPtr->lastLine = piPtr->lineNumber + 1;
+ return;
+
+ error:
+ epsPtr->firstLine = epsPtr->lastLine = -1;
+ if (image != NULL) {
+ Blt_FreeColorimage(image);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadPostScript --
+ *
+ * This routine reads and parses the few fields we need out
+ * of an EPS file.
+ *
+ * The EPS standards are outlined from Appendix H of the
+ * "PostScript Language Reference Manual" pp. 709-736.
+ *
+ * Mandatory fields:
+ *
+ * - Starts with "%!PS*"
+ * - Contains "%%BoundingBox: llx lly urx ury"
+ *
+ * Optional fields for EPS item:
+ * - "%%BeginPreview: w h bpp #lines"
+ * Preview is in hexadecimal. Each line must start with "%"
+ * - "%%EndPreview"
+ * - "%%Title: (string)"
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ReadPostScript(interp, epsPtr)
+ Tcl_Interp *interp;
+ EpsItem *epsPtr;
+{
+ char *field;
+ char *dscTitle, *dscBoundingBox, *dscEndProlog;
+ char *dscEndSetup, *dscEndComments;
+ ParseInfo pi;
+
+ pi.line[0] = '\0';
+ pi.maxBytes = epsPtr->psLength;
+ pi.lineNumber = 0;
+ pi.f = epsPtr->psFile;
+
+ if (pi.maxBytes == 0) {
+ pi.maxBytes = INT_MAX;
+ }
+ if (epsPtr->psStart >= 0) {
+ if (fseek(epsPtr->psFile, epsPtr->psStart, 0) != 0) {
+ Tcl_AppendResult(interp,
+ "can't seek to start of PostScript code in \"",
+ epsPtr->fileName, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (!ReadPsLine(&pi)) {
+ Tcl_AppendResult(interp, "file \"", epsPtr->fileName, "\" is empty?",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (strncmp(pi.line, "%!PS", 4) != 0) {
+ Tcl_AppendResult(interp, "file \"", epsPtr->fileName,
+ "\" doesn't start with \"%!PS\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize field flags to NULL. We want to look only at the
+ * first appearance of these comment fields. The file itself may
+ * have another EPS file embedded into it.
+ */
+ dscBoundingBox = dscTitle = NULL;
+ dscEndComments = dscEndSetup = dscEndProlog = NULL;
+
+ pi.lineNumber = 1;
+ while (ReadPsLine(&pi)) {
+ pi.lineNumber++;
+ if ((pi.line[0] == '%') && (pi.line[1] == '%')) { /* Header comment */
+ field = pi.line + 2;
+ if ((field[0] == 'B') &&
+ (strncmp(field, "BoundingBox:", 12) == 0)) {
+ if (dscBoundingBox == NULL) {
+ int nFields;
+
+ dscBoundingBox = field + 12;
+ nFields = sscanf(dscBoundingBox, "%d %d %d %d",
+ &(epsPtr->llx), &(epsPtr->lly),
+ &(epsPtr->urx), &(epsPtr->ury));
+ if (nFields != 4) {
+ Tcl_AppendResult(interp,
+ "bad \"%%BoundingBox\" values: \"",
+ dscBoundingBox, "\"", (char *)NULL);
+ goto error;
+ }
+ }
+ } else if ((field[0] == 'T') &&
+ (strncmp(field, "Title:", 6) == 0)) {
+ if (dscTitle == NULL) {
+ dscTitle = Blt_Strdup(field + 6);
+ }
+ } else if (field[0] == 'E') {
+ if (strncmp(field, "EndComments", 11) == 0) {
+ dscEndComments = field;
+ break; /* Done */
+ }
+ if (strncmp(field, "EndSetup", 8) == 0) {
+ dscEndSetup = field;
+ break; /* Done */
+ }
+ }
+ } /* %% */
+ }
+ if (dscEndComments != NULL) {
+ /* Check if a "%%BeginPreview" immediately follows */
+ while (ReadPsLine(&pi)) {
+ field = SkipBlanks(&pi);
+ if (field[0] != '\0') {
+ break;
+ }
+ }
+ if (strncmp(pi.line, "%%BeginPreview:", 15) == 0) {
+ ReadEPSI(epsPtr, &pi);
+ }
+ }
+ if ((dscEndSetup == NULL) && (dscEndProlog == NULL)) {
+ /* Try to find the end of the prolog or setup */
+ while (ReadPsLine(&pi)) {
+ if ((pi.line[0] == '%') && (pi.line[1] == '%')) {
+ field = pi.line + 2;
+ if (field[0] == 'E') {
+ if (strncmp(field, "EndProlog", 9) == 0) {
+ dscEndProlog = field;
+ break;
+ }
+ if (strncmp(field, "EndSetup", 8) == 0) {
+ dscEndSetup = field;
+ break; /* Done */
+ }
+ }
+ }
+ }
+ }
+ if (dscBoundingBox == NULL) {
+ Tcl_AppendResult(interp, "no \"%%BoundingBox:\" found in \"",
+ epsPtr->fileName, "\"", (char *)NULL);
+ goto error;
+ }
+ if ((dscEndSetup == NULL) && (dscEndProlog == NULL)) {
+ Tcl_AppendResult(interp,
+ "no \"%%EndProlog\" or \"%%EndSetup\" found in",
+ epsPtr->fileName, "\"", (char *)NULL);
+ goto error;
+ }
+ if (dscTitle != NULL) {
+ epsPtr->title = dscTitle;
+ }
+ /* Finally save the PostScript into a dynamic string. */
+ Tcl_DStringInit(&epsPtr->dString);
+ while (ReadPsLine(&pi)) {
+ Tcl_DStringAppend(&epsPtr->dString, pi.line, -1);
+ Tcl_DStringAppend(&epsPtr->dString, "\n", 1);
+ }
+ return TCL_OK;
+ error:
+ if (dscTitle != NULL) {
+ Blt_Free(dscTitle);
+ }
+ return TCL_ERROR; /* BoundingBox: is required. */
+}
+
+static int
+OpenEpsFile(interp, epsPtr)
+ Tcl_Interp *interp;
+ EpsItem *epsPtr;
+{
+ FILE *f;
+#ifdef WIN32
+ DOSEPSHEADER dosHeader;
+ int nBytes;
+#endif
+
+ f = fopen(epsPtr->fileName, "rb");
+ if (f == NULL) {
+ Tcl_AppendResult(epsPtr->interp, "can't open \"", epsPtr->fileName,
+ "\": ", Tcl_PosixError(epsPtr->interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ epsPtr->psFile = f;
+ epsPtr->psStart = epsPtr->psLength = 0L;
+ epsPtr->wmfStart = epsPtr->wmfLength = 0L;
+ epsPtr->tiffStart = epsPtr->tiffLength = 0L;
+
+#ifdef WIN32
+ nBytes = fread(&dosHeader, sizeof(DOSEPSHEADER), 1, f);
+ if ((nBytes == sizeof(DOSEPSHEADER)) &&
+ (dosHeader.magic[0] == 0xC5) && (dosHeader.magic[1] == 0xD0) &&
+ (dosHeader.magic[2] == 0xD3) && (dosHeader.magic[3] == 0xC6)) {
+
+ /* DOS EPS file */
+ epsPtr->psStart = dosHeader.psStart;
+ epsPtr->wmfStart = dosHeader.wmfStart;
+ epsPtr->wmfLength = dosHeader.wmfLength;
+ epsPtr->tiffStart = dosHeader.tiffStart;
+ epsPtr->tiffLength = dosHeader.tiffLength;
+ epsPtr->previewFormat = PS_PREVIEW_EPSI;
+#ifdef HAVE_TIFF_H
+ if (epsPtr->tiffLength > 0) {
+ epsPtr->previewFormat = PS_PREVIEW_TIFF;
+ }
+#endif /* HAVE_TIFF_H */
+ if (epsPtr->wmfLength > 0) {
+ epsPtr->previewFormat = PS_PREVIEW_WMF;
+ }
+ }
+ fseek(f, 0, 0);
+#endif /* WIN32 */
+ return ReadPostScript(interp, epsPtr);
+}
+
+static void
+CloseEpsFile(epsPtr)
+ EpsItem *epsPtr;
+{
+ if (epsPtr->psFile != NULL) {
+ fclose(epsPtr->psFile);
+ epsPtr->psFile = NULL;
+ }
+}
+
+static void
+ReadTiffPreview(epsPtr)
+ EpsItem *epsPtr;
+{
+#ifdef HAVE_TIFF_H
+ unsigned int width, height;
+ Blt_Colorimage image;
+ Pix32 *dataPtr;
+ FILE *f;
+ int n;
+
+ TIFFGetField(epsPtr->tiffPtr, TIFFTAG_IMAGEWIDTH, &width);
+ TIFFGetField(epsPtr->tiffPtr, TIFFTAG_IMAGELENGTH, &height);
+ image = Blt_CreateColorimage(width, height);
+ dataPtr = Blt_ColorimageBits(image);
+ if (!TIFFReadRGBAImage(epsPtr->tiffPtr, width, height, dataPtr, 0)) {
+ Blt_FreeColorimage(image);
+ return;
+ }
+ /* Reverse the order of the components for each pixel. */
+ /* ... */
+ epsPtr->colorImage = image;
+#endif
+}
+
+#ifdef notdef
+ReadWMF(f, epsPtr, headerPtr)
+ FILE *f;
+{
+ HANDLE hMem;
+ Tk_Window tkwin;
+
+ if (fseek(f, headerPtr->wmfStart, 0) != 0) {
+ Tcl_AppendResult(interp, "can't seek in \"", epsPtr->fileName,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ hMem = GlobalAlloc(GHND, size);
+ if (hMem == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't allocate global memory:",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ /* Read the header and see what kind of meta file it is. */
+ fread(buffer, sizeof(unsigned char), headerPtr->wmfLength, f);
+ mfp.mm = 0;
+ mfp.xExt = epsPtr->width;
+ mfp.yExt = epsPtr->height;
+ mfp.hMF = hMetaFile;
+ tkwin = Tk_CanvasTkwin(epsPtr->canvas);
+ hRefDC = TkWinGetDrawableDC(Tk_Display(tkwin), Tk_WindowId(tkwin), &state);
+ hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, NULL);
+ mfp.hMF = CloseEnhMetaFile(hDC);
+ hMetaFile = SetWinMetaFileBits(size, buffer, MM_ANISOTROPIC, &pict);
+ Tcl_AppendResult(graphPtr->interp, "can't get metafile data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteEps --
+ *
+ * This procedure is called to clean up the data structure
+ * associated with a EPS item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources associated with itemPtr are released.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DeleteEps(canvas, itemPtr, display)
+ Tk_Canvas canvas; /* Info about overall canvas widget. */
+ Tk_Item *itemPtr; /* Item that is being deleted. */
+ Display *display; /* Display containing window for
+ * canvas. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+
+ Tk_FreeOptions(configSpecs, (char *)epsPtr, display, 0);
+ if (epsPtr->colorImage != NULL) {
+ Blt_FreeColorimage(epsPtr->colorImage);
+ }
+ if (epsPtr->preview != NULL) {
+ Tk_FreeImage(epsPtr->preview);
+ }
+ if (epsPtr->previewName != NULL) {
+ Blt_Free(epsPtr->previewName);
+ }
+ if (epsPtr->tmpImage != NULL) {
+ Blt_DestroyTemporaryImage(epsPtr->interp, epsPtr->tmpImage);
+ }
+ if (epsPtr->pixmap != None) {
+#ifdef notyet
+ Blt_FreeColorTable(epsPtr->colorTable);
+#endif
+ Tk_FreePixmap(display, epsPtr->pixmap);
+ }
+ if (epsPtr->stipple != None) {
+ Tk_FreePixmap(display, epsPtr->stipple);
+ }
+ if (epsPtr->fillGC != NULL) {
+ Tk_FreeGC(display, epsPtr->fillGC);
+ }
+ Blt_FreeTextStyle(display, &(epsPtr->titleStyle));
+
+ if (epsPtr->title != NULL) {
+ Blt_Free(epsPtr->title);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateEps --
+ *
+ * This procedure is invoked to create a new EPS item
+ * in a canvas.
+ *
+ * Results:
+ * A standard Tcl return value. If an error occurred in
+ * creating the item, then an error message is left in
+ * interp->result; in this case itemPtr is left uninitialized,
+ * so it can be safely freed by the caller.
+ *
+ * Side effects:
+ * A new EPS item is created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateEps(interp, canvas, itemPtr, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Canvas canvas; /* Canvas to hold new item. */
+ Tk_Item *itemPtr; /* Record to hold new item; header
+ * has been initialized by caller. */
+ int argc; /* Number of arguments in argv. */
+ char **argv; /* Arguments describing rectangle. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_CanvasTkwin(canvas);
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tk_PathName(tkwin), " create ", itemPtr->typePtr->name,
+ " x1 y1 ?options?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Initialize the item's record by hand (bleah).
+ */
+ epsPtr->anchor = TK_ANCHOR_NW;
+ epsPtr->border = NULL;
+ epsPtr->borderWidth = 2;
+ epsPtr->canvas = canvas;
+ epsPtr->fileName = NULL;
+ epsPtr->psFile = NULL;
+ epsPtr->fillGC = NULL;
+ epsPtr->fillColor = NULL;
+ epsPtr->colorImage = NULL;
+ epsPtr->previewName = NULL;
+ epsPtr->preview = NULL;
+ epsPtr->interp = interp;
+ epsPtr->tmpImage = NULL;
+ epsPtr->pixmap = None;
+ epsPtr->firstLine = epsPtr->lastLine = -1;
+ epsPtr->relief = TK_RELIEF_SUNKEN;
+ epsPtr->reqTitle = NULL;
+ epsPtr->stipple = None;
+ epsPtr->showImage = TRUE;
+ epsPtr->quick = FALSE;
+ epsPtr->title = NULL;
+ epsPtr->lastWidth = epsPtr->lastHeight = 0;
+ epsPtr->width = epsPtr->height = 0;
+ epsPtr->x = epsPtr->y = 0.0;
+ epsPtr->llx = epsPtr->lly = epsPtr->urx = epsPtr->ury = 0;
+ epsPtr->canvasX = epsPtr->canvasY = 0;
+ Tcl_DStringInit(&epsPtr->dString);
+ memset(&(epsPtr->titleStyle), 0, sizeof(TextStyle));
+#define PAD 8
+ epsPtr->titleStyle.padLeft = epsPtr->titleStyle.padRight = PAD;
+ epsPtr->titleStyle.padTop = epsPtr->titleStyle.padBottom = PAD;
+
+ /*
+ * Process the arguments to fill in the item record.
+ */
+
+ if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &(epsPtr->x)) != TCL_OK) ||
+ (Tk_CanvasGetCoord(interp, canvas, argv[1], &(epsPtr->y)) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (ConfigureEps(interp, canvas, itemPtr, argc - 2, argv + 2, 0)
+ != TCL_OK) {
+ DeleteEps(canvas, itemPtr, Tk_Display(tkwin));
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ * The image is over-written each time the EPS item is resized.
+ * So we only worry if the image is deleted.
+ *
+ * We always resample from the color image we saved when the
+ * photo image was specified (-image option).
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+ EpsItem *epsPtr = clientData;
+
+ if ((epsPtr->preview == NULL) || (Tk_ImageIsDeleted(epsPtr->preview))) {
+ epsPtr->preview = NULL;
+ if (epsPtr->previewName != NULL) {
+ Blt_Free(epsPtr->previewName);
+ epsPtr->previewName = NULL;
+ }
+ Tk_CanvasEventuallyRedraw(epsPtr->canvas, epsPtr->xLeft, epsPtr->yTop,
+ epsPtr->xRight, epsPtr->yBottom);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureEps --
+ *
+ * This procedure is invoked to configure various aspects
+ * of an EPS item, such as its background color.
+ *
+ * Results:
+ * A standard Tcl result code. If an error occurs, then
+ * an error message is left in interp->result.
+ *
+ * Side effects:
+ * Configuration information may be set for itemPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureEps(interp, canvas, itemPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tk_Canvas canvas; /* Canvas containing itemPtr. */
+ Tk_Item *itemPtr; /* EPS item to reconfigure. */
+ int argc; /* Number of elements in argv. */
+ char **argv; /* Arguments describing things to configure. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+ Tk_Window tkwin;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ int width, height;
+
+ tkwin = Tk_CanvasTkwin(canvas);
+ if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,
+ argv, (char *)epsPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Determine the size of the EPS item */
+ width = height = 0;
+ /*
+ * Check for a "-image" option specifying an image to be displayed
+ * representing the EPS canvas item.
+ */
+ if (Blt_ConfigModified(configSpecs, "-image", (char *)NULL)) {
+ if (epsPtr->preview != NULL) {
+ Tk_FreeImage(epsPtr->preview); /* Release old Tk image */
+ Blt_FreeColorimage(epsPtr->colorImage);
+ epsPtr->preview = NULL;
+ epsPtr->colorImage = NULL;
+ }
+ if (epsPtr->previewName != NULL) {
+ Tk_PhotoHandle photo; /* Photo handle to Tk image. */
+ /*
+ * Allocate a new image, if one was named.
+ */
+ photo = Blt_FindPhoto(interp, epsPtr->previewName);
+ if (photo == NULL) {
+ Tcl_AppendResult(interp, "image \"", epsPtr->previewName,
+ "\" doesn't exist or is not a photo image",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ epsPtr->preview = Tk_GetImage(interp, tkwin, epsPtr->previewName,
+ ImageChangedProc, epsPtr);
+ if (epsPtr->preview == NULL) {
+ Tcl_AppendResult(interp, "can't find an image \"",
+ epsPtr->previewName, "\"", (char *)NULL);
+ Blt_Free(epsPtr->previewName);
+ epsPtr->previewName = NULL;
+ return TCL_ERROR;
+ }
+ epsPtr->colorImage = Blt_PhotoToColorimage(photo);
+ width = Blt_ColorimageWidth(epsPtr->colorImage);
+ height = Blt_ColorimageHeight(epsPtr->colorImage);
+ }
+ }
+ if (Blt_ConfigModified(configSpecs, "-file", (char *)NULL)) {
+ if (epsPtr->psFile != NULL) {
+ CloseEpsFile(epsPtr);
+ }
+ if (epsPtr->pixmap != None) {
+#ifdef notyet
+ Blt_FreeColorTable(epsPtr->colorTable);
+#endif
+ Tk_FreePixmap(Tk_Display(tkwin), epsPtr->pixmap);
+ epsPtr->pixmap = None;
+ }
+ if (epsPtr->colorImage != NULL) {
+ Blt_FreeColorimage(epsPtr->colorImage);
+ epsPtr->colorImage = NULL;
+ }
+ epsPtr->firstLine = epsPtr->lastLine = -1;
+ if (epsPtr->fileName != NULL) {
+ if (OpenEpsFile(interp, epsPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ if ((epsPtr->colorImage != NULL) && (epsPtr->tmpImage == NULL)) {
+ epsPtr->tmpImage = Blt_CreateTemporaryImage(interp, tkwin, epsPtr);
+ if (epsPtr->tmpImage == NULL) {
+ return TCL_ERROR;
+ }
+ } else if ((epsPtr->colorImage == NULL) && (epsPtr->tmpImage != NULL)) {
+ Blt_DestroyTemporaryImage(epsPtr->interp, epsPtr->tmpImage);
+ }
+ if (epsPtr->preview != NULL) {
+ Tk_SizeOfImage(epsPtr->preview, &width, &height);
+ }
+ if (epsPtr->width == 0) {
+ if (epsPtr->fileName != NULL) {
+ width = (epsPtr->urx - epsPtr->llx);
+ }
+ epsPtr->width = width;
+ }
+ if (epsPtr->height == 0) {
+ if (epsPtr->fileName != NULL) {
+ height = (epsPtr->ury - epsPtr->lly);
+ }
+ epsPtr->height = height;
+ }
+ Blt_ResetTextStyle(tkwin, &(epsPtr->titleStyle));
+
+ if (Blt_ConfigModified(configSpecs, "-quick", (char *)NULL)) {
+ epsPtr->lastWidth = epsPtr->lastHeight = 0;
+ }
+ /* Fill color GC */
+
+ newGC = NULL;
+ if (epsPtr->fillColor != NULL) {
+ gcMask = GCForeground;
+ gcValues.foreground = epsPtr->fillColor->pixel;
+ if (epsPtr->stipple != None) {
+ gcMask |= (GCStipple | GCFillStyle);
+ gcValues.stipple = epsPtr->stipple;
+ if (epsPtr->border != NULL) {
+ gcValues.foreground = Tk_3DBorderColor(epsPtr->border)->pixel;
+ gcValues.background = epsPtr->fillColor->pixel;
+ gcMask |= GCBackground;
+ gcValues.fill_style = FillOpaqueStippled;
+ } else {
+ gcValues.fill_style = FillStippled;
+ }
+ }
+ newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+ }
+ if (epsPtr->fillGC != NULL) {
+ Tk_FreeGC(Tk_Display(tkwin), epsPtr->fillGC);
+ }
+ epsPtr->fillGC = newGC;
+ CloseEpsFile(epsPtr);
+ ComputeEpsBbox(canvas, epsPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EpsCoords --
+ *
+ * This procedure is invoked to process the "coords" widget
+ * command on EPS items. See the user documentation for
+ * details on what it does.
+ *
+ * Results:
+ * Returns TCL_OK or TCL_ERROR, and sets interp->result.
+ *
+ * Side effects:
+ * The coordinates for the given item may be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EpsCoords(interp, canvas, itemPtr, argc, argv)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tk_Canvas canvas; /* Canvas containing item. */
+ Tk_Item *itemPtr; /* Item whose coordinates are to be
+ * read or modified. */
+ int argc; /* Number of coordinates supplied in
+ * argv. */
+ char **argv; /* Array of coordinates: x1, y1,
+ * x2, y2, ... */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+
+ if ((argc != 0) && (argc != 2)) {
+ Tcl_AppendResult(interp, "wrong # coordinates: expected 0 or 2, got ",
+ Blt_Itoa(argc), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 2) {
+ double x, y; /* Don't overwrite old coordinates on errors */
+
+ if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &x) != TCL_OK) ||
+ (Tk_CanvasGetCoord(interp, canvas, argv[1], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ epsPtr->x = x;
+ epsPtr->y = y;
+ ComputeEpsBbox(canvas, epsPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, epsPtr->x));
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, epsPtr->y));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeEpsBbox --
+ *
+ * This procedure is invoked to compute the bounding box of
+ * all the pixels that may be drawn as part of a EPS item.
+ * This procedure is where the preview image's placement is
+ * computed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The fields x1, y1, x2, and y2 are updated in the header
+ * for itemPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+ComputeEpsBbox(canvas, epsPtr)
+ Tk_Canvas canvas; /* Canvas that contains item. */
+ EpsItem *epsPtr; /* Item whose bbox is to be recomputed. */
+{
+ int x, y;
+
+ x = ROUND(epsPtr->x), y = ROUND(epsPtr->y);
+ Blt_TranslateAnchor(x, y, epsPtr->width, epsPtr->height, epsPtr->anchor,
+ &x, &y);
+ epsPtr->xLeft = epsPtr->canvasX = x;
+ epsPtr->yTop = epsPtr->canvasY = y;
+
+ /*
+ * The right and bottom are (weirdly) exterior to the item. Can't
+ * complain much since it's documented in the Tk_CreateItemType
+ * manual page.
+ *
+ * "These fields give a bounding box for the items using integer
+ * canvas coordinates: the item should not cover any pixels with
+ * x-coordinate lower than x1 or y-coordinate lower than y1, nor
+ * should it cover any pixels with x-coordinate greater than or
+ * equal to x2 or y-coordinate greater than or equal to y2."
+ */
+ epsPtr->xRight = x + epsPtr->width;
+ epsPtr->yBottom = y + epsPtr->height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayEps --
+ *
+ * This procedure is invoked to draw the EPS item in a
+ * given drawable. The EPS item may be drawn as either
+ * a solid rectangle or a pixmap of the preview image.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * ItemPtr is drawn in drawable using the transformation
+ * information in canvas.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DisplayEps(canvas, itemPtr, display, drawable, x, y, width, height)
+ Tk_Canvas canvas; /* Canvas that contains item. */
+ Tk_Item *itemPtr; /* Item to be displayed. */
+ Display *display; /* Display on which to draw item. */
+ Drawable drawable; /* Pixmap or window in which to draw
+ * item. */
+ int x, y, width, height; /* Describes region of canvas that
+ * must be redisplayed (not used). */
+{
+ Tk_Window tkwin;
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+ short int drawableX, drawableY;
+ char *title;
+ int twiceBW;
+ int noImage;
+
+ if ((epsPtr->width < 1) || (epsPtr->height < 1)) {
+ return;
+ }
+ tkwin = Tk_CanvasTkwin(canvas);
+ epsPtr->showImage = TRUE;
+ if ((epsPtr->showImage) && (epsPtr->colorImage != NULL) &&
+ ((epsPtr->lastWidth != epsPtr->width) ||
+ (epsPtr->lastHeight != epsPtr->height))) {
+ Blt_Colorimage image;
+
+ if (epsPtr->quick) {
+ image = Blt_ResizeColorimage(epsPtr->colorImage, 0, 0,
+ Blt_ColorimageWidth(epsPtr->colorImage),
+ Blt_ColorimageHeight(epsPtr->colorImage),
+ epsPtr->width, epsPtr->height);
+ } else {
+ image = Blt_ResampleColorimage(epsPtr->colorImage, epsPtr->width,
+ epsPtr->height, bltBoxFilterPtr, bltBoxFilterPtr);
+ }
+ if (epsPtr->tmpImage != NULL) {
+ Tk_PhotoHandle photo;
+ /*
+ * Resize the Tk photo image used to represent the EPS item.
+ * We will over-write the temporary image with a resampled one.
+ */
+ photo = Blt_FindPhoto(epsPtr->interp,
+ Blt_NameOfImage(epsPtr->tmpImage));
+ Blt_ColorimageToPhoto(image, photo);
+ } else {
+#ifdef notyet
+ epsPtr->pixmap = Blt_ColorimageToPixmap(epsPtr->interp, tkwin,
+ image, &(epsPtr->colorTable));
+#endif
+ }
+ epsPtr->lastHeight = epsPtr->height;
+ epsPtr->lastWidth = epsPtr->width;
+ Blt_FreeColorimage(image);
+ }
+ /*
+ * Translate the coordinates to those of the EPS item, then redisplay it.
+ */
+ Tk_CanvasDrawableCoords(canvas, (double)epsPtr->canvasX,
+ (double)epsPtr->canvasY, &drawableX, &drawableY);
+ x = (int)drawableX;
+ y = (int)drawableY;
+
+ twiceBW = epsPtr->borderWidth * 2;
+ title = epsPtr->title;
+
+ if (epsPtr->reqTitle != NULL) {
+ title = epsPtr->reqTitle;
+ }
+ width = epsPtr->width;
+ height = epsPtr->height;
+ noImage = ((!epsPtr->showImage) || ((epsPtr->tmpImage == NULL) &&
+ (epsPtr->pixmap == None)));
+ if (noImage) {
+ if ((twiceBW >= width) || (twiceBW >= height)) {
+ return;
+ }
+ width -= twiceBW;
+ height -= twiceBW;
+ if (epsPtr->fillGC != NULL) {
+ XSetTSOrigin(display, epsPtr->fillGC, x, y);
+ XFillRectangle(display, drawable, epsPtr->fillGC, x, y,
+ epsPtr->width, epsPtr->height);
+ XSetTSOrigin(display, epsPtr->fillGC, 0, 0);
+ }
+ } else {
+ if (epsPtr->pixmap != None) {
+ XCopyArea(Tk_Display(tkwin), epsPtr->pixmap, drawable,
+ epsPtr->fillGC, 0, 0, width, height, x, y);
+ } else {
+ Tk_RedrawImage(epsPtr->tmpImage, 0, 0, width, height, drawable,
+ x, y);
+ }
+ }
+
+ if (title != NULL) {
+ TextLayout *textPtr;
+ int rotWidth, rotHeight;
+
+ /* Translate the title to an anchor position within the EPS item */
+ textPtr = Blt_GetTextLayout(title, &(epsPtr->titleStyle));
+ Blt_GetBoundingBox(textPtr->width, textPtr->height,
+ epsPtr->titleStyle.theta, &rotWidth, &rotHeight, (Point2D *)NULL);
+ if ((rotWidth <= width) && (rotHeight <= height)) {
+ int titleX, titleY;
+
+ Blt_TranslateAnchor(x, y, width, height, epsPtr->titleStyle.anchor,
+ &titleX, &titleY);
+ if (noImage) {
+ titleX += epsPtr->borderWidth;
+ titleY += epsPtr->borderWidth;
+ }
+ Blt_DrawTextLayout(tkwin, drawable, textPtr, &(epsPtr->titleStyle),
+ titleX, titleY);
+ }
+ Blt_Free(textPtr);
+ }
+ if ((noImage) && (epsPtr->border != NULL)) {
+ Tk_Draw3DRectangle(tkwin, drawable, epsPtr->border, x, y,
+ epsPtr->width, epsPtr->height, epsPtr->borderWidth, epsPtr->relief);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EpsToPoint --
+ *
+ * Computes the distance from a given point to a given
+ * rectangle, in canvas units.
+ *
+ * Results:
+ * The return value is 0 if the point whose x and y coordinates
+ * are coordPtr[0] and coordPtr[1] is inside the EPS item. If the
+ * point isn't inside the item then the return value is the
+ * distance from the point to the EPS item.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static double
+EpsToPoint(canvas, itemPtr, coordArr)
+ Tk_Canvas canvas; /* Canvas containing item. */
+ Tk_Item *itemPtr; /* Item to check against point. */
+ double *coordArr; /* Pointer to x and y coordinates. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+ double dx, dy;
+
+ /*
+ * Point is outside rectangle.
+ */
+ if (coordArr[0] < epsPtr->xLeft) {
+ dx = epsPtr->xLeft - coordArr[0];
+ } else if (coordArr[0] > epsPtr->xRight) {
+ dx = coordArr[0] - epsPtr->xRight;
+ } else {
+ dx = 0;
+ }
+ if (coordArr[1] < epsPtr->yTop) {
+ dy = epsPtr->yTop - coordArr[1];
+ } else if (coordArr[1] > epsPtr->yBottom) {
+ dy = coordArr[1] - epsPtr->yBottom;
+ } else {
+ dy = 0;
+ }
+ return hypot(dx, dy);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EpsToArea --
+ *
+ * This procedure is called to determine whether an item
+ * lies entirely inside, entirely outside, or overlapping
+ * a given rectangle.
+ *
+ * Results:
+ * -1 is returned if the item is entirely outside the area
+ * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
+ * inside the given area.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EpsToArea(canvas, itemPtr, area)
+ Tk_Canvas canvas; /* Canvas containing item. */
+ Tk_Item *itemPtr; /* Item to check against rectangle. */
+ double area[]; /* Pointer to array of four coordinates
+ * (x1, y1, x2, y2) describing rectangular
+ * area. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+
+ if ((area[2] <= epsPtr->xLeft) || (area[0] >= epsPtr->xRight) ||
+ (area[3] <= epsPtr->yTop) || (area[1] >= epsPtr->yBottom)) {
+ return -1;
+ }
+ if ((area[0] <= epsPtr->xLeft) && (area[1] <= epsPtr->yTop) &&
+ (area[2] >= epsPtr->xRight) && (area[3] >= epsPtr->yBottom)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScaleEps --
+ *
+ * This procedure is invoked to rescale an item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The item referred to by itemPtr is rescaled so that the
+ * following transformation is applied to all point coordinates:
+ * x' = originX + scaleX*(x-originX)
+ * y' = originY + scaleY*(y-originY)
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ScaleEps(canvas, itemPtr, originX, originY, scaleX, scaleY)
+ Tk_Canvas canvas; /* Canvas containing rectangle. */
+ Tk_Item *itemPtr; /* Rectangle to be scaled. */
+ double originX, originY; /* Origin about which to scale rect. */
+ double scaleX; /* Amount to scale in X direction. */
+ double scaleY; /* Amount to scale in Y direction. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+
+ epsPtr->x = originX + scaleX * (epsPtr->x - originX);
+ epsPtr->y = originY + scaleY * (epsPtr->y - originY);
+ ComputeEpsBbox(canvas, epsPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TranslateEps --
+ *
+ * This procedure is called to move an item by a given amount.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The position of the item is offset by (xDelta, yDelta), and
+ * the bounding box is updated in the generic part of the item
+ * structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TranslateEps(canvas, itemPtr, deltaX, deltaY)
+ Tk_Canvas canvas; /* Canvas containing item. */
+ Tk_Item *itemPtr; /* Item that is being moved. */
+ double deltaX, deltaY; /* Amount by which item is to be
+ * moved. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+
+ epsPtr->x += deltaX;
+ epsPtr->y += deltaY;
+ ComputeEpsBbox(canvas, epsPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EpsToPostscript --
+ *
+ * This procedure is called to generate Postscript for EPS
+ * canvas items.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If an error
+ * occurs in generating Postscript then an error message is
+ * left in interp->result, replacing whatever used
+ * to be there. If no error occurs, then Postscript for the
+ * item is appended to the result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EpsToPostScript(interp, canvas, itemPtr, prepass)
+ Tcl_Interp *interp; /* Leave Postscript or error message
+ * here. */
+ Tk_Canvas canvas; /* Information about overall canvas. */
+ Tk_Item *itemPtr; /* Item for which Postscript is
+ * wanted. */
+ int prepass; /* 1 means this is a prepass to
+ * collect font information; 0 means
+ * final Postscript is being created. */
+{
+ EpsItem *epsPtr = (EpsItem *)itemPtr;
+ PsToken psToken;
+ Tk_Window tkwin;
+ double xScale, yScale;
+ int x, y, width, height;
+ unsigned int count; /* Tracks the current line number. */
+ char *buf;
+
+ if (prepass) {
+ return TCL_OK;
+ }
+ tkwin = Tk_CanvasTkwin(epsPtr->canvas);
+ psToken = Blt_GetPsToken(interp, tkwin);
+ x = epsPtr->canvasX;
+ y = (int)Tk_CanvasPsY(canvas, (double)epsPtr->canvasY + epsPtr->height);
+
+ if (epsPtr->fileName == NULL) {
+ /* No PostScript file, generate PostScript of resized image instead. */
+ if (epsPtr->tmpImage != NULL) {
+ Tk_PhotoHandle photo;
+
+ Blt_FormatToPostScript(psToken, "gsave\n");
+ /*
+ * First flip the PostScript y-coordinate axis so that the
+ * origin is the upper-left corner like our color image.
+ */
+ Blt_FormatToPostScript(psToken, " %d %d translate\n",
+ x, y + epsPtr->height);
+ Blt_FormatToPostScript(psToken, " 1 -1 scale\n");
+
+ photo = Blt_FindPhoto(epsPtr->interp,
+ Blt_NameOfImage(epsPtr->tmpImage));
+ Blt_PhotoToPostScript(psToken, photo, 0.0, 0.0);
+ Blt_FormatToPostScript(psToken, "grestore\n");
+
+ Tcl_AppendResult(interp, Blt_PostScriptFromToken(psToken),
+ (char *)NULL);
+ Blt_ReleasePsToken(psToken);
+ }
+ return TCL_OK;
+ }
+ if (epsPtr->psFile == NULL) {
+ Tcl_AppendResult(interp, "can't get handle to EPS file", (char *)NULL);
+ goto error;
+ }
+ /* Copy in the PostScript prolog for EPS encapsulation. */
+
+ if (Blt_FileToPostScript(psToken, "bltCanvEps.pro") != TCL_OK) {
+ goto error;
+ }
+ Blt_AppendToPostScript(psToken, "BeginEPSF\n", (char *)NULL);
+
+ width = epsPtr->width;
+ height = epsPtr->height;
+ xScale = (double)width / (double)(epsPtr->urx - epsPtr->llx);
+ yScale = (double)height / (double)(epsPtr->ury - epsPtr->lly);
+
+ /* Set up scaling and translation transformations for the EPS item */
+
+ Blt_FormatToPostScript(psToken, "%d %d translate\n", x, y);
+ Blt_FormatToPostScript(psToken, "%g %g scale\n", xScale, yScale);
+ Blt_FormatToPostScript(psToken, "%d %d translate\n", -(epsPtr->llx),
+ -(epsPtr->lly));
+ Blt_FormatToPostScript(psToken, "%d %d %d %d SetClipRegion\n",
+ epsPtr->llx, epsPtr->lly, epsPtr->urx, epsPtr->ury);
+ Blt_AppendToPostScript(psToken, "%% including \"", epsPtr->fileName,
+ "\"\n\n", (char *)NULL);
+
+ buf = Blt_ScratchBufferFromToken(psToken);
+ count = 0;
+ Blt_AppendToPostScript(psToken, Tcl_DStringValue(&epsPtr->dString),
+ (char *)NULL);
+ Blt_AppendToPostScript(psToken, "EndEPSF\n", (char *)NULL);
+ Tcl_AppendResult(interp, Blt_PostScriptFromToken(psToken), (char *)NULL);
+ Blt_ReleasePsToken(psToken);
+ return TCL_OK;
+
+ error:
+ Blt_ReleasePsToken(psToken);
+ return TCL_ERROR;
+}
+
+/*
+ * The structures below defines the EPS item type in terms of
+ * procedures that can be invoked by generic item code.
+ */
+static Tk_ItemType epsItemType =
+{
+ "eps", /* name */
+ sizeof(EpsItem), /* itemSize */
+ CreateEps, /* createProc */
+ configSpecs, /* configSpecs */
+ ConfigureEps, /* configureProc */
+ EpsCoords, /* coordProc */
+ DeleteEps, /* deleteProc */
+ DisplayEps, /* displayProc */
+ 0, /* alwaysRedraw */
+ EpsToPoint, /* pointProc */
+ EpsToArea, /* areaProc */
+ EpsToPostScript, /* postscriptProc */
+ ScaleEps, /* scaleProc */
+ TranslateEps, /* translateProc */
+ (Tk_ItemIndexProc *) NULL, /* indexProc */
+ (Tk_ItemCursorProc *) NULL, /* icursorProc */
+ (Tk_ItemSelectionProc *) NULL, /* selectionProc */
+ (Tk_ItemInsertProc *) NULL, /* insertProc */
+ (Tk_ItemDCharsProc *) NULL, /* dTextProc */
+ (Tk_ItemType *) NULL /* nextPtr */
+};
+
+/*ARGSUSED*/
+void
+Blt_InitEpsCanvasItem(interp)
+ Tcl_Interp *interp; /* Not used. */
+{
+ Tk_CreateItemType(&epsItemType);
+ /* Initialize custom canvas option routines. */
+ tagsOption.parseProc = Tk_CanvasTagsParseProc;
+ tagsOption.printProc = Tk_CanvasTagsPrintProc;
+}
diff --git a/blt/src/bltChain.c b/blt/src/bltChain.c
new file mode 100644
index 00000000000..6a5c1df4753
--- /dev/null
+++ b/blt/src/bltChain.c
@@ -0,0 +1,445 @@
+/*
+ * bltChain.c --
+ *
+ * The module implements a generic linked list package.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+
+#ifndef ALIGN
+#define ALIGN(a) \
+ (((size_t)a + (sizeof(double) - 1)) & (~(sizeof(double) - 1)))
+#endif /* ALIGN */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainCreate --
+ *
+ * Creates a new linked list (chain) structure and initializes
+ * its pointers;
+ *
+ * Results:
+ * Returns a pointer to the newly created chain structure.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Chain *
+Blt_ChainCreate()
+{
+ Blt_Chain *chainPtr;
+
+ chainPtr = Blt_Malloc(sizeof(Blt_Chain));
+ if (chainPtr != NULL) {
+ Blt_ChainInit(chainPtr);
+ }
+ return chainPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainAllocLink --
+ *
+ * Creates a new chain link. Unlink Blt_ChainNewLink, this
+ * routine also allocates extra memory in the node for data.
+ *
+ * Results:
+ * The return value is the pointer to the newly created entry.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainAllocLink(extraSize)
+ unsigned int extraSize;
+{
+ Blt_ChainLink *linkPtr;
+ unsigned int linkSize;
+
+ linkSize = ALIGN(sizeof(Blt_ChainLink));
+ linkPtr = Blt_Calloc(1, linkSize + extraSize);
+ assert(linkPtr);
+ if (extraSize > 0) {
+ /* Point clientData at the memory beyond the normal structure. */
+ linkPtr->clientData = (ClientData)((char *)linkPtr + linkSize);
+ }
+ return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainNewLink --
+ *
+ * Creates a new link.
+ *
+ * Results:
+ * The return value is the pointer to the newly created link.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainNewLink()
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_Malloc(sizeof(Blt_ChainLink));
+ assert(linkPtr);
+ linkPtr->clientData = NULL;
+ linkPtr->nextPtr = linkPtr->prevPtr = NULL;
+ return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainReset --
+ *
+ * Removes all the links from the chain, freeing the memory for
+ * each link. Memory pointed to by the link (clientData) is not
+ * freed. It's the caller's responsibility to deallocate it.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainReset(chainPtr)
+ Blt_Chain *chainPtr; /* Chain to clear */
+{
+ if (chainPtr != NULL) {
+ Blt_ChainLink *oldPtr;
+ Blt_ChainLink *linkPtr = chainPtr->headPtr;
+
+ while (linkPtr != NULL) {
+ oldPtr = linkPtr;
+ linkPtr = linkPtr->nextPtr;
+ Blt_Free(oldPtr);
+ }
+ Blt_ChainInit(chainPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainDestroy
+ *
+ * Frees all the nodes from the chain and deallocates the memory
+ * allocated for the chain structure itself. It's assumed that
+ * the chain was previous allocated by Blt_ChainCreate.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainDestroy(chainPtr)
+ Blt_Chain *chainPtr;
+{
+ if (chainPtr != NULL) {
+ Blt_ChainReset(chainPtr);
+ Blt_Free(chainPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainInit --
+ *
+ * Initializes a linked list.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainInit(chainPtr)
+ Blt_Chain *chainPtr;
+{
+ chainPtr->nLinks = 0;
+ chainPtr->headPtr = chainPtr->tailPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainLinkAfter --
+ *
+ * Inserts an entry following a given entry.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainLinkAfter(chainPtr, linkPtr, afterPtr)
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr, *afterPtr;
+{
+ if (chainPtr->headPtr == NULL) {
+ chainPtr->tailPtr = chainPtr->headPtr = linkPtr;
+ } else {
+ if (afterPtr == NULL) {
+ /* Prepend to the front of the chain */
+ linkPtr->nextPtr = chainPtr->headPtr;
+ linkPtr->prevPtr = NULL;
+ chainPtr->headPtr->prevPtr = linkPtr;
+ chainPtr->headPtr = linkPtr;
+ } else {
+ linkPtr->nextPtr = afterPtr->nextPtr;
+ linkPtr->prevPtr = afterPtr;
+ if (afterPtr == chainPtr->tailPtr) {
+ chainPtr->tailPtr = linkPtr;
+ } else {
+ afterPtr->nextPtr->prevPtr = linkPtr;
+ }
+ afterPtr->nextPtr = linkPtr;
+ }
+ }
+ chainPtr->nLinks++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainLinkBefore --
+ *
+ * Inserts a link preceding a given link.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainLinkBefore(chainPtr, linkPtr, beforePtr)
+ Blt_Chain *chainPtr; /* Chain to contain new entry */
+ Blt_ChainLink *linkPtr; /* New entry to be inserted */
+ Blt_ChainLink *beforePtr; /* Entry to link before */
+{
+ if (chainPtr->headPtr == NULL) {
+ chainPtr->tailPtr = chainPtr->headPtr = linkPtr;
+ } else {
+ if (beforePtr == NULL) {
+ /* Append onto the end of the chain */
+ linkPtr->nextPtr = NULL;
+ linkPtr->prevPtr = chainPtr->tailPtr;
+ chainPtr->tailPtr->nextPtr = linkPtr;
+ chainPtr->tailPtr = linkPtr;
+ } else {
+ linkPtr->prevPtr = beforePtr->prevPtr;
+ linkPtr->nextPtr = beforePtr;
+ if (beforePtr == chainPtr->headPtr) {
+ chainPtr->headPtr = linkPtr;
+ } else {
+ beforePtr->prevPtr->nextPtr = linkPtr;
+ }
+ beforePtr->prevPtr = linkPtr;
+ }
+ }
+ chainPtr->nLinks++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainUnlinkLink --
+ *
+ * Unlinks a link from the chain. The link is not deallocated,
+ * but only removed from the chain.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainUnlinkLink(chainPtr, linkPtr)
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+{
+ int unlinked; /* Indicates if the link is actually
+ * removed from the chain. */
+
+ unlinked = FALSE;
+ if (chainPtr->headPtr == linkPtr) {
+ chainPtr->headPtr = linkPtr->nextPtr;
+ unlinked = TRUE;
+ }
+ if (chainPtr->tailPtr == linkPtr) {
+ chainPtr->tailPtr = linkPtr->prevPtr;
+ unlinked = TRUE;
+ }
+ if (linkPtr->nextPtr != NULL) {
+ linkPtr->nextPtr->prevPtr = linkPtr->prevPtr;
+ unlinked = TRUE;
+ }
+ if (linkPtr->prevPtr != NULL) {
+ linkPtr->prevPtr->nextPtr = linkPtr->nextPtr;
+ unlinked = TRUE;
+ }
+ if (unlinked) {
+ chainPtr->nLinks--;
+ }
+ linkPtr->prevPtr = linkPtr->nextPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainDeleteLink --
+ *
+ * Unlinks and also frees the given link.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainDeleteLink(chainPtr, linkPtr)
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+{
+ Blt_ChainUnlinkLink(chainPtr, linkPtr);
+ Blt_Free(linkPtr);
+}
+
+Blt_ChainLink *
+Blt_ChainAppend(chainPtr, clientData)
+ Blt_Chain *chainPtr;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNewLink();
+ Blt_ChainLinkBefore(chainPtr, linkPtr, (Blt_ChainLink *)NULL);
+ Blt_ChainSetValue(linkPtr, clientData);
+ return linkPtr;
+}
+
+Blt_ChainLink *
+Blt_ChainPrepend(chainPtr, clientData)
+ Blt_Chain *chainPtr;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNewLink();
+ Blt_ChainLinkAfter(chainPtr, linkPtr, (Blt_ChainLink *)NULL);
+ Blt_ChainSetValue(linkPtr, clientData);
+ return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainGetNthLink --
+ *
+ * Find the link at the given position in the chain.
+ *
+ * Results:
+ * Returns the pointer to the link, if that numbered link
+ * exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainGetNthLink(chainPtr, position)
+ Blt_Chain *chainPtr; /* Chain to traverse */
+ int position; /* Index of link to select from front
+ * or back of the chain. */
+{
+ Blt_ChainLink *linkPtr;
+
+ if (chainPtr != NULL) {
+ for (linkPtr = chainPtr->headPtr; linkPtr != NULL;
+ linkPtr = linkPtr->nextPtr) {
+ if (position == 0) {
+ return linkPtr;
+ }
+ position--;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainSort --
+ *
+ * Sorts the chain according to the given comparison routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The chain is reordered.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainSort(chainPtr, proc)
+ Blt_Chain *chainPtr; /* Chain to traverse */
+ Blt_ChainCompareProc *proc;
+{
+ Blt_ChainLink **linkArr;
+ register Blt_ChainLink *linkPtr;
+ register int i;
+
+ if (chainPtr->nLinks < 2) {
+ return;
+ }
+ linkArr = Blt_Malloc(sizeof(Blt_ChainLink *) * (chainPtr->nLinks + 1));
+ if (linkArr == NULL) {
+ return; /* Out of memory. */
+ }
+ i = 0;
+ for (linkPtr = chainPtr->headPtr; linkPtr != NULL;
+ linkPtr = linkPtr->nextPtr) {
+ linkArr[i++] = linkPtr;
+ }
+ qsort((char *)linkArr, chainPtr->nLinks, sizeof(Blt_ChainLink *),
+ (QSortCompareProc *)proc);
+
+ /* Rethread the chain. */
+ linkPtr = linkArr[0];
+ chainPtr->headPtr = linkPtr;
+ linkPtr->prevPtr = NULL;
+ for (i = 1; i < chainPtr->nLinks; i++) {
+ linkPtr->nextPtr = linkArr[i];
+ linkPtr->nextPtr->prevPtr = linkPtr;
+ linkPtr = linkPtr->nextPtr;
+ }
+ chainPtr->tailPtr = linkPtr;
+ linkPtr->nextPtr = NULL;
+ Blt_Free(linkArr);
+}
diff --git a/blt/src/bltChain.h b/blt/src/bltChain.h
new file mode 100644
index 00000000000..e9ac0c5c956
--- /dev/null
+++ b/blt/src/bltChain.h
@@ -0,0 +1,85 @@
+/*
+ * bltChain.h --
+ *
+ * Copyright 1993-2000 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#ifndef _BLT_CHAIN_H
+#define _BLT_CHAIN_H
+
+typedef struct Blt_ChainLinkStruct Blt_ChainLink;
+
+/*
+ * A Blt_ChainLink is the container structure for the Blt_Chain.
+ */
+
+struct Blt_ChainLinkStruct {
+ Blt_ChainLink *prevPtr; /* Link to the previous link */
+ Blt_ChainLink *nextPtr; /* Link to the next link */
+ ClientData clientData; /* Pointer to the data object */
+};
+
+typedef int (Blt_ChainCompareProc) _ANSI_ARGS_((Blt_ChainLink **l1PtrPtr,
+ Blt_ChainLink **l2PtrPtr));
+
+/*
+ * A Blt_Chain is a doubly chained list structure.
+ */
+typedef struct {
+ Blt_ChainLink *headPtr; /* Pointer to first element in chain */
+ Blt_ChainLink *tailPtr; /* Pointer to last element in chain */
+ int nLinks; /* Number of elements in chain */
+} Blt_Chain;
+
+extern void Blt_ChainInit _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern Blt_Chain *Blt_ChainCreate _ANSI_ARGS_(());
+extern void Blt_ChainDestroy _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern Blt_ChainLink *Blt_ChainNewLink _ANSI_ARGS_((void));
+extern Blt_ChainLink *Blt_ChainAllocLink _ANSI_ARGS_((unsigned int size));
+extern Blt_ChainLink *Blt_ChainAppend _ANSI_ARGS_((Blt_Chain * chainPtr,
+ ClientData clientData));
+extern Blt_ChainLink *Blt_ChainPrepend _ANSI_ARGS_((Blt_Chain * chainPtr,
+ ClientData clientData));
+extern void Blt_ChainReset _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern void Blt_ChainLinkAfter _ANSI_ARGS_((Blt_Chain * chainPtr,
+ Blt_ChainLink * linkPtr, Blt_ChainLink * afterLinkPtr));
+extern void Blt_ChainLinkBefore _ANSI_ARGS_((Blt_Chain * chainPtr,
+ Blt_ChainLink * linkPtr, Blt_ChainLink * beforeLinkPtr));
+extern void Blt_ChainUnlinkLink _ANSI_ARGS_((Blt_Chain * chainPtr,
+ Blt_ChainLink * linkPtr));
+extern void Blt_ChainDeleteLink _ANSI_ARGS_((Blt_Chain * chainPtr,
+ Blt_ChainLink * linkPtr));
+extern Blt_ChainLink *Blt_ChainGetNthLink _ANSI_ARGS_((Blt_Chain * chainPtr, int n));
+extern void Blt_ChainSort _ANSI_ARGS_((Blt_Chain * chainPtr,
+ Blt_ChainCompareProc * proc));
+
+#define Blt_ChainGetLength(c) (((c) == NULL) ? 0 : (c)->nLinks)
+#define Blt_ChainFirstLink(c) (((c) == NULL) ? NULL : (c)->headPtr)
+#define Blt_ChainLastLink(c) (((c) == NULL) ? NULL : (c)->tailPtr)
+#define Blt_ChainPrevLink(l) ((l)->prevPtr)
+#define Blt_ChainNextLink(l) ((l)->nextPtr)
+#define Blt_ChainGetValue(l) ((l)->clientData)
+#define Blt_ChainSetValue(l, value) ((l)->clientData = (ClientData)(value))
+#define Blt_ChainAppendLink(c, l) \
+ (Blt_ChainLinkBefore((c), (l), (Blt_ChainLink *)NULL))
+#define Blt_ChainPrependLink(c, l) \
+ (Blt_ChainLinkAfter((c), (l), (Blt_ChainLink *)NULL))
+
+#endif /* _BLT_CHAIN_H */
diff --git a/blt/src/bltColor.c b/blt/src/bltColor.c
new file mode 100644
index 00000000000..aed25bf3be9
--- /dev/null
+++ b/blt/src/bltColor.c
@@ -0,0 +1,980 @@
+
+/*
+ * bltColor.c --
+ *
+ * This module contains routines for color allocation strategies
+ * used with color images in the BLT toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * Color strategies of 8-bit visuals:
+ *
+ * Try to "best" represent an N-color image into 8-bit (256 color)
+ * colormap. The simplest method is use the high bits of each RGB
+ * value (3 bits for red and green, 2 bits for blue). But this
+ * produces lots of contouring since the distribution of colors tends
+ * to be clustered. Other problems: probably can't allocate even 256
+ * colors. Other applications will have already taken some color
+ * slots. Furthermore, we might be displaying several images, and we
+ * can't assume that all images are representative of the colors used.
+ *
+ * If we use a private colormap, we may want to allocate some number
+ * of colors from the default colormap to prevent flashing when
+ * colormaps are switched.
+ *
+ * Switches:
+ *
+ * -exact boolean Try to match the colors of the image rather
+ * then generating a "best" color ramp.
+ *
+ * -threshold value Maximum average error. Indicates how far
+ * to reduce the quantized color palette.
+ *
+ * -tolerance value Allow colors within this distance to match.
+ * This will weight allocation towards harder
+ * to match colors, rather than the most
+ * frequent.
+ *
+ * -mincolors number Minimum number of reduced quantized colors.
+ * or color ramp.
+ *
+ * -dither boolean Turn on/off Floyd-Steinberg dithering.
+ *
+ * -keep number Hint to keep the first N colors in the
+ * in the default colormap. This only applies to
+ * private colormaps.
+ *
+ * -ramp number Number of colors to use in a linear ramp.
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef WIN32
+
+#include "bltHash.h"
+#include "bltImage.h"
+
+#define NCOLORS 256
+
+
+static void
+GetPaletteSizes(nColors, nRedsPtr, nGreensPtr, nBluesPtr)
+ int nColors; /* Number of colors requested. */
+ unsigned int *nRedsPtr; /* (out) Number of red components. */
+ unsigned int *nGreensPtr; /* (out) Number of green components. */
+ unsigned int *nBluesPtr; /* (out) Number of blue components. */
+{
+ unsigned int nBlues, nReds, nGreens;
+
+ assert(nColors > 1);
+ nBlues = nReds = nGreens = 0;
+ while ((nBlues * nBlues * nBlues) <= nColors) {
+ nBlues++;
+ }
+ nBlues--;
+ while ((nReds * nReds * nBlues) <= nColors) {
+ nReds++;
+ }
+ nReds--;
+ nGreens = nColors / (nBlues * nReds);
+
+ *nRedsPtr = nReds;
+ *nGreensPtr = nGreens;
+ *nBluesPtr = nBlues;
+}
+
+static void
+BuildColorRamp(palettePtr, nColors)
+ Pix32 *palettePtr;
+ int nColors;
+{
+ register unsigned int r, g, b;
+ unsigned int short red, green, blue;
+ unsigned int nReds, nGreens, nBlues;
+
+ GetPaletteSizes(nColors, &nReds, &nGreens, &nBlues);
+ for (r = 0; r < nReds; r++) {
+ red = (r * USHRT_MAX) / (nReds - 1);
+ for (g = 0; g < nGreens; g++) {
+ green = (g * USHRT_MAX) / (nGreens - 1);
+ for (b = 0; b < nBlues; b++) {
+ blue = (b * USHRT_MAX) / (nBlues - 1);
+ palettePtr->Red = red;
+ palettePtr->Green = green;
+ palettePtr->Blue = blue;
+ palettePtr++;
+ }
+ }
+ }
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * QueryColormap --
+ *
+ * This is for psuedo-color displays only. Fills an array or
+ * XColors with the color values (RGB and pixel) currently
+ * allocated in the colormap.
+ *
+ * Results:
+ * The number of colors allocated is returned. The array "colorArr"
+ * will contain the XColor values of each color in the colormap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+QueryColormap(display, colorMap, mapColors, numMapColorsPtr)
+ Display *display;
+ Colormap colorMap;
+ XColor mapColors[];
+ int *numMapColorsPtr;
+{
+ unsigned long int pixelValues[NCOLORS];
+ int numAvail, numMapColors;
+ register int i;
+ register XColor *colorPtr;
+ register unsigned long *indexPtr;
+ int inUse[NCOLORS];
+
+ /* Initially, we assume all color cells are allocated. */
+ memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+
+ /*
+ * Start allocating color cells. This will tell us which color cells
+ * haven't already been allocated in the colormap. We'll release the
+ * cells as soon as we find out how many there are.
+ */
+ numAvail = 0;
+ for (indexPtr = pixelValues, i = 0; i < NCOLORS; i++, indexPtr++) {
+ if (!XAllocColorCells(display, colorMap, False, NULL, 0, indexPtr, 1)) {
+ break;
+ }
+ inUse[*indexPtr] = TRUE;/* Indicate the cell is unallocated */
+ numAvail++;
+ }
+ XFreeColors(display, colorMap, pixelValues, numAvail, 0);
+
+ /*
+ * Put the indices of the cells already allocated into a color array.
+ * We'll use the array to query the RGB values of the allocated colors.
+ */
+ numMapColors = 0;
+ colorPtr = mapColors;
+ for (i = 0; i < NCOLORS; i++) {
+ if (!inUse[i]) {
+ colorPtr->pixel = i;
+ colorPtr->flags = (DoRed | DoGreen | DoBlue);
+ colorPtr++, numMapColors++;
+ }
+ }
+ XQueryColors(display, colorMap, mapColors, numMapColors);
+ *numMapColorsPtr = numMapColors;
+#ifdef notdef
+ fprintf(stderr, "Number of colors (allocated/free) %d/%d\n", numMapColors,
+ numAvail);
+#endif
+ return numAvail;
+}
+
+static void
+FindClosestColor(colorPtr, mapColors, numMapColors)
+ ColorInfo *colorPtr;
+ XColor mapColors[];
+ int numMapColors;
+{
+ double r, g, b;
+ register int i;
+ double dist, min;
+ XColor *lastMatch;
+ register XColor *mapColorPtr;
+
+ min = DBL_MAX; /* Any color is closer. */
+ lastMatch = NULL;
+
+ /* Linear search of color */
+
+ mapColorPtr = mapColors;
+ for (i = 0; i < numMapColors; i++, mapColorPtr++) {
+ r = (double)mapColorPtr->red - (double)colorPtr->exact.red;
+ g = (double)mapColorPtr->green - (double)colorPtr->exact.green;
+ b = (double)mapColorPtr->blue - (double)colorPtr->exact.blue;
+
+ dist = (r * r) + (b * b) + (g * g);
+ if (dist < min) {
+ min = dist;
+ lastMatch = mapColorPtr;
+ }
+ }
+ colorPtr->best = *lastMatch;
+ colorPtr->best.flags = (DoRed | DoGreen | DoBlue);
+ colorPtr->error = (float)sqrt(min);
+}
+
+static int
+CompareColors(a, b)
+ void *a, *b;
+{
+ ColorInfo *i1Ptr, *i2Ptr;
+
+ i1Ptr = *(ColorInfo **) a;
+ i2Ptr = *(ColorInfo **) b;
+ if (i2Ptr->error > i1Ptr->error) {
+ return 1;
+ } else if (i2Ptr->error < i1Ptr->error) {
+ return -1;
+ }
+ return 0;
+}
+
+static float
+MatchColors(colorTabPtr, rgbPtr, numColors, numAvailColors, numMapColors,
+ mapColors)
+ struct ColorTableStruct *colorTabPtr;
+ Pix32 *rgbPtr;
+ int numColors;
+ int numAvailColors;
+ int numMapColors;
+ XColor mapColors[NCOLORS];
+{
+ int numMatched;
+ float sum;
+ register int i;
+ register ColorInfo *colorPtr;
+
+ /*
+ * For each quantized color, compute and store the error (i.e
+ * the distance from a color that's already been allocated).
+ * We'll use this information to sort the colors based upon how
+ * badly they match and their frequency to the color image.
+ */
+ colorPtr = colorTabPtr->colorInfo;
+ for (i = 0; i < numColors; i++, colorPtr++, rgbPtr++) {
+ colorPtr->index = i;
+ colorTabPtr->sortedColors[i] = colorPtr;
+ colorPtr->exact.red = rgbPtr->Red;
+ colorPtr->exact.green = rgbPtr->Green;
+ colorPtr->exact.blue = rgbPtr->Blue;
+ colorPtr->exact.flags = (DoRed | DoGreen | DoBlue);
+ FindClosestColor(colorPtr, mapColors, numMapColors);
+ }
+
+ /* Sort the colors, first by frequency (most to least), then by
+ * matching error (worst to best).
+ */
+ qsort(colorTabPtr->sortedColors, numColors, sizeof(ColorInfo *),
+ (QSortCompareProc *)CompareColors);
+
+ for (i = 0; i < numColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ fprintf(stderr, "%d. %04x%04x%04x / %04x%04x%04x = %f (%d)\n", i,
+ colorPtr->exact.red, colorPtr->exact.green, colorPtr->exact.blue,
+ colorPtr->best.red, colorPtr->best.green, colorPtr->best.blue,
+ colorPtr->error, colorPtr->freq);
+ }
+ sum = 0.0;
+ numMatched = 0;
+ for (i = numAvailColors; i < numColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ sum += colorPtr->error;
+ numMatched++;
+ }
+ if (numMatched > 0) {
+ sum /= numMatched;
+ }
+ return sum;
+}
+
+
+static int
+AllocateColors(nImageColors, colorTabPtr, matchOnly)
+ int nImageColors;
+ struct ColorTableStruct *colorTabPtr;
+ int matchOnly;
+{
+ register int i;
+ register ColorInfo *colorPtr;
+ unsigned long int pixelValue;
+
+ for (i = 0; i < nImageColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ if (matchOnly) {
+ XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+ &colorPtr->best);
+ pixelValue = colorPtr->best.pixel;
+ } else {
+ colorPtr->allocated = XAllocColor(colorTabPtr->display,
+ colorTabPtr->colorMap, &colorPtr->exact);
+
+ if (colorPtr->allocated) {
+ pixelValue = colorPtr->exact.pixel;
+ } else {
+ XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+ &colorPtr->best);
+ pixelValue = colorPtr->best.pixel;
+ }
+ }
+ colorTabPtr->pixelValues[colorPtr->index] = pixelValue;
+ }
+ colorTabPtr->nPixels = nImageColors;
+ return 1;
+}
+
+ColorTable
+Blt_CreateColorTable(tkwin)
+ Tk_Window tkwin;
+{
+ XVisualInfo visualInfo, *visualInfoPtr;
+ int nVisuals;
+ Visual *visualPtr;
+ Display *display;
+ struct ColorTableStruct *colorTabPtr;
+
+ display = Tk_Display(tkwin);
+ visualPtr = Tk_Visual(tkwin);
+
+ colorTabPtr = Blt_Calloc(1, sizeof(struct ColorTableStruct));
+ assert(colorTabPtr);
+ colorTabPtr->display = Tk_Display(tkwin);
+ colorTabPtr->colorMap = Tk_Colormap(tkwin);
+
+ visualInfo.screen = Tk_ScreenNumber(tkwin);
+ visualInfo.visualid = XVisualIDFromVisual(visualPtr);
+ visualInfoPtr = XGetVisualInfo(display, VisualScreenMask | VisualIDMask,
+ &visualInfo, &nVisuals);
+
+ colorTabPtr->visualInfo = *visualInfoPtr;
+ XFree(visualInfoPtr);
+
+ return colorTabPtr;
+}
+
+void
+Blt_FreeColorTable(colorTabPtr)
+ struct ColorTableStruct *colorTabPtr;
+{
+ if (colorTabPtr == NULL) {
+ return;
+ }
+ if (colorTabPtr->nPixels > 0) {
+ XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+ }
+ Blt_Free(colorTabPtr);
+}
+
+extern int redAdjust, greenAdjust, blueAdjust;
+extern int redMaskShift, greenMaskShift, blueMaskShift;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DirectColorTable --
+ *
+ * Creates a color table using the DirectColor visual. We try
+ * to allocate colors across the color spectrum.
+ *
+ * Results:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+ColorTable
+Blt_DirectColorTable(interp, tkwin, image)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Blt_Colorimage image;
+{
+ struct ColorTableStruct *colorTabPtr;
+ Visual *visualPtr;
+ Display *display;
+ XColor color;
+ int nr, ng, nb;
+ int rBand, gBand, bBand;
+ int rLast, gLast, bLast;
+ unsigned int r, g, b;
+ unsigned int value;
+ register int i;
+
+ display = Tk_Display(tkwin);
+ visualPtr = Tk_Visual(tkwin);
+
+ colorTabPtr = Blt_CreateColorTable(tkwin);
+ /*
+ * Compute the number of distinct colors in each band
+ */
+ nr = ((unsigned int)visualPtr->red_mask >> redMaskShift) + 1;
+ ng = ((unsigned int)visualPtr->green_mask >> greenMaskShift) + 1;
+ nb = ((unsigned int)visualPtr->blue_mask >> blueMaskShift) + 1;
+
+#ifdef notdef
+ assert((nr <= visualPtr->map_entries) && (ng <= visualPtr->map_entries) &&
+ (nb <= visualPtr->map_entries));
+#endif
+ rBand = NCOLORS / nr;
+ gBand = NCOLORS / ng;
+ bBand = NCOLORS / nb;
+
+ retry:
+ color.flags = (DoRed | DoGreen | DoBlue);
+ rLast = gLast = bLast = 0;
+ r = g = b = 0;
+ for (i = 0; i < visualPtr->map_entries; i++) {
+ if (rLast < NCOLORS) {
+ r = rLast + rBand;
+ if (r > NCOLORS) {
+ r = NCOLORS;
+ }
+ }
+ if (gLast < NCOLORS) {
+ g = gLast + gBand;
+ if (g > NCOLORS) {
+ g = NCOLORS;
+ }
+ }
+ if (bLast < NCOLORS) {
+ b = bLast + bBand;
+ if (b > NCOLORS) {
+ b = NCOLORS;
+ }
+ }
+ color.red = (r - 1) * (NCOLORS + 1);
+ color.green = (g - 1) * (NCOLORS + 1);
+ color.blue = (b - 1) * (NCOLORS + 1);
+
+ if (!XAllocColor(display, colorTabPtr->colorMap, &color)) {
+ XFreeColors(display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, i, 0);
+ if ((colorTabPtr->flags & PRIVATE_COLORMAP) == 0) {
+ /*
+ * If we can't allocate a color in the default
+ * colormap, try again, this time with a private
+ * colormap.
+ */
+ fprintf(stderr, "Need to allocate private colormap\n");
+ colorTabPtr->colorMap = Tk_GetColormap(interp, tkwin, ".");
+
+ XSetWindowColormap(display, Tk_WindowId(tkwin),
+ colorTabPtr->colorMap);
+ colorTabPtr->flags |= PRIVATE_COLORMAP;
+ goto retry;
+ }
+#ifdef notdef
+ fprintf(stderr, "Failed to allocate after %d colors\n", i);
+#endif
+ Blt_Free(colorTabPtr);
+ return NULL; /* Ran out of colors in private map? */
+ }
+ colorTabPtr->pixelValues[i] = color.pixel;
+ /*
+ * Fill in pixel values for each band at this intensity
+ */
+ value = color.pixel & visualPtr->red_mask;
+ while (rLast < r) {
+ colorTabPtr->red[rLast++] = value;
+ }
+ value = color.pixel & visualPtr->green_mask;
+ while (gLast < g) {
+ colorTabPtr->green[gLast++] = value;
+ }
+ value = color.pixel & visualPtr->blue_mask;
+ while (bLast < b) {
+ colorTabPtr->blue[bLast++] = value;
+ }
+ }
+ colorTabPtr->nPixels = i;
+ return colorTabPtr;
+}
+
+/*
+ * First attempt:
+ * Allocate colors all the colors in the image (up to NCOLORS). Bail out
+ * on the first failure or if we need more than NCOLORS.
+ */
+static int
+GetUniqueColors(image)
+ Blt_Colorimage image;
+{
+ register int i, nColors;
+ register Pix32 *pixelPtr;
+ Pix32 color;
+ Blt_HashEntry *hPtr;
+ int isNew, nPixels;
+ int refCount;
+ Blt_HashTable colorTable;
+
+ Blt_InitHashTable(&colorTable, BLT_ONE_WORD_KEYS);
+
+ nPixels = Blt_ColorimageWidth(image) * Blt_ColorimageHeight(image);
+ nColors = 0;
+ pixelPtr = Blt_ColorimageBits(image);
+ for (i = 0; i < nPixels; i++, pixelPtr++) {
+ color.value = pixelPtr->value;
+ color.Alpha = 0xFF; /* Ignore alpha-channel values */
+ hPtr = Blt_CreateHashEntry(&colorTable, (char *)color.value, &isNew);
+ if (isNew) {
+ refCount = 1;
+ nColors++;
+ } else {
+ refCount = (int)Blt_GetHashValue(hPtr);
+ refCount++;
+ }
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ }
+ Blt_DeleteHashTable(&colorTable);
+ return nColors;
+}
+
+#define Blt_DefaultColormap(tkwin) \
+ DefaultColormap(Tk_Display(tkwin), Tk_ScreenNumber(tkwin))
+
+
+static void
+PrivateColormap(interp, colorTabPtr, image, tkwin)
+ Tcl_Interp *interp;
+ struct ColorTableStruct *colorTabPtr;
+ Blt_Colorimage image;
+ Tk_Window tkwin;
+{
+ int keepColors = 0;
+ register int i;
+ XColor usedColors[NCOLORS];
+ int nFreeColors, nUsedColors;
+ Colormap colorMap;
+ int inUse[NCOLORS];
+ XColor *colorPtr;
+ XColor *imageColors;
+
+ /*
+ * Create a private colormap if one doesn't already exist for the
+ * window.
+ */
+
+ colorTabPtr->colorMap = colorMap = Tk_Colormap(tkwin);
+
+ nUsedColors = 0; /* Number of colors allocated */
+
+ if (colorTabPtr->nPixels > 0) {
+ XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+ }
+ nFreeColors = QueryColormap(colorTabPtr->display, colorMap, usedColors,
+ &nUsedColors);
+ memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+ if ((nUsedColors == 0) && (keepColors > 0)) {
+
+ /*
+ * We're starting with a clean colormap so find out what colors
+ * have been used in the default colormap.
+ */
+
+ nFreeColors = QueryColormap(colorTabPtr->display,
+ Blt_DefaultColormap(tkwin), usedColors, &nUsedColors);
+
+ /*
+ * Copy a number of colors from the default colormap into the private
+ * colormap. We can assume that this is the working set from most
+ * (non-image related) applications. While this doesn't stop our
+ * image from flashing and looking dumb when colormaps are swapped
+ * in and out, at least everything else should remain unaffected.
+ */
+
+ if (nUsedColors > keepColors) {
+ nUsedColors = keepColors;
+ }
+ /*
+ * We want to allocate colors in the same ordering as the old colormap,
+ * and we can't assume that the colors in the old map were contiguous.
+ * So mark the colormap locations (i.e. pixels) that we find in use.
+ */
+
+ }
+ for (colorPtr = usedColors, i = 0; i < nUsedColors; i++, colorPtr++) {
+ inUse[colorPtr->pixel] = TRUE;
+ }
+
+ /*
+ * In an "exact" colormap, we try to allocate as many of colors from the
+ * image as we can fit. If necessary, we'll cheat and reduce the number
+ * of colors by quantizing.
+ */
+ imageColors = usedColors + nUsedColors;
+
+ Tk_SetWindowColormap(tkwin, colorMap);
+}
+
+ColorTable
+Blt_PseudoColorTable(interp, tkwin, image)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Blt_Colorimage image;
+{
+ struct ColorTableStruct *colorTabPtr;
+ Colormap defColorMap;
+ int usePrivate;
+
+ colorTabPtr = Blt_CreateColorTable(tkwin);
+ defColorMap = DefaultColormap(colorTabPtr->display, Tk_ScreenNumber(tkwin));
+ if (colorTabPtr->colorMap == defColorMap) {
+ fprintf(stderr, "Using default colormap\n");
+ }
+ /* All other visuals use an 8-bit colormap */
+ colorTabPtr->lut = Blt_Malloc(sizeof(unsigned int) * 33 * 33 * 33);
+ assert(colorTabPtr->lut);
+
+ usePrivate = TRUE;
+ if (usePrivate) {
+ PrivateColormap(interp, colorTabPtr, image, tkwin);
+ } else {
+#ifdef notdef
+ ReadOnlyColormap(colorTabPtr, image, tkwin);
+#endif
+ }
+ return colorTabPtr;
+}
+
+#ifdef notdef
+
+static void
+ConvoleColorimage(srcImage, destImage, kernelPtr)
+ Blt_Colorimage srcImage, destImage;
+ ConvoleKernel *kernelPtr;
+{
+ Pix32 *srcPtr, *destPtr;
+ Pix32 *src[MAXROWS];
+ register int x, y, i, j;
+ int red, green, blue;
+
+ /* i = 0 case, ignore left column of pixels */
+
+ srcPtr = Blt_ColorimageBits(srcImage);
+ destPtr = Blt_ColorimageBits(destImage);
+
+ width = Blt_ColorimageWidth(srcImage);
+ height = Blt_ColorimageHeight(srcImage);
+
+ yOffset = kernelPtr->height / 2;
+ xOffset = kernelPtr->width / 2;
+ for (y = yOffset; y < (height - yOffset); y++) {
+ /* Set up pointers to individual rows */
+ for (i = 0; i < kernelPtr->height; i++) {
+ src[i] = srcPtr + (i * width);
+ }
+ for (x = xOffset; x < (width - xOffset); x++) {
+ red = green = blue = 0;
+ kernPtr = kernelPtr->values;
+ for (i = 0; i < kernelPtr->height; i++) {
+ for (j = 0; j < kernelPtr->width; j++) {
+ red += *valuePtr * src[i][j].Red;
+ green += *valuePtr * src[i][j].Green;
+ blue += *valuePtr * src[i][j].Blue;
+ valuePtr++;
+ }
+ }
+ destPtr->Red = red / kernelPtr->sum;
+ destPtr->Green = green / kernelPtr->sum;
+ destPtr->Blue = blue / kernelPtr->sum;
+ destPtr++;
+ }
+ srcPtr += width;
+ }
+ sum = bot[0].Red +
+ red = bot[0].Red + bot[1].Red + mid[1].Red + top[0].Red + top[1].Red;
+ green = bot[0].Green + bot[1].Green + mid[1].Green + top[0].Green +
+ top[1].Green;
+ blue = bot[0].Blue + bot[1].Blue + mid[1].Blue + top[0].Blue + top[1].Blue;
+ error = (red / 5) - mid[0].Red;
+ redVal = mid[0].Red - (error * blend / blend_divisor);
+ error = (green / 5) - mid[0].Green;
+ greenVal = mid[0].Green - (error * blend / blend_divisor);
+ error = (blue / 5) - mid[0].Blue;
+ blueVal = mid[0].Blue - (error * blend / blend_divisor);
+
+ out[0].Red = CLAMP(redVal);
+ out[0].Green = CLAMP(greenVal);
+ out[0].Blue = CLAMP(blueVal);
+
+ for (i = 1; i < (width - 1); i++) {
+ for (chan = 0; chan < 3; chan++) {
+ total = bot[chan][i - 1] + bot[chan][i] + bot[chan][i + 1] +
+ mid[chan][i - 1] + mid[chan][i + 1] +
+ top[chan][i - 1] + top[chan][i] + top[chan][i + 1];
+ avg = total >> 3; /* divide by 8 */
+ diff = avg - mid[chan][i];
+ result = mid[chan][i] - (diff * blend / blend_divisor);
+ out[chan][i] = CLAMP(result);
+ }
+ }
+ /* i = in_hdr.xmax case, ignore right column of pixels */
+ for (chan = 0; chan < 3; chan++) {
+ total = bot[chan][i - 1] + bot[chan][i] +
+ mid[chan][i - 1] +
+ top[chan][i - 1] + top[chan][i];
+ avg = total / 5;
+ diff = avg - mid[chan][i];
+ result = mid[chan][i] - (diff * blend / blend_divisor);
+ out[chan][i] = CLAMP(result);
+ }
+}
+
+static void
+DitherRow(srcImage, destImage, lastRow, curRow)
+ Blt_Colorimage srcImage, destImage;
+ int width, height;
+ int bottom, top;
+{
+ int width, height;
+
+ width = Blt_ColorimageWidth(srcImage);
+ topPtr = Blt_ColorimageBits(destPtr) + (width * row);
+ rowPtr = topPtr + width;
+ botPtr = rowPtr + width;
+
+ for (x = 0; x < width; x++) {
+
+ /* Clamp current error entry */
+
+ midPtr->red = CLAMP(midPtr->red);
+ midPtr->blue = CLAMP(midPtr->blue);
+ midPtr->green = CLAMP(midPtr->green);
+
+ r = (midPtr->red >> 3) + 1;
+ g = (midPtr->green >> 3) + 1;
+ b = (midPtr->blue >> 3) + 1;
+ index = colorTabPtr->lut[r][g][b];
+
+ redVal = midPtr->red * (NCOLORS + 1);
+ greenVal = midPtr->green * (NCOLORS + 1);
+ blueVal = midPtr->blue * (NCOLORS + 1);
+
+ error = colorVal - colorMap[index].red;
+ if (x < 511) {
+ currRow[x + 1].Red = currRow[x + 1].Red + 7 * error / 16;
+ nextRow[x + 1].Red = nextRow[x + 1].Red + error / 16;
+ }
+ nextRow[x].Red = nextRow[x].Red + 5 * error / 16;
+ if (x > 0) {
+ nextRow[x - 1].Red = nextRow[x - 1].Red + 3 * error / 16;
+ }
+ error = row[x][c] - colormap[index][c];
+
+ value = srcPtr->channel[i] * error[i];
+ value = CLAMP(value);
+ destPtr->channel[i] = value;
+
+ /* Closest pixel */
+ pixel = PsuedoColorPixel();
+ error[RED] = colorPtr->Red - srcPtr->Red * (NCOLORS + 1);
+
+ /* translate pixel to colorInfoPtr to get error */
+ colorTabPtr->lut[r][g][b];
+ colorPtr = PixelToColorInfo(pixel);
+ error = colorPtr->error;
+
+ register rle_pixel *optr;
+ register int j;
+ register short *thisptr, *nextptr = NULL;
+ int chan;
+ static int nchan = 0;
+ int lastline = 0, lastpixel;
+ static int *cval = 0;
+ static rle_pixel *pixel = 0;
+
+ if (nchan != in_hdr->ncolors)
+ if (cval) {
+ Blt_Free(cval);
+ Blt_Free(pixel);
+ }
+ nchan = in_hdr->ncolors;
+ if (!cval) {
+ if ((cval = Blt_Malloc(nchan * sizeof(int))) == 0)
+ malloc_ERR;
+ if ((pixel = Blt_Malloc(nchan * sizeof(rle_pixel))) == 0)
+ malloc_ERR;
+ }
+ optr = outrow[RLE_RED];
+
+ thisptr = row_top;
+ if (row_bottom)
+ nextptr = row_bottom;
+ else
+ lastline = 1;
+
+ for (x = 0; x < width; x++) {
+ int cmap_index = 0;
+
+ lastpixel = (x == (width - 1));
+ val = srcPtr->Red;
+
+ for (chan = 0; chan < 3; chan++) {
+ cval[chan] = *thisptr++;
+
+ /*
+ * Current channel value has been accumulating error,
+ * it could be out of range.
+ */
+ if (cval[chan] < 0)
+ cval[chan] = 0;
+ else if (cval[chan] > 255)
+ cval[chan] = 255;
+
+ pixel[chan] = cval[chan];
+ }
+
+ /* find closest color */
+ find_closest(map, nchan, maplen, pixel, &cmap_index);
+ *optr++ = cmap_index;
+
+ /* thisptr is now looking at pixel to the right of current pixel
+ * nextptr is looking at pixel below current pixel
+ * So, increment thisptr as stuff gets stored. nextptr gets moved
+ * by one, and indexing is done +/- nchan.
+ */
+ for (chan = 0; chan < nchan; chan++) {
+ cval[chan] -= map[chan][cmap_index];
+
+ if (!lastpixel) {
+ thisptr[chan] += cval[chan] * 7 / 16;
+ }
+ if (!lastline) {
+ if (j != 0) {
+ nextptr[-nchan] += cval[chan] * 3 / 16;
+ }
+ nextptr[0] += cval[chan] * 5 / 16;
+ if (!lastpixel) {
+ nextptr[nchan] += cval[chan] / 16;
+ }
+ nextptr++;
+ }
+ }
+ }
+ }
+}
+
+/********************************************/
+static Blt_Colorimage
+DoColorDither(pic24, pic8, w, h, rmap, gmap, bmap, rdisp, gdisp, bdisp, maplen)
+ byte *pic24, *pic8, *rmap, *gmap, *bmap, *rdisp, *gdisp, *bdisp;
+ int w, h, maplen;
+{
+ /* takes a 24 bit picture, of size w*h, dithers with the colors in
+ rdisp, gdisp, bdisp (which have already been allocated),
+ and generates an 8-bit w*h image, which it returns.
+ ignores input value 'pic8'
+ returns NULL on error
+
+ note: the rdisp,gdisp,bdisp arrays should be the 'displayed' colors,
+ not the 'desired' colors
+
+ if pic24 is NULL, uses the passed-in pic8 (an 8-bit image) as
+ the source, and the rmap,gmap,bmap arrays as the desired colors */
+
+ byte *np, *ep, *newpic;
+ short *cache;
+ int r2, g2, b2;
+ int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
+ int i, j, rerr, gerr, berr, pwide3;
+ int imax, jmax;
+ int key;
+ long cnt1, cnt2;
+ int error[512]; /* -255 .. 0 .. +255 */
+
+ /* compute somewhat non-linear floyd-steinberg error mapping table */
+ for (i = j = 0; i <= 0x40; i++, j++) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+ for ( /*empty*/ ; i < 0x80; i++, j += !(i & 1) ? 1 : 0) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+ for ( /*empty*/ ; i <= 0xff; i++) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+
+ cnt1 = cnt2 = 0;
+ pwide3 = w * 3;
+ imax = h - 1;
+ jmax = w - 1;
+ ep = (pic24) ? pic24 : pic8;
+
+ /* attempt to malloc things */
+ newpic = Blt_Malloc((size_t) (w * h));
+ cache = Blt_Calloc((size_t) (2 << 14), sizeof(short));
+ thisline = Blt_Malloc(pwide3 * sizeof(int));
+ nextline = Blt_Malloc(pwide3 * sizeof(int));
+ if (!cache || !newpic || !thisline || !nextline) {
+ if (newpic)
+ Blt_Free(newpic);
+ if (cache)
+ Blt_Free(cache);
+ if (thisline)
+ Blt_Free(thisline);
+ if (nextline)
+ Blt_Free(nextline);
+ return (byte *) NULL;
+ }
+ np = newpic;
+
+ /* Get first line of picture in reverse order. */
+
+ srcPtr = Blt_ColorimageBits(image), tempPtr = tempArr;
+ for (x = 0; x < width; x++, tempPtr++, srcPtr--) {
+ *tempPtr = *srcPtr;
+ }
+
+ for (y = 0; y < height; y++) {
+ tempPtr = curRowPtr, curRowPtr = nextRowPtr, nextRowPtr = tempPtr;
+
+ if (y != (height - 1)) {/* get next line */
+ for (x = 0; x < width; x++, tempPtr++, srcPtr--)
+ *tempPtr = *srcPtr;
+ }
+ }
+
+
+ Blt_Free(thisline);
+ Blt_Free(nextline);
+ Blt_Free(cache);
+
+ return newpic;
+}
+
+
+static void
+DitherImage(image)
+ Blt_Colorimage image;
+{
+ int width, height;
+
+
+
+}
+
+#endif
+
+#endif /* WIN32 */
diff --git a/blt/src/bltConfig.c b/blt/src/bltConfig.c
new file mode 100644
index 00000000000..a382856371c
--- /dev/null
+++ b/blt/src/bltConfig.c
@@ -0,0 +1,1370 @@
+/*
+ * bltConfig.c --
+ *
+ * This module implements custom configuration options for the BLT
+ * toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "bltTile.h"
+
+static int StringToFill _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *FillToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltFillOption =
+{
+ StringToFill, FillToString, (ClientData)0
+};
+
+static int StringToPad _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *PadToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+ char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltPadOption =
+{
+ StringToPad, PadToString, (ClientData)0
+};
+
+static int StringToDistance _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *DistanceToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltDistanceOption =
+{
+ StringToDistance, DistanceToString, (ClientData)PIXELS_NONNEGATIVE
+};
+
+Tk_CustomOption bltPositiveDistanceOption =
+{
+ StringToDistance, DistanceToString, (ClientData)PIXELS_POSITIVE
+};
+
+Tk_CustomOption bltAnyDistanceOption =
+{
+ StringToDistance, DistanceToString, (ClientData)PIXELS_ANY
+};
+
+static int StringToCount _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *CountToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltCountOption =
+{
+ StringToCount, CountToString, (ClientData)COUNT_NONNEGATIVE
+};
+
+Tk_CustomOption bltPositiveCountOption =
+{
+ StringToCount, CountToString, (ClientData)COUNT_POSITIVE
+};
+
+static int StringToDashes _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window,
+ char *, char *, int));
+static char *DashesToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltDashesOption =
+{
+ StringToDashes, DashesToString, (ClientData)0
+};
+
+static int StringToShadow _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ Tk_Window tkwin, char *string, char *widgRec, int offset));
+static char *ShadowToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+ char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltShadowOption =
+{
+ StringToShadow, ShadowToString, (ClientData)0
+};
+
+static int StringToUid _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *UidToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltUidOption =
+{
+ StringToUid, UidToString, (ClientData)0
+};
+
+static int StringToState _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *StateToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltStateOption =
+{
+ StringToState, StateToString, (ClientData)0
+};
+
+static int StringToList _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ Tk_Window tkwin, char *string, char *widgRec, int flags));
+static char *ListToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+Tk_CustomOption bltListOption =
+{
+ StringToList, ListToString, (ClientData)0
+};
+
+static int StringToTile _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ Tk_Window tkwin, char *value, char *widgRec, int flags));
+static char *TileToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+ char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltTileOption =
+{
+ StringToTile, TileToString, (ClientData)0
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfFill --
+ *
+ * Converts the integer representing the fill direction into a string.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_NameOfFill(fill)
+ int fill;
+{
+ switch (fill) {
+ case FILL_X:
+ return "x";
+ case FILL_Y:
+ return "y";
+ case FILL_NONE:
+ return "none";
+ case FILL_BOTH:
+ return "both";
+ default:
+ return "unknown value";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFill --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none" Use neither plane.
+ * "x" X-coordinate plane.
+ * "y" Y-coordinate plane.
+ * "both" Use both coordinate planes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFill(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Fill style string */
+ char *widgRec; /* Cubicle structure record */
+ int offset; /* Offset of style in record */
+{
+ int *fillPtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *fillPtr = FILL_NONE;
+ } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) {
+ *fillPtr = FILL_X;
+ } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) {
+ *fillPtr = FILL_Y;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *fillPtr = FILL_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", string,
+ "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FillToString --
+ *
+ * Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ * The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FillToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of fill in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int fill = *(int *)(widgRec + offset);
+
+ return Blt_NameOfFill(fill);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToFlag --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToFlag(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Bit mask to be tested in status word */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Fill style string */
+ char *widgRec; /* Cubicle structure record */
+ int offset; /* Offset of style in record */
+{
+ unsigned int mask = (unsigned int)clientData; /* Bit to be tested */
+ int *flagPtr = (int *)(widgRec + offset);
+ int bool;
+
+ if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (bool) {
+ *flagPtr |= mask;
+ } else {
+ *flagPtr &= ~mask;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FlagToString --
+ *
+ * Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ * The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_FlagToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Bit mask to be test in status word */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of fill in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not Used. */
+{
+ unsigned int mask = (unsigned int)clientData; /* Bit to be tested */
+ unsigned int bool = *(unsigned int *)(widgRec + offset);
+
+ return (bool & mask) ? "1" : "0";
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPixels --
+ *
+ * Like Tk_GetPixels, but checks for negative, zero.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetPixels(interp, tkwin, string, check, valuePtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *string;
+ int check; /* Can be PIXELS_POSITIVE, PIXELS_NONNEGATIVE,
+ * or PIXELS_ANY, */
+ int *valuePtr;
+{
+ int length;
+
+ if (Tk_GetPixels(interp, tkwin, string, &length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (length >= SHRT_MAX) {
+ Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+ "too big to represent", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (check) {
+ case PIXELS_NONNEGATIVE:
+ if (length < 0) {
+ Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+ "can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+ case PIXELS_POSITIVE:
+ if (length <= 0) {
+ Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+ "must be positive", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+ case PIXELS_ANY:
+ break;
+ }
+ *valuePtr = length;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDistance --
+ *
+ * Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ * values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDistance(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Indicated how to check distance */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of pixel size in record */
+{
+ int *valuePtr = (int *)(widgRec + offset);
+ return Blt_GetPixels(interp, tkwin, string, (int)clientData, valuePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DistanceToString --
+ *
+ * Returns the string representing the positive pixel size.
+ *
+ * Results:
+ * The pixel size string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DistanceToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int value = *(int *)(widgRec + offset);
+ char *result;
+
+ result = Blt_Strdup(Blt_Itoa(value));
+ assert(result);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+int
+Blt_GetInt(interp, string, check, valuePtr)
+ Tcl_Interp *interp;
+ char *string;
+ int check; /* Can be COUNT_POSITIVE, COUNT_NONNEGATIVE,
+ * or COUNT_ANY, */
+ int *valuePtr;
+{
+ int count;
+
+ if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (check) {
+ case COUNT_NONNEGATIVE:
+ if (count < 0) {
+ Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+ "can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+ case COUNT_POSITIVE:
+ if (count <= 0) {
+ Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+ "must be positive", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+ case COUNT_ANY:
+ break;
+ }
+ *valuePtr = count;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToCount --
+ *
+ * Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ * values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToCount(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Indicated how to check distance */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of pixel size in record */
+{
+ int *valuePtr = (int *)(widgRec + offset);
+ return Blt_GetInt(interp, string, (int)clientData, valuePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CountToString --
+ *
+ * Returns the string representing the positive pixel size.
+ *
+ * Results:
+ * The pixel size string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CountToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int value = *(int *)(widgRec + offset);
+ char *result;
+
+ result = Blt_Strdup(Blt_Itoa(value));
+ assert(result);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPad --
+ *
+ * Convert a string into two pad values. The string may be in one of
+ * the following forms:
+ *
+ * n - n is a non-negative integer. This sets both
+ * pad values to n.
+ * {n m} - both n and m are non-negative integers. side1
+ * is set to n, side2 is set to m.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side Effects:
+ * The padding structure passed is updated with the new values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPad(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of pad in widget */
+{
+ Blt_Pad *padPtr = (Blt_Pad *)(widgRec + offset);
+ int nElem;
+ int pad, result;
+ char **padArr;
+
+ if (Tcl_SplitList(interp, string, &nElem, &padArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+ if ((nElem < 1) || (nElem > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in padding list",
+ (char *)NULL);
+ goto error;
+ }
+ if (Blt_GetPixels(interp, tkwin, padArr[0], PIXELS_NONNEGATIVE, &pad)
+ != TCL_OK) {
+ goto error;
+ }
+ padPtr->side1 = pad;
+ if ((nElem > 1) &&
+ (Blt_GetPixels(interp, tkwin, padArr[1], PIXELS_NONNEGATIVE, &pad)
+ != TCL_OK)) {
+ goto error;
+ }
+ padPtr->side2 = pad;
+ result = TCL_OK;
+
+ error:
+ Blt_Free(padArr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PadToString --
+ *
+ * Converts the two pad values into a Tcl list. Each pad has two
+ * pixel values. For vertical pads, they represent the top and bottom
+ * margins. For horizontal pads, they're the left and right margins.
+ * All pad values are non-negative integers.
+ *
+ * Results:
+ * The padding list is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PadToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Structure record */
+ int offset; /* Offset of pad in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Blt_Pad *padPtr = (Blt_Pad *)(widgRec + offset);
+ char *result;
+ char string[200];
+
+ sprintf(string, "%d %d", padPtr->side1, padPtr->side2);
+ result = Blt_Strdup(string);
+ if (result == NULL) {
+ return "out of memory";
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToShadow --
+ *
+ * Convert a string into two pad values. The string may be in one of
+ * the following forms:
+ *
+ * n - n is a non-negative integer. This sets both
+ * pad values to n.
+ * {n m} - both n and m are non-negative integers. side1
+ * is set to n, side2 is set to m.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side Effects:
+ * The padding structure passed is updated with the new values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToShadow(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of pad in widget */
+{
+ Shadow *shadowPtr = (Shadow *) (widgRec + offset);
+ XColor *colorPtr;
+ int dropOffset;
+
+ colorPtr = NULL;
+ dropOffset = 0;
+ if ((string != NULL) && (string[0] != '\0')) {
+ int nElem;
+ char **elemArr;
+
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((nElem < 1) || (nElem > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in drop shadow value",
+ (char *)NULL);
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ colorPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(elemArr[0]));
+ if (colorPtr == NULL) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ dropOffset = 1;
+ if (nElem == 2) {
+ if (Blt_GetPixels(interp, tkwin, elemArr[1], PIXELS_NONNEGATIVE,
+ &dropOffset) != TCL_OK) {
+ Tk_FreeColor(colorPtr);
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ }
+ Blt_Free(elemArr);
+ }
+ if (shadowPtr->color != NULL) {
+ Tk_FreeColor(shadowPtr->color);
+ }
+ shadowPtr->color = colorPtr;
+ shadowPtr->offset = dropOffset;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShadowToString --
+ *
+ * Converts the two pad values into a Tcl list. Each pad has two
+ * pixel values. For vertical pads, they represent the top and bottom
+ * margins. For horizontal pads, they're the left and right margins.
+ * All pad values are non-negative integers.
+ *
+ * Results:
+ * The padding list is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ShadowToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Structure record */
+ int offset; /* Offset of pad in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Shadow *shadowPtr = (Shadow *) (widgRec + offset);
+ char *result;
+
+ result = "";
+ if (shadowPtr->color != NULL) {
+ char string[200];
+
+ sprintf(string, "%s %d", Tk_NameOfColor(shadowPtr->color),
+ shadowPtr->offset);
+ result = Blt_Strdup(string);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetDashes --
+ *
+ * Converts a Tcl list of dash values into a dash list ready for
+ * use with XSetDashes.
+ *
+ * A valid list dash values can have zero through 11 elements
+ * (PostScript limit). Values must be between 1 and 255. Although
+ * a list of 0 (like the empty string) means no dashes.
+ *
+ * Results:
+ * A standard Tcl result. If the list represented a valid dash
+ * list TCL_OK is returned and *dashesPtr* will contain the
+ * valid dash list. Otherwise, TCL_ERROR is returned and
+ * interp->result will contain an error message.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetDashes(interp, string, dashesPtr)
+ Tcl_Interp *interp;
+ char *string;
+ Blt_Dashes *dashesPtr;
+{
+ if ((string == NULL) || (*string == '\0')) {
+ dashesPtr->values[0] = 0;
+ } else if (strcmp(string, "dash") == 0) { /* 5 2 */
+ dashesPtr->values[0] = 5;
+ dashesPtr->values[1] = 2;
+ dashesPtr->values[2] = 0;
+ } else if (strcmp(string, "dot") == 0) { /* 1 */
+ dashesPtr->values[0] = 1;
+ dashesPtr->values[1] = 0;
+ } else if (strcmp(string, "dashdot") == 0) { /* 2 4 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 0;
+ } else if (strcmp(string, "dashdotdot") == 0) { /* 2 4 2 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 2;
+ dashesPtr->values[4] = 0;
+ } else {
+ int nValues;
+ char **strArr;
+ long int value;
+ register int i;
+
+ if (Tcl_SplitList(interp, string, &nValues, &strArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nValues > 11) { /* This is the postscript limit */
+ Tcl_AppendResult(interp, "too many values in dash list \"",
+ string, "\"", (char *)NULL);
+ Blt_Free(strArr);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nValues; i++) {
+ if (Tcl_ExprLong(interp, strArr[i], &value) != TCL_OK) {
+ Blt_Free(strArr);
+ return TCL_ERROR;
+ }
+ /*
+ * Backward compatibility:
+ * Allow list of 0 to turn off dashes
+ */
+ if ((value == 0) && (nValues == 1)) {
+ break;
+ }
+ if ((value < 1) || (value > 255)) {
+ Tcl_AppendResult(interp, "dash value \"", strArr[i],
+ "\" is out of range", (char *)NULL);
+ Blt_Free(strArr);
+ return TCL_ERROR;
+ }
+ dashesPtr->values[i] = (unsigned char)value;
+ }
+ /* Make sure the array ends with a NUL byte */
+ dashesPtr->values[i] = 0;
+ Blt_Free(strArr);
+ }
+ return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDashes --
+ *
+ * Convert the list of dash values into a dashes array.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * The Dashes structure is updated with the new dash list.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDashes(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New dash value list */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to Dashes structure */
+{
+ Blt_Dashes *dashesPtr = (Blt_Dashes *)(widgRec + offset);
+
+ return GetDashes(interp, string, dashesPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DashesToString --
+ *
+ * Convert the dashes array into a list of values.
+ *
+ * Results:
+ * The string representing the dashes returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DashesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of Dashes in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Blt_Dashes *dashesPtr = (Blt_Dashes *)(widgRec + offset);
+ Tcl_DString dString;
+ unsigned char *p;
+ char *result;
+
+ if (dashesPtr->values[0] == 0) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ for (p = dashesPtr->values; *p != 0; p++) {
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(*p));
+ }
+ result = Tcl_DStringValue(&dString);
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToUid --
+ *
+ * Converts the string to a BLT Uid. Blt Uid's are hashed, reference
+ * counted strings.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToUid(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Fill style string */
+ char *widgRec; /* Cubicle structure record */
+ int offset; /* Offset of style in record */
+{
+ Tk_Uid *uidPtr = (Tk_Uid *)(widgRec + offset);
+ Tk_Uid newId;
+
+ newId = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ newId = Blt_GetUid(string);
+ }
+ if (*uidPtr != NULL) {
+ Blt_FreeUid(*uidPtr);
+ }
+ *uidPtr = newId;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UidToString --
+ *
+ * Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ * The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+UidToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of fill in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Tk_Uid uid = *(Tk_Uid *)(widgRec + offset);
+
+ return (uid == NULL) ? "" : uid;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToState --
+ *
+ * Converts the string to a state value. Valid states are
+ * disabled, normal.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToState(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of option value */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of field in record */
+{
+ int *statePtr = (int *)(widgRec + offset);
+
+ if (strcmp(string, "normal") == 0) {
+ *statePtr = STATE_NORMAL;
+ } else if (strcmp(string, "disabled") == 0) {
+ *statePtr = STATE_DISABLED;
+ } else if (strcmp(string, "active") == 0) {
+ *statePtr = STATE_ACTIVE;
+ } else {
+ Tcl_AppendResult(interp, "bad state \"", string,
+ "\": should be normal, active, or disabled", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StateToString --
+ *
+ * Returns the string representation of the state configuration field
+ *
+ * Results:
+ * The string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+StateToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of fill in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int state = *(int *)(widgRec + offset);
+
+ switch (state) {
+ case STATE_ACTIVE:
+ return "active";
+ case STATE_DISABLED:
+ return "disabled";
+ case STATE_NORMAL:
+ return "normal";
+ default:
+ return "???";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToList --
+ *
+ * Converts the string to a list.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToList(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of option value */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of field in record */
+{
+ char ***listPtr = (char ***)(widgRec + offset);
+ char **elemArr;
+ int nElem;
+
+ if (*listPtr != NULL) {
+ Blt_Free(*listPtr);
+ *listPtr = NULL;
+ }
+ if ((string == NULL) || (*string == '\0')) {
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem > 0) {
+ *listPtr = elemArr;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListToString --
+ *
+ * Returns the string representation of the state configuration field
+ *
+ * Results:
+ * The string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ListToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record. */
+ int offset; /* Offset of fill in widget record. */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ char **list = *(char ***)(widgRec + offset);
+ register char **p;
+ char *result;
+ Tcl_DString dString;
+
+ if (list == NULL) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ for (p = list; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ result = Tcl_DStringValue(&dString);
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToTile --
+ *
+ * Converts the name of an image into a tile.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToTile(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window on same display as tile */
+ char *string; /* Name of image */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of tile in record */
+{
+ Blt_Tile *tilePtr = (Blt_Tile *)(widgRec + offset);
+ Blt_Tile tile, oldTile;
+
+ oldTile = *tilePtr;
+ tile = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ if (Blt_GetTile(interp, tkwin, string, &tile) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ /* Don't delete the information for the old tile, until we know
+ * that we successfully allocated a new one. */
+ if (oldTile != NULL) {
+ Blt_FreeTile(oldTile);
+ }
+ *tilePtr = tile;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileToString --
+ *
+ * Returns the name of the tile.
+ *
+ * Results:
+ * The name of the tile is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+TileToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record */
+ int offset; /* Offset of tile in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Blt_Tile tile = *(Blt_Tile *)(widgRec + offset);
+
+ return Blt_NameOfTile(tile);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigModified --
+ *
+ * Given the configuration specifications and one or more option
+ * patterns (terminated by a NULL), indicate if any of the matching
+ * configuration options has been reset.
+ *
+ * Results:
+ * Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int Blt_ConfigModified
+TCL_VARARGS_DEF(Tk_ConfigSpec *, arg1)
+{
+ va_list argList;
+ Tk_ConfigSpec *specs;
+ register Tk_ConfigSpec *specPtr;
+ register char *option;
+
+ specs = TCL_VARARGS_START(Tk_ConfigSpec *, arg1, argList);
+ while ((option = va_arg(argList, char *)) != NULL) {
+ for (specPtr = specs; specPtr->type != TK_CONFIG_END; specPtr++) {
+ if ((Tcl_StringMatch(specPtr->argvName, option)) &&
+ (specPtr->specFlags & TK_CONFIG_OPTION_SPECIFIED)) {
+ va_end(argList);
+ return 1;
+ }
+ }
+ }
+ va_end(argList);
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureWidgetComponent --
+ *
+ * Configures a component of a widget. This is useful for
+ * widgets that have multiple components which aren't uniquely
+ * identified by a Tk_Window. It allows us, for example, set
+ * resources for axes of the graph widget. The graph really has
+ * only one window, but its convenient to specify components in a
+ * hierarchy of options.
+ *
+ * *graph.x.logScale yes
+ * *graph.Axis.logScale yes
+ * *graph.temperature.scaleSymbols yes
+ * *graph.Element.scaleSymbols yes
+ *
+ * This is really a hack to work around the limitations of the Tk
+ * resource database. It creates a temporary window, needed to
+ * call Tk_ConfigureWidget, using the name of the component.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * A temporary window is created merely to pass to Tk_ConfigureWidget.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureWidgetComponent(interp, parent, resName, className, specsPtr,
+ argc, argv, widgRec, flags)
+ Tcl_Interp *interp;
+ Tk_Window parent; /* Window to associate with component */
+ char resName[]; /* Name of component */
+ char className[];
+ Tk_ConfigSpec *specsPtr;
+ int argc;
+ char *argv[];
+ char *widgRec;
+ int flags;
+{
+ Tk_Window tkwin;
+ int result;
+ char *tempName;
+ int isTemporary = FALSE;
+
+ tempName = Blt_Strdup(resName);
+
+ /* Window name can't start with an upper case letter */
+ tempName[0] = tolower(resName[0]);
+
+ /*
+ * Create component if a child window by the component's name
+ * doesn't already exist.
+ */
+ tkwin = Blt_FindChild(parent, tempName);
+ if (tkwin == NULL) {
+ tkwin = Tk_CreateWindow(interp, parent, tempName, (char *)NULL);
+ isTemporary = TRUE;
+ }
+ if (tkwin == NULL) {
+ Tcl_AppendResult(interp, "can't find window in \"",
+ Tk_PathName(parent), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ assert(Tk_Depth(tkwin) == Tk_Depth(parent));
+ Blt_Free(tempName);
+
+ Tk_SetClass(tkwin, className);
+ result = Tk_ConfigureWidget(interp, tkwin, specsPtr, argc, argv, widgRec,
+ flags);
+ if (isTemporary) {
+ Tk_DestroyWindow(tkwin);
+ }
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToEnum --
+ *
+ * Converts the string into its enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToEnum(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Vectors of valid strings. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String to match. */
+ char *widgRec; /* Widget record. */
+ int offset; /* Offset of field in record */
+{
+ int *enumPtr = (int *)(widgRec + offset);
+ char c;
+ register char **p;
+ register int i;
+ int count;
+
+ c = string[0];
+ count = 0;
+ for (p = (char **)clientData; *p != NULL; p++) {
+ if ((c == p[0][0]) && (strcmp(string, *p) == 0)) {
+ *enumPtr = count;
+ return TCL_OK;
+ }
+ count++;
+ }
+ *enumPtr = -1;
+
+ Tcl_AppendResult(interp, "bad value \"", string, "\": should be ",
+ (char *)NULL);
+ p = (char **)clientData;
+ if (count > 0) {
+ Tcl_AppendResult(interp, p[0], (char *)NULL);
+ }
+ for (i = 1; i < (count - 1); i++) {
+ Tcl_AppendResult(interp, " ", p[i], ", ", (char *)NULL);
+ }
+ if (count > 1) {
+ Tcl_AppendResult(interp, " or ", p[count - 1], ".", (char *)NULL);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EnumToString --
+ *
+ * Returns the string associated with the enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_EnumToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* List of strings. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int value = *(int *)(widgRec + offset);
+ char **p;
+ int count;
+
+ count = 0;
+ for (p = (char **)clientData; *p != NULL; p++) {
+ count++;
+ }
+ if ((value >= count) || (value < 0)) {
+ return "unknown value";
+ }
+ p = (char **)clientData;
+ return p[value];
+}
+
diff --git a/blt/src/bltConfig.h.in b/blt/src/bltConfig.h.in
new file mode 100644
index 00000000000..9e95c1795bd
--- /dev/null
+++ b/blt/src/bltConfig.h.in
@@ -0,0 +1,137 @@
+/* src/bltConfig.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+#undef size_t
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+/* Define if DBL_EPSILON is not defined in float.h */
+#undef BLT_DBL_EPSILON
+
+/* Define if drand48 isn't declared in math.h. */
+#undef NEED_DECL_DRAND48
+
+/* Define if srand48 isn't declared in math.h. */
+#undef NEED_DECL_SRAND48
+
+/* Define if strdup isn't declared in a standard header file. */
+#undef NEED_DECL_STRDUP
+
+/* Define if j1 isn't declared in a standard header file. */
+#undef NEED_DECL_J1
+
+/* Define if union wait type is defined incorrectly. */
+#undef HAVE_UNION_WAIT
+
+/* Define if isfinite is found in libm. */
+#undef HAVE_ISFINITE
+
+/* The number of bytes in a int. */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long. */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a long long. */
+#undef SIZEOF_LONG_LONG
+
+/* The number of bytes in a void *. */
+#undef SIZEOF_VOID_P
+
+/* Define if you have the XExtendedMaxRequestSize function. */
+#undef HAVE_XEXTENDEDMAXREQUESTSIZE
+
+/* Define if you have the drand48 function. */
+#undef HAVE_DRAND48
+
+/* Define if you have the finite function. */
+#undef HAVE_FINITE
+
+/* Define if you have the srand48 function. */
+#undef HAVE_SRAND48
+
+/* Define if you have the strcasecmp function. */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strdup function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the strncasecmp function. */
+#undef HAVE_STRNCASECMP
+
+/* Define if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <float.h> header file. */
+#undef HAVE_FLOAT_H
+
+/* Define if you have the <ieeefp.h> header file. */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if you have the <jpeglib.h> header file. */
+#undef HAVE_JPEGLIB_H
+
+/* Define if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <math.h> header file. */
+#undef HAVE_MATH_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <setjmp.h> header file. */
+#undef HAVE_SETJMP_H
+
+/* Define if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <waitflags.h> header file. */
+#undef HAVE_WAITFLAGS_H
+
+/* Define if you have the m library (-lm). */
+#undef HAVE_LIBM
+
+/* Define if you have the nsl library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket). */
+#undef HAVE_LIBSOCKET
diff --git a/blt/src/bltContainer.c b/blt/src/bltContainer.c
new file mode 100644
index 00000000000..bc343755456
--- /dev/null
+++ b/blt/src/bltContainer.c
@@ -0,0 +1,1701 @@
+
+/*
+ * bltContainer.c --
+ *
+ * This module implements a container widget for the BLT toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Container widget created by George A. Howlett
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_CONTAINER
+#include "bltChain.h"
+#ifndef WIN32
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#endif
+
+#define XDEBUG
+
+#define SEARCH_TRIES 100 /* Maximum number of attempts to check for
+ * a given window before failing. */
+#define SEARCH_INTERVAL 20 /* Number milliseconds to wait after each
+ * attempt to find a window. */
+
+#define SEARCH_TKWIN (1<<0) /* Search via Tk window pathname. */
+#define SEARCH_XID (1<<1) /* Search via an XID 0x0000000. */
+#define SEARCH_CMD (1<<2) /* Search via a command-line arguments. */
+#define SEARCH_NAME (1<<3) /* Search via the application name. */
+#define SEARCH_ALL (SEARCH_TKWIN | SEARCH_XID | SEARCH_CMD | SEARCH_NAME)
+
+#define CONTAINER_REDRAW (1<<1)
+#define CONTAINER_MAPPED (1<<2)
+#define CONTAINER_FOCUS (1<<4)
+#define CONTAINER_INIT (1<<5)
+#define CONTAINER_MOVE (1<<7)
+
+#define DEF_CONTAINER_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_CONTAINER_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_CONTAINER_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_CONTAINER_COMMAND (char *)NULL
+#define DEF_CONTAINER_CURSOR (char *)NULL
+#define DEF_CONTAINER_HEIGHT "0"
+#define DEF_CONTAINER_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_CONTAINER_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_CONTAINER_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_CONTAINER_HIGHLIGHT_WIDTH "2"
+#define DEF_CONTAINER_RELIEF "sunken"
+#define DEF_CONTAINER_TAKE_FOCUS "0"
+#define DEF_CONTAINER_TIMEOUT "20"
+#define DEF_CONTAINER_WIDTH "0"
+#define DEF_CONTAINER_WINDOW (char *)NULL
+
+#if (TK_MAJOR_VERSION == 4)
+#define TK_REPARENTED 0x2000
+#endif
+
+typedef struct SearchInfoStruct SearchInfo;
+typedef void (SearchProc) _ANSI_ARGS_((Display *display, Window window,
+ SearchInfo *searchPtr));
+
+struct SearchInfoStruct {
+ SearchProc *proc;
+ char *pattern; /* Search pattern. */
+
+ Window window; /* XID of last window that matches criteria. */
+ int nMatches; /* Number of windows that match the pattern. */
+ int saveNames; /* Indicates to save the names of the
+ * window XIDs that match the search
+ * criteria. */
+ Tcl_DString dString; /* Will contain the strings of the
+ * window XIDs matching the search
+ * criteria. */
+};
+
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+
+ Tcl_Command cmdToken; /* Token for widget's command. */
+
+ unsigned int flags; /* For bit-field definitions, see above. */
+
+ int inset; /* Total width of borders; focus
+ * highlight and 3-D border. Indicates
+ * the offset from outside edges to
+ * leave room for borders. */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ Tk_3DBorder border; /* 3D border surrounding the adopted
+ * window. */
+ int borderWidth; /* Width of 3D border. */
+ int relief; /* 3D border relief. */
+
+ Tk_Window tkToplevel; /* Toplevel (wrapper) window of
+ * container. It's used to track the
+ * location of the container. If it
+ * moves we need to notify the
+ * embedded application. */
+ /*
+ * Focus highlight ring
+ */
+ int highlightWidth; /* Width in pixels of highlight to
+ * draw around widget when it has the
+ * focus. <= 0 means don't draw a
+ * highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal
+ * highlight area when highlight is
+ * off. */
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ GC highlightGC; /* GC for focus highlight. */
+
+ char *takeFocus; /* Says whether to select this widget during
+ * tab traveral operations. This value isn't
+ * used in C code, but for the widget's Tcl
+ * bindings. */
+
+ int reqWidth, reqHeight; /* Requested dimensions of the container
+ * window. */
+
+ Window adopted; /* X window Id or Win32 handle of adopted
+ * window contained by the widget. If None,
+ * no window has been reparented. */
+ Tk_Window tkAdopted; /* Non-NULL if this is a Tk window that's
+ * been adopted. */
+ int adoptedX, adoptedY; /* Current position of the adopted window. */
+ int adoptedWidth; /* Current width of the adopted window. */
+ int adoptedHeight; /* Current height of the adopted window. */
+
+ int origX, origY;
+ int origWidth, origHeight; /* Dimensions of the window when it
+ * was adopted. When the window is
+ * released it's returned to it's
+ * original dimensions. */
+
+ int timeout;
+} Container;
+
+
+static Tk_OptionParseProc StringToXID;
+static Tk_OptionPrintProc XIDToString;
+
+static Tk_CustomOption XIDOption =
+{
+ StringToXID, XIDToString, (ClientData)(SEARCH_TKWIN | SEARCH_XID),
+};
+
+#ifndef WIN32
+static Tk_CustomOption XIDNameOption =
+{
+ StringToXID, XIDToString, (ClientData)SEARCH_NAME,
+};
+
+static Tk_CustomOption XIDCmdOption =
+{
+ StringToXID, XIDToString, (ClientData)SEARCH_CMD,
+};
+#endif
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveCountOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_CONTAINER_BG_MONO, Tk_Offset(Container, border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_CONTAINER_BG_COLOR, Tk_Offset(Container, border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_CONTAINER_BORDER_WIDTH, Tk_Offset(Container, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+#ifndef WIN32
+ {TK_CONFIG_CUSTOM, "-command", "command", "Command",
+ DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
+ TK_CONFIG_DONT_SET_DEFAULT, &XIDCmdOption},
+#endif
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_CONTAINER_CURSOR, Tk_Offset(Container, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_CONTAINER_HEIGHT, Tk_Offset(Container, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_CONTAINER_HIGHLIGHT_BG_COLOR,
+ Tk_Offset(Container, highlightBgColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_CONTAINER_HIGHLIGHT_BG_MONO,
+ Tk_Offset(Container, highlightBgColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_CONTAINER_HIGHLIGHT_COLOR,
+ Tk_Offset(Container, highlightColor), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_CONTAINER_HIGHLIGHT_WIDTH, Tk_Offset(Container, highlightWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+#ifndef WIN32
+ {TK_CONFIG_CUSTOM, "-name", "name", "Name",
+ DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
+ TK_CONFIG_DONT_SET_DEFAULT, &XIDNameOption},
+#endif
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_CONTAINER_RELIEF, Tk_Offset(Container, relief), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_CONTAINER_TAKE_FOCUS, Tk_Offset(Container, takeFocus),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-timeout", "timeout", "Timeout",
+ DEF_CONTAINER_TIMEOUT, Tk_Offset(Container, timeout),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_CONTAINER_WIDTH, Tk_Offset(Container, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-window", "window", "Window",
+ DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
+ TK_CONFIG_DONT_SET_DEFAULT, &XIDOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static Tcl_IdleProc DisplayContainer;
+static Tcl_CmdProc ContainerInstCmd;
+static Tcl_CmdDeleteProc ContainerInstCmdDeleteProc;
+static Tk_EventProc ToplevelEventProc;
+static Tk_GenericProc AdoptedWindowEventProc;
+static Tk_EventProc ContainerEventProc;
+static Tcl_FreeProc DestroyContainer;
+static Tcl_CmdProc ContainerCmd;
+
+static void EventuallyRedraw _ANSI_ARGS_((Container *cntrPtr));
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowId --
+ *
+ * Returns the XID for the Tk_Window given. Starting in Tk 8.0,
+ * the toplevel widgets are wrapped by another window.
+ * Currently there's no way to get at that window, other than
+ * what is done here: query the X window hierarchy and grab the
+ * parent.
+ *
+ * Results:
+ * Returns the X Window ID of the widget. If it's a toplevel, then
+ * the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Window
+GetXID(tkwin)
+ Tk_Window tkwin;
+{
+ HWND hWnd;
+ TkWinWindow *twdPtr;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+#if (TK_MAJOR_VERSION > 4)
+ if (Tk_IsTopLevel(tkwin)) {
+ hWnd = GetParent(hWnd);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+ twdPtr = Blt_Malloc(sizeof(TkWinWindow));
+ twdPtr->handle = hWnd;
+ twdPtr->type = TWD_WINDOW;
+ twdPtr->winPtr = tkwin;
+ return (Window)twdPtr;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfId --
+ *
+ * Returns a string representing the given XID.
+ *
+ * Results:
+ * A static string containing either the hexidecimal number or
+ * the pathname of a Tk window.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfId(display, window)
+ Display *display; /* Display containing both the container widget
+ * and the adopted window. */
+ Window window; /* XID of the adopted window. */
+{
+ if (window != None) {
+ Tk_Window tkwin;
+ static char string[200];
+
+ /* See first if it's a window that Tk knows about. */
+ /*
+ * Note: If the wrapper window is reparented, Tk pretends it's
+ * no longer connected to the toplevel, so if you look for
+ * the child of the wrapper tkwin, it's NULL.
+ */
+ tkwin = Tk_IdToWindow(display, window);
+ if ((tkwin != NULL) && (Tk_PathName(tkwin) != NULL)) {
+ return Tk_PathName(tkwin);
+ }
+ sprintf(string, "0x%x", (unsigned int)window);
+ return string;
+ }
+ return ""; /* Return empty string if XID is None. */
+}
+
+#ifndef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGeometryErrorProc --
+ *
+ * Flags errors generated from XGetGeometry calls to the X server.
+ *
+ * Results:
+ * Always returns 0.
+ *
+ * Side Effects:
+ * Sets a flag, indicating an error occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+XGeometryErrorProc(clientData, eventPtr)
+ ClientData clientData;
+ XErrorEvent *eventPtr; /* Not used. */
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetAdoptedWindowGeometry --
+ *
+ * Computes the requested geometry of the container using the
+ * size of adopted window as a reference.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Sets a flag, indicating an error occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetAdoptedWindowGeometry(interp, cntrPtr)
+ Tcl_Interp *interp;
+ Container *cntrPtr;
+{
+ int x, y, width, height, borderWidth, depth;
+ int xOffset, yOffset;
+ Window root, dummy;
+ Tk_ErrorHandler handler;
+ int result;
+ int any = -1;
+
+ width = height = 1;
+ xOffset = yOffset = 0;
+ if (cntrPtr->adopted != None) {
+ handler = Tk_CreateErrorHandler(cntrPtr->display, any, X_GetGeometry,
+ any, XGeometryErrorProc, &result);
+ root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ XTranslateCoordinates(cntrPtr->display, cntrPtr->adopted,
+ root, 0, 0, &xOffset, &yOffset, &dummy);
+ result = XGetGeometry(cntrPtr->display, cntrPtr->adopted, &root,
+ &x, &y, (unsigned int *)&width, (unsigned int *)&height,
+ (unsigned int *)&borderWidth, (unsigned int *)&depth);
+ Tk_DeleteErrorHandler(handler);
+ XSync(cntrPtr->display, False);
+ if (result == 0) {
+ Tcl_AppendResult(interp, "can't get geometry for \"",
+ NameOfId(cntrPtr->display, cntrPtr->adopted), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ cntrPtr->origX = xOffset;
+ cntrPtr->origY = yOffset;
+ cntrPtr->origWidth = width;
+ cntrPtr->origHeight = height;
+ } else {
+ cntrPtr->origX = cntrPtr->origY = 0;
+ cntrPtr->origWidth = cntrPtr->origHeight = 0;
+ }
+ cntrPtr->adoptedX = x;
+ cntrPtr->adoptedY = y;
+ cntrPtr->adoptedWidth = width;
+ cntrPtr->adoptedHeight = height;
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetChildren --
+ *
+ * Returns a chain of the child windows according to their stacking
+ * order. The window ids are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetChildren(display, window)
+ Display *display;
+ Window window;
+{
+ Window *children;
+ unsigned int nChildren;
+ Window dummy;
+
+ if (!XQueryTree(display, window, &dummy /*parent*/, &dummy /*root*/,
+ &children, &nChildren)) {
+ return NULL;
+ }
+ if (nChildren > 0) {
+ Blt_Chain *chainPtr;
+ register int i;
+
+ chainPtr = Blt_ChainCreate();
+ for (i = 0; i < nChildren; i++) {
+ /*
+ * XQuery returns windows in bottom to top order.
+ * We'll reverse the order.
+ */
+ Blt_ChainPrepend(chainPtr, (ClientData)children[i]);
+ }
+ if (children != NULL) {
+ XFree((char *)children);
+ }
+ return chainPtr;
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameSearch --
+ *
+ * Traverses the entire window hierarchy, searching for windows
+ * matching the name field in the SearchInfo structure. This
+ * routine is recursively called for each successive level in
+ * the window hierarchy.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The SearchInfo structure will track the number of windows that
+ * match the given criteria.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NameSearch(display, window, searchPtr)
+ Display *display;
+ Window window;
+ SearchInfo *searchPtr;
+{
+ Blt_Chain *chainPtr;
+ char *wmName;
+
+ if (XFetchName(display, window, &wmName)) {
+ /* Compare the name of the window to the search pattern. */
+ if (Tcl_StringMatch(wmName, searchPtr->pattern)) {
+ if (searchPtr->saveNames) { /* Record names of matching windows. */
+ Tcl_DStringAppendElement(&(searchPtr->dString),
+ NameOfId(display, window));
+ Tcl_DStringAppendElement(&(searchPtr->dString), wmName);
+ }
+ searchPtr->window = window;
+ searchPtr->nMatches++;
+ }
+ XFree(wmName);
+ }
+ /* Process the window's descendants. */
+ chainPtr = GetChildren(display, window);
+ if (chainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ Window child;
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ child = (Window)Blt_ChainGetValue(linkPtr);
+ NameSearch(display, child, searchPtr);
+ }
+ Blt_ChainDestroy(chainPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CmdSearch --
+ *
+ * Traverses the entire window hierarchy, searching for windows
+ * matching the command-line specified in the SearchInfo structure.
+ * This routine is recursively called for each successive level
+ * in the window hierarchy.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The SearchInfo structure will track the number of windows that
+ * match the given command-line.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CmdSearch(display, window, searchPtr)
+ Display *display;
+ Window window;
+ SearchInfo *searchPtr;
+{
+ Blt_Chain *chainPtr;
+ int cmdArgc;
+ char **cmdArgv;
+
+ if (XGetCommand(display, window, &cmdArgv, &cmdArgc)) {
+ char *string;
+
+ string = Tcl_Merge(cmdArgc, cmdArgv);
+ XFreeStringList(cmdArgv);
+ if (Tcl_StringMatch(string, searchPtr->pattern)) {
+ if (searchPtr->saveNames) { /* Record names of matching windows. */
+ Tcl_DStringAppendElement(&(searchPtr->dString),
+ NameOfId(display, window));
+ Tcl_DStringAppendElement(&(searchPtr->dString), string);
+ }
+ searchPtr->window = window;
+ searchPtr->nMatches++;
+ }
+ Blt_Free(string);
+ }
+ /* Process the window's descendants. */
+ chainPtr = GetChildren(display, window);
+ if (chainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ Window child;
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ child = (Window)Blt_ChainGetValue(linkPtr);
+ CmdSearch(display, child, searchPtr);
+ }
+ Blt_ChainDestroy(chainPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TimeoutProc --
+ *
+ * Procedure called when the timer event elapses. Used to wait
+ * between attempts checking for the designated window.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Sets a flag, indicating the timeout occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TimeoutProc(clientData)
+ ClientData clientData;
+{
+ int *expirePtr = clientData;
+
+ *expirePtr = TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TestAndWaitForWindow --
+ *
+ * Searches, possibly multiple times, for windows matching the
+ * criteria given, using the search proc also given.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Sets a flag, indicating the timeout occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TestAndWaitForWindow(cntrPtr, searchPtr)
+ Container *cntrPtr; /* Container widget record. */
+ SearchInfo *searchPtr; /* Search criteria. */
+{
+ Window root;
+ Tcl_TimerToken timerToken;
+ int expire;
+ int i;
+
+ /* Get the root window to start the search. */
+ root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ timerToken = NULL;
+ for (i = 0; i < SEARCH_TRIES; i++) {
+ searchPtr->nMatches = 0;
+ (*searchPtr->proc)(cntrPtr->display, root, searchPtr);
+ if (searchPtr->nMatches > 0) {
+ if (timerToken != NULL) {
+ Tcl_DeleteTimerHandler(timerToken);
+ }
+ return;
+ }
+ expire = FALSE;
+ /*
+ * If the X11 application associated with the adopted window
+ * was just started (via "exec" or "bgexec"), the window may
+ * not exist yet. We have to wait a little bit for the program
+ * to start up. Create a timer event break us out of an wait
+ * loop. We'll wait for a given interval for the adopted window
+ * to appear.
+ */
+ timerToken = Tcl_CreateTimerHandler(cntrPtr->timeout, TimeoutProc,
+ &expire);
+ while (!expire) {
+ /* Should file events be allowed? */
+ Tcl_DoOneEvent(TCL_TIMER_EVENTS | TCL_WINDOW_EVENTS |
+ TCL_FILE_EVENTS);
+ }
+ }
+}
+#else
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetChildren --
+ *
+ * Returns a chain of the child windows according to their stacking
+ * order. The window ids are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetChildren(Display *display, Window window)
+{
+ Blt_Chain *chainPtr;
+ HWND hWnd;
+ HWND parent;
+
+ parent = Tk_GetHWND(window);
+ chainPtr = Blt_ChainCreate();
+ for (hWnd = GetTopWindow(parent); hWnd != NULL;
+ hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) {
+ Blt_ChainAppend(chainPtr, (ClientData)hWnd);
+ }
+ return chainPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetAdoptedWindowGeometry --
+ *
+ * Computes the requested geometry of the container using the
+ * size of adopted window as a reference.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Sets a flag, indicating an error occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetAdoptedWindowGeometry(Tcl_Interp *interp, Container *cntrPtr)
+{
+ int x, y, width, height;
+ int xOffset, yOffset;
+ Window root, dummy;
+ int any = -1;
+
+ width = height = 1;
+ xOffset = yOffset = 0;
+
+ if (cntrPtr->adopted != None) {
+ HWND hWnd;
+ RECT rect;
+
+ hWnd = Tk_GetHWND(cntrPtr->adopted);
+ if (GetWindowRect(hWnd, &rect)) {
+ x = rect.left;
+ y = rect.top;
+ width = rect.right - rect.left + 1;
+ height = rect.bottom - rect.top + 1;
+ } else {
+ Tcl_AppendResult(interp, "can't get geometry for \"",
+ NameOfId(cntrPtr->display, cntrPtr->adopted), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ XTranslateCoordinates(cntrPtr->display, cntrPtr->adopted,
+ root, 0, 0, &xOffset, &yOffset, &dummy);
+ cntrPtr->origX = xOffset;
+ cntrPtr->origY = yOffset;
+ cntrPtr->origWidth = width;
+ cntrPtr->origHeight = height;
+ } else {
+ cntrPtr->origX = cntrPtr->origY = 0;
+ cntrPtr->origWidth = cntrPtr->origHeight = 0;
+ }
+ cntrPtr->adoptedX = x;
+ cntrPtr->adoptedY = y;
+ cntrPtr->adoptedWidth = width;
+ cntrPtr->adoptedHeight = height;
+ return TCL_OK;
+}
+
+#endif /*WIN32*/
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * MapTree --
+ *
+ * Maps each window in the hierarchy. This is needed because
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Each window in the hierarchy is mapped.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+MapTree(display, window)
+ Display *display;
+ Window window;
+{
+ Blt_Chain *chainPtr;
+
+ XMapWindow(display, window);
+ chainPtr = GetChildren(display, window);
+ if (chainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ Window child;
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ child = (Window)Blt_ChainGetValue(linkPtr);
+ MapTree(display, child);
+ }
+ Blt_ChainDestroy(chainPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToXID --
+ *
+ * Converts a string into an X window Id.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *---------------------------------------------------------------------- */
+/*ARGSUSED*/
+static int
+StringToXID(clientData, interp, parent, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window parent; /* Parent window */
+ char *string; /* String representation. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset to field in structure */
+{
+ unsigned int flags = (int)clientData;
+ Container *cntrPtr = (Container *)widgRec;
+ Window *winPtr = (Window *) (widgRec + offset);
+ Tk_Window tkAdopted;
+ Window window;
+
+ tkAdopted = NULL;
+ if ((flags & SEARCH_TKWIN) && (string[0] == '.')) {
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (!Tk_IsTopLevel(tkwin)) {
+ Tcl_AppendResult(interp, "can't reparent non-toplevel Tk windows",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkAdopted = tkwin;
+ Tk_MakeWindowExist(tkwin);
+ window = Blt_GetRealWindowId(tkwin);
+#ifndef WIN32
+ } else if ((flags & SEARCH_XID) && (string[0] == '0') &&
+ (string[1] == 'x')) {
+ int token;
+
+ /* Hexidecimal string specifying the Window token. */
+ if (Tcl_GetInt(interp, string, &token) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ window = token;
+ } else if ((string == NULL) || (string[0] == '\0')) {
+ window = None;
+ } else {
+ SearchInfo search;
+
+ memset(&search, 0, sizeof(search));
+ if (flags & (SEARCH_NAME | SEARCH_CMD)) {
+ search.pattern = string;
+ if (flags & SEARCH_NAME) {
+ search.proc = NameSearch;
+ } else if (flags & SEARCH_CMD) {
+ search.proc = CmdSearch;
+ }
+ TestAndWaitForWindow(cntrPtr, &search);
+ if (search.nMatches > 1) {
+ Tcl_AppendResult(interp, "more than one window matches \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (search.nMatches == 0) {
+ Tcl_AppendResult(interp, "can't find window from pattern \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ window = search.window;
+#endif /*WIN32*/
+ }
+ if (*winPtr != None) {
+ Window root;
+
+ root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ if (Blt_ReparentWindow(cntrPtr->display, *winPtr, root,
+ cntrPtr->origX, cntrPtr->origY)
+ != TCL_OK) {
+ Tcl_AppendResult(interp, "can't restore \"",
+ NameOfId(cntrPtr->display, *winPtr),
+ "\" window to root", (char *)NULL);
+ return TCL_ERROR;
+ }
+ cntrPtr->flags &= ~CONTAINER_MAPPED;
+ if (cntrPtr->tkAdopted == NULL) {
+ /* This wasn't a Tk window. So deselect the event mask. */
+ XSelectInput(cntrPtr->display, *winPtr, 0);
+ } else {
+ MapTree(cntrPtr->display, *winPtr);
+ }
+ XMoveResizeWindow(cntrPtr->display, *winPtr, cntrPtr->origX,
+ cntrPtr->origY, cntrPtr->origWidth, cntrPtr->origHeight);
+ }
+ cntrPtr->tkAdopted = tkAdopted;
+ *winPtr = window;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XIDToString --
+ *
+ * Converts the Tk window back to its string representation (i.e.
+ * its name).
+ *
+ * Results:
+ * The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+XIDToString(clientData, parent, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window parent; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Container *cntrPtr = (Container *) widgRec;
+ Window window = *(Window *)(widgRec + offset);
+
+ if (cntrPtr->tkAdopted != NULL) {
+ return Tk_PathName(cntrPtr->tkAdopted);
+ }
+ return NameOfId(cntrPtr->display, window);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(cntrPtr)
+ Container *cntrPtr;
+{
+ if ((cntrPtr->tkwin != NULL) && !(cntrPtr->flags & CONTAINER_REDRAW)) {
+ cntrPtr->flags |= CONTAINER_REDRAW;
+ Tcl_DoWhenIdle(DisplayContainer, cntrPtr);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * AdoptedWindowEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on the encapsulated window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets resized or exposed, it's redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static int
+AdoptedWindowEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Container *cntrPtr = (Container *) clientData;
+
+ if (eventPtr->xany.window != cntrPtr->adopted) {
+ return 0;
+ }
+ if (eventPtr->type == DestroyNotify) {
+ cntrPtr->adopted = None;
+ EventuallyRedraw(cntrPtr);
+ }
+ return 1;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * ContainerEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on container widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+ContainerEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Container *cntrPtr = clientData;
+
+ switch (eventPtr->type) {
+ case Expose:
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedraw(cntrPtr);
+ }
+ break;
+
+ case FocusIn:
+ case FocusOut:
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ cntrPtr->flags |= CONTAINER_FOCUS;
+ } else {
+ cntrPtr->flags &= ~CONTAINER_FOCUS;
+ }
+ EventuallyRedraw(cntrPtr);
+ }
+ break;
+
+ case ConfigureNotify:
+ EventuallyRedraw(cntrPtr);
+ break;
+
+ case DestroyNotify:
+ if (cntrPtr->tkwin != NULL) {
+ cntrPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(cntrPtr->interp, cntrPtr->cmdToken);
+ }
+ if (cntrPtr->flags & CONTAINER_REDRAW) {
+ Tcl_CancelIdleCall(DisplayContainer, cntrPtr);
+ }
+ Tcl_EventuallyFree(cntrPtr, DestroyContainer);
+ break;
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * ToplevelEventProc --
+ *
+ * Some applications assume that they are always a toplevel
+ * window and play tricks accordingly. For example, Netscape
+ * positions menus in relation to the toplevel. But if the
+ * container's toplevel is moved, this positioning is wrong.
+ * So watch if the toplevel is moved.
+ *
+ * [This would be easier and cleaner if Tk toplevels weren't so
+ * botched by the addition of menubars. It's not enough to
+ * track the )
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+ToplevelEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Container *cntrPtr = clientData;
+
+ if ((cntrPtr->adopted != None) && (cntrPtr->tkwin != NULL) &&
+ (eventPtr->type == ConfigureNotify)) {
+ cntrPtr->flags |= CONTAINER_MOVE;
+ EventuallyRedraw(cntrPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyContainer --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of the widget at a safe
+ * time (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyContainer(dataPtr)
+ DestroyData dataPtr; /* Pointer to the widget record. */
+{
+ Container *cntrPtr = (Container *) dataPtr;
+
+ if (cntrPtr->highlightGC != NULL) {
+ Tk_FreeGC(cntrPtr->display, cntrPtr->highlightGC);
+ }
+ if (cntrPtr->flags & CONTAINER_INIT) {
+ Tk_DeleteGenericHandler(AdoptedWindowEventProc, cntrPtr);
+ }
+ if (cntrPtr->tkToplevel != NULL) {
+ Tk_DeleteEventHandler(cntrPtr->tkToplevel, StructureNotifyMask,
+ ToplevelEventProc, cntrPtr);
+ }
+ Tk_FreeOptions(configSpecs, (char *)cntrPtr, cntrPtr->display, 0);
+ Blt_Free(cntrPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureContainer --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for cntrPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureContainer(interp, cntrPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors. */
+ Container *cntrPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+ int argc;
+ char **argv;
+ int flags;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ int width, height;
+
+ if (Tk_ConfigureWidget(interp, cntrPtr->tkwin, configSpecs, argc, argv,
+ (char *)cntrPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ cntrPtr->inset = cntrPtr->borderWidth + cntrPtr->highlightWidth;
+ if (Tk_WindowId(cntrPtr->tkwin) == None) {
+ Tk_MakeWindowExist(cntrPtr->tkwin);
+ }
+ if (GetAdoptedWindowGeometry(interp, cntrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ConfigModified(configSpecs, "-window", "-name", "-command",
+ (char *)NULL)) {
+ cntrPtr->flags &= ~CONTAINER_MAPPED;
+ if (cntrPtr->adopted != None) {
+ if (Blt_ReparentWindow(cntrPtr->display, cntrPtr->adopted,
+ Tk_WindowId(cntrPtr->tkwin), cntrPtr->inset,
+ cntrPtr->inset) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't adopt window \"",
+ NameOfId(cntrPtr->display, cntrPtr->adopted),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ XSelectInput(cntrPtr->display, cntrPtr->adopted,
+ StructureNotifyMask);
+ if ((cntrPtr->flags & CONTAINER_INIT) == 0) {
+ Tk_CreateGenericHandler(AdoptedWindowEventProc, cntrPtr);
+ cntrPtr->flags |= CONTAINER_INIT;
+ }
+ }
+ }
+ /* Add the designated inset to the requested dimensions. */
+ width = cntrPtr->origWidth + 2 * cntrPtr->inset;
+ height = cntrPtr->origHeight + 2 * cntrPtr->inset;
+
+ if (cntrPtr->reqWidth > 0) {
+ width = cntrPtr->reqWidth;
+ }
+ if (cntrPtr->reqHeight > 0) {
+ height = cntrPtr->reqHeight;
+ }
+ /* Set the requested width and height for the container. */
+ if ((Tk_ReqWidth(cntrPtr->tkwin) != width) ||
+ (Tk_ReqHeight(cntrPtr->tkwin) != height)) {
+ Tk_GeometryRequest(cntrPtr->tkwin, width, height);
+ }
+
+ /*
+ * GC for focus highlight.
+ */
+ gcMask = GCForeground;
+ gcValues.foreground = cntrPtr->highlightColor->pixel;
+ newGC = Tk_GetGC(cntrPtr->tkwin, gcMask, &gcValues);
+ if (cntrPtr->highlightGC != NULL) {
+ Tk_FreeGC(cntrPtr->display, cntrPtr->highlightGC);
+ }
+ cntrPtr->highlightGC = newGC;
+
+ EventuallyRedraw(cntrPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ContainerInstCmdDeleteProc --
+ *
+ * This procedure can be called if the window was destroyed
+ * (tkwin will be NULL) and the command was deleted
+ * automatically. In this case, we need to do nothing.
+ *
+ * Otherwise this routine was called because the command was
+ * deleted. Then we need to clean-up and destroy the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ContainerInstCmdDeleteProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Container *cntrPtr = clientData;
+
+ if (cntrPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = cntrPtr->tkwin;
+ cntrPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ContainerCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * See the user documentation.
+ *
+ * -----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ContainerCmd(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. */
+{
+ Container *cntrPtr;
+ Tk_Window tkwin;
+ unsigned int mask;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_MainWindow(interp);
+ tkwin = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ cntrPtr = Blt_Calloc(1, sizeof(Container));
+ assert(cntrPtr);
+ cntrPtr->tkwin = tkwin;
+ cntrPtr->display = Tk_Display(tkwin);
+ cntrPtr->interp = interp;
+ cntrPtr->flags = 0;
+ cntrPtr->timeout = SEARCH_INTERVAL;
+ cntrPtr->borderWidth = cntrPtr->highlightWidth = 2;
+ cntrPtr->relief = TK_RELIEF_SUNKEN;
+ Tk_SetClass(tkwin, "Container");
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, cntrPtr);
+#endif
+ if (ConfigureContainer(interp, cntrPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ Tk_DestroyWindow(cntrPtr->tkwin);
+ return TCL_ERROR;
+ }
+ mask = (StructureNotifyMask | ExposureMask | FocusChangeMask);
+ Tk_CreateEventHandler(tkwin, mask, ContainerEventProc, cntrPtr);
+ cntrPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], ContainerInstCmd,
+ cntrPtr, ContainerInstCmdDeleteProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(cntrPtr->tkwin, cntrPtr->cmdToken);
+#endif
+ Tk_MakeWindowExist(tkwin);
+
+ Tcl_SetResult(interp, Tk_PathName(cntrPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayContainer --
+ *
+ * This procedure is invoked to display the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayContainer(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Container *cntrPtr = clientData;
+ Drawable drawable;
+ int width, height;
+
+ cntrPtr->flags &= ~CONTAINER_REDRAW;
+ if (cntrPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (!Tk_IsMapped(cntrPtr->tkwin)) {
+ return;
+ }
+ drawable = Tk_WindowId(cntrPtr->tkwin);
+
+#ifndef WIN32
+ if (cntrPtr->tkToplevel == NULL) {
+ Window window;
+ Tk_Window tkToplevel;
+
+ /* Create an event handler for the toplevel of the container. */
+ tkToplevel = Blt_Toplevel(cntrPtr->tkwin);
+ window = Blt_GetRealWindowId(tkToplevel);
+ cntrPtr->tkToplevel = Tk_IdToWindow(cntrPtr->display, window);
+ if (cntrPtr->tkToplevel != NULL) {
+ Tk_CreateEventHandler(cntrPtr->tkToplevel, StructureNotifyMask,
+ ToplevelEventProc, cntrPtr);
+ }
+ }
+#endif /* WIN32 */
+ if (cntrPtr->adopted != None) {
+#ifndef WIN32
+ if (cntrPtr->flags & CONTAINER_MOVE) {
+ /*
+ * Some applications like Netscape cache its location to
+ * position its popup menus. But when it's reparented, it
+ * thinks it's always at the same position. It doesn't
+ * know when the container's moved. The hack here is to
+ * force the application to update its coordinates by
+ * moving the adopted window (over by 1 pixel and
+ * then back in case the application is comparing the last
+ * location).
+ */
+ XMoveWindow(cntrPtr->display, cntrPtr->adopted,
+ cntrPtr->inset + 1, cntrPtr->inset + 1);
+ XMoveWindow(cntrPtr->display, cntrPtr->adopted,
+ cntrPtr->inset, cntrPtr->inset);
+ cntrPtr->flags &= ~CONTAINER_MOVE;
+ }
+#endif /* WIN32 */
+ /* Compute the available space inside the container. */
+ width = Tk_Width(cntrPtr->tkwin) - (2 * cntrPtr->inset);
+ height = Tk_Height(cntrPtr->tkwin) - (2 * cntrPtr->inset);
+
+ if ((cntrPtr->adoptedX != cntrPtr->inset) ||
+ (cntrPtr->adoptedY != cntrPtr->inset) ||
+ (cntrPtr->adoptedWidth != width) ||
+ (cntrPtr->adoptedHeight != height)) {
+ /* Resize the window to the new size */
+ if (width < 1) {
+ width = 1;
+ }
+ if (height < 1) {
+ height = 1;
+ }
+ XMoveResizeWindow(cntrPtr->display, cntrPtr->adopted,
+ cntrPtr->inset, cntrPtr->inset, width, height);
+ cntrPtr->adoptedWidth = width;
+ cntrPtr->adoptedHeight = height;
+ cntrPtr->adoptedX = cntrPtr->adoptedY = cntrPtr->inset;
+ if (cntrPtr->tkAdopted != NULL) {
+ Tk_ResizeWindow(cntrPtr->tkAdopted, width, height);
+ }
+ }
+#ifndef WIN32
+ if (!(cntrPtr->flags & CONTAINER_MAPPED)) {
+ XMapWindow(cntrPtr->display, cntrPtr->adopted);
+ cntrPtr->flags |= CONTAINER_MAPPED;
+ }
+#endif
+ if (cntrPtr->borderWidth > 0) {
+ Tk_Draw3DRectangle(cntrPtr->tkwin, drawable, cntrPtr->border,
+ cntrPtr->highlightWidth, cntrPtr->highlightWidth,
+ Tk_Width(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
+ Tk_Height(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
+ cntrPtr->borderWidth, cntrPtr->relief);
+ }
+ } else {
+ Tk_Fill3DRectangle(cntrPtr->tkwin, drawable, cntrPtr->border,
+ cntrPtr->highlightWidth, cntrPtr->highlightWidth,
+ Tk_Width(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
+ Tk_Height(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
+ cntrPtr->borderWidth, cntrPtr->relief);
+ }
+
+ /* Draw focus highlight ring. */
+ if (cntrPtr->highlightWidth > 0) {
+ XColor *color;
+ GC gc;
+
+ color = (cntrPtr->flags & CONTAINER_FOCUS)
+ ? cntrPtr->highlightColor : cntrPtr->highlightBgColor;
+ gc = Tk_GCForColor(color, drawable);
+ Tk_DrawFocusHighlight(cntrPtr->tkwin, gc, cntrPtr->highlightWidth,
+ drawable);
+ }
+}
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * SendOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SendOp(cntrPtr, interp, argc, argv)
+ Container *cntrPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+
+ if (cntrPtr->adopted != None) {
+ XEvent event;
+ char *p;
+ KeySym symbol;
+ int xid;
+ Window window;
+
+ if (Tcl_GetInt(interp, argv[2], &xid) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ window = (Window)xid;
+ event.xkey.type = KeyPress;
+ event.xkey.serial = 0;
+ event.xkey.display = cntrPtr->display;
+ event.xkey.window = event.xkey.subwindow = window;
+ event.xkey.time = CurrentTime;
+ event.xkey.x = event.xkey.x = 100;
+ event.xkey.root =
+ RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ event.xkey.x_root = Tk_X(cntrPtr->tkwin) + cntrPtr->inset;
+ event.xkey.x_root = Tk_Y(cntrPtr->tkwin) + cntrPtr->inset;
+ event.xkey.state = 0;
+ event.xkey.same_screen = TRUE;
+
+ for (p = argv[3]; *p != '\0'; p++) {
+ if (*p == '\r') {
+ symbol = XStringToKeysym("Return");
+ } else if (*p == ' ') {
+ symbol = XStringToKeysym("space");
+ } else {
+ char save;
+
+ save = *(p+1);
+ *(p+1) = '\0';
+ symbol = XStringToKeysym(p);
+ *(p+1) = save;
+ }
+ event.xkey.keycode = XKeysymToKeycode(cntrPtr->display, symbol);
+ event.xkey.type = KeyPress;
+ if (!XSendEvent(cntrPtr->display, window, False, KeyPress, &event)) {
+ fprintf(stderr, "send press event failed\n");
+ }
+ event.xkey.type = KeyRelease;
+ if (!XSendEvent(cntrPtr->display, window, False, KeyRelease,
+ &event)) {
+ fprintf(stderr, "send release event failed\n");
+ }
+ }
+ }
+ return TCL_OK;
+}
+#endif
+
+#ifndef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindOp(cntrPtr, interp, argc, argv)
+ Container *cntrPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Window root;
+ SearchInfo search;
+
+ memset(&search, 0, sizeof(search));
+ search.pattern = argv[3];
+ Tcl_DStringInit(&(search.dString));
+ search.saveNames = TRUE; /* Indicates to record all matching XIDs. */
+ if (strcmp(argv[2], "-name") == 0) {
+ search.proc = NameSearch;
+ } else if (strcmp(argv[2], "-command") == 0) {
+ search.proc = CmdSearch;
+ } else {
+ Tcl_AppendResult(interp, "missing \"-name\" or \"-command\" switch",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
+ (*search.proc)(cntrPtr->display, root, &search);
+ Tcl_DStringResult(interp, &search.dString);
+ return TCL_OK;
+}
+#endif /*WIN32*/
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(cntrPtr, interp, argc, argv)
+ Container *cntrPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, cntrPtr->tkwin, configSpecs,
+ (char *)cntrPtr, argv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for cntrPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(cntrPtr, interp, argc, argv)
+ Container *cntrPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, cntrPtr->tkwin, configSpecs,
+ (char *)cntrPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, cntrPtr->tkwin, configSpecs,
+ (char *)cntrPtr, argv[2], 0);
+ }
+ if (ConfigureContainer(interp, cntrPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EventuallyRedraw(cntrPtr);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * ContainerCmd --
+ *
+ * This procedure is invoked to process the "container" command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec opSpecs[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+#ifndef WIN32
+ {"find", 1, (Blt_Op)FindOp, 3, 4, "?-command|-name? pattern",},
+#endif /*WIN32*/
+#ifdef notdef
+ {"send", 1, (Blt_Op)SendOp, 4, 4, "window string",},
+#endif
+};
+
+static int nSpecs = sizeof(opSpecs) / sizeof(Blt_OpSpec);
+
+static int
+ContainerInstCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ Container *cntrPtr = clientData;
+ int result;
+
+ proc = Blt_GetOp(interp, nSpecs, opSpecs, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(cntrPtr);
+ result = (*proc)(cntrPtr, interp, argc, argv);
+ Tcl_Release(cntrPtr);
+ return result;
+}
+
+int
+Blt_ContainerInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "container", ContainerCmd,
+ };
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_CONTAINER */
diff --git a/blt/src/bltCutbuffer.c b/blt/src/bltCutbuffer.c
new file mode 100644
index 00000000000..f1d38ce0167
--- /dev/null
+++ b/blt/src/bltCutbuffer.c
@@ -0,0 +1,265 @@
+/*
+ * bltCutbuffer.c --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_CUTBUFFER
+
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+static int
+GetCutNumber(interp, string, bufferPtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *bufferPtr;
+{
+ int number;
+
+ if (Tcl_GetInt(interp, string, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((number < 0) || (number > 7)) {
+ Tcl_AppendResult(interp, "bad buffer # \"", string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *bufferPtr = number;
+ return TCL_OK;
+}
+
+/* ARGSUSED */
+static int
+RotateErrorProc(clientData, errEventPtr)
+ ClientData clientData;
+ XErrorEvent *errEventPtr;
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+static int
+GetOp(interp, tkwin, argc, argv)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ int argc;
+ char **argv;
+{
+ char *string;
+ int buffer;
+ int nBytes;
+
+ buffer = 0;
+ if (argc == 3) {
+ if (GetCutNumber(interp, argv[2], &buffer) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ string = XFetchBuffer(Tk_Display(tkwin), &nBytes, buffer);
+ if (string != NULL) {
+ int limit;
+ register char *p;
+ register int i;
+ int c;
+
+ if (string[nBytes - 1] == '\0') {
+ limit = nBytes - 1;
+ } else {
+ limit = nBytes;
+ }
+ for (p = string, i = 0; i < limit; i++, p++) {
+ c = (unsigned char)*p;
+ if (c == 0) {
+ *p = ' '; /* Convert embedded NUL bytes */
+ }
+ }
+ if (limit == nBytes) {
+ char *newPtr;
+
+ /*
+ * Need to copy the string into a bigger buffer so we can
+ * add a NUL byte on the end.
+ */
+ newPtr = Blt_Malloc(nBytes + 1);
+ assert(newPtr);
+ memcpy(newPtr, string, nBytes);
+ newPtr[nBytes] = '\0';
+ Blt_Free(string);
+ string = newPtr;
+ }
+ Tcl_SetResult(interp, string, TCL_DYNAMIC);
+ }
+ return TCL_OK;
+}
+
+static int
+RotateOp(interp, tkwin, argc, argv)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ int argc;
+ char **argv;
+{
+ int count;
+ int result;
+ Tk_ErrorHandler handler;
+
+ count = 1; /* Default: rotate one position */
+ if (argc == 3) {
+ if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((count < 0) || (count > 8)) {
+ Tcl_AppendResult(interp, "bad rotate count \"", argv[2], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ result = TCL_OK;
+ handler = Tk_CreateErrorHandler(Tk_Display(tkwin), BadMatch,
+ X_RotateProperties, -1, RotateErrorProc, &result);
+ XRotateBuffers(Tk_Display(tkwin), count);
+ Tk_DeleteErrorHandler(handler);
+ XSync(Tk_Display(tkwin), False);
+ if (result != TCL_OK) {
+ Tcl_AppendResult(interp, "can't rotate cutbuffers unless all are set",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+static int
+SetOp(interp, tkwin, argc, argv)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ int argc;
+ char **argv;
+{
+ int buffer;
+
+ buffer = 0;
+ if (argc == 4) {
+ if (GetCutNumber(interp, argv[3], &buffer) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ XStoreBuffer(Tk_Display(tkwin), argv[2], strlen(argv[2]) + 1, buffer);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BLT Sub-command specification:
+ *
+ * - Name of the sub-command.
+ * - Minimum number of characters needed to unambiguously
+ * recognize the sub-command.
+ * - Pointer to the function to be called for the sub-command.
+ * - Minimum number of arguments accepted.
+ * - Maximum number of arguments accepted.
+ * - String to be displayed for usage.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec cbOps[] =
+{
+ {"get", 1, (Blt_Op)GetOp, 2, 3, "?buffer?",},
+ {"rotate", 1, (Blt_Op)RotateOp, 2, 3, "?count?",},
+ {"set", 1, (Blt_Op)SetOp, 3, 4, "value ?buffer?",},
+};
+static int numCbOps = sizeof(cbOps) / sizeof(Blt_OpSpec);
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CutBufferCmd --
+ *
+ * This procedure is invoked to process the "cutbuffer" Tcl
+ * command. See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CutbufferCmd(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;
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, numCbOps, cbOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ tkwin = Tk_MainWindow(interp);
+ result = (*proc) (interp, tkwin, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CutbufferInit --
+ *
+ * This procedure is invoked to initialize the "cutbuffer" Tcl
+ * command. See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CutbufferInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"cutbuffer", CutbufferCmd,};
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_CUTBUFFER */
diff --git a/blt/src/bltDebug.c b/blt/src/bltDebug.c
new file mode 100644
index 00000000000..f9a5378dbfd
--- /dev/null
+++ b/blt/src/bltDebug.c
@@ -0,0 +1,329 @@
+
+/*
+ * bltDebug.c --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_BLTDEBUG
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+#include "bltChain.h"
+static Blt_Chain watchChain;
+
+#ifdef __STDC__
+static Tcl_CmdTraceProc DebugProc;
+static Tcl_CmdProc DebugCmd;
+#endif
+
+typedef struct {
+ char *pattern;
+ char *name;
+} WatchInfo;
+
+static WatchInfo *
+GetWatch(name)
+ char *name;
+{
+ Blt_ChainLink *linkPtr;
+ char c;
+ WatchInfo *infoPtr;
+
+ c = name[0];
+ for (linkPtr = Blt_ChainFirstLink(&watchChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ infoPtr = Blt_ChainGetValue(linkPtr);
+ if ((infoPtr->name[0] == c) && (strcmp(name, infoPtr->name) == 0)) {
+ return infoPtr;
+ }
+ }
+ linkPtr = Blt_ChainAllocLink(sizeof(WatchInfo));
+ infoPtr = Blt_ChainGetValue(linkPtr);
+ infoPtr->name = Blt_Strdup(name);
+ Blt_ChainLinkAfter(&watchChain, linkPtr, (Blt_ChainLink *)NULL);
+ return infoPtr;
+}
+
+static void
+DeleteWatch(watchName)
+ char *watchName;
+{
+ Blt_ChainLink *linkPtr;
+ char c;
+ WatchInfo *infoPtr;
+
+ c = watchName[0];
+ for (linkPtr = Blt_ChainFirstLink(&watchChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ infoPtr = Blt_ChainGetValue(linkPtr);
+ if ((infoPtr->name[0] == c) &&
+ (strcmp(infoPtr->name, watchName) == 0)) {
+ Blt_Free(infoPtr->name);
+ Blt_ChainDeleteLink(&watchChain, linkPtr);
+ return;
+ }
+ }
+}
+
+/*ARGSUSED*/
+static void
+DebugProc(clientData, interp, level, command, proc, cmdClientData,
+ argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Not used. */
+ int level; /* Current level */
+ char *command; /* Command before substitution */
+ Tcl_CmdProc *proc; /* Not used. */
+ ClientData cmdClientData; /* Not used. */
+ int argc;
+ char **argv; /* Command after parsing, but before
+ * evaluation */
+{
+ static unsigned char traceStack[200];
+ register int i;
+ char *string;
+ Tcl_Channel errChannel;
+ Tcl_DString dString;
+ char prompt[200];
+ register char *p;
+ char *lineStart;
+ int count;
+
+ /* This is pretty crappy, but there's no way to trigger stack pops */
+ for (i = level + 1; i < 200; i++) {
+ traceStack[i] = 0;
+ }
+ if (Blt_ChainGetLength(&watchChain) > 0) {
+ WatchInfo *infoPtr;
+ int found;
+ Blt_ChainLink *linkPtr;
+
+ found = FALSE;
+ for (linkPtr = Blt_ChainFirstLink(&watchChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ infoPtr = Blt_ChainGetValue(linkPtr);
+ if (Tcl_StringMatch(argv[0], infoPtr->name)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if ((found) && (level < 200)) {
+ traceStack[level] = 1;
+ traceStack[level + 1] = 1;
+ }
+ if ((level >= 200) || (!traceStack[level])) {
+ return;
+ }
+ }
+ /*
+ * Use stderr channel, for compatibility with systems that don't have a
+ * tty (like WIN32). In reality, it doesn't make a difference since
+ * Tk's Win32 console can't handle large streams of data anyways.
+ */
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel == NULL) {
+ Tcl_AppendResult(interp, "can't get stderr channel", (char *)NULL);
+ Tcl_BackgroundError(interp);
+ return;
+ }
+ Tcl_DStringInit(&dString);
+
+ sprintf(prompt, "%-2d-> ", level);
+ p = command;
+ /* Skip leading spaces in command line. */
+ while(isspace(UCHAR(*p))) {
+ p++;
+ }
+ lineStart = p;
+ count = 0;
+ for (/* empty */; *p != '\0'; /* empty */) {
+ if (*p == '\n') {
+ if (count > 0) {
+ Tcl_DStringAppend(&dString, " ", -1);
+ } else {
+ Tcl_DStringAppend(&dString, prompt, -1);
+ }
+ Tcl_DStringAppend(&dString, lineStart, p - lineStart);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ p++;
+ lineStart = p;
+ count++;
+ if (count > 6) {
+ break;
+ }
+ } else {
+ p++;
+ }
+ }
+ while (isspace(UCHAR(*lineStart))) {
+ lineStart++;
+ }
+ if (lineStart < p) {
+ if (count > 0) {
+ Tcl_DStringAppend(&dString, " ", -1);
+ } else {
+ Tcl_DStringAppend(&dString, prompt, -1);
+ }
+ Tcl_DStringAppend(&dString, lineStart, p - lineStart);
+ if (count <= 6) {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ if (count > 6) {
+ Tcl_DStringAppend(&dString, " ...\n", -1);
+ }
+ string = Tcl_Merge(argc, argv);
+ lineStart = string;
+ sprintf(prompt, " <- ");
+ count = 0;
+ for (p = string; *p != '\0'; /* empty */) {
+ if (*p == '\n') {
+ if (count > 0) {
+ Tcl_DStringAppend(&dString, " ", -1);
+ } else {
+ Tcl_DStringAppend(&dString, prompt, -1);
+ }
+ count++;
+ Tcl_DStringAppend(&dString, lineStart, p - lineStart);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ p++;
+ lineStart = p;
+ if (count > 6) {
+ break;
+ }
+ } else {
+ p++;
+ }
+ }
+ if (lineStart < p) {
+ if (count > 0) {
+ Tcl_DStringAppend(&dString, " ", -1);
+ } else {
+ Tcl_DStringAppend(&dString, prompt, -1);
+ }
+ Tcl_DStringAppend(&dString, lineStart, p - lineStart);
+ if (count <= 6) {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ if (count > 6) {
+ Tcl_DStringAppend(&dString, " ...\n", -1);
+ }
+ Tcl_DStringAppend(&dString, "\n", -1);
+ Blt_Free(string);
+ Tcl_Write(errChannel, (char *)Tcl_DStringValue(&dString), -1);
+ Tcl_Flush(errChannel);
+ Tcl_DStringFree(&dString);
+}
+
+/*ARGSUSED*/
+static int
+DebugCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ static Tcl_Trace token;
+ static int level;
+ int newLevel;
+ char c;
+ int length;
+ WatchInfo *infoPtr;
+ Blt_ChainLink *linkPtr;
+ register int i;
+
+ if (argc == 1) {
+ Tcl_SetResult(interp, Blt_Itoa(level), TCL_VOLATILE);
+ return TCL_OK;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'w') && (strncmp(argv[1], "watch", length) == 0)) {
+ /* Add patterns of command names to watch to the chain */
+ for (i = 2; i < argc; i++) {
+ GetWatch(argv[i]);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)) {
+ for (i = 2; i < argc; i++) {
+ DeleteWatch(argv[i]);
+ }
+ } else {
+ goto levelTest;
+ }
+ /* Return the current watch patterns */
+ for (linkPtr = Blt_ChainFirstLink(&watchChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ infoPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, infoPtr->name);
+ }
+ return TCL_OK;
+
+ levelTest:
+ if (Tcl_GetBoolean(interp, argv[1], &newLevel) == TCL_OK) {
+ if (newLevel > 0) {
+ newLevel = 10000; /* Max out the level */
+ }
+ } else if (Tcl_GetInt(interp, argv[1], &newLevel) == TCL_OK) {
+ if (newLevel < 0) {
+ newLevel = 0;
+ }
+ } else {
+ return TCL_ERROR;
+ }
+ if (token != 0) {
+ Tcl_DeleteTrace(interp, token);
+ }
+ if (newLevel > 0) {
+ token = Tcl_CreateTrace(interp, newLevel, DebugProc, (ClientData)0);
+ }
+ level = newLevel;
+ Tcl_SetResult(interp, Blt_Itoa(level), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+int
+Blt_DebugInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"bltdebug", DebugCmd,};
+
+ Blt_ChainInit(&watchChain);
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_BLTDEBUG */
diff --git a/blt/src/bltDragdrop.c b/blt/src/bltDragdrop.c
new file mode 100644
index 00000000000..f0140a9c173
--- /dev/null
+++ b/blt/src/bltDragdrop.c
@@ -0,0 +1,2715 @@
+/*
+ * bltDnd.c --
+ *
+ * This module implements a drag-and-drop mechanism for the Tk
+ * Toolkit. Allows widgets to be registered as drag&drop sources
+ * and targets for handling "drag-and-drop" operations between
+ * Tcl/Tk applications.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "drag&drop" command was created by Michael J. McLennan.
+ */
+#include "bltInt.h"
+
+#ifndef NO_DRAGDROP
+#include "bltHash.h"
+#include "bltChain.h"
+
+#include <X11/Xatom.h>
+
+#ifdef WIN32
+#define MAX_PROP_SIZE 255 /* Maximum size of property. */
+typedef HWND WINDOW;
+#else
+#define MAX_PROP_SIZE 1000 /* Maximum size of property. */
+typedef Window WINDOW;
+static Atom dndAtom;
+#endif
+
+/*
+ * Each "drag&drop" target widget is tagged with a "BltDrag&DropTarget"
+ * property in XA_STRING format. This property identifies the window
+ * as a "drag&drop" target. It's formated as a Tcl list and contains
+ * the following information:
+ *
+ * "INTERP_NAME TARGET_NAME DATA_TYPE DATA_TYPE ..."
+ *
+ * INTERP_NAME Name of the target application's interpreter.
+ * TARGET_NAME Path name of widget registered as the drop target.
+ * DATA_TYPE One or more "types" handled by the target.
+ *
+ * When the user invokes the "drag" operation, the window hierarchy
+ * is progressively examined. Window information is cached during
+ * the operation, to minimize X server traffic. Windows carrying a
+ * "BltDrag&DropTarget" property are identified. When the token is
+ * dropped over a valid site, the drop information is sent to the
+ * application
+ * via the usual "send" command. If communication fails, the drag&drop
+ * facility automatically posts a rejection symbol on the token window.
+ */
+
+#define INTERP_NAME 0
+#define TARGET_NAME 1
+#define DATA_TYPE 2
+
+/* Error Proc used to report drag&drop background errors */
+#define DEF_ERROR_PROC "bgerror"
+/*
+ * CONFIG PARAMETERS
+ */
+#define DEF_DND_BUTTON_BG_COLOR RGB_YELLOW
+#define DEF_DND_BUTTON_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_DND_BUTTON_NUMBER "3"
+#define DEF_DND_PACKAGE_COMMAND (char *)NULL
+#define DEF_DND_SELF_TARGET "no"
+#define DEF_DND_SEND "all"
+#define DEF_DND_SITE_COMMAND (char *)NULL
+#define DEF_TOKEN_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BORDERWIDTH "3"
+#define DEF_TOKEN_ACTIVE_RELIEF "sunken"
+#define DEF_TOKEN_ANCHOR "se"
+#define DEF_TOKEN_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TOKEN_BORDERWIDTH "3"
+#define DEF_TOKEN_CURSOR "arrow"
+#define DEF_TOKEN_OUTLINE_COLOR RGB_BLACK
+#define DEF_TOKEN_OUTLINE_MONO RGB_BLACK
+#define DEF_TOKEN_REJECT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_REJECT_BG_MONO RGB_WHITE
+#define DEF_TOKEN_REJECT_FG_COLOR RGB_RED
+#define DEF_TOKEN_REJECT_FG_MONO RGB_BLACK
+#define DEF_TOKEN_REJECT_STIPPLE_COLOR (char *)NULL
+#define DEF_TOKEN_REJECT_STIPPLE_MONO RGB_GREY50
+#define DEF_TOKEN_RELIEF "raised"
+
+#if HAVE_NAMESPACES
+static char dragDropCmd[] = "blt::drag&drop";
+#else
+static char dragDropCmd[] = "drag&drop";
+#endif
+
+static char className[] = "DragDropToken"; /* CLASS NAME of token window */
+static char propName[] = "BltDrag&DropTarget"; /* Property name */
+
+static Blt_HashTable sourceTable;
+static Blt_HashTable targetTable;
+static char *errorCmd;
+static int nActive;
+static int locX, locY;
+static int initialized = FALSE;
+
+/*
+ * Percent substitutions
+ */
+typedef struct {
+ char letter; /* character like 'x' in "%x" */
+ char *value; /* value to be substituted in place of "%x" */
+} SubstDescriptors;
+
+
+/*
+ * AnyWindow --
+ *
+ * This structure represents a window hierarchy examined during
+ * a single "drag" operation. It's used to cache information
+ * to reduce the round-trip calls to the server needed to query
+ * window geometry information and grab the target property.
+ */
+typedef struct AnyWindowStruct AnyWindow;
+
+struct AnyWindowStruct {
+ WINDOW nativeWindow; /* Native window: HWINDOW (Win32) or
+ * Window (X11). */
+
+ int initialized; /* If non-zero, the rest of this structure's
+ * information had been previously built. */
+
+ int x1, y1, x2, y2; /* Extents of the window (upper-left and
+ * lower-right corners). */
+
+ AnyWindow *parentPtr; /* Parent node. NULL if root. Used to
+ * compute offset for X11 windows. */
+
+ Blt_Chain *chainPtr; /* List of this window's children. If NULL,
+ * there are no children. */
+
+ char **targetInfo; /* An array of target window drag&drop
+ * information: target interpreter,
+ * pathname, and optionally possible
+ * type matches. NULL if the window is
+ * not a drag&drop target or is not a
+ * valid match for the drop source. */
+
+};
+
+/*
+ * Drag&Drop Registration Data
+ */
+typedef struct {
+
+ /*
+ * This is a goof in the Tk API. It assumes that only an official
+ * Tk "toplevel" widget will ever become a toplevel window (i.e. a
+ * window whose parent is the root window). Because under Win32,
+ * Tk tries to use the widget record associated with the TopLevel
+ * as a Tk frame widget, to read its menu name. What this means
+ * is that any widget that's going to be a toplevel, must also look
+ * like a frame. Therefore we've copied the frame widget structure
+ * fields into the token.
+ */
+
+ Tk_Window tkwin; /* Window that embodies the frame. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up. */
+ Display *display; /* Display containing widget. Used, among
+ * other things, so that resources can be
+ * freed even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for frame's widget command. */
+ char *className; /* Class name for widget (from configuration
+ * option). Malloc-ed. */
+ int mask; /* Either FRAME or TOPLEVEL; used to select
+ * which configuration options are valid for
+ * widget. */
+ char *screenName; /* Screen on which widget is created. Non-null
+ * only for top-levels. Malloc-ed, may be
+ * NULL. */
+ char *visualName; /* Textual description of visual for window,
+ * from -visual option. Malloc-ed, may be
+ * NULL. */
+ char *colormapName; /* Textual description of colormap for window,
+ * from -colormap option. Malloc-ed, may be
+ * NULL. */
+ char *menuName; /* Textual description of menu to use for
+ * menubar. Malloc-ed, may be NULL. */
+ Colormap colormap; /* If not None, identifies a colormap
+ * allocated for this window, which must be
+ * freed when the window is deleted. */
+ Tk_3DBorder border; /* Structure used to draw 3-D border and
+ * background. NULL means no background
+ * or border. */
+ int borderWidth; /* Width of 3-D border (if any). */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int width; /* Width to request for window. <= 0 means
+ * don't request any size. */
+ int height; /* Height to request for window. <= 0 means
+ * don't request any size. */
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int isContainer; /* 1 means this window is a container, 0 means
+ * that it isn't. */
+ char *useThis; /* If the window is embedded, this points to
+ * the name of the window in which it is
+ * embedded (malloc'ed). For non-embedded
+ * windows this is NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+
+ /* Token specific fields */
+
+ int lastX, lastY; /* last position of token window */
+ int active; /* non-zero => over target window */
+ Tcl_TimerToken timer; /* token for routine to hide tokenwin */
+ GC rejectFgGC; /* GC used to draw rejection fg: (\) */
+ GC rejectBgGC; /* GC used to draw rejection bg: (\) */
+
+ /* User-configurable fields */
+
+ Tk_Anchor anchor; /* Position of token win relative to mouse */
+ Tk_3DBorder outline; /* Outline border around token window */
+ Tk_3DBorder normalBorder; /* Border/background for token window */
+ Tk_3DBorder activeBorder; /* Border/background for token window */
+ int activeRelief;
+ int activeBorderWidth; /* Border width in pixels */
+ XColor *rejectFg; /* Color used to draw rejection fg: (\) */
+ XColor *rejectBg; /* Color used to draw rejection bg: (\) */
+ Pixmap rejectStipple; /* Stipple used to draw rejection: (\) */
+} Token;
+
+typedef struct {
+ Tcl_Interp *interp; /* Interpreter associated with the Tk source
+ * widget. */
+
+ Tk_Window tkwin; /* Tk window registered as the drag&drop
+ * source. */
+
+ Display *display; /* Drag&drop source window display */
+
+ Blt_HashTable handlerTable; /* Table of data handlers (converters)
+ * registered for this source. */
+
+ int button; /* Button used to invoke drag operation. */
+
+ Token token; /* Token used to provide special cursor. */
+
+ int pkgCmdInProgress; /* Indicates if a pkgCmd is currently active. */
+ char *pkgCmd; /* Tcl command executed at start of "drag"
+ * operation to gather information about
+ * the source data. */
+
+ char *pkgCmdResult; /* Result returned by the most recent
+ * pkgCmd. */
+
+ char *siteCmd; /* Tcl command executed to update token
+ * window. */
+
+ AnyWindow *rootPtr; /* Cached window information: Gathered
+ * and used during the "drag" operation
+ * to see if the mouse pointer is over a
+ * valid target. */
+
+ int selfTarget; /* Indicated if the source should drop onto
+ * itself. */
+
+ Tk_Cursor cursor; /* cursor restored after dragging */
+
+ char **sendTypes; /* list of data handler names or "all" */
+
+ Blt_HashEntry *hashPtr;
+
+ AnyWindow *windowPtr; /* Last target examined. If NULL, mouse
+ * pointer is not currently over a valid
+ * target. */
+} Source;
+
+typedef struct {
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* drag&drop target window */
+ Display *display; /* drag&drop target window display */
+ Blt_HashTable handlerTable; /* Table of data handlers (converters)
+ * registered for this target. */
+ Blt_HashEntry *hashPtr;
+} Target;
+
+
+extern Tk_CustomOption bltListOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_INT, "-button", "buttonBinding", "ButtonBinding",
+ DEF_DND_BUTTON_NUMBER, Tk_Offset(Source, button), 0},
+ {TK_CONFIG_STRING, "-packagecmd", "packageCommand", "Command",
+ DEF_DND_PACKAGE_COMMAND, Tk_Offset(Source, pkgCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Source, token.rejectBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Source, token.rejectFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Source, token.rejectStipple),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Source, token.rejectStipple),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BOOLEAN, "-selftarget", "selfTarget", "SelfTarget",
+ DEF_DND_SELF_TARGET, Tk_Offset(Source, selfTarget), 0},
+ {TK_CONFIG_CUSTOM, "-send", "send", "Send", DEF_DND_SEND,
+ Tk_Offset(Source, sendTypes), TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_STRING, "-sitecmd", "siteCommand", "Command",
+ DEF_DND_SITE_COMMAND, Tk_Offset(Source, siteCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-tokenanchor", "tokenAnchor", "Anchor",
+ DEF_TOKEN_ANCHOR, Tk_Offset(Source, token.anchor), 0},
+ {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR,
+ Tk_Offset(Source, token.activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO,
+ Tk_Offset(Source, token.activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background",
+ DEF_TOKEN_BG_COLOR, Tk_Offset(Source, token.normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background",
+ DEF_TOKEN_BG_MONO, Tk_Offset(Source, token.normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline",
+ DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Source, token.outline),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline",
+ DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Source, token.outline),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
+ DEF_TOKEN_BORDERWIDTH, Tk_Offset(Source, token.borderWidth), 0},
+ {TK_CONFIG_CURSOR, "-tokencursor", "tokenCursor", "Cursor",
+ DEF_TOKEN_CURSOR, Tk_Offset(Source, token.cursor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+static Tk_ConfigSpec tokenConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "activeRelief",
+ DEF_TOKEN_ACTIVE_RELIEF, Tk_Offset(Token, activeRelief), 0},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_TOKEN_ANCHOR, Tk_Offset(Token, anchor), 0},
+ {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth",
+ "ActiveBorderWidth", DEF_TOKEN_ACTIVE_BORDERWIDTH,
+ Tk_Offset(Token, activeBorderWidth), 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_COLOR, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_MONO, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TOKEN_BORDERWIDTH, Tk_Offset(Token, borderWidth), 0},
+ {TK_CONFIG_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_TOKEN_CURSOR, Tk_Offset(Token, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-outline", "outline", "Outline",
+ DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Token, outline),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-outline", "outline", "Outline",
+ DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Token, outline),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Token, rejectBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Token, rejectFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TOKEN_RELIEF, Tk_Offset(Token, relief), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+
+/*
+ * Forward Declarations
+ */
+static int DragDropCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ int argc, char **argv));
+static void TokenEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void TargetEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void MoveToken _ANSI_ARGS_((Source * srcPtr, Token *tokenPtr));
+static void UpdateToken _ANSI_ARGS_((ClientData clientData));
+static void HideToken _ANSI_ARGS_((Token *tokenPtr));
+static void RejectToken _ANSI_ARGS_((Token *tokenPtr));
+
+static int GetSource _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ Source **srcPtrPtr));
+static Source *CreateSource _ANSI_ARGS_((Tcl_Interp *interp, char *pathname,
+ int *newEntry));
+static void DestroySource _ANSI_ARGS_((Source * srcPtr));
+static void SourceEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int ConfigureSource _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr,
+ int argc, char **argv, int flags));
+static int ConfigureToken _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr,
+ int argc, char **argv));
+
+static Target *CreateTarget _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin));
+static Target *FindTarget _ANSI_ARGS_((Tk_Window tkwin));
+static void DestroyTarget _ANSI_ARGS_((DestroyData dataPtr));
+static int OverTarget _ANSI_ARGS_((Source * srcPtr, int x, int y));
+static void AddTargetProperty _ANSI_ARGS_((Tcl_Interp *interp,
+ Target * targetPtr));
+
+static void DndSend _ANSI_ARGS_((Source * srcPtr));
+
+static void InitRoot _ANSI_ARGS_((Source * srcPtr));
+static void RemoveWindow _ANSI_ARGS_((AnyWindow *wr));
+static void QueryWindow _ANSI_ARGS_((Display *display, AnyWindow * windowPtr));
+
+static char *ExpandPercents _ANSI_ARGS_((char *str, SubstDescriptors * subs,
+ int nsubs, Tcl_DString *resultPtr));
+
+
+
+#ifdef WIN32
+
+#if _MSC_VER
+#include <tchar.h>
+#endif
+
+typedef struct {
+ char *prefix;
+ int prefixSize;
+ char *propReturn;
+} PropertyInfo;
+
+
+static BOOL CALLBACK
+GetEnumWindowsProc(HWND hWnd, LPARAM clientData)
+{
+ Blt_Chain *chainPtr = (Blt_Chain *) clientData;
+
+ Blt_ChainAppend(chainPtr, (ClientData)hWnd);
+ return TRUE;
+}
+
+static WINDOW
+GetNativeWindow(Tk_Window tkwin)
+{
+ return (WINDOW) Tk_GetHWND(Tk_WindowId(tkwin));
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowZOrder --
+ *
+ * Returns a list of the child windows according to their stacking
+ * order. The window handles are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetWindowZOrder(
+ Display *display,
+ HWND parent)
+{
+ Blt_Chain *chainPtr;
+ HWND hWnd;
+
+ chainPtr = Blt_ChainCreate();
+ for (hWnd = GetTopWindow(parent); hWnd != NULL;
+ hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) {
+ Blt_ChainAppend(chainPtr, (ClientData)hWnd);
+ }
+ return chainPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetEnumPropsExProc --
+ *
+ * ------------------------------------------------------------------------
+ */
+static BOOL CALLBACK
+GetEnumPropsExProc(
+ HWND hwnd,
+ LPCTSTR atom,
+ HANDLE hData,
+ DWORD clientData)
+{
+ PropertyInfo *infoPtr = (PropertyInfo *) clientData;
+
+ if (strncmp(infoPtr->prefix, atom, infoPtr->prefixSize) == 0) {
+ assert(infoPtr->propReturn == NULL);
+ infoPtr->propReturn = (char *)atom;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetPropData --
+ *
+ * This is a bad Windows hack to pass property information between
+ * applications. (Ab)Normally the property data (one-word value) is
+ * stored in the data handle. But the data content is available only
+ * within the application. The pointer value is meaningless outside
+ * of the current application address space. Not really useful at all.
+ *
+ * So the trick here is to encode the property name with all the
+ * necessary information and to loop through all the properties
+ * of a window, looking for one that starts with our property name
+ * prefix. The downside is that the property name is limited to
+ * 255 bytes. But that should be enough. It's also slower since
+ * we examine each property until we find ours.
+ *
+ * We'll plug in the OLE stuff later.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static char *
+GetPropData(HWND hWnd, char *atom)
+{
+ PropertyInfo propInfo;
+ if (hWnd == NULL) {
+ return NULL;
+ }
+ propInfo.prefix = atom;
+ propInfo.prefixSize = strlen(atom);
+ propInfo.propReturn = NULL;
+ EnumPropsEx(hWnd, (PROPENUMPROCEX)GetEnumPropsExProc, (DWORD)&propInfo);
+ return propInfo.propReturn;
+}
+
+
+static char *
+GetProperty(Display *display, HWND hWnd)
+{
+ ATOM atom;
+
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != (ATOM)0) {
+ char buffer[MAX_PROP_SIZE + 1];
+ UINT nBytes;
+
+ nBytes = GlobalGetAtomName(atom, buffer, MAX_PROP_SIZE);
+ if (nBytes > 0) {
+ buffer[nBytes] = '\0';
+ return Blt_Strdup(buffer);
+ }
+ }
+ return NULL;
+}
+
+static void
+SetProperty(Tk_Window tkwin, char *data)
+{
+ HWND hWnd;
+ ATOM atom;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+ if (hWnd == NULL) {
+ return;
+ }
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != 0) {
+ GlobalDeleteAtom(atom);
+ }
+ atom = GlobalAddAtom(data);
+ if (atom != (ATOM)0) {
+ SetProp(hWnd, propName, (HANDLE)atom);
+ }
+}
+
+static void
+RemoveProperty(Tk_Window tkwin)
+{
+ HWND hWnd;
+ ATOM atom;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+ if (hWnd == NULL) {
+ return;
+ }
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != 0) {
+ GlobalDeleteAtom(atom);
+ }
+ RemoveProp(hWnd, propName);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowRegion --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetWindowRegion(
+ Display *display, /* Not used. */
+ HWND hWnd,
+ int *x1Ptr,
+ int *y1Ptr,
+ int *x2Ptr,
+ int *y2Ptr)
+{
+ RECT rect;
+
+ if (GetWindowRect(hWnd, &rect)) {
+ *x1Ptr = rect.left;
+ *y1Ptr = rect.top;
+ *x2Ptr = rect.right;
+ *y2Ptr = rect.bottom;
+ return IsWindowVisible(hWnd);
+ }
+ return FALSE;
+}
+
+#else
+
+static WINDOW
+GetNativeWindow(tkwin)
+ Tk_Window tkwin;
+{
+ return Tk_WindowId(tkwin);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowZOrder --
+ *
+ * Returns a chain of the child windows according to their stacking
+ * order. The window ids are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetWindowZOrder(display, window)
+ Display *display;
+ Window window;
+{
+ Blt_Chain *chainPtr;
+ Window *childArr;
+ unsigned int nChildren;
+ Window dummy;
+
+ chainPtr = NULL;
+ if ((XQueryTree(display, window, &dummy, &dummy, &childArr, &nChildren)) &&
+ (nChildren > 0)) {
+ register int i;
+
+ chainPtr = Blt_ChainCreate();
+ for (i = 0; i < nChildren; i++) {
+ /*
+ * XQuery returns windows in bottom to top order.
+ * We only care about the top window.
+ */
+ Blt_ChainPrepend(chainPtr, (ClientData)childArr[i]);
+ }
+ if (childArr != NULL) {
+ XFree((char *)childArr); /* done with list of kids */
+ }
+ }
+ return chainPtr;
+}
+
+static char *
+GetProperty(display, window)
+ Display *display;
+ Window window;
+{
+ char *data;
+ int result, actualFormat;
+ Atom actualType;
+ unsigned long nItems, bytesAfter;
+
+ if (window == None) {
+ return NULL;
+ }
+ data = NULL;
+ result = XGetWindowProperty(display, window, dndAtom, 0, MAX_PROP_SIZE,
+ False, XA_STRING, &actualType, &actualFormat, &nItems, &bytesAfter,
+ (unsigned char **)&data);
+ if ((result != Success) || (actualFormat != 8) ||
+ (actualType != XA_STRING)) {
+ if (data != NULL) {
+ XFree((char *)data);
+ data = NULL;
+ }
+ }
+ return data;
+}
+
+static void
+SetProperty(tkwin, data)
+ Tk_Window tkwin;
+ char *data;
+{
+ XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), dndAtom, XA_STRING,
+ 8, PropModeReplace, (unsigned char *)data, strlen(data) + 1);
+}
+
+static int
+GetWindowRegion(display, window, x1Ptr, y1Ptr, x2Ptr, y2Ptr)
+ Display *display;
+ Window window;
+ int *x1Ptr, *y1Ptr, *x2Ptr, *y2Ptr;
+{
+ XWindowAttributes winAttrs;
+
+ if (XGetWindowAttributes(display, window, &winAttrs)) {
+ *x1Ptr = winAttrs.x;
+ *y1Ptr = winAttrs.y;
+ *x2Ptr = winAttrs.x + winAttrs.width - 1;
+ *y2Ptr = winAttrs.y + winAttrs.height - 1;
+ }
+ return (winAttrs.map_state == IsViewable);
+}
+
+#endif /* WIN32 */
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ChangeToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+ChangeToken(tokenPtr, active)
+ Token *tokenPtr;
+ int active;
+{
+ int relief;
+ Tk_3DBorder border;
+ int borderWidth;
+
+ Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->outline, 0, 0, Tk_Width(tokenPtr->tkwin),
+ Tk_Height(tokenPtr->tkwin), 0, TK_RELIEF_FLAT);
+ if (active) {
+ relief = tokenPtr->activeRelief;
+ border = tokenPtr->activeBorder;
+ borderWidth = tokenPtr->activeBorderWidth;
+ } else {
+ relief = tokenPtr->relief;
+ border = tokenPtr->normalBorder;
+ borderWidth = tokenPtr->borderWidth;
+ }
+ Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), border,
+ 2, 2, Tk_Width(tokenPtr->tkwin) - 4, Tk_Height(tokenPtr->tkwin) - 4,
+ borderWidth, relief);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenEventProc --
+ *
+ * Invoked by the Tk dispatcher to handle widget events.
+ * Manages redraws for the drag&drop token window.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TokenEventProc(clientData, eventPtr)
+ ClientData clientData; /* data associated with widget */
+ XEvent *eventPtr; /* information about event */
+{
+ Token *tokenPtr = clientData;
+
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ if (tokenPtr->tkwin != NULL) {
+ ChangeToken(tokenPtr, tokenPtr->active);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ tokenPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * HideToken --
+ *
+ * Unmaps the drag&drop token. Invoked directly at the end of a
+ * successful communication, or after a delay if the communication
+ * fails (allowing the user to see a graphical picture of failure).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+HideToken(tokenPtr)
+ Token *tokenPtr;
+{
+ if (tokenPtr->tkwin != NULL) {
+ Tk_UnmapWindow(tokenPtr->tkwin);
+ }
+ tokenPtr->timer = NULL;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RaiseToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RaiseToken(tokenPtr)
+ Token *tokenPtr;
+{
+ Blt_MapTopLevelWindow(tokenPtr->tkwin);
+ Blt_RaiseTopLevelWindow(tokenPtr->tkwin);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * MoveToken --
+ *
+ * Invoked during "drag" operations to move a token window to its
+ * current "drag" coordinate.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+MoveToken(srcPtr, tokenPtr)
+ Source *srcPtr; /* drag&drop source window data */
+ Token *tokenPtr;
+{
+ int x, y;
+ int maxX, maxY;
+ int vx, vy, vw, vh;
+ Screen *screenPtr;
+
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(srcPtr->tkwin, &vx, &vy, &vw, &vh);
+ x = tokenPtr->lastX + vx - 3;
+ y = tokenPtr->lastY + vy - 3;
+
+ screenPtr = Tk_Screen(srcPtr->tkwin);
+ maxX = WidthOfScreen(screenPtr) - Tk_Width(tokenPtr->tkwin);
+ maxY = HeightOfScreen(screenPtr) - Tk_Height(tokenPtr->tkwin);
+ Blt_TranslateAnchor(x, y, Tk_Width(tokenPtr->tkwin),
+ Tk_Height(tokenPtr->tkwin), tokenPtr->anchor, &x, &y);
+ if (x > maxX) {
+ x = maxX;
+ } else if (x < 0) {
+ x = 0;
+ }
+ if (y > maxY) {
+ y = maxY;
+ } else if (y < 0) {
+ y = 0;
+ }
+ if ((x != Tk_X(tokenPtr->tkwin)) || (y != Tk_Y(tokenPtr->tkwin))) {
+ Tk_MoveToplevelWindow(tokenPtr->tkwin, x, y);
+ }
+ RaiseToken(tokenPtr);
+}
+
+static Tk_Cursor
+GetWidgetCursor(interp, tkwin)
+ Tk_Window tkwin;
+ Tcl_Interp *interp;
+{
+ char *cursorName;
+ Tk_Cursor cursor;
+
+ cursor = None;
+ if (Tcl_VarEval(interp, Tk_PathName(tkwin), " cget -cursor",
+ (char *)NULL) != TCL_OK) {
+ return None;
+ }
+ cursorName = Tcl_GetStringResult(interp);
+ if ((cursorName != NULL) && (cursorName[0] != '\0')) {
+ cursor = Tk_GetCursor(interp, tkwin, Tk_GetUid(cursorName));
+ }
+ Tcl_ResetResult(interp);
+ return cursor;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * UpdateToken --
+ *
+ * Invoked when the event loop is idle to determine whether or not
+ * the current drag&drop token position is over another drag&drop
+ * target.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+UpdateToken(clientData)
+ ClientData clientData; /* widget data */
+{
+ Source *srcPtr = clientData;
+ Token *tokenPtr = &(srcPtr->token);
+
+ ChangeToken(tokenPtr, tokenPtr->active);
+ /*
+ * If the source has a site command, then invoke it to
+ * modify the appearance of the token window. Pass any
+ * errors onto the drag&drop error handler.
+ */
+ if (srcPtr->siteCmd) {
+ char buffer[200];
+ Tcl_DString dString;
+ int result;
+ SubstDescriptors subs[2];
+
+ sprintf(buffer, "%d", tokenPtr->active);
+ subs[0].letter = 's';
+ subs[0].value = buffer;
+ subs[1].letter = 't';
+ subs[1].value = Tk_PathName(tokenPtr->tkwin);
+
+ Tcl_DStringInit(&dString);
+ result = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(srcPtr->siteCmd, subs, 2, &dString));
+ Tcl_DStringFree(&dString);
+ if ((result != TCL_OK) && (errorCmd != NULL) && (*errorCmd)) {
+ (void)Tcl_VarEval(srcPtr->interp, errorCmd, " {",
+ Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL);
+ }
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RejectToken --
+ *
+ * Draws a rejection mark on the current drag&drop token, and arranges
+ * for the token to be unmapped after a small delay.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RejectToken(tokenPtr)
+ Token *tokenPtr;
+{
+ int divisor = 6; /* controls size of rejection symbol */
+ int w, h, lineWidth, x, y, margin;
+
+ margin = 2 * tokenPtr->borderWidth;
+ w = Tk_Width(tokenPtr->tkwin) - 2 * margin;
+ h = Tk_Height(tokenPtr->tkwin) - 2 * margin;
+ lineWidth = (w < h) ? w / divisor : h / divisor;
+ lineWidth = (lineWidth < 1) ? 1 : lineWidth;
+
+ w = h = lineWidth * (divisor - 1);
+ x = (Tk_Width(tokenPtr->tkwin) - w) / 2;
+ y = (Tk_Height(tokenPtr->tkwin) - h) / 2;
+
+ /*
+ * Draw the rejection symbol background (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectBgGC,
+ lineWidth + 4, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectBgGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectBgGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ /*
+ * Draw the rejection symbol foreground (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectFgGC,
+ lineWidth, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectFgGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectFgGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ /*
+ * Arrange for token window to disappear eventually.
+ */
+ tokenPtr->timer = Tcl_CreateTimerHandler(1000, (Tcl_TimerProc *) HideToken,
+ tokenPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureToken(interp, srcPtr, argc, argv)
+ Tcl_Interp *interp;
+ Source *srcPtr;
+ int argc;
+ char **argv;
+{
+ Token *tokenPtr;
+
+ tokenPtr = &(srcPtr->token);
+ if (Tk_ConfigureWidget(interp, srcPtr->tkwin, tokenConfigSpecs, argc, argv,
+ (char *)tokenPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return ConfigureSource(interp, srcPtr, 0, (char **)NULL,
+ TK_CONFIG_ARGV_ONLY);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+CreateToken(interp, srcPtr)
+ Tcl_Interp *interp;
+ Source *srcPtr;
+{
+ XSetWindowAttributes attrs;
+ Tk_Window tkwin;
+ char string[200];
+ static int nextTokenId = 0;
+ unsigned int mask;
+ Token *tokenPtr = &(srcPtr->token);
+
+ sprintf(string, "dd-token%d", ++nextTokenId);
+
+ /* Create toplevel on parent's screen. */
+ tkwin = Tk_CreateWindow(interp, srcPtr->tkwin, string, "");
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Tk_SetClass(tkwin, className);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ TokenEventProc, tokenPtr);
+
+ attrs.override_redirect = True;
+ attrs.backing_store = WhenMapped;
+ attrs.save_under = True;
+ mask = CWOverrideRedirect | CWSaveUnder | CWBackingStore;
+ Tk_ChangeWindowAttributes(tkwin, mask, &attrs);
+
+ Tk_SetInternalBorder(tkwin, tokenPtr->borderWidth + 2);
+ tokenPtr->tkwin = tkwin;
+#ifdef WIN32
+ {
+ Tk_FakeWin *winPtr = (Tk_FakeWin *) tkwin;
+ winPtr->dummy18 = tokenPtr;
+ }
+#endif /* WIN32 */
+ Tk_MakeWindowExist(tkwin);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateSource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Creates a new record if the widget name is not already
+ * registered. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Source *
+CreateSource(interp, pathName, newPtr)
+ Tcl_Interp *interp;
+ char *pathName; /* widget pathname for desired record */
+ int *newPtr; /* returns non-zero => new record created */
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+ Source *srcPtr;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ hPtr = Blt_CreateHashEntry(&sourceTable, (char *)tkwin, newPtr);
+ if (!(*newPtr)) {
+ return (Source *) Blt_GetHashValue(hPtr);
+ }
+ srcPtr = Blt_Calloc(1, sizeof(Source));
+ assert(srcPtr);
+ srcPtr->tkwin = tkwin;
+ srcPtr->display = Tk_Display(tkwin);
+ srcPtr->interp = interp;
+ srcPtr->token.anchor = TK_ANCHOR_SE;
+ srcPtr->token.relief = TK_RELIEF_RAISED;
+ srcPtr->token.activeRelief = TK_RELIEF_SUNKEN;
+ srcPtr->token.borderWidth = srcPtr->token.activeBorderWidth = 3;
+ srcPtr->hashPtr = hPtr;
+ Blt_InitHashTable(&(srcPtr->handlerTable), BLT_STRING_KEYS);
+ if (ConfigureSource(interp, srcPtr, 0, (char **)NULL, 0) != TCL_OK) {
+ DestroySource(srcPtr);
+ return NULL;
+ }
+ Blt_SetHashValue(hPtr, srcPtr);
+ /*
+ * Arrange for the window to unregister itself when it
+ * is destroyed.
+ */
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, SourceEventProc, srcPtr);
+ return srcPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DestroySource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Destroys the record if found.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroySource(srcPtr)
+ Source *srcPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *cmd;
+
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+ if (srcPtr->token.timer) {
+ Tcl_DeleteTimerHandler(srcPtr->token.timer);
+ }
+ Tk_FreeOptions(configSpecs, (char *)srcPtr, srcPtr->display, 0);
+
+ if (srcPtr->token.rejectFgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC);
+ }
+ if (srcPtr->token.rejectBgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC);
+ }
+ if (srcPtr->pkgCmdResult) {
+ Blt_Free(srcPtr->pkgCmdResult);
+ }
+ if (srcPtr->rootPtr != NULL) {
+ RemoveWindow(srcPtr->rootPtr);
+ }
+ if (srcPtr->cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->cursor);
+ }
+ if (srcPtr->token.cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->token.cursor);
+ }
+ Blt_Free(srcPtr->sendTypes);
+
+ for (hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(srcPtr->handlerTable));
+ if (srcPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&sourceTable, srcPtr->hashPtr);
+ }
+ Blt_Free(srcPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetSource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetSource(interp, pathName, srcPtrPtr)
+ Tcl_Interp *interp;
+ char *pathName; /* widget pathname for desired record */
+ Source **srcPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&sourceTable, (char *)tkwin);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", pathName,
+ "\" has not been initialized as a drag&drop source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *srcPtrPtr = (Source *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureSource --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureSource(interp, srcPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* current interpreter */
+ register Source *srcPtr; /* drag&drop source widget record */
+ int argc; /* number of arguments */
+ char **argv; /* argument strings */
+ int flags; /* flags controlling interpretation */
+{
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+ Tcl_DString dString;
+ Tcl_CmdInfo cmdInfo;
+ int result;
+
+ /*
+ * Handle the bulk of the options...
+ */
+ if (Tk_ConfigureWidget(interp, srcPtr->tkwin, configSpecs, argc, argv,
+ (char *)srcPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Check the button binding for valid range (0 or 1-5)
+ */
+ if ((srcPtr->button < 0) || (srcPtr->button > 5)) {
+ Tcl_AppendResult(interp,
+ "button number must be 1-5, or 0 for no bindings",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Set up the rejection foreground GC for the token window...
+ */
+ gcValues.foreground = srcPtr->token.rejectFg->pixel;
+ gcValues.subwindow_mode = IncludeInferiors;
+ gcValues.graphics_exposures = False;
+ gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures;
+
+ if (srcPtr->token.rejectStipple != None) {
+ gcValues.stipple = srcPtr->token.rejectStipple;
+ gcValues.fill_style = FillStippled;
+ gcMask |= GCForeground | GCStipple | GCFillStyle;
+ }
+ newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues);
+
+ if (srcPtr->token.rejectFgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC);
+ }
+ srcPtr->token.rejectFgGC = newGC;
+
+ /*
+ * Set up the rejection background GC for the token window...
+ */
+ gcValues.foreground = srcPtr->token.rejectBg->pixel;
+ gcValues.subwindow_mode = IncludeInferiors;
+ gcValues.graphics_exposures = False;
+ gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures;
+
+ newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues);
+
+ if (srcPtr->token.rejectBgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC);
+ }
+ srcPtr->token.rejectBgGC = newGC;
+
+ /*
+ * Reset the border width in case it has changed...
+ */
+ if (srcPtr->token.tkwin) {
+ Tk_SetInternalBorder(srcPtr->token.tkwin,
+ srcPtr->token.borderWidth + 2);
+ }
+ if (!Tcl_GetCommandInfo(interp, "blt::Drag&DropInit", &cmdInfo)) {
+ static char cmd[] = "source [file join $blt_library dragdrop.tcl]";
+
+ if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (while loading bindings for blt::drag&drop)");
+ return TCL_ERROR;
+ }
+ }
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "blt::Drag&DropInit",
+ Tk_PathName(srcPtr->tkwin), Blt_Itoa(srcPtr->button), (char *)NULL);
+ result = Tcl_Eval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SourceEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+SourceEventProc(clientData, eventPtr)
+ ClientData clientData; /* drag&drop registration list */
+ XEvent *eventPtr; /* event description */
+{
+ Source *srcPtr = (Source *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ DestroySource(srcPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FindTarget --
+ *
+ * Looks for a Target record in the hash table for drag&drop
+ * target widgets. Creates a new record if the widget name is
+ * not already registered. Returns a pointer to the desired
+ * record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Target *
+FindTarget(tkwin)
+ Tk_Window tkwin; /* Widget pathname for desired record */
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&targetTable, (char *)tkwin);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return (Target *) Blt_GetHashValue(hPtr);
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateTarget --
+ *
+ * Looks for a Target record in the hash table for drag&drop
+ * target widgets. Creates a new record if the widget name is
+ * not already registered. Returns a pointer to the desired
+ * record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Target *
+CreateTarget(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Widget pathname for desired record */
+{
+ Target *targetPtr;
+ int isNew;
+
+ targetPtr = Blt_Calloc(1, sizeof(Target));
+ assert(targetPtr);
+ targetPtr->display = Tk_Display(tkwin);
+ targetPtr->tkwin = tkwin;
+ Blt_InitHashTable(&(targetPtr->handlerTable), BLT_STRING_KEYS);
+ targetPtr->hashPtr = Blt_CreateHashEntry(&targetTable, (char *)tkwin,
+ &isNew);
+ Blt_SetHashValue(targetPtr->hashPtr, targetPtr);
+
+ /*
+ * Arrange for the target to removed if the host window is destroyed.
+ */
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, TargetEventProc,
+ targetPtr);
+ /*
+ * If this is a new target, attach a property to identify
+ * window as "drag&drop" target, and arrange for the window
+ * to un-register itself when it is destroyed.
+ */
+ Tk_MakeWindowExist(targetPtr->tkwin);
+ AddTargetProperty(interp, targetPtr);
+ return targetPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DestroyTarget --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroyTarget(data)
+ DestroyData data;
+{
+ Target *targetPtr = (Target *)data;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *cmd;
+
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(targetPtr->handlerTable));
+ if (targetPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&targetTable, targetPtr->hashPtr);
+ }
+ Tk_DeleteEventHandler(targetPtr->tkwin, StructureNotifyMask,
+ TargetEventProc, targetPtr);
+ Blt_Free(targetPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TargetEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop target widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TargetEventProc(clientData, eventPtr)
+ ClientData clientData; /* drag&drop registration list */
+ XEvent *eventPtr; /* event description */
+{
+ Target *targetPtr = (Target *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+#ifdef WIN32
+ /*
+ * Under Win32 the properties must be removed before the window
+ * can be destroyed.
+ */
+ RemoveProperty(targetPtr->tkwin);
+#endif
+ DestroyTarget((DestroyData)targetPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DndSend --
+ *
+ * Invoked after a drop operation to send data to the drop
+ * application.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DndSend(srcPtr)
+ register Source *srcPtr; /* drag&drop source record */
+{
+ int status;
+ SubstDescriptors subs[3];
+ Tcl_DString dString;
+ Blt_HashEntry *hPtr;
+ char *dataType;
+ char **targetInfo;
+ char *cmd;
+
+ /* See if current position is over drop point. */
+ if (!OverTarget(srcPtr, srcPtr->token.lastX, srcPtr->token.lastY)) {
+ return;
+ }
+ targetInfo = srcPtr->windowPtr->targetInfo;
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME],
+ dragDropCmd, "location", (char *)NULL);
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastX));
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastY));
+ status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString));
+
+ Tcl_DStringFree(&dString);
+ if (status != TCL_OK) {
+ goto reject;
+ }
+ if (targetInfo[DATA_TYPE] == NULL) {
+ Blt_HashSearch cursor;
+
+ hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ dataType = Blt_GetHashKey(&(srcPtr->handlerTable), hPtr);
+ } else {
+ hPtr = Blt_FindHashEntry(&(srcPtr->handlerTable),
+ targetInfo[DATA_TYPE]);
+ dataType = targetInfo[DATA_TYPE];
+ }
+ /* Start building the command line here, before we invoke any Tcl
+ * commands. The is because the Tcl command may let in another
+ * drag event and change the target property data. */
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME],
+ dragDropCmd, "target", targetInfo[TARGET_NAME], "handle",
+ dataType, (char *)NULL);
+ cmd = NULL;
+ if (hPtr != NULL) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ }
+ if (cmd != NULL) {
+ Tcl_DString cmdString;
+
+ subs[0].letter = 'i';
+ subs[0].value = targetInfo[INTERP_NAME];
+ subs[1].letter = 'w';
+ subs[1].value = targetInfo[TARGET_NAME];
+ subs[2].letter = 'v';
+ subs[2].value = srcPtr->pkgCmdResult;
+
+ Tcl_DStringInit(&cmdString);
+ status = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(cmd, subs, 3, &cmdString));
+ Tcl_DStringFree(&cmdString);
+
+ Tcl_DStringAppendElement(&dString, Tcl_GetStringResult(srcPtr->interp));
+ } else {
+ Tcl_DStringAppendElement(&dString, srcPtr->pkgCmdResult);
+ }
+
+ /*
+ * Part 2: Now tell target application to handle the data.
+ */
+ status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (status != TCL_OK) {
+ goto reject;
+ }
+ HideToken(&(srcPtr->token));
+ return;
+ reject:
+ /*
+ * Give failure information to user. If an error occurred and an
+ * error proc is defined, then use it to handle the error.
+ */
+ RejectToken(&(srcPtr->token));
+ if (errorCmd != NULL) {
+ Tcl_VarEval(srcPtr->interp, errorCmd, " {",
+ Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * InitRoot --
+ *
+ * Invoked at the start of a "drag" operation to capture the
+ * positions of all windows on the current root. Queries the
+ * entire window hierarchy and determines the placement of each
+ * window. Queries the "BltDrag&DropTarget" property info where
+ * appropriate. This information is used during the drag
+ * operation to determine when the drag&drop token is over a
+ * valid drag&drop target.
+ *
+ * Results:
+ * Returns the record for the root window, which contains records
+ * for all other windows as children.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+InitRoot(srcPtr)
+ Source *srcPtr;
+{
+ srcPtr->rootPtr = Blt_Calloc(1, sizeof(AnyWindow));
+ assert(srcPtr->rootPtr);
+#ifdef WIN32
+ srcPtr->rootPtr->nativeWindow = GetDesktopWindow();
+#else
+ srcPtr->rootPtr->nativeWindow = DefaultRootWindow(srcPtr->display);
+#endif
+ srcPtr->windowPtr = NULL;
+ QueryWindow(srcPtr->display, srcPtr->rootPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FindTopWindow --
+ *
+ * Searches for the topmost window at a given pair of X-Y coordinates.
+ *
+ * Results:
+ * Returns a pointer to the node representing the window containing
+ * the point. If one can't be found, NULL is returned.
+ *
+ * ------------------------------------------------------------------------
+ */
+static AnyWindow *
+FindTopWindow(srcPtr, x, y)
+ Source *srcPtr;
+ int x, y;
+{
+ AnyWindow *rootPtr;
+ register Blt_ChainLink *linkPtr;
+ register AnyWindow *windowPtr;
+ WINDOW nativeTokenWindow;
+
+ rootPtr = srcPtr->rootPtr;
+ if (!rootPtr->initialized) {
+ QueryWindow(srcPtr->display, rootPtr);
+ }
+ if ((x < rootPtr->x1) || (x > rootPtr->x2) ||
+ (y < rootPtr->y1) || (y > rootPtr->y2)) {
+ return NULL; /* Point is not over window */
+ }
+ windowPtr = rootPtr;
+
+ nativeTokenWindow = (WINDOW)Blt_GetRealWindowId(srcPtr->token.tkwin);
+ /*
+ * The window list is ordered top to bottom, so stop when we find
+ * the first child that contains the X-Y coordinate. It will be
+ * the topmost window in that hierarchy. If none exists, then we
+ * already have the topmost window.
+ */
+ descend:
+ for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rootPtr = Blt_ChainGetValue(linkPtr);
+ if (!rootPtr->initialized) {
+ QueryWindow(srcPtr->display, rootPtr);
+ }
+ if (rootPtr->nativeWindow == nativeTokenWindow) {
+ continue; /* Don't examine the token window. */
+ }
+ if ((x >= rootPtr->x1) && (x <= rootPtr->x2) &&
+ (y >= rootPtr->y1) && (y <= rootPtr->y2)) {
+ /*
+ * Remember the last window containing the pointer and
+ * descend into its window hierarchy. We'll look for a
+ * child that also contains the pointer.
+ */
+ windowPtr = rootPtr;
+ goto descend;
+ }
+ }
+ return windowPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * OverTarget --
+ *
+ * Checks to see if a compatible drag&drop target exists at the
+ * given position. A target is "compatible" if it is a drag&drop
+ * window, and if it has a handler that is compatible with the
+ * current source window.
+ *
+ * Results:
+ * Returns a pointer to a structure describing the target, or NULL
+ * if no compatible target is found.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+OverTarget(srcPtr, x, y)
+ Source *srcPtr; /* drag&drop source window */
+ int x, y; /* current drag&drop location
+ * (in virtual coords) */
+{
+ int virtX, virtY;
+ int dummy;
+ AnyWindow *newPtr, *oldPtr;
+ char **elemArr;
+ int nElems;
+ char *data;
+ int result;
+
+ /*
+ * If no window info has been been gathered yet for this target,
+ * then abort this call. This probably means that the token is
+ * moved before it has been properly built.
+ */
+ if (srcPtr->rootPtr == NULL) {
+ return FALSE;
+ }
+ if (srcPtr->sendTypes == NULL) {
+ return FALSE; /* Send is turned off. */
+ }
+
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(srcPtr->tkwin, &virtX, &virtY, &dummy, &dummy);
+ x += virtX;
+ y += virtY;
+
+ oldPtr = srcPtr->windowPtr;
+ srcPtr->windowPtr = NULL;
+
+ newPtr = FindTopWindow(srcPtr, x, y);
+ if (newPtr == NULL) {
+ return FALSE; /* Not over a window. */
+ }
+ if ((!srcPtr->selfTarget) &&
+ (GetNativeWindow(srcPtr->tkwin) == newPtr->nativeWindow)) {
+ return FALSE; /* If the self-target flag isn't set,
+ * don't allow the source window to
+ * drop onto itself. */
+ }
+ if (newPtr == oldPtr) {
+ srcPtr->windowPtr = oldPtr;
+ /* No need to collect the target information if we're still
+ * over the same window. */
+ return (newPtr->targetInfo != NULL);
+ }
+
+ /* See if this window has a "BltDrag&DropTarget" property. */
+ data = GetProperty(srcPtr->display, newPtr->nativeWindow);
+ if (data == NULL) {
+ return FALSE; /* No such property on window. */
+ }
+ result = Tcl_SplitList(srcPtr->interp, data, &nElems, &elemArr);
+ XFree((char *)data);
+ if (result != TCL_OK) {
+ return FALSE; /* Malformed property list. */
+ }
+ srcPtr->windowPtr = newPtr;
+ /* Interpreter name, target name, type1, type2, ... */
+ if (nElems > 2) {
+ register char **s;
+ int count;
+ register int i;
+
+ /*
+ * The candidate target has a list of possible types.
+ * Compare this with what types the source is willing to
+ * transmit and compress the list down to just the matching
+ * types. It's up to the target to request the specific type
+ * it wants.
+ */
+ count = 2;
+ for (i = 2; i < nElems; i++) {
+ for (s = srcPtr->sendTypes; *s != NULL; s++) {
+ if (((**s == 'a') && (strcmp(*s, "all") == 0)) ||
+ ((**s == elemArr[i][0]) && (strcmp(*s, elemArr[i]) == 0))) {
+ elemArr[count++] = elemArr[i];
+ }
+ }
+ }
+ if (count == 2) {
+ Blt_Free(elemArr);
+ fprintf(stderr, "source/target mismatch: No matching types\n");
+ return FALSE; /* No matching data type. */
+ }
+ elemArr[count] = NULL;
+ }
+ newPtr->targetInfo = elemArr;
+ return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RemoveWindow --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RemoveWindow(windowPtr)
+ AnyWindow *windowPtr; /* window rep to be freed */
+{
+ AnyWindow *childPtr;
+ Blt_ChainLink *linkPtr;
+
+ /* Throw away leftover slots. */
+ for (linkPtr = Blt_ChainFirstLink(windowPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_ChainGetValue(linkPtr);
+ RemoveWindow(childPtr);
+ }
+ Blt_ChainDestroy(windowPtr->chainPtr);
+ if (windowPtr->targetInfo != NULL) {
+ Blt_Free(windowPtr->targetInfo);
+ }
+ Blt_Free(windowPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * QueryWindow --
+ *
+ * Invoked during "drag" operations. Digs into the root window
+ * hierarchy and caches the resulting information.
+ * If a point coordinate lies within an uninitialized AnyWindow,
+ * this routine is called to query window coordinates and
+ * drag&drop info. If this particular window has any children,
+ * more uninitialized AnyWindow structures are allocated.
+ * Further queries will cause these structures to be initialized
+ * in turn.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+QueryWindow(display, windowPtr)
+ Display *display;
+ AnyWindow *windowPtr; /* window rep to be initialized */
+{
+ int visible;
+
+ if (windowPtr->initialized) {
+ return;
+ }
+ /*
+ * Query for the window coordinates.
+ */
+ visible = GetWindowRegion(display, windowPtr->nativeWindow,
+ &(windowPtr->x1), &(windowPtr->y1), &(windowPtr->x2), &(windowPtr->y2));
+ if (visible) {
+ Blt_ChainLink *linkPtr;
+ Blt_Chain *chainPtr;
+ AnyWindow *childPtr;
+
+#ifndef WIN32
+ /* Add offset from parent's origin to coordinates */
+ if (windowPtr->parentPtr != NULL) {
+ windowPtr->x1 += windowPtr->parentPtr->x1;
+ windowPtr->y1 += windowPtr->parentPtr->y1;
+ windowPtr->x2 += windowPtr->parentPtr->x1;
+ windowPtr->y2 += windowPtr->parentPtr->y1;
+ }
+#endif
+ /*
+ * Collect a list of child windows, sorted in z-order. The
+ * topmost window will be first in the list.
+ */
+ chainPtr = GetWindowZOrder(display, windowPtr->nativeWindow);
+
+ /* Add and initialize extra slots if needed. */
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_Calloc(1, sizeof(AnyWindow));
+ assert(childPtr);
+ childPtr->initialized = FALSE;
+ childPtr->nativeWindow = (WINDOW)Blt_ChainGetValue(linkPtr);
+ childPtr->parentPtr = windowPtr;
+ Blt_ChainSetValue(linkPtr, childPtr);
+ }
+ windowPtr->chainPtr = chainPtr;
+ } else {
+ /* If it's not viewable don't bother doing anything else. */
+ windowPtr->x1 = windowPtr->y1 = windowPtr->x2 = windowPtr->y2 = -1;
+ windowPtr->chainPtr = NULL;
+ }
+ windowPtr->initialized = TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * AddTargetProperty --
+ *
+ * Attaches a drag&drop property to the given target window.
+ * This property allows us to recognize the window later as a
+ * valid target. It also stores important information including
+ * the interpreter managing the target and the pathname of the
+ * target window. Usually this routine is called when the target
+ * is first registered or first exposed (so that the X-window
+ * really exists).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+AddTargetProperty(interp, targetPtr)
+ Tcl_Interp *interp;
+ Target *targetPtr; /* drag&drop target window data */
+{
+ Tcl_DString dString;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ if (targetPtr->tkwin == NULL) {
+ return;
+ }
+ Tcl_DStringInit(&dString);
+ /*
+ * Each target window's dnd property contains
+ *
+ * 1. name of the application (ie. the interpreter's name).
+ * 2. Tk path name of the target window.
+ * 3. List of all the data types that can be handled. If none
+ * are listed, then all can be handled.
+ */
+ Tcl_DStringAppendElement(&dString, Tk_Name(Tk_MainWindow(interp)));
+ Tcl_DStringAppendElement(&dString, Tk_PathName(targetPtr->tkwin));
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_DStringAppendElement(&dString,
+ Blt_GetHashKey(&(targetPtr->handlerTable), hPtr));
+ }
+ SetProperty(targetPtr->tkwin, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ExpandPercents --
+ *
+ * Expands all percent substitutions found in the input "str"
+ * that match specifications in the "subs" list. Any percent
+ * field that is not found in the "subs" list is left alone.
+ * Returns a string that remains valid until the next call to
+ * this routine.
+ *
+ * ------------------------------------------------------------------------
+ */
+static char *
+ExpandPercents(string, subsArr, nSubs, resultPtr)
+ char *string; /* Incoming command string */
+ SubstDescriptors *subsArr; /* Array of known substitutions */
+ int nSubs; /* Number of elements in subs array */
+ Tcl_DString *resultPtr;
+{
+ register char *chunk, *p;
+ char letter;
+ char percentSign;
+ int i;
+
+ /*
+ * Scan through the copy of the input string, look for
+ * the next '%' character, and try to make the substitution.
+ * Continue doing this to the end of the string.
+ */
+ chunk = p = string;
+ while ((p = strchr(p, '%')) != NULL) {
+
+ /* Copy up to the percent sign. Repair the string afterwards */
+ percentSign = *p;
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, chunk, -1);
+ *p = percentSign;
+
+ /* Search for a matching substition rule */
+ letter = *(p + 1);
+ for (i = 0; i < nSubs; i++) {
+ if (subsArr[i].letter == letter) {
+ break;
+ }
+ }
+ if (i < nSubs) {
+ /* Make the substitution */
+ Tcl_DStringAppend(resultPtr, subsArr[i].value, -1);
+ } else {
+ /* Copy in the %letter verbatim */
+ char verbatim[3];
+
+ verbatim[0] = '%';
+ verbatim[1] = letter;
+ verbatim[2] = '\0';
+ Tcl_DStringAppend(resultPtr, verbatim, -1);
+ }
+ p += 2; /* Skip % + letter */
+ if (letter == '\0') {
+ p += 1; /* Premature % substitution (end of string) */
+ }
+ chunk = p;
+ }
+ /* Pick up last chunk if a substition wasn't the last thing in the string */
+ if (*chunk != '\0') {
+ Tcl_DStringAppend(resultPtr, chunk, -1);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+
+static int
+DragOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int x, y;
+ Token *tokenPtr;
+ int status;
+ Source *srcPtr;
+ SubstDescriptors subst[2];
+ int active;
+ char *result;
+
+ /*
+ * HANDLE: drag&drop drag <path> <x> <y>
+ */
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " drag pathname x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+
+ tokenPtr->lastX = locX = x; /* Save drag&drop location */
+ tokenPtr->lastY = locY = y;
+
+ /* If HideToken() is pending, then do it now! */
+ if (tokenPtr->timer != 0) {
+ Tcl_DeleteTimerHandler(tokenPtr->timer);
+ HideToken(tokenPtr);
+ }
+
+ /*
+ * If pkgCmd is in progress, then ignore subsequent calls
+ * until it completes. Only perform drag if pkgCmd
+ * completed successfully and token window is mapped.
+ */
+ if ((!Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) {
+ Tcl_DString dString;
+
+ /*
+ * No list of send handlers? Then source is disabled.
+ * Abort drag quietly.
+ */
+ if (srcPtr->sendTypes == NULL) {
+ return TCL_OK;
+ }
+ /*
+ * No token command? Then cannot build token.
+ * Signal error.
+ */
+ if (srcPtr->pkgCmd == NULL) {
+ Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Execute token command to initialize token window.
+ */
+ srcPtr->pkgCmdInProgress = TRUE;
+ subst[0].letter = 'W';
+ subst[0].value = Tk_PathName(srcPtr->tkwin);
+ subst[1].letter = 't';
+ subst[1].value = Tk_PathName(tokenPtr->tkwin);
+
+ Tcl_DStringInit(&dString);
+ status = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(srcPtr->pkgCmd, subst, 2, &dString));
+ Tcl_DStringFree(&dString);
+
+ srcPtr->pkgCmdInProgress = FALSE;
+
+ result = Tcl_GetStringResult(interp);
+ /*
+ * Null string from the package command?
+ * Then quietly abort the drag&drop operation.
+ */
+ if (result[0] == '\0') {
+ return TCL_OK;
+ }
+
+ /* Save result of token command for send command. */
+ if (srcPtr->pkgCmdResult != NULL) {
+ Blt_Free(srcPtr->pkgCmdResult);
+ }
+ srcPtr->pkgCmdResult = Blt_Strdup(result);
+ if (status != TCL_OK) {
+ /*
+ * Token building failed. If an error handler is defined,
+ * then signal the error. Otherwise, abort quietly.
+ */
+ if ((errorCmd != NULL) && (errorCmd[0] != '\0')) {
+ return Tcl_VarEval(interp, errorCmd, " {", result, "}",
+ (char *)NULL);
+ }
+ return TCL_OK;
+ }
+ /* Install token cursor. */
+ if (tokenPtr->cursor != None) {
+ Tk_Cursor cursor;
+
+ /* Save the old cursor */
+ cursor = GetWidgetCursor(srcPtr->interp, srcPtr->tkwin);
+ if (srcPtr->cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->cursor);
+ }
+ srcPtr->cursor = cursor;
+ /* Temporarily install the token cursor */
+ Tk_DefineCursor(srcPtr->tkwin, tokenPtr->cursor);
+ }
+ /*
+ * Get ready to drag token window...
+ * 1) Cache info for all windows on root
+ * 2) Map token window to begin drag operation
+ */
+ if (srcPtr->rootPtr != NULL) {
+ RemoveWindow(srcPtr->rootPtr);
+ }
+ InitRoot(srcPtr);
+
+ nActive++; /* One more drag&drop window active */
+
+ if (Tk_WindowId(tokenPtr->tkwin) == None) {
+ Tk_MakeWindowExist(tokenPtr->tkwin);
+ }
+ if (!Tk_IsMapped(tokenPtr->tkwin)) {
+ Tk_MapWindow(tokenPtr->tkwin);
+ }
+ RaiseToken(tokenPtr);
+ }
+
+ /* Arrange to update status of token window. */
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+
+ active = OverTarget(srcPtr, x, y);
+ if (tokenPtr->active != active) {
+ tokenPtr->active = active;
+ Tcl_DoWhenIdle(UpdateToken, srcPtr);
+ }
+ MoveToken(srcPtr, tokenPtr); /* Move token window to current drag point. */
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop drop <path> <x> <y>
+ */
+static int
+DropOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+ Token *tokenPtr;
+ int x, y;
+
+ if (argc < 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " drop pathname x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+ tokenPtr->lastX = locX = x; /* Save drag&drop location */
+ tokenPtr->lastY = locY = y;
+
+ /* Put the cursor back to its usual state. */
+ if (srcPtr->cursor == None) {
+ Tk_UndefineCursor(srcPtr->tkwin);
+ } else {
+ Tk_DefineCursor(srcPtr->tkwin, srcPtr->cursor);
+ }
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+
+ /*
+ * Make sure that token window was not dropped before it
+ * was either mapped or packed with info.
+ */
+ if ((Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) {
+ int active;
+
+ active = OverTarget(srcPtr, tokenPtr->lastX, tokenPtr->lastY);
+ if (tokenPtr->active != active) {
+ tokenPtr->active = active;
+ UpdateToken(srcPtr);
+ }
+ if (srcPtr->sendTypes != NULL) {
+ if (tokenPtr->active) {
+ DndSend(srcPtr);
+ } else {
+ HideToken(tokenPtr);
+ }
+ }
+ nActive--; /* One less active token window. */
+ }
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop errors ?<proc>?
+ */
+static int
+ErrorsOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ if (errorCmd) {
+ Blt_Free(errorCmd);
+ }
+ errorCmd = Blt_Strdup(argv[2]);
+ } else if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " errors ?proc?\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, errorCmd, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop active
+ */
+static int
+ActiveOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " active\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_SetBooleanResult(interp, (nActive > 0));
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop location ?<x> <y>?
+ */
+static int
+LocationOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if ((argc != 2) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " location ?x y?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ int x, y;
+
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ locX = x;
+ locY = y;
+ }
+ Tcl_AppendElement(interp, Blt_Itoa(locX));
+ Tcl_AppendElement(interp, Blt_Itoa(locY));
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop token <pathName>
+ */
+static int
+TokenOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+
+ if (GetSource(interp, argv[2], &srcPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((argc > 3) &&
+ (ConfigureToken(interp, srcPtr, argc - 3, argv + 3) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(srcPtr->token.tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+static int
+HandlerOpOp(srcPtr, interp, argc, argv)
+ Source *srcPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ int isNew;
+ char *cmd;
+
+ /*
+ * HANDLE: drag&drop source <pathName> handler \
+ * ?<data>? ?<scmd>...?
+ */
+ if (argc == 4) {
+ /* Show source handler data types */
+ for (hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(srcPtr->handlerTable), hPtr));
+ }
+ return TCL_OK;
+ }
+ hPtr = Blt_CreateHashEntry(&(srcPtr->handlerTable), argv[4], &isNew);
+
+ /*
+ * HANDLE: drag&drop source <pathName> handler <data>
+ *
+ * Create the new <data> type if it doesn't already
+ * exist, and return the code associated with it.
+ */
+ if (argc == 5) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd == NULL) {
+ cmd = "";
+ }
+ Tcl_SetResult(interp, cmd, TCL_VOLATILE);
+ return TCL_OK;
+ }
+ /*
+ * HANDLE: drag&drop source <pathName> handler \
+ * <data> <cmd> ?<arg>...?
+ *
+ * Create the new <data> type and set its command
+ */
+ cmd = Tcl_Concat(argc - 5, argv + 5);
+ Blt_SetHashValue(hPtr, cmd);
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop source
+ * drag&drop source <pathName> ?options...?
+ * drag&drop source <pathName> handler ?<data>? ?<scmd> <arg>...?
+ */
+static int
+SourceOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+ int isNew;
+ Token *tokenPtr;
+
+ if (argc == 2) {
+ Blt_HashSearch cursor;
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ for (hPtr = Blt_FirstHashEntry(&sourceTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tkwin = (Tk_Window)Blt_GetHashKey(&sourceTable, hPtr);
+ Tcl_AppendElement(interp, Tk_PathName(tkwin));
+ }
+ return TCL_OK;
+ }
+ /*
+ * Find or create source info...
+ */
+ srcPtr = CreateSource(interp, argv[2], &isNew);
+ if (srcPtr == NULL) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+ if (argc > 3) {
+ char c;
+ int length;
+ int status;
+
+ /*
+ * HANDLE: drag&drop source <pathName> ?options...?
+ */
+ c = argv[3][0];
+ length = strlen(argv[3]);
+
+ if (c == '-') {
+ if (argc == 3) {
+ status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs,
+ (char *)srcPtr, (char *)NULL, 0);
+ } else if (argc == 4) {
+ status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs,
+ (char *)srcPtr, argv[3], 0);
+ } else {
+ status = ConfigureSource(interp, srcPtr, argc - 3, argv + 3,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ if (status != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0) {
+ return HandlerOpOp(srcPtr, interp, argc, argv);
+ } else {
+ Tcl_AppendResult(interp, "bad operation \"", argv[3],
+ "\": must be \"handler\" or a configuration option",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (isNew) {
+ /*
+ * Create the window for the drag&drop token...
+ */
+ if (CreateToken(interp, srcPtr) != TCL_OK) {
+ DestroySource(srcPtr);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop target ?<pathName>? ?handling info...?
+ */
+static int
+TargetOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SubstDescriptors subst[2];
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ Target *targetPtr;
+ int isNew;
+
+ if (argc == 2) {
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&targetTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tkwin = (Tk_Window)Blt_GetHashKey(&targetTable, hPtr);
+ Tcl_AppendElement(interp, Tk_PathName(tkwin));
+ }
+ return TCL_OK;
+ }
+ tkwin = Tk_NameToWindow(interp, argv[2], Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ targetPtr = FindTarget(tkwin);
+ if (targetPtr == NULL) {
+ targetPtr = CreateTarget(interp, tkwin);
+ }
+ if (targetPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ if ((argc >= 4) && (strcmp(argv[3], "handler") == 0)) {
+ /*
+ * HANDLE: drag&drop target <pathName> handler
+ * drag&drop target <pathName> handler ?<data> <cmd> <arg>...?
+ */
+ if (argc == 4) {
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(targetPtr->handlerTable), hPtr));
+ }
+ return TCL_OK;
+ } else if (argc >= 6) {
+ char *cmd;
+
+ /*
+ * Process handler definition
+ */
+ hPtr = Blt_CreateHashEntry(&(targetPtr->handlerTable), argv[4],
+ &isNew);
+ cmd = Tcl_Concat(argc - 5, argv + 5);
+ if (hPtr != NULL) {
+ char *oldCmd;
+
+ oldCmd = (char *)Blt_GetHashValue(hPtr);
+ if (oldCmd != NULL) {
+ Blt_Free(oldCmd);
+ }
+ }
+ Blt_SetHashValue(hPtr, cmd);
+ /*
+ * Update the target property on the window.
+ */
+ AddTargetProperty(interp, targetPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ", argv[1], " ", argv[2], " ", argv[3],
+ " data command ?arg arg...?", (char *)NULL);
+ return TCL_ERROR;
+ } else if ((argc >= 4) && (strcmp(argv[3], "handle") == 0)) {
+ /*
+ * HANDLE: drag&drop target <pathName> handle <data> ?<value>?
+ */
+ Tcl_DString dString;
+ int result;
+ char *cmd;
+
+ if (argc < 5 || argc > 6) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ", argv[1], " ", argv[2], " handle data ?value?",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(targetPtr->handlerTable), argv[4]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "target can't handle datatype: ",
+ argv[4], (char *)NULL);
+ return TCL_ERROR; /* no handler found */
+ }
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ subst[0].letter = 'W';
+ subst[0].value = Tk_PathName(targetPtr->tkwin);
+ subst[1].letter = 'v';
+ if (argc > 5) {
+ subst[1].value = argv[5];
+ } else {
+ subst[1].value = "";
+ }
+ Tcl_DStringInit(&dString);
+ result = Tcl_Eval(interp, ExpandPercents(cmd, subst, 2, &dString));
+ Tcl_DStringFree(&dString);
+ return result;
+ }
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "usage: ", argv[0], " target ", argv[2],
+ " handler ?data command arg arg...?\n or: ",
+ argv[0], " target ", argv[2], " handle <data>",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DragDropCmd --
+ *
+ * Invoked by TCL whenever the user issues a drag&drop command.
+ * Handles the following syntax:
+ *
+ * drag&drop source
+ * drag&drop source <pathName> ?options...?
+ * drag&drop source <pathName> handler ?<dataType>? ?<cmd> <arg>...?
+ *
+ * drag&drop target
+ * drag&drop target <pathName> handler ?<dataType> <cmd> <arg>...?
+ * drag&drop target <pathName> handle <dataType> ?<value>?
+ *
+ * drag&drop token <pathName>
+ * drag&drop drag <pathName> <x> <y>
+ * drag&drop drop <pathName> <x> <y>
+ *
+ * drag&drop errors ?<proc>?
+ * drag&drop active
+ * drag&drop location ?<x> <y>?
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DragDropCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter */
+ int argc; /* # of arguments */
+ char **argv; /* Argument strings */
+{
+ int length;
+ char c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " oper ?args?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ if ((c == 's') && strncmp(argv[1], "source", length) == 0) {
+ return SourceOp(interp, argc, argv);
+ } else if ((c == 't') && (length >= 2) &&
+ (strncmp(argv[1], "target", length) == 0)) {
+ return TargetOp(interp, argc, argv);
+ } else if ((c == 't') && (length >= 2) &&
+ (strncmp(argv[1], "token", length) == 0)) {
+ return TokenOp(interp, argc, argv);
+ } else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0) {
+ return DragOp(interp, argc, argv);
+ } else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0) {
+ return DropOp(interp, argc, argv);
+ } else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0) {
+ return ErrorsOp(interp, argc, argv);
+ } else if ((c == 'a') && strncmp(argv[1], "active", length) == 0) {
+ return ActiveOp(interp, argc, argv);
+ } else if ((c == 'l') && strncmp(argv[1], "location", length) == 0) {
+ return LocationOp(interp, argc, argv);
+ }
+ /*
+ * Report improper command arguments
+ */
+ Tcl_AppendResult(interp, "bad operation \"", argv[1],
+ "\": must be active, drag, drop, errors, location, ",
+ "source, target or token",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * Blt_DragDropInit --
+ *
+ * Adds the drag&drop command to the given interpreter. Should
+ * be invoked to properly install the command whenever a new
+ * interpreter is created.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_DragDropInit(interp)
+ Tcl_Interp *interp; /* interpreter to be updated */
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "drag&drop", DragDropCmd,
+ };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ if (!initialized) {
+ Blt_InitHashTable(&sourceTable, BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&targetTable, BLT_ONE_WORD_KEYS);
+ errorCmd = Blt_Strdup(DEF_ERROR_PROC);
+ nActive = 0;
+ locX = locY = 0;
+ initialized = TRUE;
+#ifndef WIN32
+ dndAtom = XInternAtom(Tk_Display(Tk_MainWindow(interp)), propName,
+ False);
+#endif
+ }
+ return TCL_OK;
+}
+#endif /* NO_DRAGDROP */
diff --git a/blt/src/bltGrAxis.c b/blt/src/bltGrAxis.c
new file mode 100644
index 00000000000..e9d4f2a30b3
--- /dev/null
+++ b/blt/src/bltGrAxis.c
@@ -0,0 +1,4549 @@
+
+/*
+ * bltGrAxis.c --
+ *
+ * This module implements coordinate axes for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltGrElem.h"
+#include <X11/Xutil.h>
+
+#define HIDE_ALL -1
+
+#define DEF_NUM_TICKS 4 /* Each minor tick is 20% */
+#define STATIC_TICK_SPACE 10
+
+#define TICK_LABEL_SIZE 200
+#define MAXTICKS 10001
+
+#define CLAMP(val,low,high) \
+ (((val) < (low)) ? (low) : ((val) > (high)) ? (high) : (val))
+
+/*
+ * Round x in terms of units
+ */
+#define UROUND(x,u) (Round((x)/(u))*(u))
+#define UCEIL(x,u) (ceil((x)/(u))*(u))
+#define UFLOOR(x,u) (floor((x)/(u))*(u))
+
+#define LENGTH_MAJOR_TICK 0.030 /* Length of a major tick */
+#define LENGTH_MINOR_TICK 0.015 /* Length of a minor (sub)tick */
+#define LENGTH_LABEL_TICK 0.040 /* Distance from graph to start of the
+ * label */
+#define NUMDIGITS 15 /* Specifies the number of
+ * digits of accuracy used when
+ * outputting axis tick labels. */
+#define AVG_TICK_NUM_CHARS 16 /* Assumed average tick label size */
+
+#define TICK_RANGE_TIGHT 0
+#define TICK_RANGE_LOOSE 1
+#define TICK_RANGE_ALWAYS_LOOSE 2
+
+#define AXIS_TITLE_PAD 2 /* Padding for axis title. */
+#define AXIS_LINE_PAD 1 /* Padding for axis line. */
+
+#define HORIZMARGIN(m) (!((m)->site & 0x1)) /* Even sites are horizontal */
+/* Map graph coordinates to normalized coordinates [0..1] */
+#define NORMALIZE(A,x) (((x) - (A)->tickRange.min) / (A)->tickRange.range)
+
+typedef enum AxisComponents {
+ MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
+} AxisComponent;
+
+
+typedef struct {
+ int axis; /* Length of the axis. */
+ int t1; /* Length of a major tick (in pixels). */
+ int t2; /* Length of a minor tick (in pixels). */
+ int label; /* Distance from axis to tick label. */
+} AxisInfo;
+
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltListOption;
+
+static Tk_OptionParseProc StringToBounds;
+static Tk_OptionPrintProc BoundsToString;
+static Tk_OptionParseProc StringToTicks;
+static Tk_OptionPrintProc TicksToString;
+static Tk_OptionParseProc StringToAxis;
+static Tk_OptionPrintProc AxisToString;
+static Tk_OptionParseProc StringToAnyAxis;
+static Tk_OptionParseProc StringToFormat;
+static Tk_OptionPrintProc FormatToString;
+static Tk_OptionParseProc StringToLoose;
+static Tk_OptionPrintProc LooseToString;
+static Tk_OptionParseProc StringToHide;
+static Tk_OptionPrintProc HideToString;
+
+static Tk_CustomOption minOption =
+{
+ StringToBounds, BoundsToString, (ClientData)AXIS_CONFIG_MIN,
+};
+static Tk_CustomOption maxOption =
+{
+ StringToBounds, BoundsToString, (ClientData)AXIS_CONFIG_MAX,
+};
+static Tk_CustomOption majorTicksOption =
+{
+ StringToTicks, TicksToString, (ClientData)AXIS_CONFIG_MAJOR,
+};
+static Tk_CustomOption minorTicksOption =
+{
+ StringToTicks, TicksToString, (ClientData)AXIS_CONFIG_MINOR,
+};
+Tk_CustomOption bltXAxisOption =
+{
+ StringToAxis, AxisToString, (ClientData)&bltXAxisUid
+};
+Tk_CustomOption bltYAxisOption =
+{
+ StringToAxis, AxisToString, (ClientData)&bltYAxisUid
+};
+Tk_CustomOption bltAnyXAxisOption =
+{
+ StringToAnyAxis, AxisToString, (ClientData)&bltXAxisUid
+};
+Tk_CustomOption bltAnyYAxisOption =
+{
+ StringToAnyAxis, AxisToString, (ClientData)&bltYAxisUid
+};
+static Tk_CustomOption formatOption =
+{
+ StringToFormat, FormatToString, (ClientData)0,
+};
+static Tk_CustomOption looseOption =
+{
+ StringToLoose, LooseToString, (ClientData)0,
+};
+static Tk_CustomOption hideOption =
+{
+ StringToHide, HideToString, (ClientData)0,
+};
+
+/* Axis flags: */
+
+#define DEF_AXIS_ALT_HIDE "no"
+#define DEF_AXIS_COMMAND (char *)NULL
+#define DEF_AXIS_DESCENDING "no"
+#define DEF_AXIS_FG_COLOR RGB_BLACK
+#define DEF_AXIS_FG_MONO RGB_BLACK
+#define DEF_AXIS_HIDE "no"
+#define DEF_AXIS_HIDE_ALT "no"
+#define DEF_AXIS_HIDE_STD "yes"
+#define DEF_AXIS_JUSTIFY "center"
+#define DEF_AXIS_LIMITS_FORMAT (char *)NULL
+#define DEF_AXIS_LINE_WIDTH "1"
+#define DEF_AXIS_LOGSCALE "no"
+#define DEF_AXIS_LOOSE "no"
+#define DEF_AXIS_MAJOR_TICKS (char *)NULL
+#define DEF_AXIS_MAX (char *)NULL
+#define DEF_AXIS_MIN (char *)NULL
+#define DEF_AXIS_RANGE "0.0"
+#define DEF_AXIS_ROTATE "0.0"
+#define DEF_AXIS_SCROLL_INCREMENT "10"
+#define DEF_AXIS_SHADOW_COLOR (char *)NULL
+#define DEF_AXIS_SHADOW_MONO (char *)NULL
+#define DEF_AXIS_SHIFTBY "0.0"
+#define DEF_AXIS_SHOWTICKS "yes"
+#define DEF_AXIS_STEPSIZE "0.0"
+#define DEF_AXIS_SUBDIVISIONS "2"
+#define DEF_AXIS_TAGS "all"
+#define DEF_AXIS_TICKS "0"
+#ifdef WIN32
+#define DEF_AXIS_TICK_FONT "{Arial Narrow} 8"
+#else
+#define DEF_AXIS_TICK_FONT "*-Helvetica-Medium-R-Normal-*-10-*"
+#endif
+#define DEF_AXIS_TICK_LENGTH "8"
+#define DEF_AXIS_TICK_VALUES (char *)NULL
+#define DEF_AXIS_TITLE (char *)NULL
+#define DEF_AXIS_TITLE_FG RGB_BLACK
+#define DEF_AXIS_TITLE_FONT STD_FONT
+#define DEF_AXIS_X_STEPSIZE_BARCHART "1.0"
+#define DEF_AXIS_X_SUBDIVISIONS_BARCHART "0"
+#define DEF_AXIS_BACKGROUND (char *)NULL
+#define DEF_AXIS_BORDER_WIDTH "0"
+#define DEF_AXIS_RELIEF "flat"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_DOUBLE, "-autorange", "autoRange", "AutoRange",
+ DEF_AXIS_RANGE, Tk_Offset(Axis, autoRange),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_AXIS_BACKGROUND, Tk_Offset(Axis, border),
+ ALL_GRAPHS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_AXIS_TAGS, Tk_Offset(Axis, tags),
+ ALL_GRAPHS | TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, ALL_GRAPHS},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_AXIS_BORDER_WIDTH, Tk_Offset(Axis, borderWidth),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_AXIS_FG_COLOR, Tk_Offset(Axis, tickStyle.color),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_AXIS_FG_MONO, Tk_Offset(Axis, tickStyle.color),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_STRING, "-command", "command", "Command",
+ DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
+ TK_CONFIG_NULL_OK | ALL_GRAPHS},
+ {TK_CONFIG_BOOLEAN, "-descending", "descending", "Descending",
+ DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-hide", "hide", "Hide",
+ DEF_AXIS_HIDE, Tk_Offset(Axis, hidden),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &hideOption},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_AXIS_JUSTIFY, Tk_Offset(Axis, titleStyle.justify),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_COLOR, "-limitscolor", "limitsColor", "Color",
+ DEF_AXIS_FG_COLOR, Tk_Offset(Axis, limitsStyle.color),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_COLOR, "-limitscolor", "limitsColor", "Color",
+ DEF_AXIS_FG_MONO, Tk_Offset(Axis, limitsStyle.color),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_FONT, "-limitsfont", "limitsFont", "Font",
+ DEF_AXIS_TICK_FONT, Tk_Offset(Axis, limitsStyle.font), ALL_GRAPHS},
+ {TK_CONFIG_CUSTOM, "-limitsformat", "limitsFormat", "LimitsFormat",
+ DEF_AXIS_LIMITS_FORMAT, Tk_Offset(Axis, limitsFormats),
+ TK_CONFIG_NULL_OK | ALL_GRAPHS, &formatOption},
+ {TK_CONFIG_CUSTOM, "-limitsshadow", "limitsShadow", "Shadow",
+ DEF_AXIS_SHADOW_COLOR, Tk_Offset(Axis, limitsStyle.shadow),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-limitsshadow", "limitsShadow", "Shadow",
+ DEF_AXIS_SHADOW_MONO, Tk_Offset(Axis, limitsStyle.shadow),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-logscale", "logScale", "LogScale",
+ DEF_AXIS_LOGSCALE, Tk_Offset(Axis, logScale),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-loose", "loose", "Loose",
+ DEF_AXIS_LOOSE, 0, ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT,
+ &looseOption},
+ {TK_CONFIG_CUSTOM, "-majorticks", "majorTicks", "MajorTicks",
+ DEF_AXIS_MAJOR_TICKS, Tk_Offset(Axis, t1Ptr),
+ TK_CONFIG_NULL_OK | ALL_GRAPHS, &majorTicksOption},
+ {TK_CONFIG_CUSTOM, "-max", "max", "Max",
+ DEF_AXIS_MIN, 0, TK_CONFIG_NULL_OK | ALL_GRAPHS, &maxOption},
+ {TK_CONFIG_CUSTOM, "-min", "min", "Min",
+ DEF_AXIS_MAX, 0, TK_CONFIG_NULL_OK | ALL_GRAPHS, &minOption},
+ {TK_CONFIG_CUSTOM, "-minorticks", "minorTicks", "MinorTicks",
+ DEF_AXIS_TICK_VALUES, Tk_Offset(Axis, t2Ptr),
+ TK_CONFIG_NULL_OK | ALL_GRAPHS, &minorTicksOption},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_AXIS_RELIEF, Tk_Offset(Axis, relief),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_AXIS_ROTATE, Tk_Offset(Axis, tickStyle.theta),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(Axis, scrollCmdPrefix),
+ ALL_GRAPHS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement", "ScrollIncrement",
+ DEF_AXIS_SCROLL_INCREMENT, Tk_Offset(Axis, scrollUnits),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_DOUBLE, "-shiftby", "shiftBy", "ShiftBy",
+ DEF_AXIS_SHIFTBY, Tk_Offset(Axis, shiftBy),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-showticks", "showTicks", "ShowTicks",
+ DEF_AXIS_SHOWTICKS, Tk_Offset(Axis, showTicks),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-stepsize", "stepSize", "StepSize",
+ DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
+ ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_INT, "-subdivisions", "subdivisions", "Subdivisions",
+ DEF_AXIS_SUBDIVISIONS, Tk_Offset(Axis, reqNumMinorTicks),
+ ALL_GRAPHS},
+ {TK_CONFIG_FONT, "-tickfont", "tickFont", "Font",
+ DEF_AXIS_TICK_FONT, Tk_Offset(Axis, tickStyle.font), ALL_GRAPHS},
+ {TK_CONFIG_PIXELS, "-ticklength", "tickLength", "TickLength",
+ DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_GRAPHS},
+ {TK_CONFIG_CUSTOM, "-tickshadow", "tickShadow", "Shadow",
+ DEF_AXIS_SHADOW_COLOR, Tk_Offset(Axis, tickStyle.shadow),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-tickshadow", "tickShadow", "Shadow",
+ DEF_AXIS_SHADOW_MONO, Tk_Offset(Axis, tickStyle.shadow),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_STRING, "-title", "title", "Title",
+ DEF_AXIS_TITLE, Tk_Offset(Axis, titleText),
+ TK_CONFIG_DONT_SET_DEFAULT | TK_CONFIG_NULL_OK | ALL_GRAPHS},
+ {TK_CONFIG_COLOR, "-titlecolor", "titleColor", "Color",
+ DEF_AXIS_FG_COLOR, Tk_Offset(Axis, titleStyle.color),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_COLOR, "-titlecolor", "titleColor", "TitleColor",
+ DEF_AXIS_FG_MONO, Tk_Offset(Axis, titleStyle.color),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+ DEF_AXIS_TITLE_FONT, Tk_Offset(Axis, titleStyle.font), ALL_GRAPHS},
+ {TK_CONFIG_CUSTOM, "-titleshadow", "titleShadow", "Shadow",
+ DEF_AXIS_SHADOW_COLOR, Tk_Offset(Axis, titleStyle.shadow),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-titleshadow", "titleShadow", "Shadow",
+ DEF_AXIS_SHADOW_MONO, Tk_Offset(Axis, titleStyle.shadow),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+static void DestroyAxis _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr));
+static int GetAxis _ANSI_ARGS_((Graph *graphPtr, char *name, Tk_Uid classUid,
+ Axis **axisPtrPtr));
+static void FreeAxis _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr));
+
+INLINE static int
+Round(x)
+ register double x;
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+INLINE static double
+Fabs(x)
+ register double x;
+{
+ return ((x < 0.0) ? -x : x);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ * Determines if a value lies within a given range.
+ *
+ * The value is normalized and compared against the interval
+ * [0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ * DBL_EPSILON is the smallest number that can be represented
+ * on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ * Please note, *max* can't equal *min*.
+ *
+ * Results:
+ * If the value is within the interval [min..max], 1 is
+ * returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(value, rangePtr)
+ register double value;
+ AxisRange *rangePtr;
+{
+ if (rangePtr->range < DBL_EPSILON) {
+ return (FABS(rangePtr->max - value) >= DBL_EPSILON);
+ } else {
+ double norm;
+
+ norm = (value - rangePtr->min) / rangePtr->range;
+ return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+ }
+}
+
+INLINE static int
+AxisIsHorizontal(graphPtr, axisPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+{
+ return ((axisPtr->classUid == bltYAxisUid) == graphPtr->inverted);
+}
+
+
+/* ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAnyAxis --
+ *
+ * Converts the name of an axis to a pointer to its axis structure.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The axis flags are
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAnyAxis(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Class identifier of the type of
+ * axis we are looking for. */
+ Tcl_Interp *interp; /* Interpreter to send results back to. */
+ Tk_Window tkwin; /* Used to look up pointer to graph. */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+ Tk_Uid classUid = *(Tk_Uid *)clientData;
+ Graph *graphPtr;
+ Axis *axisPtr;
+
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ if (*axisPtrPtr != NULL) {
+ FreeAxis(graphPtr, *axisPtrPtr);
+ }
+ if (string[0] == '\0') {
+ axisPtr = NULL;
+ } else if (GetAxis(graphPtr, string, classUid, &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAxis --
+ *
+ * Converts the name of an axis to a pointer to its axis structure.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The axis flags are
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAxis(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Class identifier of the type of
+ * axis we are looking for. */
+ Tcl_Interp *interp; /* Interpreter to send results back to. */
+ Tk_Window tkwin; /* Used to look up pointer to graph. */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+ Tk_Uid classUid = *(Tk_Uid *)clientData;
+ Graph *graphPtr;
+
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ if (*axisPtrPtr != NULL) {
+ FreeAxis(graphPtr, *axisPtrPtr);
+ }
+ if (GetAxis(graphPtr, string, classUid, axisPtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AxisToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+AxisToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Pointer to structure record .*/
+ int offset; /* Offset of field in structure. */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Axis *axisPtr = *(Axis **)(widgRec + offset);
+
+ if (axisPtr == NULL) {
+ return "";
+ }
+ return axisPtr->name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ * Convert the name of virtual axis to an pointer.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The axis flags are
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to. */
+ Tk_Window tkwin; /* Used to look up pointer to graph */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ char **elemArr;
+ int nElem;
+
+ if (axisPtr->limitsFormats != NULL) {
+ Blt_Free(axisPtr->limitsFormats);
+ }
+ axisPtr->limitsFormats = NULL;
+ axisPtr->nFormats = 0;
+
+ if ((string == NULL) || (*string == '\0')) {
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem > 2) {
+ Tcl_AppendResult(interp, "too many elements in limits format list \"",
+ string, "\"", (char *)NULL);
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ axisPtr->limitsFormats = elemArr;
+ axisPtr->nFormats = nElem;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of limits field */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+
+ if (axisPtr->nFormats == 0) {
+ return "";
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return Tcl_Merge(axisPtr->nFormats, axisPtr->limitsFormats);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToBounds --
+ *
+ * Convert the string representation of an axis limit into its numeric
+ * form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToBounds(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Either AXIS_CONFIG_MIN or AXIS_CONFIG_MAX.
+ * Indicates which axis limit to set. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ unsigned int mask = (unsigned int)clientData;
+
+ if ((string == NULL) || (*string == '\0')) {
+ if (mask == AXIS_CONFIG_MIN) {
+ axisPtr->min = DBL_MAX;
+ } else {
+ axisPtr->max = -DBL_MAX;
+ }
+ axisPtr->flags &= ~mask;
+ } else {
+ double value;
+
+ if (Tcl_ExprDouble(interp, string, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (mask == AXIS_CONFIG_MIN) {
+ axisPtr->min = value;
+ } else {
+ axisPtr->max = value;
+ }
+ axisPtr->flags |= mask;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BoundsToString --
+ *
+ * Convert the floating point axis limits into a string.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+BoundsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Either LMIN or LMAX */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* */
+ int offset;
+ Tcl_FreeProc **freeProcPtr;
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ unsigned int mask = (unsigned int)clientData;
+ char *result;
+
+ result = "";
+ if (axisPtr->flags & mask) {
+ char string[TCL_DOUBLE_SPACE + 1];
+ double value;
+ Graph *graphPtr;
+
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ value = (mask == AXIS_CONFIG_MIN) ? axisPtr->min : axisPtr->max;
+ Tcl_PrintDouble(graphPtr->interp, value, string);
+ result = Blt_Strdup(string);
+ if (result == NULL) {
+ return "";
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ }
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToTicks --
+ *
+ *
+ * Results:
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToTicks(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ unsigned int mask = (unsigned int)clientData;
+ Axis *axisPtr = (Axis *)widgRec;
+ Ticks **ticksPtrPtr = (Ticks **) (widgRec + offset);
+ int nTicks;
+ Ticks *ticksPtr;
+
+ nTicks = 0;
+ ticksPtr = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ int nExprs;
+ char **exprArr;
+
+ if (Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nExprs > 0) {
+ register int i;
+ int result = TCL_ERROR;
+ double value;
+
+ ticksPtr = Blt_Malloc(sizeof(Ticks) + (nExprs * sizeof(double)));
+ assert(ticksPtr);
+ for (i = 0; i < nExprs; i++) {
+ result = Tcl_ExprDouble(interp, exprArr[i], &value);
+ if (result != TCL_OK) {
+ break;
+ }
+ ticksPtr->tickArr[i] = value;
+ }
+ Blt_Free(exprArr);
+ if (result != TCL_OK) {
+ Blt_Free(ticksPtr);
+ return TCL_ERROR;
+ }
+ nTicks = nExprs;
+ }
+ }
+ axisPtr->flags &= ~mask;
+ if (ticksPtr != NULL) {
+ axisPtr->flags |= mask;
+ ticksPtr->nTicks = nTicks;
+ }
+ if (*ticksPtrPtr != NULL) {
+ Blt_Free(*ticksPtrPtr);
+ }
+ *ticksPtrPtr = ticksPtr;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TicksToString --
+ *
+ * Convert array of tick coordinates to a list.
+ *
+ * Results:
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+TicksToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* */
+ int offset;
+ Tcl_FreeProc **freeProcPtr;
+{
+ Ticks *ticksPtr = *(Ticks **) (widgRec + offset);
+ char string[TCL_DOUBLE_SPACE + 1];
+ register int i;
+ char *result;
+ Tcl_DString dString;
+ Graph *graphPtr;
+
+ if (ticksPtr == NULL) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ for (i = 0; i < ticksPtr->nTicks; i++) {
+ Tcl_PrintDouble(graphPtr->interp, ticksPtr->tickArr[i], string);
+ Tcl_DStringAppendElement(&dString, string);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToLoose --
+ *
+ * Convert a string to one of three values.
+ * 0 - false, no, off
+ * 1 - true, yes, on
+ * 2 - always
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToLoose(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing new value. */
+ char *widgRec; /* Pointer to structure record. */
+ int offset; /* Offset of field in structure. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ register int i;
+ int nElems;
+ char **elemArr;
+ int values[2];
+
+ if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((nElems < 1) || (nElems > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in loose value \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nElems; i++) {
+ if ((elemArr[i][0] == 'a') && (strcmp(elemArr[i], "always") == 0)) {
+ values[i] = TICK_RANGE_ALWAYS_LOOSE;
+ } else {
+ int bool;
+
+ if (Tcl_GetBoolean(interp, elemArr[i], &bool) != TCL_OK) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ values[i] = bool;
+ }
+ }
+ axisPtr->looseMin = axisPtr->looseMax = values[0];
+ if (nElems > 1) {
+ axisPtr->looseMax = values[1];
+ }
+ Blt_Free(elemArr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LooseToString --
+ *
+ * Results:
+ * The string representation of the auto boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+LooseToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of flags field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Axis *axisPtr = (Axis *)widgRec;
+ Tcl_DString dString;
+ char *result;
+
+ Tcl_DStringInit(&dString);
+ if (axisPtr->looseMin == TICK_RANGE_TIGHT) {
+ Tcl_DStringAppendElement(&dString, "0");
+ } else if (axisPtr->looseMin == TICK_RANGE_LOOSE) {
+ Tcl_DStringAppendElement(&dString, "1");
+ } else if (axisPtr->looseMin == TICK_RANGE_ALWAYS_LOOSE) {
+ Tcl_DStringAppendElement(&dString, "always");
+ }
+ if (axisPtr->looseMin != axisPtr->looseMax) {
+ if (axisPtr->looseMax == TICK_RANGE_TIGHT) {
+ Tcl_DStringAppendElement(&dString, "0");
+ } else if (axisPtr->looseMax == TICK_RANGE_LOOSE) {
+ Tcl_DStringAppendElement(&dString, "1");
+ } else if (axisPtr->looseMax == TICK_RANGE_ALWAYS_LOOSE) {
+ Tcl_DStringAppendElement(&dString, "always");
+ }
+ }
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToHide --
+ *
+ * Convert a string to one of three values.
+ * 0 - false, no, off
+ * 1 - true, yes, on
+ * 2 - all
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToHide(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of new value. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset in the widget structure. */
+{
+ int *hidePtr = (int *)(widgRec + offset);
+
+ if ((string[0] == 'a') && (strcmp(string, "all") == 0)) {
+ *hidePtr = HIDE_ALL;
+ } else {
+ int bool;
+
+ if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *hidePtr = bool;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideToString --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+HideToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of flags field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ int hide = *(int *)(widgRec + offset);
+
+ switch (hide) {
+ case FALSE:
+ return "0";
+ case TRUE:
+ return "1";
+ case HIDE_ALL:
+ return "all";
+ default:
+ return "unknown hide value";
+ }
+}
+
+
+static void
+FreeLabels(chainPtr)
+ Blt_Chain *chainPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TickLabel *labelPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ labelPtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(labelPtr);
+ }
+ Blt_ChainReset(chainPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MakeLabel --
+ *
+ * Converts a floating point tick value to a string to be used as its
+ * label.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Returns a new label in the string character buffer. The formatted
+ * tick label will be displayed on the graph.
+ *
+ * ----------------------------------------------------------------------
+ */
+static TickLabel *
+MakeLabel(graphPtr, axisPtr, value)
+ Graph *graphPtr;
+ Axis *axisPtr; /* Axis structure */
+ double value; /* Value to be convert to a decimal string */
+{
+ char string[TICK_LABEL_SIZE + 1];
+ TickLabel *labelPtr;
+
+ /* Generate a default tick label based upon the tick value. */
+ if (axisPtr->logScale) {
+ sprintf(string, "1E%d", ROUND(value));
+ } else {
+ sprintf(string, "%.*g", NUMDIGITS, value);
+ }
+
+ if (axisPtr->formatCmd != NULL) {
+ Tcl_Interp *interp = graphPtr->interp;
+ Tk_Window tkwin = graphPtr->tkwin;
+
+ /*
+ * A Tcl proc was designated to format tick labels. Append the path
+ * name of the widget and the default tick label as arguments when
+ * invoking it. Copy and save the new label from interp->result.
+ */
+ Tcl_ResetResult(interp);
+ if (Tcl_VarEval(interp, axisPtr->formatCmd, " ", Tk_PathName(tkwin),
+ " ", string, (char *)NULL) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ } else {
+ /*
+ * The proc could return a string of any length, so arbitrarily
+ * limit it to what will fit in the return string.
+ */
+ strncpy(string, Tcl_GetStringResult(interp), TICK_LABEL_SIZE);
+ string[TICK_LABEL_SIZE] = '\0';
+
+ Tcl_ResetResult(interp); /* Clear the interpreter's result. */
+ }
+ }
+ labelPtr = Blt_Malloc(sizeof(TickLabel) + strlen(string));
+ assert(labelPtr);
+ strcpy(labelPtr->string, string);
+ labelPtr->anchorPos.x = labelPtr->anchorPos.y = DBL_MAX;
+ return labelPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvHMap --
+ *
+ * Maps the given screen coordinate back to a graph coordinate.
+ * Called by the graph locater routine.
+ *
+ * Results:
+ * Returns the graph coordinate value at the given window
+ * y-coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+InvHMap(graphPtr, axisPtr, x)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double x;
+{
+ double norm, value;
+
+ norm = (double)(x - graphPtr->hOffset) / (double)(graphPtr->hRange);
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ value = (norm * axisPtr->tickRange.range) + axisPtr->tickRange.min;
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ return value;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvVMap --
+ *
+ * Maps the given window y-coordinate back to a graph coordinate
+ * value. Called by the graph locater routine.
+ *
+ * Results:
+ * Returns the graph coordinate value at the given window
+ * y-coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+InvVMap(graphPtr, axisPtr, y)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double y;
+{
+ double norm, value;
+
+ norm = (double)(y - graphPtr->vOffset) / (double)graphPtr->vRange;
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ /* Note: This assumes that the tick range is as big or bigger than the
+ * the range of data points. */
+ value = ((1.0 - norm) * axisPtr->tickRange.range) + axisPtr->tickRange.min;
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ return value;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * HMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a window
+ * position.
+ *
+ * Results:
+ * Returns a double precision number representing the window coordinate
+ * position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+HMap(graphPtr, axisPtr, x)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double x;
+{
+ register double norm;
+
+ if ((axisPtr->logScale) && (x != 0.0)) {
+ x = log10(Fabs(x));
+ }
+ norm = NORMALIZE(axisPtr, x);
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ return ((norm * (graphPtr->hRange)) + graphPtr->hOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a window
+ * position.
+ *
+ * Results:
+ * Returns a double precision number representing the window coordinate
+ * position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+VMap(graphPtr, axisPtr, y)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double y;
+{
+ register double norm;
+
+ if ((axisPtr->logScale) && (y != 0.0)) {
+ y = log10(Fabs(y));
+ }
+ norm = NORMALIZE(axisPtr, y);
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ return (((1.0 - norm) * (graphPtr->vRange)) + graphPtr->vOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Map2D --
+ *
+ * Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ * Returns a XPoint structure containing the window coordinates of
+ * the given graph x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+Point2D
+Blt_Map2D(graphPtr, x, y, axesPtr)
+ Graph *graphPtr;
+ double x, y; /* Graph x and y coordinates */
+ Axis2D *axesPtr; /* Specifies which axes to use */
+{
+ Point2D point;
+
+ if (graphPtr->inverted) {
+ point.x = HMap(graphPtr, axesPtr->y, y);
+ point.y = VMap(graphPtr, axesPtr->x, x);
+ } else {
+ point.x = HMap(graphPtr, axesPtr->x, x);
+ point.y = VMap(graphPtr, axesPtr->y, y);
+ }
+ return point;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InvMap2D --
+ *
+ * Maps the given window x,y coordinates to graph values.
+ *
+ * Results:
+ * Returns a structure containing the graph coordinates of
+ * the given window x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+Point2D
+Blt_InvMap2D(graphPtr, x, y, axesPtr)
+ Graph *graphPtr;
+ double x, y; /* Window x and y coordinates */
+ Axis2D *axesPtr; /* Specifies which axes to use */
+{
+ Point2D point;
+
+ if (graphPtr->inverted) {
+ point.x = InvVMap(graphPtr, axesPtr->x, y);
+ point.y = InvHMap(graphPtr, axesPtr->y, x);
+ } else {
+ point.x = InvHMap(graphPtr, axesPtr->x, x);
+ point.y = InvVMap(graphPtr, axesPtr->y, y);
+ }
+ return point;
+}
+
+
+static void
+GetDataLimits(axisPtr, min, max)
+ Axis *axisPtr;
+ double min, max;
+{
+ if (axisPtr->dataRange.min > min) {
+ axisPtr->dataRange.min = min;
+ }
+ if (axisPtr->dataRange.max < max) {
+ axisPtr->dataRange.max = max;
+ }
+ axisPtr->flags |= AXIS_MAPS_ELEM; /* Mark axis in use */
+}
+
+static void
+FixAxisRange(axisPtr)
+ Axis *axisPtr;
+{
+ /*
+ * When auto-scaling, the axis limits are the bounds of the element
+ * data. If no data exists, set arbitrary limits (wrt to log/linear
+ * scale).
+ */
+ if (axisPtr->dataRange.min == DBL_MAX) {
+ axisPtr->dataRange.min = (axisPtr->logScale) ? 0.001 : 0.0;
+ }
+ if (axisPtr->dataRange.max == -DBL_MAX) {
+ axisPtr->dataRange.max = 1.0;
+ }
+ if (axisPtr->dataRange.min >= axisPtr->dataRange.max) {
+ double value;
+
+ /*
+ * There is no range of data (i.e. min is not less than max),
+ * so manufacture one.
+ */
+ value = axisPtr->dataRange.min;
+ if (value == 0.0) {
+ axisPtr->dataRange.min = -0.1, axisPtr->dataRange.max = 0.1;
+ } else {
+ double x;
+
+ x = Fabs(value * 0.1);
+ axisPtr->dataRange.min = value - x;
+ axisPtr->dataRange.max = value + x;
+ }
+ }
+ SetRange(axisPtr->dataRange);
+
+ /*
+ * If the user hasn't already specified axis limits with the
+ * -min or -max options, set the default axis limits to the be
+ * current extents of the data.
+ */
+
+ if (!(axisPtr->flags & AXIS_CONFIG_MIN)) {
+ axisPtr->min = axisPtr->dataRange.min;
+ }
+ if (!(axisPtr->flags & AXIS_CONFIG_MAX)) {
+ axisPtr->max = axisPtr->dataRange.max;
+ }
+
+ if (axisPtr->max < axisPtr->min) {
+
+ /*
+ * If the limits still don't make sense, it's because only
+ * one limit configuration option (-min or -max) was set and
+ * the other default (based upon the data) is too small/large.
+ * Make up a new limit from the one that was set.
+ */
+
+ if (!(axisPtr->flags & AXIS_CONFIG_MIN)) {
+ axisPtr->min = axisPtr->max - (Fabs(axisPtr->max) * 0.1);
+ }
+ if (!(axisPtr->flags & AXIS_CONFIG_MAX)) {
+ axisPtr->max = axisPtr->min + (Fabs(axisPtr->max) * 0.1);
+ }
+ }
+ /* Auto range */
+ if ((axisPtr->autoRange > 0.0) &&
+ ((axisPtr->flags & (AXIS_CONFIG_MAX | AXIS_CONFIG_MIN)) == 0)) {
+ double max;
+
+ if (axisPtr->shiftBy < 0.0) {
+ axisPtr->shiftBy = 0.0;
+ }
+ max = axisPtr->min + axisPtr->autoRange;
+ if (axisPtr->max >= max) {
+ if (axisPtr->shiftBy > 0.0) {
+ max = UCEIL(axisPtr->max, axisPtr->shiftBy);
+ }
+ axisPtr->min = max - axisPtr->autoRange;
+ }
+ axisPtr->max = max;
+ }
+ if ((axisPtr->max != axisPtr->prevMax) ||
+ (axisPtr->min != axisPtr->prevMin)) {
+ /* Indicate if the axis limits have changed */
+ axisPtr->flags |= AXIS_CONFIG_DIRTY;
+ /* and save the previous minimum and maximum values */
+ axisPtr->prevMin = axisPtr->min;
+ axisPtr->prevMax = axisPtr->max;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NiceNum --
+ *
+ * Reference: Paul Heckbert, "Nice Numbers for Graph Labels",
+ * Graphics Gems, pp 61-63.
+ *
+ * Finds a "nice" number approximately equal to x.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+NiceNum(x, round)
+ double x;
+ int round; /* If non-zero, round. Otherwise take ceiling
+ * of value. */
+{
+ double exponX; /* exponent of x */
+ double fractX; /* fractional part of x */
+ double nf; /* nice, rounded fraction */
+
+ exponX = floor(log10(x));
+ fractX = x / EXP10(exponX); /* between 1 and 10 */
+ if (round) {
+ if (fractX < 1.5) {
+ nf = 1.0;
+ } else if (fractX < 3.0) {
+ nf = 2.0;
+ } else if (fractX < 7.0) {
+ nf = 5.0;
+ } else {
+ nf = 10.0;
+ }
+ } else if (fractX <= 1.0) {
+ nf = 1.0;
+ } else if (fractX <= 2.0) {
+ nf = 2.0;
+ } else if (fractX <= 5.0) {
+ nf = 5.0;
+ } else {
+ nf = 10.0;
+ }
+ return nf * EXP10(exponX);
+}
+
+static Ticks *
+GenerateTicks(sweepPtr)
+ TickSweep *sweepPtr;
+{
+ Ticks *ticksPtr;
+ register int i;
+ double value;
+
+ static double logTable[] = /* Precomputed log10 values [1..10] */
+ {
+ 0.0,
+ 0.301029995663981, 0.477121254719662,
+ 0.602059991327962, 0.698970004336019,
+ 0.778151250383644, 0.845098040014257,
+ 0.903089986991944, 0.954242509439325,
+ 1.0
+ };
+ ticksPtr = Blt_Malloc(sizeof(Ticks) + (sweepPtr->nSteps * sizeof(double)));
+ assert(ticksPtr);
+ value = sweepPtr->initial; /* Start from smallest axis tick */
+
+ if (sweepPtr->step == 0.0) { /* Hack: Zero step indicates to use
+ * log values */
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ ticksPtr->tickArr[i] = logTable[i];
+ }
+ } else {
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ value = UROUND(value, sweepPtr->step);
+ ticksPtr->tickArr[i] = value;
+ value += sweepPtr->step;
+ }
+ }
+ ticksPtr->nTicks = sweepPtr->nSteps;
+ return ticksPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LogScaleAxis --
+ *
+ * Determine the range and units of a log scaled axis.
+ *
+ * Unless the axis limits are specified, the axis is scaled
+ * automatically, where the smallest and largest major ticks encompass
+ * the range of actual data values. When an axis limit is specified,
+ * that value represents the smallest(min)/largest(max) value in the
+ * displayed range of values.
+ *
+ * Both manual and automatic scaling are affected by the step used. By
+ * default, the step is the largest power of ten to divide the range in
+ * more than one piece.
+ *
+ * Automatic scaling:
+ * Find the smallest number of units which contain the range of values.
+ * The minimum and maximum major tick values will be represent the
+ * range of values for the axis. This greatest number of major ticks
+ * possible is 10.
+ *
+ * Manual scaling:
+ * Make the minimum and maximum data values the represent the range of
+ * the values for the axis. The minimum and maximum major ticks will be
+ * inclusive of this range. This provides the largest area for plotting
+ * and the expected results when the axis min and max values have be set
+ * by the user (.e.g zooming). The maximum number of major ticks is 20.
+ *
+ * For log scale, there's the possibility that the minimum and
+ * maximum data values are the same magnitude. To represent the
+ * points properly, at least one full decade should be shown.
+ * However, if you zoom a log scale plot, the results should be
+ * predictable. Therefore, in that case, show only minor ticks.
+ * Lastly, there should be an appropriate way to handle numbers
+ * <=0.
+ *
+ * maxY
+ * | units = magnitude (of least significant digit)
+ * | high = largest unit tick < max axis value
+ * high _| low = smallest unit tick > min axis value
+ * |
+ * | range = high - low
+ * | # ticks = greatest factor of range/units
+ * _|
+ * U |
+ * n |
+ * i |
+ * t _|
+ * |
+ * |
+ * |
+ * low _|
+ * |
+ * |_minX________________maxX__
+ * | | | | |
+ * minY low high
+ * minY
+ *
+ *
+ * numTicks = Number of ticks
+ * min = Minimum value of axis
+ * max = Maximum value of axis
+ * range = Range of values (max - min)
+ *
+ * If the number of decades is greater than ten, it is assumed
+ * that the full set of log-style ticks can't be drawn properly.
+ *
+ * Results:
+ * None
+ *
+ * ---------------------------------------------------------------------- */
+static void
+LogScaleAxis(axisPtr)
+ Axis *axisPtr;
+{
+ double range;
+ double min, max;
+ double tickMin, tickMax;
+ double stepMajor, stepMinor;
+ int nMajor, nMinor;
+
+ min = (axisPtr->min != 0.0) ? log10(Fabs(axisPtr->min)) : 0.0;
+ max = (axisPtr->max != 0.0) ? log10(Fabs(axisPtr->max)) : 1.0;
+
+ tickMin = floor(min);
+ tickMax = ceil(max);
+ range = tickMax - tickMin;
+
+ if (range > 10) {
+ /* There are too many decades to display a major tick at every
+ * decade. Instead, treat the axis as a linear scale. */
+ range = NiceNum(range, 0);
+ stepMajor = NiceNum(range / DEF_NUM_TICKS, 1);
+ tickMin = UFLOOR(tickMin, stepMajor);
+ tickMax = UCEIL(tickMax, stepMajor);
+ nMajor = (int)((tickMax - tickMin) / stepMajor) + 1;
+ stepMinor = EXP10(floor(log10(stepMajor)));
+ if (stepMinor == stepMajor) {
+ nMinor = 4, stepMinor = 0.2;
+ } else {
+ nMinor = Round(stepMajor / stepMinor) - 1;
+ }
+ } else {
+ if (tickMin == tickMax) {
+ tickMax++;
+ }
+ stepMajor = 1.0;
+ nMajor = (int)((tickMax - tickMin) + 1); /* FIXME: Check this. */
+
+ stepMinor = 0.0; /* This is a special hack to pass
+ * information to the GenerateTicks
+ * routine. An interval of 0.0 tells
+ * 1) this is a minor sweep and
+ * 2) the axis is log scale.
+ */
+ nMinor = 10;
+ }
+ if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+ ((axisPtr->looseMin == TICK_RANGE_LOOSE) &&
+ (axisPtr->flags & AXIS_CONFIG_MIN))) {
+ tickMin = min;
+ nMajor++;
+ }
+ if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+ ((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+ (axisPtr->flags & AXIS_CONFIG_MAX))) {
+ tickMax = max;
+ }
+ SetLimits(axisPtr->tickRange, tickMin, tickMax);
+
+ axisPtr->majorSweep.step = stepMajor;
+ axisPtr->majorSweep.initial = floor(tickMin);
+ axisPtr->majorSweep.nSteps = nMajor;
+ axisPtr->minorSweep.initial = axisPtr->minorSweep.step = stepMinor;
+ axisPtr->minorSweep.nSteps = nMinor;
+
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LinearScaleAxis --
+ *
+ * Determine the units of a linear scaled axis.
+ *
+ * The axis limits are either the range of the data values mapped
+ * to the axis (autoscaled), or the values specified by the -min
+ * and -max options (manual).
+ *
+ * If autoscaled, the smallest and largest major ticks will
+ * encompass the range of data values. If the -loose option is
+ * selected, the next outer ticks are choosen. If tight, the
+ * ticks are at or inside of the data limits are used.
+ *
+ * If manually set, the ticks are at or inside the data limits
+ * are used. This makes sense for zooming. You want the
+ * selected range to represent the next limit, not something a
+ * bit bigger.
+ *
+ * Note: I added an "always" value to the -loose option to force
+ * the manually selected axes to be loose. It's probably
+ * not a good idea.
+ *
+ * maxY
+ * | units = magnitude (of least significant digit)
+ * | high = largest unit tick < max axis value
+ * high _| low = smallest unit tick > min axis value
+ * |
+ * | range = high - low
+ * | # ticks = greatest factor of range/units
+ * _|
+ * U |
+ * n |
+ * i |
+ * t _|
+ * |
+ * |
+ * |
+ * low _|
+ * |
+ * |_minX________________maxX__
+ * | | | | |
+ * minY low high
+ * minY
+ *
+ *
+ * numTicks = Number of ticks
+ * min = Minimum value of axis
+ * max = Maximum value of axis
+ * range = Range of values (max - min)
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The axis tick information is set. The actual tick values will
+ * be generated later.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+LinearScaleAxis(axisPtr)
+ Axis *axisPtr;
+{
+ double range;
+ double min, max;
+ double tickMin, tickMax;
+ double stepMajor, stepMinor;
+ int nMajor, nMinor;
+
+ min = axisPtr->min;
+ max = axisPtr->max;
+ range = max - min;
+
+ /* Calculate the major step. If a step interval was designated,
+ * use it only if it fits within the current range of the axis. */
+ if ((axisPtr->reqStep > 0.0) && (axisPtr->reqStep <= range) &&
+ ((int)(range / axisPtr->reqStep) <= MAXTICKS)) {
+ stepMajor = axisPtr->reqStep;
+ } else {
+ range = NiceNum(range, 0);
+ stepMajor = NiceNum(range / DEF_NUM_TICKS, 1);
+ }
+
+ /* Get the outer tick values. Add 0.0 to prevent getting an IEEE -0.0. */
+
+ tickMin = UFLOOR(min, stepMajor) + 0.0;
+ tickMax = UCEIL(max, stepMajor) + 0.0;
+ nMajor = Round((tickMax - tickMin) / stepMajor) + 1;
+
+ axisPtr->majorSweep.step = stepMajor;
+ axisPtr->majorSweep.initial = tickMin;
+ axisPtr->majorSweep.nSteps = nMajor;
+
+ /*
+ * The limits of the axis are either the range of the data
+ * ("tight"), or at the next outer tick interval ("loose"). The
+ * looseness or tightness has to do with how the axis fits the
+ * range of data values. This option is overridden when
+ * the user sets an axis limit (by either -min or -max option).
+ * The axis limit is always at the selected limit (otherwise we
+ * assume that user would have picked a different number).
+ */
+ if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+ ((axisPtr->looseMin == TICK_RANGE_LOOSE) &&
+ (axisPtr->flags & AXIS_CONFIG_MIN))) {
+ tickMin = min;
+ }
+ if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+ ((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+ (axisPtr->flags & AXIS_CONFIG_MAX))) {
+ tickMax = max;
+ }
+ SetLimits(axisPtr->tickRange, tickMin, tickMax);
+
+ /* Now calculate the minor tick step and number. */
+
+ if ((axisPtr->reqNumMinorTicks > 0) &&
+ ((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0)) {
+ nMinor = axisPtr->reqNumMinorTicks - 1;
+ stepMinor = 1.0 / (nMinor + 1);
+ } else {
+ nMinor = 0; /* No minor ticks. */
+ stepMinor = 0.5; /* Don't set the minor tick interval
+ * to 0.0. It makes the GenerateTicks
+ * routine create minor log-scale tick
+ * marks. */
+ }
+ axisPtr->minorSweep.initial = axisPtr->minorSweep.step = stepMinor;
+ axisPtr->minorSweep.nSteps = nMinor;
+}
+
+
+static void
+SweepTicks(axisPtr)
+ Axis *axisPtr;
+{
+ if ((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0) {
+ if (axisPtr->t1Ptr != NULL) {
+ Blt_Free(axisPtr->t1Ptr);
+ }
+ axisPtr->t1Ptr = GenerateTicks(&(axisPtr->majorSweep));
+ }
+ if ((axisPtr->flags & AXIS_CONFIG_MINOR) == 0) {
+ if (axisPtr->t2Ptr != NULL) {
+ Blt_Free(axisPtr->t2Ptr);
+ }
+ axisPtr->t2Ptr = GenerateTicks(&(axisPtr->minorSweep));
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_ResetAxes --
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_ResetAxes(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+ Axis *axisPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Extents2D exts;
+
+ /* FIXME: This should be called whenever the display list of
+ * elements change. Maybe yet another flag INIT_STACKS to
+ * indicate that the element display list has changed.
+ * Needs to be done before the axis limits are set.
+ */
+ Blt_InitFreqTable(graphPtr);
+ if ((graphPtr->mode == MODE_STACKED) && (graphPtr->nStacks > 0)) {
+ Blt_ComputeStacks(graphPtr);
+ }
+ /*
+ * Step 1: Reset all axes. Initialize the data limits of the axis to
+ * impossible values.
+ */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ axisPtr->flags &= ~AXIS_MAPS_ELEM;
+ axisPtr->dataRange.min = DBL_MAX;
+ axisPtr->dataRange.max = -DBL_MAX;
+ }
+
+ /*
+ * Step 2: For each element that's to be displayed, get the smallest
+ * and largest data values mapped to each X and Y-axis. This
+ * will be the axis limits if the user doesn't override them
+ * with -min and -max options.
+ */
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (!elemPtr->hidden) {
+ (*elemPtr->procsPtr->extentsProc) (elemPtr, &exts);
+ GetDataLimits(elemPtr->axes.x, exts.left, exts.right);
+ GetDataLimits(elemPtr->axes.y, exts.top, exts.bottom);
+ }
+ }
+ /*
+ * Step 3: Now that we know the range of data values for each axis,
+ * set axis limits and compute a sweep to generate tick values.
+ */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ if (axisPtr->hidden == HIDE_ALL) {
+ continue;
+ }
+ FixAxisRange(axisPtr);
+ /* Calculate min/max tick (major/minor) layouts */
+ if (axisPtr->logScale) {
+ LogScaleAxis(axisPtr);
+ } else {
+ LinearScaleAxis(axisPtr);
+ }
+ if (axisPtr->flags & AXIS_CONFIG_DIRTY) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ }
+
+ graphPtr->flags &= ~RESET_AXES;
+
+ /*
+ * When any axis changes, we need to layout the entire graph.
+ */
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED |
+ MAP_ALL | REDRAW_WORLD);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetTextStyles --
+ *
+ * Configures axis attributes (font, line width, label, etc) and
+ * allocates a new (possibly shared) graphics context. Line cap
+ * style is projecting. This is for the problem of when a tick
+ * sits directly at the end point of the axis.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Axis resources are allocated (GC, font). Axis layout is
+ * deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetTextStyles(graphPtr, axisPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ Blt_ResetTextStyle(graphPtr->tkwin, &(axisPtr->titleStyle));
+ Blt_ResetTextStyle(graphPtr->tkwin, &(axisPtr->tickStyle));
+ Blt_ResetTextStyle(graphPtr->tkwin, &(axisPtr->limitsStyle));
+
+ gcMask = (GCForeground | GCLineWidth | GCCapStyle);
+ gcValues.foreground = axisPtr->tickStyle.color->pixel;
+ gcValues.line_width = LineWidth(axisPtr->lineWidth);
+ gcValues.cap_style = CapProjecting;
+
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (axisPtr->tickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+ }
+ axisPtr->tickGC = newGC;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyAxis --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources (font, color, gc, labels, etc.) associated with the
+ * axis are deallocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyAxis(graphPtr, axisPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+{
+ int flags;
+
+ flags = Blt_GraphType(graphPtr);
+ Tk_FreeOptions(configSpecs, (char *)axisPtr, graphPtr->display, flags);
+ if (graphPtr->bindTable != NULL) {
+ Blt_DeleteBindings(graphPtr->bindTable, axisPtr);
+ }
+ if (axisPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(axisPtr->chainPtr, axisPtr->linkPtr);
+ }
+ if (axisPtr->name != NULL) {
+ Blt_Free(axisPtr->name);
+ }
+ if (axisPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(graphPtr->axes.table), axisPtr->hashPtr);
+ }
+ Blt_FreeTextStyle(graphPtr->display, &(axisPtr->titleStyle));
+ Blt_FreeTextStyle(graphPtr->display, &(axisPtr->limitsStyle));
+ Blt_FreeTextStyle(graphPtr->display, &(axisPtr->tickStyle));
+
+ if (axisPtr->tickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+ }
+ if (axisPtr->t1Ptr != NULL) {
+ Blt_Free(axisPtr->t1Ptr);
+ }
+ if (axisPtr->t2Ptr != NULL) {
+ Blt_Free(axisPtr->t2Ptr);
+ }
+ if (axisPtr->limitsFormats != NULL) {
+ Blt_Free(axisPtr->limitsFormats);
+ }
+ FreeLabels(axisPtr->tickLabels);
+ Blt_ChainDestroy(axisPtr->tickLabels);
+ if (axisPtr->segments != NULL) {
+ Blt_Free(axisPtr->segments);
+ }
+ if (axisPtr->tags != NULL) {
+ Blt_Free(axisPtr->tags);
+ }
+ Blt_Free(axisPtr);
+}
+
+static double titleRotate[4] = /* Rotation for each axis title */
+{
+ 0.0, 90.0, 0.0, 270.0
+};
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * AxisOffsets --
+ *
+ * Determines the sites of the axis, major and minor ticks,
+ * and title of the axis.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+AxisOffsets(graphPtr, axisPtr, margin, axisOffset, infoPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int margin;
+ int axisOffset;
+ AxisInfo *infoPtr;
+{
+ int pad; /* Offset of axis from interior region. This
+ * includes a possible border and the axis
+ * line width. */
+ int p;
+ int majorOffset, minorOffset, labelOffset;
+ int isMultiple;
+ int offset;
+ int x, y;
+
+ isMultiple = (graphPtr->margins[margin].nAxes > 1);
+ axisPtr->titleStyle.theta = titleRotate[margin];
+
+ majorOffset = minorOffset = 0;
+ labelOffset = AXIS_TITLE_PAD;
+ if (axisPtr->lineWidth > 0) {
+ majorOffset = ABS(axisPtr->tickLength);
+ minorOffset = 10 * majorOffset / 15;
+ labelOffset = majorOffset + AXIS_TITLE_PAD + axisPtr->lineWidth / 2;
+ }
+ /* Adjust offset for the interior border width and the line width */
+ pad = axisPtr->lineWidth + 1;
+ if (graphPtr->plotBW > 0) {
+ pad += graphPtr->plotBW + 1;
+ }
+ offset = axisOffset + 1 + pad;
+ if ((margin == MARGIN_LEFT) || (margin == MARGIN_TOP)) {
+ majorOffset = -majorOffset;
+ minorOffset = -minorOffset;
+ labelOffset = -labelOffset;
+ }
+ /*
+ * Pre-calculate the x-coordinate positions of the axis, tick labels, and
+ * the individual major and minor ticks.
+ */
+ p = 0; /* Suppress compiler warning */
+
+ switch (margin) {
+ case MARGIN_TOP:
+ p = graphPtr->top - axisOffset - pad;
+ if (isMultiple) {
+ x = graphPtr->right + AXIS_TITLE_PAD;
+ y = graphPtr->top - axisOffset - (axisPtr->height / 2);
+ axisPtr->titleStyle.anchor = TK_ANCHOR_W;
+ } else {
+ x = (graphPtr->right + graphPtr->left) / 2;
+ y = graphPtr->top - axisOffset - axisPtr->height - AXIS_TITLE_PAD;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_N;
+ }
+ axisPtr->tickStyle.anchor = TK_ANCHOR_S;
+ offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+ axisPtr->region.left = graphPtr->hOffset - offset - 2;
+ axisPtr->region.right = graphPtr->hOffset + graphPtr->hRange +
+ offset - 1;
+ axisPtr->region.top = p + labelOffset - 1;
+ axisPtr->region.bottom = p;
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_BOTTOM:
+ p = graphPtr->bottom + axisOffset + pad;
+ if (isMultiple) {
+ x = graphPtr->right + AXIS_TITLE_PAD;
+ y = graphPtr->bottom + axisOffset + (axisPtr->height / 2);
+ axisPtr->titleStyle.anchor = TK_ANCHOR_W;
+ } else {
+ x = (graphPtr->right + graphPtr->left) / 2;
+ y = graphPtr->bottom + axisOffset + axisPtr->height +
+ AXIS_TITLE_PAD;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_S;
+ }
+ axisPtr->tickStyle.anchor = TK_ANCHOR_N;
+ offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+ axisPtr->region.left = graphPtr->hOffset - offset - 2;
+ axisPtr->region.right = graphPtr->hOffset + graphPtr->hRange +
+ offset - 1;
+
+ axisPtr->region.top = graphPtr->bottom + axisOffset +
+ axisPtr->lineWidth - axisPtr->lineWidth / 2;
+ axisPtr->region.bottom = graphPtr->bottom + axisOffset +
+ axisPtr->lineWidth + labelOffset + 1;
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_LEFT:
+ p = graphPtr->left - axisOffset - pad;
+ if (isMultiple) {
+ x = graphPtr->left - axisOffset - (axisPtr->width / 2);
+ y = graphPtr->top - AXIS_TITLE_PAD;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_SW;
+ } else {
+ x = graphPtr->left - axisOffset - axisPtr->width -
+ graphPtr->plotBW;
+ y = (graphPtr->bottom + graphPtr->top) / 2;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_W;
+ }
+ axisPtr->tickStyle.anchor = TK_ANCHOR_E;
+ axisPtr->region.left = graphPtr->left - offset + labelOffset - 1;
+ axisPtr->region.right = graphPtr->left - offset + 2;
+
+ offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+ axisPtr->region.top = graphPtr->vOffset - offset - 2;
+ axisPtr->region.bottom = graphPtr->vOffset + graphPtr->vRange +
+ offset - 1;
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_RIGHT:
+ p = graphPtr->right + axisOffset + pad;
+ if (isMultiple) {
+ x = graphPtr->right + axisOffset + (axisPtr->width / 2);
+ y = graphPtr->top - AXIS_TITLE_PAD;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_SE;
+ } else {
+ x = graphPtr->right + axisOffset + axisPtr->width +
+ AXIS_TITLE_PAD;
+ y = (graphPtr->bottom + graphPtr->top) / 2;
+ axisPtr->titleStyle.anchor = TK_ANCHOR_E;
+ }
+ axisPtr->tickStyle.anchor = TK_ANCHOR_W;
+
+ axisPtr->region.left = graphPtr->right + axisOffset +
+ axisPtr->lineWidth - axisPtr->lineWidth / 2;
+ axisPtr->region.right = graphPtr->right + axisOffset +
+ labelOffset + axisPtr->lineWidth + 1;
+
+ offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+ axisPtr->region.top = graphPtr->vOffset - offset - 2;
+ axisPtr->region.bottom = graphPtr->vOffset + graphPtr->vRange +
+ offset - 1;
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_NONE:
+ break;
+ }
+ infoPtr->axis = p - (axisPtr->lineWidth / 2);
+ infoPtr->t1 = p + majorOffset;
+ infoPtr->t2 = p + minorOffset;
+ infoPtr->label = p + labelOffset;
+
+ if (axisPtr->tickLength < 0) {
+ int hold;
+
+ hold = infoPtr->t1;
+ infoPtr->t1 = infoPtr->axis;
+ infoPtr->axis = hold;
+ }
+}
+
+static void
+MakeAxisLine(graphPtr, axisPtr, line, segPtr)
+ Graph *graphPtr;
+ Axis *axisPtr; /* Axis information */
+ int line;
+ Segment2D *segPtr;
+{
+ double min, max;
+
+ min = axisPtr->tickRange.min;
+ max = axisPtr->tickRange.max;
+ if (axisPtr->logScale) {
+ min = EXP10(min);
+ max = EXP10(max);
+ }
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ segPtr->p.x = HMap(graphPtr, axisPtr, min);
+ segPtr->q.x = HMap(graphPtr, axisPtr, max);
+ segPtr->p.y = segPtr->q.y = line;
+ } else {
+ segPtr->q.x = segPtr->p.x = line;
+ segPtr->p.y = VMap(graphPtr, axisPtr, min);
+ segPtr->q.y = VMap(graphPtr, axisPtr, max);
+ }
+}
+
+
+static void
+MakeTick(graphPtr, axisPtr, value, tick, line, segPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double value;
+ int tick, line; /* Lengths of tick and axis line. */
+ Segment2D *segPtr;
+{
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ segPtr->p.x = segPtr->q.x = HMap(graphPtr, axisPtr, value);
+ segPtr->p.y = line;
+ segPtr->q.y = tick;
+ } else {
+ segPtr->p.x = line;
+ segPtr->p.y = segPtr->q.y = VMap(graphPtr, axisPtr, value);
+ segPtr->q.x = tick;
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * MapAxis --
+ *
+ * Pre-calculates positions of the axis, ticks, and labels (to be
+ * used later when displaying the axis). Calculates the values
+ * for each major and minor tick and checks to see if they are in
+ * range (the outer ticks may be outside of the range of plotted
+ * values).
+ *
+ * Line segments for the minor and major ticks are saved into one
+ * XSegment array so that they can be drawn by a single
+ * XDrawSegments call. The positions of the tick labels are also
+ * computed and saved.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Line segments and tick labels are saved and used later to draw
+ * the axis.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+MapAxis(graphPtr, axisPtr, offset, margin)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int offset;
+ int margin;
+{
+ int arraySize;
+ int nMajorTicks, nMinorTicks;
+ AxisInfo info;
+ Segment2D *segments;
+ Segment2D *segPtr;
+
+ AxisOffsets(graphPtr, axisPtr, margin, offset, &info);
+
+ /* Save all line coordinates in an array of line segments. */
+
+ if (axisPtr->segments != NULL) {
+ Blt_Free(axisPtr->segments);
+ }
+ nMajorTicks = nMinorTicks = 0;
+ if (axisPtr->t1Ptr != NULL) {
+ nMajorTicks = axisPtr->t1Ptr->nTicks;
+ }
+ if (axisPtr->t2Ptr != NULL) {
+ nMinorTicks = axisPtr->t2Ptr->nTicks;
+ }
+ arraySize = 1 + (nMajorTicks * (nMinorTicks + 1));
+ segments = Blt_Malloc(arraySize * sizeof(Segment2D));
+ assert(segments);
+
+ segPtr = segments;
+ if (axisPtr->lineWidth > 0) {
+ /* Axis baseline */
+ MakeAxisLine(graphPtr, axisPtr, info.axis, segPtr);
+ segPtr++;
+ }
+ if (axisPtr->showTicks) {
+ double t1, t2;
+ double labelPos;
+ register int i, j;
+ int isHoriz;
+ TickLabel *labelPtr;
+ Blt_ChainLink *linkPtr;
+
+ isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+ linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels);
+ for (i = 0; i < axisPtr->t1Ptr->nTicks; i++) {
+ t1 = axisPtr->t1Ptr->tickArr[i];
+ /* Minor ticks */
+ for (j = 0; j < axisPtr->t2Ptr->nTicks; j++) {
+ t2 = t1 +
+ (axisPtr->majorSweep.step * axisPtr->t2Ptr->tickArr[j]);
+ if (InRange(t2, &(axisPtr->tickRange))) {
+ MakeTick(graphPtr, axisPtr, t2, info.t2, info.axis,
+ segPtr);
+ segPtr++;
+ }
+ }
+ if (!InRange(t1, &(axisPtr->tickRange))) {
+ continue;
+ }
+ /* Major tick and label position */
+ MakeTick(graphPtr, axisPtr, t1, info.t1, info.axis, segPtr);
+ labelPos = (double)info.label;
+
+ labelPtr = Blt_ChainGetValue(linkPtr);
+ linkPtr = Blt_ChainNextLink(linkPtr);
+
+ /* Save tick label X-Y position. */
+ if (isHoriz) {
+ labelPtr->anchorPos.x = segPtr->p.x;
+ labelPtr->anchorPos.y = labelPos;
+ } else {
+ labelPtr->anchorPos.x = labelPos;
+ labelPtr->anchorPos.y = segPtr->p.y;
+ }
+ segPtr++;
+ }
+ }
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ axisPtr->width = graphPtr->right - graphPtr->left;
+ } else {
+ axisPtr->height = graphPtr->bottom - graphPtr->top;
+ }
+ axisPtr->segments = segments;
+ axisPtr->nSegments = segPtr - segments;
+ assert(axisPtr->nSegments <= arraySize);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustViewport --
+ *
+ * Adjusts the offsets of the viewport according to the scroll mode.
+ * This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ * "canvas" The viewport scrolls within the range of world
+ * coordinates. This way the viewport always displays
+ * a full page of the world. If the world is smaller
+ * than the viewport, then (bizarrely) the world and
+ * viewport are inverted so that the world moves up
+ * and down within the viewport.
+ *
+ * "listbox" The viewport can scroll beyond the range of world
+ * coordinates. Every entry can be displayed at the
+ * top of the viewport. This also means that the
+ * scrollbar thumb weirdly shrinks as the last entry
+ * is scrolled upward.
+ *
+ * Results:
+ * The corrected offset is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+AdjustViewport(offset, windowSize)
+ double offset, windowSize;
+{
+ /*
+ * Canvas-style scrolling allows the world to be scrolled
+ * within the window.
+ */
+ if (windowSize > 1.0) {
+ if (windowSize < (1.0 - offset)) {
+ offset = 1.0 - windowSize;
+ }
+ if (offset > 0.0) {
+ offset = 0.0;
+ }
+ } else {
+ if ((offset + windowSize) > 1.0) {
+ offset = 1.0 - windowSize;
+ }
+ if (offset < 0.0) {
+ offset = 0.0;
+ }
+ }
+ return offset;
+}
+
+static int
+GetAxisScrollInfo(interp, argc, argv, offsetPtr, windowSize, scrollUnits)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ double *offsetPtr;
+ double windowSize;
+ double scrollUnits;
+{
+ char c;
+ unsigned int length;
+ double offset;
+ int count;
+ double fract;
+
+ offset = *offsetPtr;
+ c = argv[0][0];
+ length = strlen(argv[0]);
+ if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
+ assert(argc == 3);
+ /* scroll number unit/page */
+ if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
+ fract = (double)count * scrollUnits;
+ } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count * windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += fract;
+ } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
+ assert(argc == 2);
+ /* moveto fraction */
+ if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = fract;
+ } else {
+ /* Treat like "scroll units" */
+ if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count * scrollUnits;
+ offset += fract;
+ return TCL_OK;
+ }
+ *offsetPtr = AdjustViewport(offset, windowSize);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawAxis --
+ *
+ * Draws the axis, ticks, and labels onto the canvas.
+ *
+ * Initializes and passes text attribute information through
+ * TextStyle structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Axis gets drawn on window.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawAxis(graphPtr, drawable, axisPtr)
+ Graph *graphPtr;
+ Drawable drawable;
+ Axis *axisPtr;
+{
+ if (axisPtr->border != NULL) {
+ Tk_Fill3DRectangle(graphPtr->tkwin, drawable, axisPtr->border,
+ axisPtr->region.left + graphPtr->plotBW,
+ axisPtr->region.top + graphPtr->plotBW,
+ axisPtr->region.right - axisPtr->region.left,
+ axisPtr->region.bottom - axisPtr->region.top,
+ axisPtr->borderWidth, axisPtr->relief);
+ }
+ if (axisPtr->titleText != NULL) {
+ Blt_DrawText(graphPtr->tkwin, drawable, axisPtr->titleText,
+ &(axisPtr->titleStyle), (int)axisPtr->titlePos.x,
+ (int)axisPtr->titlePos.y);
+ }
+ if (axisPtr->scrollCmdPrefix != NULL) {
+ double viewWidth, viewMin, viewMax;
+ double worldWidth, worldMin, worldMax;
+ double fract;
+ int isHoriz;
+
+ worldMin = axisPtr->dataRange.min;
+ worldMax = axisPtr->dataRange.max;
+ viewMin = axisPtr->min;
+ viewMax = axisPtr->max;
+ if (axisPtr->logScale) {
+ worldMin = log10(worldMin);
+ worldMax = log10(worldMax);
+ viewMin = log10(viewMin);
+ viewMax = log10(viewMax);
+ }
+ worldWidth = worldMax - worldMin;
+ viewWidth = viewMax - viewMin;
+ isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+
+ if (isHoriz != axisPtr->descending) {
+ fract = (viewMin - worldMin) / worldWidth;
+ } else {
+ fract = (worldMax - viewMax) / worldWidth;
+ }
+ fract = AdjustViewport(fract, viewWidth / worldWidth);
+
+ if (isHoriz != axisPtr->descending) {
+ viewMin = (fract * worldWidth);
+ axisPtr->min = viewMin + worldMin;
+ axisPtr->max = axisPtr->min + viewWidth;
+ viewMax = viewMin + viewWidth;
+ if (axisPtr->logScale) {
+ axisPtr->min = EXP10(axisPtr->min);
+ axisPtr->max = EXP10(axisPtr->max);
+ }
+ Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdPrefix,
+ (viewMin / worldWidth), (viewMax / worldWidth));
+ } else {
+ viewMax = (fract * worldWidth);
+ axisPtr->max = worldMax - viewMax;
+ axisPtr->min = axisPtr->max - viewWidth;
+ viewMin = viewMax + viewWidth;
+ if (axisPtr->logScale) {
+ axisPtr->min = EXP10(axisPtr->min);
+ axisPtr->max = EXP10(axisPtr->max);
+ }
+ Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdPrefix,
+ (viewMax / worldWidth), (viewMin / worldWidth));
+ }
+ }
+ if (axisPtr->showTicks) {
+ register Blt_ChainLink *linkPtr;
+ TickLabel *labelPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ /* Draw major tick labels */
+ labelPtr = Blt_ChainGetValue(linkPtr);
+ Blt_DrawText(graphPtr->tkwin, drawable, labelPtr->string,
+ &(axisPtr->tickStyle), (int)labelPtr->anchorPos.x,
+ (int)labelPtr->anchorPos.y);
+ }
+ }
+ if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {
+ /* Draw the tick marks and axis line. */
+ Blt_DrawSegments2D(graphPtr->display, drawable, axisPtr->tickGC,
+ axisPtr->segments, axisPtr->nSegments);
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * AxisToPostScript --
+ *
+ * Generates PostScript output to draw the axis, ticks, and
+ * labels.
+ *
+ * Initializes and passes text attribute information through
+ * TextStyle structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * PostScript output is left in graphPtr->interp->result;
+ *
+ * -----------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+AxisToPostScript(psToken, axisPtr)
+ PsToken psToken;
+ Axis *axisPtr;
+{
+ if (axisPtr->titleText != NULL) {
+ Blt_TextToPostScript(psToken, axisPtr->titleText,
+ &(axisPtr->titleStyle), axisPtr->titlePos.x, axisPtr->titlePos.y);
+ }
+ if (axisPtr->showTicks) {
+ register Blt_ChainLink *linkPtr;
+ TickLabel *labelPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ labelPtr = Blt_ChainGetValue(linkPtr);
+ Blt_TextToPostScript(psToken, labelPtr->string,
+ &(axisPtr->tickStyle), labelPtr->anchorPos.x,
+ labelPtr->anchorPos.y);
+ }
+ }
+ if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {
+ Blt_LineAttributesToPostScript(psToken, axisPtr->tickStyle.color,
+ axisPtr->lineWidth, (Blt_Dashes *)NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, axisPtr->segments,
+ axisPtr->nSegments);
+ }
+}
+
+static void
+MakeGridLine(graphPtr, axisPtr, value, segPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double value;
+ Segment2D *segPtr;
+{
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ /* Grid lines run orthogonally to the axis */
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ segPtr->p.y = graphPtr->top;
+ segPtr->q.y = graphPtr->bottom;
+ segPtr->p.x = segPtr->q.x = HMap(graphPtr, axisPtr, value);
+ } else {
+ segPtr->p.x = graphPtr->left;
+ segPtr->q.x = graphPtr->right;
+ segPtr->p.y = segPtr->q.y = VMap(graphPtr, axisPtr, value);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetAxisSegments --
+ *
+ * Assembles the grid lines associated with an axis. Generates
+ * tick positions if necessary (this happens when the axis is
+ * not a logical axis too).
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GetAxisSegments(graphPtr, axisPtr, segPtrPtr, nSegmentsPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ Segment2D **segPtrPtr;
+ int *nSegmentsPtr;
+{
+ int needed;
+ Ticks *t1Ptr, *t2Ptr;
+ register int i;
+ double value;
+ Segment2D *segments, *segPtr;
+
+ *nSegmentsPtr = 0;
+ *segPtrPtr = NULL;
+ if (axisPtr == NULL) {
+ return;
+ }
+ t1Ptr = axisPtr->t1Ptr;
+ if (t1Ptr == NULL) {
+ t1Ptr = GenerateTicks(&(axisPtr->majorSweep));
+ }
+ t2Ptr = axisPtr->t2Ptr;
+ if (t2Ptr == NULL) {
+ t2Ptr = GenerateTicks(&(axisPtr->minorSweep));
+ }
+
+ needed = t1Ptr->nTicks;
+ if (graphPtr->gridPtr->minorGrid) {
+ needed += (t1Ptr->nTicks * t2Ptr->nTicks);
+ }
+ if (needed == 0) {
+ return;
+ }
+ segments = Blt_Malloc(sizeof(Segment2D) * needed);
+ if (segments == NULL) {
+ return; /* Can't allocate memory for grid. */
+ }
+
+ segPtr = segments;
+ for (i = 0; i < t1Ptr->nTicks; i++) {
+ value = t1Ptr->tickArr[i];
+ if (graphPtr->gridPtr->minorGrid) {
+ register int j;
+ double subValue;
+
+ for (j = 0; j < t2Ptr->nTicks; j++) {
+ subValue = value +
+ (axisPtr->majorSweep.step * t2Ptr->tickArr[j]);
+ if (InRange(subValue, &(axisPtr->tickRange))) {
+ MakeGridLine(graphPtr, axisPtr, subValue, segPtr);
+ segPtr++;
+ }
+ }
+ }
+ if (InRange(value, &(axisPtr->tickRange))) {
+ MakeGridLine(graphPtr, axisPtr, value, segPtr);
+ segPtr++;
+ }
+ }
+
+ if (t1Ptr != axisPtr->t1Ptr) {
+ Blt_Free(t1Ptr); /* Free generated ticks. */
+ }
+ if (t2Ptr != axisPtr->t2Ptr) {
+ Blt_Free(t2Ptr); /* Free generated ticks. */
+ }
+ *nSegmentsPtr = segPtr - segments;
+ assert(*nSegmentsPtr <= needed);
+ *segPtrPtr = segments;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetAxisGeometry --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetAxisGeometry(graphPtr, axisPtr, isMultiple)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int isMultiple;
+{
+ int height;
+
+ FreeLabels(axisPtr->tickLabels);
+ height = 0;
+ if (axisPtr->lineWidth > 0) {
+ /* Leave room for axis baseline (and pad) */
+ height += axisPtr->lineWidth + 2;
+ }
+ if (axisPtr->showTicks) {
+ int pad;
+ register int i, nLabels;
+ int labelWidth, labelHeight;
+ double x;
+ int maxWidth, maxHeight;
+ TickLabel *labelPtr;
+
+ SweepTicks(axisPtr);
+
+ /* Hey Sani, does this check fail under AIX? */
+ assert((axisPtr->t1Ptr->nTicks >= 0) &&
+ (axisPtr->t1Ptr->nTicks <= MAXTICKS));
+
+ maxHeight = maxWidth = 0;
+ nLabels = 0;
+ for (i = 0; i < axisPtr->t1Ptr->nTicks; i++) {
+ x = axisPtr->t1Ptr->tickArr[i];
+ if (!InRange(x, &(axisPtr->tickRange))) {
+ continue;
+ }
+ labelPtr = MakeLabel(graphPtr, axisPtr, x);
+ Blt_ChainAppend(axisPtr->tickLabels, labelPtr);
+ nLabels++;
+
+ /* Get dimensions of each tick label. Remember tick labels
+ * can be multi-lined and/or rotated. */
+ Blt_GetTextExtents(&(axisPtr->tickStyle), labelPtr->string,
+ &labelWidth, &labelHeight);
+ labelPtr->width = labelWidth;
+ labelPtr->height = labelHeight;
+ if (axisPtr->tickStyle.theta > 0.0) {
+ Blt_GetBoundingBox(labelWidth, labelHeight,
+ axisPtr->tickStyle.theta, &labelWidth, &labelHeight,
+ (Point2D *)NULL);
+ }
+ if (maxWidth < labelWidth) {
+ maxWidth = labelWidth;
+ }
+ if (maxHeight < labelHeight) {
+ maxHeight = labelHeight;
+ }
+ }
+ assert(nLabels <= axisPtr->t1Ptr->nTicks);
+
+ /* Because the axis cap style is "CapProjecting", there's
+ * an extra 1.5 linewidth to be accounted for at the ends
+ * of each line. */
+
+ pad = ((axisPtr->lineWidth * 15) / 10);
+
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ height += maxHeight + pad;
+ } else {
+ height += maxWidth + pad;
+ }
+ if (axisPtr->lineWidth > 0) {
+ /* Distance from axis line to tick label. */
+ height += AXIS_TITLE_PAD;
+ height += ABS(axisPtr->tickLength);
+ }
+ }
+
+ if (axisPtr->titleText != NULL) {
+ if (isMultiple) {
+ if (height < axisPtr->titleHeight) {
+ height = axisPtr->titleHeight;
+ }
+ } else {
+ height += axisPtr->titleHeight + AXIS_TITLE_PAD;
+ }
+ }
+
+ /* Correct for orientation of the axis. */
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ axisPtr->height = height;
+ } else {
+ axisPtr->width = height;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMarginGeometry --
+ *
+ * Examines all the axes in the given margin and determines the
+ * area required to display them.
+ *
+ * Note: For multiple axes, the titles are displayed in another
+ * margin. So we must keep track of the widest title.
+ *
+ * Results:
+ * Returns the width or height of the margin, depending if it
+ * runs horizontally along the graph or vertically.
+ *
+ * Side Effects:
+ * The area width and height set in the margin. Note again that
+ * this may be corrected later (mulitple axes) to adjust for
+ * the longest title in another margin.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetMarginGeometry(graphPtr, marginPtr)
+ Graph *graphPtr;
+ Margin *marginPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Axis *axisPtr;
+ int width, height;
+ int isHoriz;
+ int length, count;
+
+ /* Count the number of visible axes. */
+ count = 0;
+ isHoriz = HORIZMARGIN(marginPtr);
+ for (linkPtr = Blt_ChainFirstLink(marginPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ if (!axisPtr->hidden) {
+ count++;
+ }
+ }
+ length = width = height = 0;
+ for (linkPtr = Blt_ChainFirstLink(marginPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ if (axisPtr->hidden) {
+ continue;
+ }
+ if (graphPtr->flags & GET_AXIS_GEOMETRY) {
+ GetAxisGeometry(graphPtr, axisPtr, (count > 1));
+ }
+ if (length < axisPtr->titleWidth) {
+ length = axisPtr->titleWidth;
+ }
+ if (isHoriz) {
+ height += axisPtr->height;
+ } else {
+ width += axisPtr->width;
+ }
+ }
+ /* Enforce a minimum size for margins. */
+ if (width < 3) {
+ width = 3;
+ }
+ if (height < 3) {
+ height = 3;
+ }
+ marginPtr->nAxes = count;
+ marginPtr->axesTitleLength = length;
+ marginPtr->width = width;
+ marginPtr->height = height;
+ marginPtr->axesOffset = (HORIZMARGIN(marginPtr)) ? height : width;
+ return marginPtr->axesOffset;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeMargins --
+ *
+ * Computes the size of the margins and the plotting area. We
+ * first compute the space needed for the axes in each margin.
+ * Then how much space the legend will occupy. Finally, if the
+ * user has requested a margin size, we override the computed
+ * value.
+ *
+ * Results:
+ *
+ *---------------------------------------------------------------------- */
+static void
+ComputeMargins(graphPtr)
+ Graph *graphPtr;
+{
+ int left, right, top, bottom;
+ int width, height;
+ int insets;
+
+ /*
+ * Step 1: Compute the amount of space needed to display the
+ * axes (there many be 0 or more) associated with the
+ * margin.
+ */
+ top = GetMarginGeometry(graphPtr, &(graphPtr->topMargin));
+ bottom = GetMarginGeometry(graphPtr, &(graphPtr->bottomMargin));
+ left = GetMarginGeometry(graphPtr, &(graphPtr->leftMargin));
+ right = GetMarginGeometry(graphPtr, &(graphPtr->rightMargin));
+
+ /*
+ * Step 2: Add the graph title height to the top margin.
+ */
+ if (graphPtr->titleText != NULL) {
+ top += graphPtr->titleStyle.height;
+ }
+ insets = 2 * (graphPtr->inset + graphPtr->plotBW);
+
+ /*
+ * Step 3: Use the current estimate of the plot area to compute
+ * the legend size. Add it to the proper margin.
+ */
+ width = graphPtr->width - (insets + left + right);
+ height = graphPtr->height - (insets + top + bottom);
+ Blt_MapLegend(graphPtr->legend, width, height);
+ if (!Blt_LegendIsHidden(graphPtr->legend)) {
+ switch (Blt_LegendSite(graphPtr->legend)) {
+ case LEGEND_RIGHT:
+ right += Blt_LegendWidth(graphPtr->legend) + 2;
+ break;
+ case LEGEND_LEFT:
+ left += Blt_LegendWidth(graphPtr->legend) + 2;
+ break;
+ case LEGEND_TOP:
+ top += Blt_LegendHeight(graphPtr->legend) + 2;
+ break;
+ case LEGEND_BOTTOM:
+ bottom += Blt_LegendHeight(graphPtr->legend) + 2;
+ break;
+ case LEGEND_XY:
+ case LEGEND_PLOT:
+ case LEGEND_WINDOW:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ /*
+ * Recompute the plotarea, now accounting for the legend.
+ */
+ width = graphPtr->width - (insets + left + right);
+ height = graphPtr->height - (insets + top + bottom);
+
+ /*
+ * Step 5: If necessary, correct for the requested plot area
+ * aspect ratio.
+ */
+ if (graphPtr->aspect > 0.0) {
+ double ratio;
+
+ /*
+ * Shrink one dimension of the plotarea to fit the requested
+ * width/height aspect ratio.
+ */
+ ratio = (double)width / (double)height;
+ if (ratio > graphPtr->aspect) {
+ int scaledWidth;
+
+ /* Shrink the width. */
+ scaledWidth = (int)(height * graphPtr->aspect);
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ right += (width - scaledWidth); /* Add the difference to
+ * the right margin. */
+ width = scaledWidth;
+ } else {
+ int scaledHeight;
+
+ /* Shrink the height. */
+ scaledHeight = (int)(width / graphPtr->aspect);
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ top += (height - scaledHeight); /* Add the difference to
+ * the top margin. */
+ height = scaledHeight;
+ }
+ }
+
+ /*
+ * Step 6: If there's multiple axes in a margin, the axis
+ * titles will be displayed in the adjoining marging.
+ * Make sure there's room for the longest axis titles.
+ */
+
+ if ((graphPtr->leftMargin.nAxes > 1) &&
+ (top < graphPtr->leftMargin.axesTitleLength)) {
+ top = graphPtr->leftMargin.axesTitleLength;
+ }
+ if ((graphPtr->bottomMargin.nAxes > 1) &&
+ (right < graphPtr->bottomMargin.axesTitleLength)) {
+ right = graphPtr->bottomMargin.axesTitleLength;
+ }
+ if ((graphPtr->rightMargin.nAxes > 1) &&
+ (top < graphPtr->rightMargin.axesTitleLength)) {
+ top = graphPtr->rightMargin.axesTitleLength;
+ }
+ if ((graphPtr->topMargin.nAxes > 1) &&
+ (right < graphPtr->topMargin.axesTitleLength)) {
+ right = graphPtr->topMargin.axesTitleLength;
+ }
+
+ /*
+ * Step 7: Override calculated values with requested margin
+ * sizes.
+ */
+
+ graphPtr->leftMargin.width = left;
+ graphPtr->rightMargin.width = right;
+ graphPtr->topMargin.height = top;
+ graphPtr->bottomMargin.height = bottom;
+
+ if (graphPtr->leftMargin.reqSize > 0) {
+ graphPtr->leftMargin.width = graphPtr->leftMargin.reqSize;
+ }
+ if (graphPtr->rightMargin.reqSize > 0) {
+ graphPtr->rightMargin.width = graphPtr->rightMargin.reqSize;
+ }
+ if (graphPtr->topMargin.reqSize > 0) {
+ graphPtr->topMargin.height = graphPtr->topMargin.reqSize;
+ }
+ if (graphPtr->bottomMargin.reqSize > 0) {
+ graphPtr->bottomMargin.height = graphPtr->bottomMargin.reqSize;
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LayoutMargins --
+ *
+ * Calculate the layout of the graph. Based upon the data,
+ * axis limits, X and Y titles, and title height, determine
+ * the cavity left which is the plotting surface. The first
+ * step get the data and axis limits for calculating the space
+ * needed for the top, bottom, left, and right margins.
+ *
+ * 1) The LEFT margin is the area from the left border to the
+ * Y axis (not including ticks). It composes the border
+ * width, the width an optional Y axis label and its padding,
+ * and the tick numeric labels. The Y axis label is rotated
+ * 90 degrees so that the width is the font height.
+ *
+ * 2) The RIGHT margin is the area from the end of the graph
+ * to the right window border. It composes the border width,
+ * some padding, the font height (this may be dubious. It
+ * appears to provide a more even border), the max of the
+ * legend width and 1/2 max X tick number. This last part is
+ * so that the last tick label is not clipped.
+ *
+ * Window Width
+ * ___________________________________________________________
+ * | | | |
+ * | | TOP height of title | |
+ * | | | |
+ * | | x2 title | |
+ * | | | |
+ * | | height of x2-axis | |
+ * |__________|_______________________________|_______________| W
+ * | | -plotpady | | i
+ * |__________|_______________________________|_______________| n
+ * | | top right | | d
+ * | | | | o
+ * | LEFT | | RIGHT | w
+ * | | | |
+ * | y | Free area = 104% | y2 | H
+ * | | Plotting surface = 100% | | e
+ * | t | Tick length = 2 + 2% | t | i
+ * | i | | i | g
+ * | t | | t legend| h
+ * | l | | l width| t
+ * | e | | e |
+ * | height| |height |
+ * | of | | of |
+ * | y-axis| |y2-axis |
+ * | | | |
+ * | |origin 0,0 | |
+ * |__________|_left___________________bottom___|_______________|
+ * | |-plotpady | |
+ * |__________|_______________________________|_______________|
+ * | | (xoffset, yoffset) | |
+ * | | | |
+ * | | height of x-axis | |
+ * | | | |
+ * | | BOTTOM x title | |
+ * |__________|_______________________________|_______________|
+ *
+ * 3) The TOP margin is the area from the top window border to the top
+ * of the graph. It composes the border width, twice the height of
+ * the title font (if one is given) and some padding between the
+ * title.
+ *
+ * 4) The BOTTOM margin is area from the bottom window border to the
+ * X axis (not including ticks). It composes the border width, the height
+ * an optional X axis label and its padding, the height of the font
+ * of the tick labels.
+ *
+ * The plotting area is between the margins which includes the X and Y axes
+ * including the ticks but not the tick numeric labels. The length of
+ * the ticks and its padding is 5% of the entire plotting area. Hence the
+ * entire plotting area is scaled as 105% of the width and height of the
+ * area.
+ *
+ * The axis labels, ticks labels, title, and legend may or may not be
+ * displayed which must be taken into account.
+ *
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LayoutMargins(graphPtr)
+ Graph *graphPtr;
+{
+ int width, height;
+ int titleY;
+ int left, right, top, bottom;
+
+ ComputeMargins(graphPtr);
+ left = graphPtr->leftMargin.width + graphPtr->inset + graphPtr->plotBW;
+ right = graphPtr->rightMargin.width + graphPtr->inset + graphPtr->plotBW;
+ top = graphPtr->topMargin.height + graphPtr->inset + graphPtr->plotBW;
+ bottom = graphPtr->bottomMargin.height + graphPtr->inset + graphPtr->plotBW;
+
+ /* Based upon the margins, calculate the space left for the graph. */
+ width = graphPtr->width - (left + right);
+ height = graphPtr->height - (top + bottom);
+ if (width < 1) {
+ width = 1;
+ }
+ if (height < 1) {
+ height = 1;
+ }
+ graphPtr->left = left;
+ graphPtr->right = left + width;
+ graphPtr->bottom = top + height;
+ graphPtr->top = top;
+
+ graphPtr->vOffset = top + graphPtr->padTop;
+ graphPtr->vRange = height - PADDING(graphPtr->padY);
+ graphPtr->hOffset = left + graphPtr->padLeft;
+ graphPtr->hRange = width - PADDING(graphPtr->padX);
+ if (graphPtr->vRange < 1) {
+ graphPtr->vRange = 1;
+ }
+ if (graphPtr->hRange < 1) {
+ graphPtr->hRange = 1;
+ }
+
+ /*
+ * Calculate the placement of the graph title so it is centered within the
+ * space provided for it in the top margin
+ */
+ titleY = graphPtr->titleStyle.height;
+ graphPtr->titleY = (titleY / 2) + graphPtr->inset;
+ graphPtr->titleX = (graphPtr->right + graphPtr->left) / 2;
+
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureAxis --
+ *
+ * Configures axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Axis layout is deferred until the height and width of the
+ * window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static int
+ConfigureAxis(graphPtr, axisPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+{
+ char errMsg[200];
+
+ /* Check the requested axis limits. Can't allow -min to be greater
+ * than -max, or have undefined log scale limits. */
+ if (((axisPtr->flags & AXIS_CONFIG_BOTH) == AXIS_CONFIG_BOTH) &&
+ (axisPtr->min >= axisPtr->max)) {
+ sprintf(errMsg, "impossible limits (min %g >= max %g) for axis \"%s\"",
+ axisPtr->min, axisPtr->max, axisPtr->name);
+ Tcl_AppendResult(graphPtr->interp, errMsg, (char *)NULL);
+ /* Bad values, turn on axis auto-scaling */
+ axisPtr->flags &= ~AXIS_CONFIG_BOTH;
+ return TCL_ERROR;
+ }
+ if ((axisPtr->logScale) && (axisPtr->flags & AXIS_CONFIG_MIN) &&
+ (axisPtr->min <= 0.0)) {
+ sprintf(errMsg, "bad logscale limits (min=%g,max=%g) for axis \"%s\"",
+ axisPtr->min, axisPtr->max, axisPtr->name);
+ Tcl_AppendResult(graphPtr->interp, errMsg, (char *)NULL);
+ /* Bad minimum value, turn on auto-scaling */
+ axisPtr->flags &= ~AXIS_CONFIG_MIN;
+ return TCL_ERROR;
+ }
+ axisPtr->tickStyle.theta = FMOD(axisPtr->tickStyle.theta, 360.0);
+ if (axisPtr->tickStyle.theta < 0.0) {
+ axisPtr->tickStyle.theta += 360.0;
+ }
+ ResetTextStyles(graphPtr, axisPtr);
+
+ axisPtr->titleWidth = axisPtr->titleHeight = 0;
+ if (axisPtr->titleText != NULL) {
+ int w, h;
+
+ Blt_GetTextExtents(&(axisPtr->titleStyle), axisPtr->titleText, &w, &h);
+ axisPtr->titleWidth = (short int)w;
+ axisPtr->titleHeight = (short int)h;
+ }
+
+ /*
+ * Don't bother to check what configuration options have changed.
+ * Almost every option changes the size of the plotting area
+ * (except for -color and -titlecolor), requiring the graph and
+ * its contents to be completely redrawn.
+ *
+ * Recompute the scale and offset of the axis in case -min, -max
+ * options have changed.
+ */
+ graphPtr->flags |= REDRAW_WORLD;
+ if (!Blt_ConfigModified(configSpecs, "-*color", "-background", "-bg",
+ (char *)NULL)) {
+ graphPtr->flags |= (MAP_WORLD | RESET_AXES);
+ axisPtr->flags |= AXIS_CONFIG_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateAxis --
+ *
+ * Create and initialize a structure containing information to
+ * display a graph axis.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Axis *
+CreateAxis(graphPtr, name, margin)
+ Graph *graphPtr;
+ char *name; /* Identifier for axis. */
+ int margin;
+{
+ Axis *axisPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (name[0] == '-') {
+ Tcl_AppendResult(graphPtr->interp, "name of axis \"", name,
+ "\" can't start with a '-'", (char *)NULL);
+ return NULL;
+ }
+ hPtr = Blt_CreateHashEntry(&(graphPtr->axes.table), name, &isNew);
+ if (!isNew) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ if (!axisPtr->deletePending) {
+ Tcl_AppendResult(graphPtr->interp, "axis \"", name,
+ "\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ axisPtr->deletePending = FALSE;
+ } else {
+ axisPtr = Blt_Calloc(1, sizeof(Axis));
+ assert(axisPtr);
+
+ axisPtr->name = Blt_Strdup(name);
+ axisPtr->hashPtr = hPtr;
+ axisPtr->classUid = NULL;
+ axisPtr->looseMin = axisPtr->looseMax = TICK_RANGE_TIGHT;
+ axisPtr->reqNumMinorTicks = 2;
+ axisPtr->scrollUnits = 10;
+ axisPtr->showTicks = TRUE;
+
+ if ((graphPtr->classUid == bltBarElementUid) &&
+ ((margin == MARGIN_TOP) || (margin == MARGIN_BOTTOM))) {
+ axisPtr->reqStep = 1.0;
+ axisPtr->reqNumMinorTicks = 0;
+ }
+ if ((margin == MARGIN_RIGHT) || (margin == MARGIN_TOP)) {
+ axisPtr->hidden = TRUE;
+ }
+ Blt_InitTextStyle(&(axisPtr->titleStyle));
+ Blt_InitTextStyle(&(axisPtr->limitsStyle));
+ Blt_InitTextStyle(&(axisPtr->tickStyle));
+ axisPtr->tickLabels = Blt_ChainCreate();
+ axisPtr->lineWidth = 1;
+ axisPtr->tickStyle.padX.side1 = axisPtr->tickStyle.padX.side2 = 2;
+ Blt_SetHashValue(hPtr, axisPtr);
+ }
+ return axisPtr;
+}
+
+static int
+NameToAxis(graphPtr, name, axisPtrPtr)
+ Graph *graphPtr; /* Graph widget record. */
+ char *name; /* Name of the axis to be searched for. */
+ Axis **axisPtrPtr; /* (out) Pointer to found axis structure. */
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->axes.table), name);
+ if (hPtr != NULL) {
+ Axis *axisPtr;
+
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ if (!axisPtr->deletePending) {
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(graphPtr->interp, "can't find axis \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ *axisPtrPtr = NULL;
+ return TCL_ERROR;
+}
+
+static int
+GetAxis(graphPtr, axisName, classUid, axisPtrPtr)
+ Graph *graphPtr;
+ char *axisName;
+ Tk_Uid classUid;
+ Axis **axisPtrPtr;
+{
+ Axis *axisPtr;
+
+ if (NameToAxis(graphPtr, axisName, &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (classUid != NULL) {
+ if ((axisPtr->refCount == 0) || (axisPtr->classUid == NULL)) {
+ /* Set the axis type on the first use of it. */
+ axisPtr->classUid = classUid;
+ } else if (axisPtr->classUid != classUid) {
+ Tcl_AppendResult(graphPtr->interp, "axis \"", axisName,
+ "\" is already in use on an opposite ", axisPtr->classUid,
+ "-axis", (char *)NULL);
+ return TCL_ERROR;
+ }
+ axisPtr->refCount++;
+ }
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+}
+
+static void
+FreeAxis(graphPtr, axisPtr)
+ Graph *graphPtr;
+ Axis *axisPtr;
+{
+ axisPtr->refCount--;
+ if ((axisPtr->deletePending) && (axisPtr->refCount == 0)) {
+ DestroyAxis(graphPtr, axisPtr);
+ }
+}
+
+
+void
+Blt_DestroyAxes(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Axis *axisPtr;
+ int i;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ axisPtr->hashPtr = NULL;
+ DestroyAxis(graphPtr, axisPtr);
+ }
+ Blt_DeleteHashTable(&(graphPtr->axes.table));
+ for (i = 0; i < 4; i++) {
+ Blt_ChainDestroy(graphPtr->axisChain[i]);
+ }
+ Blt_DeleteHashTable(&(graphPtr->axes.tagTable));
+ Blt_ChainDestroy(graphPtr->axes.chainPtr);
+}
+
+int
+Blt_DefaultAxes(graphPtr)
+ Graph *graphPtr;
+{
+ register int i;
+ Axis *axisPtr;
+ Blt_Chain *chainPtr;
+ static char *axisNames[4] = { "x", "y", "x2", "y2" } ;
+ int flags;
+
+ flags = Blt_GraphType(graphPtr);
+ for (i = 0; i < 4; i++) {
+ chainPtr = Blt_ChainCreate();
+ graphPtr->axisChain[i] = chainPtr;
+
+ /* Create a default axis for each chain. */
+ axisPtr = CreateAxis(graphPtr, axisNames[i], i);
+ if (axisPtr == NULL) {
+ return TCL_ERROR;
+ }
+ axisPtr->refCount = 1;
+ axisPtr->classUid = (i & 1) ? bltYAxisUid : bltXAxisUid;
+
+ /*
+ * Blt_ConfigureWidgetComponent creates a temporary child window
+ * by the name of the axis. It's used so that the Tk routines
+ * that access the X resource database can describe a single
+ * component and not the entire graph.
+ */
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ axisPtr->name, "Axis", configSpecs, 0, (char **)NULL,
+ (char *)axisPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ axisPtr->linkPtr = Blt_ChainAppend(chainPtr, axisPtr);
+ axisPtr->chainPtr = chainPtr;
+ }
+ return TCL_OK;
+}
+
+
+/*----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g axis bind axisName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BindOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int argc;
+ char **argv;
+{
+ Tcl_Interp *interp = graphPtr->interp;
+
+ return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+ Blt_MakeAxisTag(graphPtr, axisPtr->name), argc, argv);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard Tcl result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ return Tk_ConfigureValue(graphPtr->interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, argv[0], Blt_GraphType(graphPtr));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard Tcl result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * Side Effects:
+ * Axis resources are possibly allocated (GC, font). Axis layout is
+ * deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int argc;
+ char *argv[];
+{
+ int flags;
+
+ flags = TK_CONFIG_ARGV_ONLY | Blt_GraphType(graphPtr);
+ if (argc == 0) {
+ return Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, (char *)NULL, flags);
+ } else if (argc == 1) {
+ return Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, argv[0], flags);
+ }
+ if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
+ argc, argv, (char *)axisPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (axisPtr->refCount > 0) {
+ if (!Blt_ConfigModified(configSpecs, "-*color", "-background", "-bg",
+ (char *)NULL)) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ graphPtr->flags |= DRAW_MARGINS;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Returns the name of the picked axis (using the axis
+ * bind operation). Right now, the only name accepted is
+ * "current".
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain
+ * the name of the axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ Tcl_Interp *interp = graphPtr->interp;
+ register Axis *axisPtr;
+
+ axisPtr = (Axis *)Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on axes. */
+ if ((axisPtr != NULL) &&
+ ((axisPtr->classUid == bltXAxisUid) ||
+ (axisPtr->classUid == bltYAxisUid) ||
+ (axisPtr->classUid == NULL))) {
+ char c;
+
+ c = argv[3][0];
+ if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
+ Tcl_SetResult(interp, axisPtr->name, TCL_VOLATILE);
+ } else if ((c == 'd') && (strcmp(argv[3], "detail") == 0)) {
+ Tcl_SetResult(interp, axisPtr->detail, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LimitsOp --
+ *
+ * This procedure returns a string representing the axis limits
+ * of the graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LimitsOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+
+{
+ Tcl_Interp *interp = graphPtr->interp;
+ double min, max;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (axisPtr->logScale) {
+ min = EXP10(axisPtr->tickRange.min);
+ max = EXP10(axisPtr->tickRange.max);
+ } else {
+ min = axisPtr->tickRange.min;
+ max = axisPtr->tickRange.max;
+ }
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, min));
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, max));
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvTransformOp --
+ *
+ * Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the axis value. If an error occurred, TCL_ERROR is returned
+ * and interp->result will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvTransformOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x; /* Integer window coordinate*/
+ double y; /* Real graph coordinate */
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Tcl_GetInt(graphPtr->interp, argv[0], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Is the axis vertical or horizontal?
+ *
+ * Check the site where the axis was positioned. If the axis is
+ * virtual, all we have to go on is how it was mapped to an
+ * element (using either -mapx or -mapy options).
+ */
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ y = InvHMap(graphPtr, axisPtr, (double)x);
+ } else {
+ y = InvVMap(graphPtr, axisPtr, (double)x);
+ }
+ Tcl_AppendElement(graphPtr->interp, Blt_Dtoa(graphPtr->interp, y));
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ * Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the window coordinate. If an error occurred, TCL_ERROR
+ * is returned and interp->result will contain an error
+ * message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr; /* Axis */
+ int argc; /* Not used. */
+ char **argv;
+{
+ double x;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Tcl_ExprDouble(graphPtr->interp, argv[0], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ x = HMap(graphPtr, axisPtr, x);
+ } else {
+ x = VMap(graphPtr, axisPtr, x);
+ }
+ Tcl_SetResult(graphPtr->interp, Blt_Itoa((int)x), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * UseOp --
+ *
+ * Changes the virtual axis used by the logical axis.
+ *
+ * Results:
+ * A standard Tcl result. If the named axis doesn't exist
+ * an error message is put in interp->result.
+ *
+ * .g xaxis use "abc def gah"
+ * .g xaxis use [lappend abc [.g axis use]]
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UseOp(graphPtr, axisPtr, argc, argv)
+ Graph *graphPtr;
+ Axis *axisPtr; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Blt_Chain *chainPtr;
+ int nElem;
+ char **elemArr;
+ Blt_ChainLink *linkPtr;
+ int i;
+ Tk_Uid classUid;
+ int margin;
+
+ margin = (int)argv[-1];
+ chainPtr = graphPtr->margins[margin].chainPtr;
+ if (argc == 0) {
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr!= NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+ }
+ return TCL_OK;
+ }
+ if ((margin == MARGIN_BOTTOM) || (margin == MARGIN_TOP)) {
+ classUid = (graphPtr->inverted) ? bltYAxisUid : bltXAxisUid;
+ } else {
+ classUid = (graphPtr->inverted) ? bltXAxisUid : bltYAxisUid;
+ }
+ if (Tcl_SplitList(graphPtr->interp, argv[0], &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr!= NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ axisPtr->linkPtr = NULL;
+ /* Clear the type of axes not currently mapped to elements.*/
+ if (!(axisPtr->flags & AXIS_MAPS_ELEM)) {
+ axisPtr->classUid = NULL;
+ }
+ }
+ Blt_ChainReset(chainPtr);
+ for (i = 0; i < nElem; i++) {
+ if (NameToAxis(graphPtr, elemArr[i], &axisPtr) != TCL_OK) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ if (axisPtr->classUid == NULL) {
+ axisPtr->classUid = classUid;
+ } else if (axisPtr->classUid != classUid) {
+ Tcl_AppendResult(graphPtr->interp, "wrong type axis \"",
+ axisPtr->name, "\": can't use ", classUid, " type axis.",
+ (char *)NULL);
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ if (axisPtr->linkPtr != NULL) {
+ /* Move the axis from the old margin's "use" list to the new. */
+ Blt_ChainUnlinkLink(axisPtr->chainPtr, axisPtr->linkPtr);
+ Blt_ChainAppendLink(chainPtr, axisPtr->linkPtr);
+ } else {
+ axisPtr->linkPtr = Blt_ChainAppend(chainPtr, axisPtr);
+ }
+ axisPtr->chainPtr = chainPtr;
+ }
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+ /* When any axis changes, we need to layout the entire graph. */
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_EventuallyRedrawGraph(graphPtr);
+
+ Blt_Free(elemArr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateVirtualOp --
+ *
+ * Creates a new axis.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CreateVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Axis *axisPtr;
+ int flags;
+
+ axisPtr = CreateAxis(graphPtr, argv[3], MARGIN_NONE);
+ if (axisPtr == NULL) {
+ return TCL_ERROR;
+ }
+ flags = Blt_GraphType(graphPtr);
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ axisPtr->name, "Axis", configSpecs, argc - 4, argv + 4,
+ (char *)axisPtr, flags) != TCL_OK) {
+ goto error;
+ }
+ if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+ goto error;
+ }
+ Tcl_SetResult(graphPtr->interp, axisPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+ error:
+ DestroyAxis(graphPtr, axisPtr);
+ return TCL_ERROR;
+}
+
+/*----------------------------------------------------------------------
+ *
+ * BindVirtualOp --
+ *
+ * .g axis bind axisName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Tcl_Interp *interp = graphPtr->interp;
+
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(graphPtr->axes.tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+ Blt_MakeAxisTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetVirtualOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard Tcl result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char *argv[];
+{
+ Axis *axisPtr;
+
+ if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return CgetOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureVirtualOp --
+ *
+ * Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard Tcl result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * Side Effects:
+ * Axis resources are possibly allocated (GC, font). Axis layout is
+ * deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char *argv[];
+{
+ Axis *axisPtr;
+ int nNames, nOpts;
+ char **options;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of pen names specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + i; /* Start of options in argv */
+
+ for (i = 0; i < nNames; i++) {
+ if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureOp(graphPtr, axisPtr, nOpts, options) != TCL_OK) {
+ break;
+ }
+ }
+ if (i < nNames) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteVirtualOp --
+ *
+ * Deletes one or more axes. The actual removal may be deferred
+ * until the axis is no longer used by any element. The axis
+ * can't be referenced by its name any longer and it may be
+ * recreated.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ register int i;
+ Axis *axisPtr;
+
+ for (i = 3; i < argc; i++) {
+ if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ axisPtr->deletePending = TRUE;
+ if (axisPtr->refCount == 0) {
+ DestroyAxis(graphPtr, axisPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvTransformVirtualOp --
+ *
+ * Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the axis value. If an error occurred, TCL_ERROR is returned
+ * and interp->result will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvTransformVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Axis *axisPtr;
+
+ if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return InvTransformOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LimitsVirtualOp --
+ *
+ * This procedure returns a string representing the axis limits
+ * of the graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LimitsVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+
+{
+ Axis *axisPtr;
+
+ if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return LimitsOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesVirtualOp --
+ *
+ * Return a list of the names of all the axes.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+NamesVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Axis *axisPtr;
+ register int i;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ if (axisPtr->deletePending) {
+ continue;
+ }
+ if (argc == 3) {
+ Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+ continue;
+ }
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(axisPtr->name, argv[i])) {
+ Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TransformVirtualOp --
+ *
+ * Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the window coordinate. If an error occurred, TCL_ERROR
+ * is returned and interp->result will contain an error
+ * message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformVirtualOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Axis *axisPtr;
+
+ if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TransformOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+static int
+ViewOp(graphPtr, argc, argv)
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Tcl_Interp *interp = graphPtr->interp;
+ double width, worldWidth;
+ double axisOffset, scrollUnits;
+ double fract;
+ Axis *axisPtr;
+ double min, max, worldMin, worldMax;
+
+ if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ worldMin = axisPtr->dataRange.min;
+ worldMax = axisPtr->dataRange.max;
+ min = axisPtr->min;
+ max = axisPtr->max;
+ if (axisPtr->logScale) {
+ worldMin = log10(worldMin);
+ worldMax = log10(worldMax);
+ min = log10(min);
+ max = log10(max);
+ }
+ worldWidth = worldMax - worldMin;
+ width = max - min;
+
+ /* Unlike horizontal axes, vertical axis values run opposite of
+ * the scrollbar first/last values. So instead of pushing the
+ * axis minimum around, we move the maximum instead. */
+
+ if (AxisIsHorizontal(graphPtr, axisPtr) != axisPtr->descending) {
+ axisOffset = min - worldMin;
+ scrollUnits = (double)axisPtr->scrollUnits / (double)graphPtr->hRange;
+ } else {
+ axisOffset = worldMax - max;
+ scrollUnits = (double)axisPtr->scrollUnits / (double)graphPtr->vRange;
+ }
+ if (argc == 4) {
+ /* Note: Bound the fractions between 0.0 and 1.0 to support
+ * "canvas"-style scrolling. */
+ fract = axisOffset / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (axisOffset + width) / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ fract = axisOffset / worldWidth;
+ if (GetAxisScrollInfo(interp, argc - 4, argv + 4, &fract,
+ width / worldWidth, scrollUnits) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (AxisIsHorizontal(graphPtr, axisPtr) != axisPtr->descending) {
+ axisPtr->min = (fract * worldWidth) + worldMin;
+ axisPtr->max = axisPtr->min + width;
+ } else {
+ axisPtr->max = worldMax - (fract * worldWidth);
+ axisPtr->min = axisPtr->max - width;
+ }
+ if (axisPtr->logScale) {
+ axisPtr->min = EXP10(axisPtr->min);
+ axisPtr->max = EXP10(axisPtr->max);
+ }
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+int
+Blt_VirtualAxisOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+ static Blt_OpSpec axisOps[] =
+ {
+ {"bind", 1, (Blt_Op)BindVirtualOp, 3, 6,
+ "axisName sequence command",},
+ {"cget", 2, (Blt_Op)CgetVirtualOp, 5, 5, "axisName option",},
+ {"configure", 2, (Blt_Op)ConfigureVirtualOp, 4, 0,
+ "axisName ?axisName?... ?option value?...",},
+ {"create", 2, (Blt_Op)CreateVirtualOp, 4, 0,
+ "axisName ?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteVirtualOp, 3, 0, "?axisName?...",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+ {"invtransform", 1, (Blt_Op)InvTransformVirtualOp, 5, 5,
+ "axisName value",},
+ {"limits", 1, (Blt_Op)LimitsVirtualOp, 4, 4, "axisName",},
+ {"names", 1, (Blt_Op)NamesVirtualOp, 3, 0, "?pattern?...",},
+ {"transform", 1, (Blt_Op)TransformVirtualOp, 5, 5, "axisName value",},
+ {"view", 1, (Blt_Op)ViewOp, 4, 7,
+ "axisName ?moveto fract? ?scroll number what?",},
+ };
+ static int nAxisOps = sizeof(axisOps) / sizeof(Blt_OpSpec);
+
+ proc = Blt_GetOp(interp, nAxisOps, axisOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, argc, argv);
+ return result;
+}
+
+int
+Blt_AxisOp(graphPtr, margin, argc, argv)
+ Graph *graphPtr;
+ int margin;
+ int argc;
+ char **argv;
+{
+ int result;
+ Blt_Op proc;
+ Axis *axisPtr;
+ static Blt_OpSpec axisOps[] =
+ {
+ {"bind", 1, (Blt_Op)BindOp, 2, 5, "sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+ {"invtransform", 1, (Blt_Op)InvTransformOp, 4, 4, "value",},
+ {"limits", 1, (Blt_Op)LimitsOp, 3, 3, "",},
+ {"transform", 1, (Blt_Op)TransformOp, 4, 4, "value",},
+ {"use", 1, (Blt_Op)UseOp, 3, 4, "?axisName?",},
+ };
+ static int nAxisOps = sizeof(axisOps) / sizeof(Blt_OpSpec);
+
+ proc = Blt_GetOp(graphPtr->interp, nAxisOps, axisOps, BLT_OP_ARG2,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ argv[2] = (char *)margin; /* Hack. Slide a reference to the margin in
+ * the argument list. Needed only for UseOp.
+ */
+ axisPtr = Blt_GetFirstAxis(graphPtr->margins[margin].chainPtr);
+ result = (*proc)(graphPtr, axisPtr, argc - 3, argv + 3);
+ return result;
+}
+
+void
+Blt_MapAxes(graphPtr)
+ Graph *graphPtr;
+{
+ Axis *axisPtr;
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+ register int margin;
+ int offset;
+
+ for (margin = 0; margin < 4; margin++) {
+ chainPtr = graphPtr->margins[margin].chainPtr;
+ offset = 0;
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ if (axisPtr->hidden) {
+ continue;
+ }
+ MapAxis(graphPtr, axisPtr, offset, margin);
+ if (AxisIsHorizontal(graphPtr, axisPtr)) {
+ offset += axisPtr->height;
+ } else {
+ offset += axisPtr->width;
+ }
+ }
+ }
+}
+
+void
+Blt_DrawAxes(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable;
+{
+ Axis *axisPtr;
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+ register int i;
+
+ for (i = 0; i < 4; i++) {
+ chainPtr = graphPtr->margins[i].chainPtr;
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ if (!axisPtr->hidden) {
+ DrawAxis(graphPtr, drawable, axisPtr);
+ }
+ }
+ }
+}
+
+void
+Blt_AxesToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ Axis *axisPtr;
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+ register int i;
+
+ for (i = 0; i < 4; i++) {
+ chainPtr = graphPtr->margins[i].chainPtr;
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ axisPtr = Blt_ChainGetValue(linkPtr);
+ if (!axisPtr->hidden) {
+ AxisToPostScript(psToken, axisPtr);
+ }
+ }
+ }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_DrawAxisLimits --
+ *
+ * Draws the min/max values of the axis in the plotting area.
+ * The text strings are formatted according to the "sprintf"
+ * format descriptors in the limitsFormats array.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws the numeric values of the axis limits into the outer
+ * regions of the plotting area.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_DrawAxisLimits(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable;
+{
+ Axis *axisPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Dim2D textDim;
+ int isHoriz;
+ char *minPtr, *maxPtr;
+ char *minFormat, *maxFormat;
+ char minString[200], maxString[200];
+ int vMin, hMin, vMax, hMax;
+
+#define SPACING 8
+ vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+ hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2; /* Offsets */
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+
+ if (axisPtr->nFormats == 0) {
+ continue;
+ }
+ isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+ minPtr = maxPtr = NULL;
+ minFormat = maxFormat = axisPtr->limitsFormats[0];
+ if (axisPtr->nFormats > 1) {
+ maxFormat = axisPtr->limitsFormats[1];
+ }
+ if (minFormat[0] != '\0') {
+ minPtr = minString;
+ sprintf(minString, minFormat, axisPtr->tickRange.min);
+ }
+ if (maxFormat[0] != '\0') {
+ maxPtr = maxString;
+ sprintf(maxString, maxFormat, axisPtr->tickRange.max);
+ }
+ if (axisPtr->descending) {
+ char *tmp;
+
+ tmp = minPtr, minPtr = maxPtr, maxPtr = tmp;
+ }
+ if (maxPtr != NULL) {
+ if (isHoriz) {
+ axisPtr->limitsStyle.theta = 90.0;
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_SE;
+ Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+ &(axisPtr->limitsStyle), graphPtr->right, hMax, &textDim);
+ hMax -= (textDim.height + SPACING);
+ } else {
+ axisPtr->limitsStyle.theta = 0.0;
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_NW;
+ Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+ &(axisPtr->limitsStyle), vMax, graphPtr->top, &textDim);
+ vMax += (textDim.width + SPACING);
+ }
+ }
+ if (minPtr != NULL) {
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_SW;
+ if (isHoriz) {
+ axisPtr->limitsStyle.theta = 90.0;
+ Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+ &(axisPtr->limitsStyle), graphPtr->left, hMin, &textDim);
+ hMin -= (textDim.height + SPACING);
+ } else {
+ axisPtr->limitsStyle.theta = 0.0;
+ Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+ &(axisPtr->limitsStyle), vMin, graphPtr->bottom, &textDim);
+ vMin += (textDim.width + SPACING);
+ }
+ }
+ } /* Loop on axes */
+}
+
+void
+Blt_AxisLimitsToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ Axis *axisPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ double vMin, hMin, vMax, hMax;
+ char string[200];
+ int textWidth, textHeight;
+ char *minFmt, *maxFmt;
+
+#define SPACING 8
+ vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+ hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2; /* Offsets */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+
+ if (axisPtr->nFormats == 0) {
+ continue;
+ }
+ minFmt = maxFmt = axisPtr->limitsFormats[0];
+ if (axisPtr->nFormats > 1) {
+ maxFmt = axisPtr->limitsFormats[1];
+ }
+ if (*maxFmt != '\0') {
+ sprintf(string, maxFmt, axisPtr->tickRange.max);
+ Blt_GetTextExtents(&(axisPtr->tickStyle), string, &textWidth,
+ &textHeight);
+ if ((textWidth > 0) && (textHeight > 0)) {
+ if (axisPtr->classUid == bltXAxisUid) {
+ axisPtr->limitsStyle.theta = 90.0;
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_SE;
+ Blt_TextToPostScript(psToken, string,
+ &(axisPtr->limitsStyle),
+ (double)graphPtr->right, hMax);
+ hMax -= (textWidth + SPACING);
+ } else {
+ axisPtr->limitsStyle.theta = 0.0;
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_NW;
+ Blt_TextToPostScript(psToken, string,
+ &(axisPtr->limitsStyle), vMax, (double)graphPtr->top);
+ vMax += (textWidth + SPACING);
+ }
+ }
+ }
+ if (*minFmt != '\0') {
+ sprintf(string, minFmt, axisPtr->tickRange.min);
+ Blt_GetTextExtents(&(axisPtr->tickStyle), string, &textWidth,
+ &textHeight);
+ if ((textWidth > 0) && (textHeight > 0)) {
+ axisPtr->limitsStyle.anchor = TK_ANCHOR_SW;
+ if (axisPtr->classUid == bltXAxisUid) {
+ axisPtr->limitsStyle.theta = 90.0;
+ Blt_TextToPostScript(psToken, string,
+ &(axisPtr->limitsStyle),
+ (double)graphPtr->left, hMin);
+ hMin -= (textWidth + SPACING);
+ } else {
+ axisPtr->limitsStyle.theta = 0.0;
+ Blt_TextToPostScript(psToken, string,
+ &(axisPtr->limitsStyle),
+ vMin, (double)graphPtr->bottom);
+ vMin += (textWidth + SPACING);
+ }
+ }
+ }
+ }
+}
+
+Axis *
+Blt_GetFirstAxis(chainPtr)
+ Blt_Chain *chainPtr;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainFirstLink(chainPtr);
+ if (linkPtr == NULL) {
+ return NULL;
+ }
+ return Blt_ChainGetValue(linkPtr);
+}
+
+Axis *
+Blt_NearestAxis(graphPtr, x, y)
+ Graph *graphPtr;
+ int x, y; /* Point to be tested */
+{
+ register Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Axis *axisPtr;
+ int w, h;
+ Point2D bbox[5];
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->axes.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+ if (axisPtr->hidden) {
+ continue;
+ }
+ if (axisPtr->showTicks) {
+ register Blt_ChainLink *linkPtr;
+ TickLabel *labelPtr;
+ Point2D t;
+
+ for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ labelPtr = Blt_ChainGetValue(linkPtr);
+ Blt_GetBoundingBox(labelPtr->width, labelPtr->height,
+ axisPtr->tickStyle.theta, &w, &h, bbox);
+ t = Blt_TranslatePoint(&(labelPtr->anchorPos), w, h,
+ axisPtr->tickStyle.anchor);
+ t.x = x - t.x - (w * 0.5);
+ t.y = y - t.y - (h * 0.5);
+
+ bbox[4] = bbox[0];
+ if (Blt_PointInPolygon(&t, bbox, 5)) {
+ axisPtr->detail = "label";
+ return axisPtr;
+ }
+ }
+ }
+ if (axisPtr->titleText != NULL) { /* and then the title string. */
+ Point2D t;
+
+ Blt_GetTextExtents(&(axisPtr->titleStyle), axisPtr->titleText,
+ &w, &h);
+ Blt_GetBoundingBox(w, h, axisPtr->titleStyle.theta, &w, &h, bbox);
+ t = Blt_TranslatePoint(&(axisPtr->titlePos), w, h,
+ axisPtr->titleStyle.anchor);
+ /* Translate the point so that the 0,0 is the upper left
+ * corner of the bounding box. */
+ t.x = x - t.x - (w / 2);
+ t.y = y - t.y - (h / 2);
+
+ bbox[4] = bbox[0];
+ if (Blt_PointInPolygon(&t, bbox, 5)) {
+ axisPtr->detail = "title";
+ return axisPtr;
+ }
+ }
+ if (axisPtr->lineWidth > 0) { /* Check for the axis region */
+ if (PointInRegion(&axisPtr->region, x, y)) {
+ axisPtr->detail = "line";
+ return axisPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+ClientData
+Blt_MakeAxisTag(graphPtr, tagName)
+ Graph *graphPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&(graphPtr->axes.tagTable), tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&(graphPtr->axes.tagTable), hPtr);
+}
diff --git a/blt/src/bltGrAxis.h b/blt/src/bltGrAxis.h
new file mode 100644
index 00000000000..6ec791d1a60
--- /dev/null
+++ b/blt/src/bltGrAxis.h
@@ -0,0 +1,301 @@
+/*
+ * bltGrAxis.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_AXIS_H
+#define _BLT_GR_AXIS_H
+
+#include "bltList.h"
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * AxisRange --
+ *
+ * Designates a range of values by a minimum and maximum limit.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ double min, max, range;
+} AxisRange;
+
+#define SetRange(l) \
+ ((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON)
+#define SetLimits(l, lo, hi) \
+ ((l).min = (lo), (l).max = (hi), SetRange(l))
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TickLabel --
+ *
+ * Structure containing the X-Y screen coordinates of the tick
+ * label (anchored at its center).
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ Point2D anchorPos;
+ int width, height;
+ char string[1];
+} TickLabel;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Ticks --
+ *
+ * Structure containing information where the ticks (major or
+ * minor) will be displayed on the graph.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ int nTicks; /* # of ticks on axis */
+ double tickArr[1]; /* Array of tick values (malloc-ed). */
+} Ticks;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TickSweep --
+ *
+ * Structure containing information where the ticks (major or
+ * minor) will be displayed on the graph.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ double initial; /* Initial value */
+ double step; /* Size of interval */
+ int nSteps; /* Number of intervals. */
+} TickSweep;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Axis --
+ *
+ * Structure contains options controlling how the axis will be
+ * displayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier to refer the element.
+ * Used in the "insert", "delete", or
+ * "show", commands. */
+
+ Tk_Uid classUid; /* Type of axis. */
+
+ Graph *graphPtr; /* Graph widget of element*/
+
+ unsigned int flags; /* Set bit field definitions below */
+
+ /*
+ * AXIS_MAPS_ELEM Axis is currently mapping element coordinates
+ * AXIS_DRAWN Axis is designated as a logical axis
+ * AXIS_CONFIG_DIRTY
+ *
+ * AXIS_CONFIG_MIN User specified the axis minimum.
+ * AXIS_CONFIG_MAX User specified the axis maximum.
+ * AXIS_CONFIG_MAJOR User specified major ticks.
+ * AXIS_CONFIG_MINOR User specified minor ticks.
+ */
+
+ char **tags;
+
+ char *detail;
+
+ int deletePending; /* Indicates that the axis was
+ * scheduled for deletion. The actual
+ * deletion may be deferred until the
+ * axis is no longer in use. */
+
+ int refCount; /* Number of elements referencing this
+ * axis. */
+
+ Blt_HashEntry *hashPtr; /* Points to axis entry in hash
+ * table. Used to quickly remove axis
+ * entries. */
+
+ int logScale; /* If non-zero, scale the axis values
+ * logarithmically. */
+
+ int hidden; /* If non-zero, don't display the
+ * axis title, ticks, or line. */
+
+ int showTicks; /* If non-zero, display tick marks and
+ * labels. */
+
+ int descending; /* If non-zero, display the range of
+ * values on the axis in descending
+ * order, from high to low. */
+
+ int looseMin, looseMax; /* If non-zero, axis range extends to
+ * the outer major ticks, otherwise at
+ * the limits of the data values. This
+ * is overriddened by setting the -min
+ * and -max options. */
+
+ char *titleText; /* Title of axis. */
+
+ TextStyle titleStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the axis
+ * title. */
+
+ Point2D titlePos; /* Position of the title */
+
+ unsigned short int titleWidth, titleHeight;
+
+ int lineWidth; /* Width of lines representing axis
+ * (including ticks). If zero, then
+ * no axis lines or ticks are
+ * drawn. */
+
+ char **limitsFormats; /* One or two strings of sprintf-like
+ * formats describing how to display
+ * virtual axis limits. If NULL,
+ * display no limits. */
+ int nFormats;
+
+ TextStyle limitsStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the limits. */
+
+ double autoRange; /* Size of a sliding window of values
+ * used to scale the axis automatically
+ * as new data values are added. The axis
+ * will always display the latest values
+ * in this range. */
+
+ double shiftBy; /* Shift maximum by this interval. */
+
+ int tickLength; /* Length of major ticks in pixels */
+
+ TextStyle tickStyle; /* Text attributes (color, font, rotation,
+ * etc.) for labels at each major tick. */
+
+ char *formatCmd; /* Specifies a Tcl command, to be invoked
+ * by the axis whenever it has to generate
+ * tick labels. */
+
+ char *scrollCmdPrefix;
+ int scrollUnits;
+
+ double min, max; /* The actual axis range. */
+
+ double reqMin, reqMax; /* Requested axis bounds. Consult the
+ * axisPtr->flags field for
+ * AXIS_CONFIG_MIN and AXIS_CONFIG_MAX
+ * to see if the requested bound have
+ * been set. They override the
+ * computed range of the axis
+ * (determined by auto-scaling). */
+
+ AxisRange dataRange; /* Range of data values of elements mapped
+ * to this axis. This is used to auto-scale
+ * the axis in "tight" mode. */
+
+ AxisRange tickRange; /* Smallest and largest major tick values
+ * for the axis. The tick values lie outside
+ * the range of data values. This is used to
+ * auto-scale the axis in "loose" mode. */
+
+ double prevMin, prevMax;
+
+ double reqStep; /* If > 0.0, overrides the computed major
+ * tick interval. Otherwise a stepsize
+ * is automatically calculated, based
+ * upon the range of elements mapped to the
+ * axis. The default value is 0.0. */
+
+ GC tickGC; /* Graphics context for axis and tick labels */
+
+ Ticks *t1Ptr; /* Array of major tick positions. May be
+ * set by the user or generated from the
+ * major sweep below. */
+
+ Ticks *t2Ptr; /* Array of minor tick positions. May be
+ * set by the user or generated from the
+ * minor sweep below. */
+
+ TickSweep minorSweep, majorSweep;
+
+ int reqNumMinorTicks; /* If non-zero, represents the
+ * requested the number of minor ticks
+ * to be uniformally displayed along
+ * each major tick. */
+
+
+
+ /* The following fields are specific to logical axes */
+
+ Blt_ChainLink *linkPtr; /* Axis link in margin list. */
+ Blt_Chain *chainPtr;
+
+ short int width, height; /* Extents of axis */
+
+ Segment2D *segments; /* Array of line segments representing
+ * the major and minor ticks, but also
+ * the axis line itself. The segment
+ * coordinates are relative to the
+ * axis. */
+
+ int nSegments; /* Number of segments in the above array. */
+
+ Blt_Chain *tickLabels; /* Contains major tick label strings
+ * and their offsets along the axis. */
+ Region2D region;
+
+ Tk_3DBorder border;
+ int borderWidth;
+ int relief;
+} Axis;
+
+#define AXIS_CONFIG_MAX (1<<2) /* User specified maximum axis value. */
+#define AXIS_CONFIG_MIN (1<<3) /* User specified minimum axis value. */
+#define AXIS_CONFIG_BOTH (AXIS_CONFIG_MIN | AXIS_CONFIG_MAX)
+#define AXIS_CONFIG_MAJOR (1<<4) /* User specified major tick intervals. */
+#define AXIS_CONFIG_MINOR (1<<5) /* User specified minor tick intervals. */
+
+#define AXIS_MAPS_ELEM (1<<6) /* Axis is used to map element coordinates */
+#define AXIS_CONFIG_DIRTY (1<<7)
+
+
+#define AXIS_ALLOW_NULL (1<<12)
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Axis2D --
+ *
+ * The pair of axes mapping a point onto the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ Axis *x, *y;
+} Axis2D;
+
+#endif /* _BLT_GR_AXIS_H */
diff --git a/blt/src/bltGrBar.c b/blt/src/bltGrBar.c
new file mode 100644
index 00000000000..6f581153f01
--- /dev/null
+++ b/blt/src/bltGrBar.c
@@ -0,0 +1,2270 @@
+
+/*
+ * bltGrBar.c --
+ *
+ * This module implements barchart elements for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+typedef struct {
+ char *name; /* Pen style identifier. If NULL pen
+ * was statically allocated. */
+ Tk_Uid classUid; /* Type of pen */
+ char *typeId; /* String token identifying the type of pen */
+ unsigned int flags; /* Indicates if the pen element is active or
+ * normal */
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+
+ XColor *fg; /* Foreground color of bar */
+ Tk_3DBorder border; /* 3D border and background color */
+ int borderWidth; /* 3D border width of bar */
+ int relief; /* Relief of the bar */
+ Pixmap stipple; /* Stipple */
+ GC gc; /* Graphics context */
+
+ /* Error bar attributes. */
+ int errorShow; /* Describes which error bars to
+ * display: none, x, y, or * both. */
+
+ int errorWidth; /* Width of the error bar segments. */
+
+ XColor *errorColor; /* Color of the error bar. */
+
+ GC errorGC; /* Error bar graphics context. */
+
+ /* Show value attributes. */
+ int valueShow; /* Indicates whether to display data value.
+ * Values are x, y, or none. */
+
+ char *valueFormat; /* A printf format string. */
+ TextStyle valueStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the value. */
+
+} BarPen;
+
+typedef struct {
+ AxisRange weight; /* Weight range where this pen is valid. */
+
+ BarPen *penPtr; /* Pen to draw */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to the
+ * current graph size. */
+
+ /* Bar chart specific data. */
+ XRectangle *rectangles; /* Indicates starting location in bar
+ * array for this pen. */
+ int nRects; /* Number of bar segments for this pen. */
+
+} BarPenStyle;
+
+typedef struct {
+ char *name; /* Identifier to refer the
+ * element. Used in the "insert",
+ * "delete", or "show", commands. */
+
+ Tk_Uid classUid; /* Type of element; either
+ * bltBarElementUid, bltLineElementUid, or
+ * bltStripElementUid. */
+
+ Graph *graphPtr; /* Graph widget of element*/
+ unsigned int flags; /* Indicates if the entire element is
+ * active, or if coordinates need to
+ * be calculated */
+ char **tags;
+ int hidden; /* If non-zero, don't display the element. */
+
+ Blt_HashEntry *hashPtr;
+ char *label; /* Label displayed in legend */
+ int labelRelief; /* Relief of label in legend. */
+
+ Axis2D axes;
+ ElemVector x, y, w; /* Contains array of numeric values */
+
+ ElemVector xError; /* Relative/symmetric X error values. */
+ ElemVector yError; /* Relative/symmetric Y error values. */
+ ElemVector xHigh, xLow; /* Absolute/asymmetric X-coordinate high/low
+ error values. */
+ ElemVector yHigh, yLow; /* Absolute/asymmetric Y-coordinate high/low
+ error values. */
+
+ int *reqActive; /* Array of indices (malloc-ed) that
+ * indicate the data points have been
+ * selected as active (drawn with
+ * "active" colors). */
+
+ int nReqActive; /* Number of active data points. Special
+ * case: if nReqActive < 0 and the
+ * active bit is set in "flags", then all
+ * data points are drawn active. */
+
+ ElementProcs *procsPtr; /* Class information for bar elements */
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int *xErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+ int *yErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+
+ BarPen *activePenPtr; /* Standard Pens */
+ BarPen *normalPenPtr;
+ Blt_Chain *palette; /* Array of pen style information */
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+ /*
+ * Bar specific attributes
+ */
+ BarPen builtinPen;
+
+ int *rectToData;
+ XRectangle *rectangles; /* Array of rectangles comprising the bar
+ * segments of the element. */
+ int nRects; /* # of visible bar segments for element */
+
+ int padX; /* Spacing on either side of bar */
+ double barWidth;
+ int nActive;
+
+ XRectangle *activeRects;
+ int *activeToData;
+} Bar;
+
+extern Tk_CustomOption bltBarPenOption;
+extern Tk_CustomOption bltDataOption;
+extern Tk_CustomOption bltDataPairsOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltColorOption;
+
+extern Tk_OptionParseProc Blt_StringToStyles;
+extern Tk_OptionPrintProc Blt_StylesToString;
+static Tk_OptionParseProc StringToBarMode;
+static Tk_OptionPrintProc BarModeToString;
+
+static Tk_CustomOption stylesOption =
+{
+ Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(BarPenStyle)
+};
+
+Tk_CustomOption bltBarModeOption =
+{
+ StringToBarMode, BarModeToString, (ClientData)0
+};
+
+#define DEF_BAR_ACTIVE_PEN "activeBar"
+#define DEF_BAR_AXIS_X "x"
+#define DEF_BAR_AXIS_Y "y"
+#define DEF_BAR_BG_COLOR "navyblue"
+#define DEF_BAR_BG_MONO BLACK
+#define DEF_BAR_BORDERWIDTH "2"
+#define DEF_BAR_DATA (char *)NULL
+#define DEF_BAR_ERRORBAR_COLOR "defcolor"
+#define DEF_BAR_ERRORBAR_WIDTH "1"
+#define DEF_BAR_FG_COLOR "blue"
+#define DEF_BAR_FG_MONO WHITE
+#define DEF_BAR_HIDE "no"
+#define DEF_BAR_LABEL (char *)NULL
+#define DEF_BAR_LABEL_RELIEF "flat"
+#define DEF_BAR_NORMAL_STIPPLE ""
+#define DEF_BAR_RELIEF "raised"
+#define DEF_BAR_SHOW_ERRORBARS "both"
+#define DEF_BAR_STYLES ""
+#define DEF_BAR_TAGS "all"
+#define DEF_BAR_WIDTH "0.0"
+#define DEF_BAR_DATA (char *)NULL
+
+#define DEF_PEN_ACTIVE_BG_COLOR "red"
+#define DEF_PEN_ACTIVE_BG_MONO WHITE
+#define DEF_PEN_ACTIVE_FG_COLOR "pink"
+#define DEF_PEN_ACTIVE_FG_MONO BLACK
+#define DEF_PEN_BORDERWIDTH "2"
+#define DEF_PEN_NORMAL_BG_COLOR "navyblue"
+#define DEF_PEN_NORMAL_BG_MONO BLACK
+#define DEF_PEN_NORMAL_FG_COLOR "blue"
+#define DEF_PEN_NORMAL_FG_MONO WHITE
+#define DEF_PEN_RELIEF "raised"
+#define DEF_PEN_STIPPLE ""
+#define DEF_PEN_TYPE "bar"
+#define DEF_PEN_VALUE_ANCHOR "s"
+#define DEF_PEN_VALUE_COLOR RGB_BLACK
+#define DEF_PEN_VALUE_FONT STD_FONT_SMALL
+#define DEF_PEN_VALUE_FORMAT "%g"
+#define DEF_PEN_VALUE_ROTATE (char *)NULL
+#define DEF_PEN_VALUE_SHADOW (char *)NULL
+#define DEF_PEN_SHOW_VALUES "no"
+
+static Tk_ConfigSpec barPenConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_PEN_ACTIVE_BG_COLOR, Tk_Offset(BarPen, border),
+ TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_PEN_ACTIVE_BG_COLOR, Tk_Offset(BarPen, border),
+ TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_PEN_NORMAL_BG_COLOR, Tk_Offset(BarPen, border),
+ TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_PEN_NORMAL_BG_COLOR, Tk_Offset(BarPen, border),
+ TK_CONFIG_MONO_ONLY | NORMAL_PEN},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_PEN_BORDERWIDTH, Tk_Offset(BarPen, borderWidth), ALL_PENS,
+ &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_BAR_ERRORBAR_COLOR, Tk_Offset(BarPen, errorColor),
+ ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_BAR_ERRORBAR_WIDTH, Tk_Offset(BarPen, errorWidth),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_ACTIVE_FG_COLOR, Tk_Offset(BarPen, fg),
+ ACTIVE_PEN | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_ACTIVE_FG_COLOR, Tk_Offset(BarPen, fg),
+ ACTIVE_PEN | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_NORMAL_FG_COLOR, Tk_Offset(BarPen, fg),
+ NORMAL_PEN | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_NORMAL_FG_COLOR, Tk_Offset(BarPen, fg),
+ NORMAL_PEN | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_PEN_RELIEF, Tk_Offset(BarPen, relief), ALL_PENS},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_BAR_SHOW_ERRORBARS, Tk_Offset(BarPen, errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(BarPen, valueShow),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_PEN_STIPPLE, Tk_Offset(BarPen, stipple),
+ ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
+ DEF_PEN_TYPE, Tk_Offset(BarPen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Tk_Offset(BarPen, valueStyle.anchor), ALL_PENS},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(BarPen, valueStyle.color), ALL_PENS},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(BarPen, valueStyle.font), ALL_PENS},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(BarPen, valueFormat),
+ ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(BarPen, valueStyle.theta), ALL_PENS},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(BarPen, valueStyle.shadow),
+ ALL_PENS, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec barElemConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_BAR_ACTIVE_PEN, Tk_Offset(Bar, activePenPtr),
+ TK_CONFIG_NULL_OK, &bltBarPenOption},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BAR_BG_COLOR, Tk_Offset(Bar, builtinPen.border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BAR_BG_COLOR, Tk_Offset(Bar, builtinPen.border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
+ DEF_BAR_WIDTH, Tk_Offset(Bar, barWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_BAR_TAGS, Tk_Offset(Bar, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_BAR_BORDERWIDTH, Tk_Offset(Bar, builtinPen.borderWidth),
+ 0, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_BAR_ERRORBAR_COLOR, Tk_Offset(Bar, builtinPen.errorColor),
+ 0, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_BAR_ERRORBAR_WIDTH, Tk_Offset(Bar, builtinPen.errorWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+ (char *)NULL, 0, 0, &bltDataPairsOption},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BAR_FG_COLOR, Tk_Offset(Bar, builtinPen.fg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BAR_FG_COLOR, Tk_Offset(Bar, builtinPen.fg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ DEF_BAR_LABEL, Tk_Offset(Bar, label), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+ DEF_BAR_LABEL_RELIEF, Tk_Offset(Bar, labelRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_BAR_HIDE, Tk_Offset(Bar, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_BAR_AXIS_X, Tk_Offset(Bar, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_BAR_AXIS_Y, Tk_Offset(Bar, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+ (char *)NULL, Tk_Offset(Bar, normalPenPtr),
+ TK_CONFIG_NULL_OK, &bltBarPenOption},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_BAR_RELIEF, Tk_Offset(Bar, builtinPen.relief), 0},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_BAR_SHOW_ERRORBARS, Tk_Offset(Bar, builtinPen.errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(Bar, builtinPen.valueShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_BAR_NORMAL_STIPPLE, Tk_Offset(Bar, builtinPen.stipple),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+ DEF_BAR_STYLES, Tk_Offset(Bar, palette),
+ TK_CONFIG_NULL_OK, &stylesOption},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Tk_Offset(Bar, builtinPen.valueStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(Bar, builtinPen.valueStyle.color), 0},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(Bar, builtinPen.valueStyle.font), 0},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(Bar, builtinPen.valueFormat),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(Bar, builtinPen.valueStyle.theta), 0},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(Bar, builtinPen.valueStyle.shadow),
+ 0, &bltShadowOption},
+
+ {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+ (char *)NULL, Tk_Offset(Bar, w), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-x", "xdata", "Xdata",
+ DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-y", "ydata", "Ydata",
+ DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xdata", "xdata", "Xdata",
+ DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ydata", "ydata", "Ydata",
+ DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError",
+ DEF_BAR_DATA, Tk_Offset(Bar, xError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh",
+ DEF_BAR_DATA, Tk_Offset(Bar, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow",
+ DEF_BAR_DATA, Tk_Offset(Bar, xLow), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError",
+ DEF_BAR_DATA, Tk_Offset(Bar, yError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh",
+ DEF_BAR_DATA, Tk_Offset(Bar, yHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow",
+ DEF_BAR_DATA, Tk_Offset(Bar, yLow), 0, &bltDataOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+#ifdef __STDC__
+static PenConfigureProc ConfigurePen;
+static PenDestroyProc DestroyPen;
+static ElementClosestProc ClosestBar;
+static ElementConfigProc ConfigureBar;
+static ElementDestroyProc DestroyBar;
+static ElementDrawProc DrawActiveBar;
+static ElementDrawProc DrawNormalBar;
+static ElementDrawSymbolProc DrawSymbol;
+static ElementExtentsProc GetBarExtents;
+static ElementToPostScriptProc ActiveBarToPostScript;
+static ElementToPostScriptProc NormalBarToPostScript;
+static ElementSymbolToPostScriptProc SymbolToPostScript;
+static ElementMapProc MapBar;
+#endif /* __STDC__ */
+
+INLINE static double
+Fabs(x)
+ register double x;
+{
+ return ((x < 0.0) ? -x : x);
+}
+
+INLINE static int
+Round(x)
+ register double x;
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NameOfBarMode --
+ *
+ * Converts the integer representing the mode style into a string.
+ *
+ * ----------------------------------------------------------------------
+ */
+static char *
+NameOfBarMode(mode)
+ BarMode mode;
+{
+ switch (mode) {
+ case MODE_INFRONT:
+ return "infront";
+ case MODE_OVERLAP:
+ return "overlap";
+ case MODE_STACKED:
+ return "stacked";
+ case MODE_ALIGNED:
+ return "aligned";
+ default:
+ return "unknown mode value";
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToMode --
+ *
+ * Converts the mode string into its numeric representation.
+ *
+ * Valid mode strings are:
+ *
+ * "infront" Draw a full bar at each point in the element.
+ *
+ * "stacked" Stack bar segments vertically. Each stack is defined
+ * by each ordinate at a particular abscissa. The height
+ * of each segment is represented by the sum the previous
+ * ordinates.
+ *
+ * "aligned" Align bar segments as smaller slices one next to
+ * the other. Like "stacks", aligned segments are
+ * defined by each ordinate at a particular abscissa.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToBarMode(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Mode style string */
+ char *widgRec; /* Cubicle structure record */
+ int offset; /* Offset of style in record */
+{
+ BarMode *modePtr = (BarMode *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "normal", length) == 0)) {
+ *modePtr = MODE_INFRONT;
+ } else if ((c == 'i') && (strncmp(string, "infront", length) == 0)) {
+ *modePtr = MODE_INFRONT;
+ } else if ((c == 's') && (strncmp(string, "stacked", length) == 0)) {
+ *modePtr = MODE_STACKED;
+ } else if ((c == 'a') && (strncmp(string, "aligned", length) == 0)) {
+ *modePtr = MODE_ALIGNED;
+ } else if ((c == 'o') && (strncmp(string, "overlap", length) == 0)) {
+ *modePtr = MODE_OVERLAP;
+ } else {
+ Tcl_AppendResult(interp, "bad mode argument \"", string,
+ "\": should be \"infront\", \"stacked\", \"overlap\", or \"aligned\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BarModeToString --
+ *
+ * Returns the mode style string based upon the mode flags.
+ *
+ * Results:
+ * The mode style string is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+BarModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Row/column structure record */
+ int offset; /* Offset of mode in Partition record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ BarMode mode = *(BarMode *)(widgRec + offset);
+
+ return NameOfBarMode(mode);
+}
+
+
+/*
+ * Zero out the style's number of rectangles and errorbars.
+ */
+static void
+ClearPalette(palette)
+ Blt_Chain *palette;
+{
+ register BarPenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = stylePtr->nRects = 0;
+ }
+}
+
+static int
+ConfigurePen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ BarPen *bpPtr = (BarPen *)penPtr;
+ XColor *colorPtr;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+
+ Blt_ResetTextStyle(graphPtr->tkwin, &(bpPtr->valueStyle));
+ gcMask = GCForeground | GCBackground;
+ colorPtr = bpPtr->fg;
+ gcValues.foreground = colorPtr->pixel;
+ gcValues.background = (Tk_3DBorderColor(bpPtr->border))->pixel;
+ if (bpPtr->stipple != None) {
+ gcValues.stipple = bpPtr->stipple;
+ gcValues.fill_style = FillOpaqueStippled;
+ gcMask |= (GCStipple | GCFillStyle);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bpPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bpPtr->gc);
+ }
+ bpPtr->gc = newGC;
+
+ gcMask = GCForeground | GCLineWidth;
+ colorPtr = bpPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = bpPtr->fg;
+ }
+ gcValues.line_width = LineWidth(bpPtr->errorWidth);
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bpPtr->errorGC);
+ }
+ bpPtr->errorGC = newGC;
+
+ return TCL_OK;
+}
+
+static void
+DestroyPen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ BarPen *bpPtr = (BarPen *)penPtr;
+
+ Blt_FreeTextStyle(graphPtr->display, &(bpPtr->valueStyle));
+ if (bpPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bpPtr->gc);
+ }
+ if (bpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bpPtr->errorGC);
+ }
+}
+
+static void
+InitPen(penPtr)
+ BarPen *penPtr;
+{
+ Blt_InitTextStyle(&(penPtr->valueStyle));
+ penPtr->configSpecs = barPenConfigSpecs;
+ penPtr->configProc = ConfigurePen;
+ penPtr->destroyProc = DestroyPen;
+ penPtr->relief = TK_RELIEF_RAISED;
+ penPtr->flags = NORMAL_PEN;
+ penPtr->errorShow = SHOW_BOTH;
+ penPtr->valueShow = SHOW_NONE;
+ penPtr->borderWidth = 2;
+}
+
+Pen *
+Blt_BarPen(penName)
+ char *penName;
+{
+ BarPen *penPtr;
+
+ penPtr = Blt_Calloc(1, sizeof(BarPen));
+ assert(penPtr);
+ InitPen(penPtr);
+ penPtr->name = Blt_Strdup(penName);
+ if (strcmp(penName, "activeBar") == 0) {
+ penPtr->flags = ACTIVE_PEN;
+ }
+ return (Pen *) penPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CheckStacks --
+ *
+ * Check that the data limits are not superseded by the heights
+ * of stacked bar segments. The heights are calculated by
+ * Blt_ComputeStacks.
+ *
+ * Results:
+ * If the y-axis limits need to be adjusted for stacked segments,
+ * *minPtr* or *maxPtr* are updated.
+ *
+ * Side effects:
+ * Autoscaling of the y-axis is affected.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+CheckStacks(graphPtr, pairPtr, minPtr, maxPtr)
+ Graph *graphPtr;
+ Axis2D *pairPtr;
+ double *minPtr, *maxPtr; /* Current minimum maximum for y-axis */
+{
+ FreqInfo *infoPtr;
+ register int i;
+
+ if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
+ return;
+ }
+ infoPtr = graphPtr->freqArr;
+ for (i = 0; i < graphPtr->nStacks; i++) {
+ if ((infoPtr->axes.x == pairPtr->x) &&
+ (infoPtr->axes.y == pairPtr->y)) {
+ /*
+
+ * Check if any of the y-values (because of stacking) are
+ * greater than the current limits of the graph.
+ */
+ if (infoPtr->sum < 0.0) {
+ if (*minPtr > infoPtr->sum) {
+ *minPtr = infoPtr->sum;
+ }
+ } else {
+ if (*maxPtr < infoPtr->sum) {
+ *maxPtr = infoPtr->sum;
+ }
+ }
+ }
+ infoPtr++;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureBar --
+ *
+ * Sets up the appropriate configuration parameters in the GC.
+ * It is assumed the parameters have been previously set by
+ * a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as bar foreground/background
+ * color and stipple etc. get set in a new GC.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureBar(graphPtr, elemPtr)
+ Graph *graphPtr;
+ register Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (ConfigurePen(graphPtr, (Pen *)&(barPtr->builtinPen)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Point to the static normal pen if no external pens have
+ * been selected.
+ */
+ if (barPtr->normalPenPtr == NULL) {
+ barPtr->normalPenPtr = &(barPtr->builtinPen);
+ }
+ linkPtr = Blt_ChainFirstLink(barPtr->palette);
+ if (linkPtr != NULL) {
+ BarPenStyle *stylePtr;
+
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->penPtr = barPtr->normalPenPtr;
+ }
+ if (Blt_ConfigModified(barPtr->configSpecs, "-barwidth", "-*data",
+ "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
+ barPtr->flags |= MAP_ITEM;
+ }
+ return TCL_OK;
+}
+
+static void
+GetBarExtents(elemPtr, extsPtr)
+ Element *elemPtr;
+ Extents2D *extsPtr;
+{
+ Graph *graphPtr = elemPtr->graphPtr;
+ Bar *barPtr = (Bar *)elemPtr;
+ double middle, barWidth;
+ int nPoints;
+
+ extsPtr->top = extsPtr->left = DBL_MAX;
+ extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+ nPoints = NumberOfPoints(barPtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ barWidth = graphPtr->barWidth;
+ if (barPtr->barWidth > 0.0) {
+ barWidth = barPtr->barWidth;
+ }
+ middle = barWidth * 0.5;
+ extsPtr->left = barPtr->x.min - middle;
+ extsPtr->right = barPtr->x.max + middle;
+
+ extsPtr->top = barPtr->y.min;
+ extsPtr->bottom = barPtr->y.max;
+ if (extsPtr->bottom < graphPtr->baseline) {
+ extsPtr->bottom = graphPtr->baseline;
+ }
+ /*
+ * Handle "stacked" bar elements specially.
+ *
+ * If element is stacked, the sum of its ordinates may be outside
+ * the minimum/maximum limits of the element's data points.
+ */
+ if ((graphPtr->mode == MODE_STACKED) && (graphPtr->nStacks > 0)) {
+ CheckStacks(graphPtr, &(elemPtr->axes), &(extsPtr->top),
+ &(extsPtr->bottom));
+ }
+ /* Warning: You get what you deserve if the x-axis is logScale */
+ if (elemPtr->axes.x->logScale) {
+ extsPtr->left = Blt_FindElemVectorMinimum(&(barPtr->x), DBL_MIN) +
+ middle;
+ }
+ /* Fix y-min limits for barchart */
+ if (elemPtr->axes.y->logScale) {
+ if ((extsPtr->top <= 0.0) || (extsPtr->top > 1.0)) {
+ extsPtr->top = 1.0;
+ }
+ } else {
+ if (extsPtr->top > 0.0) {
+ extsPtr->top = 0.0;
+ }
+ }
+ /* Correct the extents for error bars if they exist. */
+ if (elemPtr->xError.nValues > 0) {
+ register int i;
+ double x;
+
+ /* Correct the data limits for error bars */
+ nPoints = MIN(elemPtr->xError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
+ if (x > extsPtr->right) {
+ extsPtr->right = x;
+ }
+ x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
+ if (elemPtr->axes.x->logScale) {
+ if (x < 0.0) {
+ x = -x; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((x > DBL_MIN) && (x < extsPtr->left)) {
+ extsPtr->left = x;
+ }
+ } else if (x < extsPtr->left) {
+ extsPtr->left = x;
+ }
+ }
+ } else {
+ if ((elemPtr->xHigh.nValues > 0) &&
+ (elemPtr->xHigh.max > extsPtr->right)) {
+ extsPtr->right = elemPtr->xHigh.max;
+ }
+ if (elemPtr->xLow.nValues > 0) {
+ double left;
+
+ if ((elemPtr->xLow.min <= 0.0) &&
+ (elemPtr->axes.x->logScale)) {
+ left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
+ } else {
+ left = elemPtr->xLow.min;
+ }
+ if (left < extsPtr->left) {
+ extsPtr->left = left;
+ }
+ }
+ }
+ if (elemPtr->yError.nValues > 0) {
+ register int i;
+ double y;
+
+ nPoints = MIN(elemPtr->yError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
+ if (y > extsPtr->bottom) {
+ extsPtr->bottom = y;
+ }
+ y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
+ if (elemPtr->axes.y->logScale) {
+ if (y < 0.0) {
+ y = -y; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((y > DBL_MIN) && (y < extsPtr->left)) {
+ extsPtr->top = y;
+ }
+ } else if (y < extsPtr->top) {
+ extsPtr->top = y;
+ }
+ }
+ } else {
+ if ((elemPtr->yHigh.nValues > 0) &&
+ (elemPtr->yHigh.max > extsPtr->bottom)) {
+ extsPtr->bottom = elemPtr->yHigh.max;
+ }
+ if (elemPtr->yLow.nValues > 0) {
+ double top;
+
+ if ((elemPtr->yLow.min <= 0.0) &&
+ (elemPtr->axes.y->logScale)) {
+ top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
+ } else {
+ top = elemPtr->yLow.min;
+ }
+ if (top < extsPtr->top) {
+ extsPtr->top = top;
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ClosestBar --
+ *
+ * Find the bar segment closest to the window coordinates point
+ * specified.
+ *
+ * Note: This does not return the height of the stacked segment
+ * (in graph coordinates) properly.
+ *
+ * Results:
+ * Returns 1 if the point is width any bar segment, otherwise 0.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ClosestBar(graphPtr, elemPtr, searchPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Element *elemPtr; /* Bar element */
+ ClosestSearch *searchPtr; /* Info of closest point in element */
+{
+ Bar *barPtr = (Bar *)elemPtr;
+ Point2D *pointPtr, *endPtr;
+ Point2D t, outline[5];
+ XRectangle *rectPtr;
+ double left, right, top, bottom;
+ double minDist, dist;
+ int imin;
+ register int i;
+
+ minDist = searchPtr->dist;
+ imin = 0;
+
+ rectPtr = barPtr->rectangles;
+ for (i = 0; i < barPtr->nRects; i++) {
+ if (PointInRectangle(rectPtr, searchPtr->x, searchPtr->y)) {
+ imin = barPtr->rectToData[i];
+ minDist = 0.0;
+ break;
+ }
+ left = rectPtr->x, top = rectPtr->y;
+ right = (double)(rectPtr->x + rectPtr->width);
+ bottom = (double)(rectPtr->y + rectPtr->height);
+ outline[4].x = outline[3].x = outline[0].x = left;
+ outline[4].y = outline[1].y = outline[0].y = top;
+ outline[2].x = outline[1].x = right;
+ outline[3].y = outline[2].y = bottom;
+
+ for (pointPtr = outline, endPtr = outline + 4; pointPtr < endPtr;
+ pointPtr++) {
+ t = Blt_GetProjection(searchPtr->x, searchPtr->y,
+ pointPtr, pointPtr + 1);
+ if (t.x > right) {
+ t.x = right;
+ } else if (t.x < left) {
+ t.x = left;
+ }
+ if (t.y > bottom) {
+ t.y = bottom;
+ } else if (t.y < top) {
+ t.y = top;
+ }
+ dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y));
+ if (dist < minDist) {
+ minDist = dist;
+ imin = barPtr->rectToData[i];
+ }
+ }
+ rectPtr++;
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->elemPtr = (Element *)elemPtr;
+ searchPtr->dist = minDist;
+ searchPtr->index = imin;
+ searchPtr->point.x = (double)barPtr->x.valueArr[imin];
+ searchPtr->point.y = (double)barPtr->y.valueArr[imin];
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ * Reorders the both arrays of points and errorbars to merge pens.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The old arrays are freed and new ones allocated containing
+ * the reordered points and errorbars.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MergePens(barPtr, dataToStyle)
+ Bar *barPtr;
+ PenStyle **dataToStyle;
+{
+ BarPenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+
+ if (Blt_ChainGetLength(barPtr->palette) < 2) {
+ linkPtr = Blt_ChainFirstLink(barPtr->palette);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->nRects = barPtr->nRects;
+ stylePtr->rectangles = barPtr->rectangles;
+ stylePtr->symbolSize = barPtr->rectangles->width / 2;
+ stylePtr->xErrorBarCnt = barPtr->xErrorBarCnt;
+ stylePtr->xErrorBars = barPtr->xErrorBars;
+ stylePtr->yErrorBarCnt = barPtr->yErrorBarCnt;
+ stylePtr->yErrorBars = barPtr->yErrorBars;
+ return;
+ }
+ /* We have more than one style. Group bar segments of like pen
+ * styles together. */
+
+ if (barPtr->nRects > 0) {
+ XRectangle *rectangles;
+ int *rectToData;
+ int dataIndex;
+ register XRectangle *rectPtr;
+ register int *indexPtr;
+ register int i;
+
+ rectangles = Blt_Malloc(barPtr->nRects * sizeof(XRectangle));
+ rectToData = Blt_Malloc(barPtr->nRects * sizeof(int));
+ assert(rectangles && rectToData);
+
+ rectPtr = rectangles, indexPtr = rectToData;
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolSize = rectPtr->width / 2;
+ stylePtr->rectangles = rectPtr;
+ for (i = 0; i < barPtr->nRects; i++) {
+ dataIndex = barPtr->rectToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *rectPtr++ = barPtr->rectangles[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->nRects = rectPtr - stylePtr->rectangles;
+ }
+ Blt_Free(barPtr->rectangles);
+ barPtr->rectangles = rectangles;
+ Blt_Free(barPtr->rectToData);
+ barPtr->rectToData = rectToData;
+ }
+ if (barPtr->xErrorBarCnt > 0) {
+ Segment2D *errorBars, *segPtr;
+ int *errorToData, *indexPtr;
+ int dataIndex;
+ register int i;
+
+ errorBars = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(Segment2D));
+ errorToData = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(int));
+ assert(errorBars);
+ segPtr = errorBars, indexPtr = errorToData;
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->xErrorBars = segPtr;
+ for (i = 0; i < barPtr->xErrorBarCnt; i++) {
+ dataIndex = barPtr->xErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = barPtr->xErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
+ }
+ Blt_Free(barPtr->xErrorBars);
+ barPtr->xErrorBars = errorBars;
+ Blt_Free(barPtr->xErrorToData);
+ barPtr->xErrorToData = errorToData;
+ }
+ if (barPtr->yErrorBarCnt > 0) {
+ Segment2D *errorBars, *segPtr;
+ int *errorToData, *indexPtr;
+ int dataIndex;
+ register int i;
+
+ errorBars = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(Segment2D));
+ errorToData = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(int));
+ assert(errorBars);
+ segPtr = errorBars, indexPtr = errorToData;
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->yErrorBars = segPtr;
+ for (i = 0; i < barPtr->yErrorBarCnt; i++) {
+ dataIndex = barPtr->yErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = barPtr->yErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
+ }
+ Blt_Free(barPtr->yErrorBars);
+ barPtr->yErrorBars = errorBars;
+ Blt_Free(barPtr->yErrorToData);
+ barPtr->yErrorToData = errorToData;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapActiveBars --
+ *
+ * Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the active point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapActiveBars(barPtr)
+ Bar *barPtr;
+{
+ if (barPtr->activeRects != NULL) {
+ Blt_Free(barPtr->activeRects);
+ barPtr->activeRects = NULL;
+ }
+ if (barPtr->activeToData != NULL) {
+ Blt_Free(barPtr->activeToData);
+ barPtr->activeToData = NULL;
+ }
+ barPtr->nActive = 0;
+
+ if (barPtr->nReqActive > 0) {
+ XRectangle *activeRects;
+ int *activeToData;
+ register int i, n;
+ register int count;
+
+ activeRects = Blt_Malloc(sizeof(XRectangle) * barPtr->nReqActive);
+ assert(activeRects);
+ activeToData = Blt_Malloc(sizeof(int) * barPtr->nReqActive);
+ assert(activeToData);
+ count = 0;
+ for (i = 0; i < barPtr->nRects; i++) {
+ for (n = 0; n < barPtr->nReqActive; n++) {
+ if (barPtr->rectToData[i] == barPtr->reqActive[n]) {
+ activeRects[count] = barPtr->rectangles[i];
+ activeToData[count] = i;
+ count++;
+ }
+ }
+ }
+ barPtr->nActive = count;
+ barPtr->activeRects = activeRects;
+ barPtr->activeToData = activeToData;
+ }
+ barPtr->flags &= ~ACTIVE_PENDING;
+}
+
+static void
+ResetBar(barPtr)
+ Bar *barPtr;
+{
+ /* Release any storage associated with the display of the bar */
+ ClearPalette(barPtr->palette);
+ if (barPtr->activeRects != NULL) {
+ Blt_Free(barPtr->activeRects);
+ }
+ if (barPtr->activeToData != NULL) {
+ Blt_Free(barPtr->activeToData);
+ }
+ if (barPtr->xErrorBars != NULL) {
+ Blt_Free(barPtr->xErrorBars);
+ }
+ if (barPtr->xErrorToData != NULL) {
+ Blt_Free(barPtr->xErrorToData);
+ }
+ if (barPtr->yErrorBars != NULL) {
+ Blt_Free(barPtr->yErrorBars);
+ }
+ if (barPtr->yErrorToData != NULL) {
+ Blt_Free(barPtr->yErrorToData);
+ }
+ if (barPtr->rectangles != NULL) {
+ Blt_Free(barPtr->rectangles);
+ }
+ if (barPtr->rectToData != NULL) {
+ Blt_Free(barPtr->rectToData);
+ }
+ barPtr->activeToData = barPtr->xErrorToData = barPtr->yErrorToData =
+ barPtr->rectToData = NULL;
+ barPtr->activeRects = barPtr->rectangles = NULL;
+ barPtr->xErrorBars = barPtr->yErrorBars = NULL;
+ barPtr->nActive = barPtr->xErrorBarCnt = barPtr->yErrorBarCnt =
+ barPtr->nRects = 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapBar --
+ *
+ * Calculates the actual window coordinates of the bar element.
+ * The window coordinates are saved in the bar element structure.
+ *
+ * Results:
+ * None.
+ *
+ * Notes:
+ * A bar can have multiple segments (more than one x,y pairs).
+ * In this case, the bar can be represented as either a set of
+ * non-contiguous bars or a single multi-segmented (stacked) bar.
+ *
+ * The x-axis layout for a barchart may be presented in one of
+ * two ways. If abscissas are used, the bars are placed at those
+ * coordinates. Otherwise, the range will represent the number
+ * of values.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapBar(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+ FreqKey key;
+ PenStyle **dataToStyle;
+ Point2D c1, c2; /* Two opposite corners of the rectangle
+ * in graph coordinates. */
+ double *x, *y;
+ double barWidth, barOffset;
+ double baseline;
+ int *rectToData; /* Maps rectangles to data point indices */
+ int height;
+ int invertBar;
+ int nPoints, count;
+ register XRectangle *rectPtr, *rectangles;
+ register int i;
+
+ ResetBar(barPtr);
+ nPoints = NumberOfPoints(barPtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ barWidth = graphPtr->barWidth;
+ if (barPtr->barWidth > 0.0) {
+ barWidth = barPtr->barWidth;
+ }
+ baseline = (barPtr->axes.y->logScale) ? 1.0 : graphPtr->baseline;
+ barOffset = barWidth * 0.5;
+
+ /*
+ * Create an array of rectangles representing the screen coordinates
+ * of all the segments in the bar.
+ */
+ rectPtr = rectangles = Blt_Malloc(nPoints * sizeof(XRectangle));
+ assert(rectangles);
+ rectToData = Blt_Calloc(nPoints, sizeof(int));
+ assert(rectToData);
+
+ x = barPtr->x.valueArr, y = barPtr->y.valueArr;
+ count = 0;
+ for (i = 0; i < nPoints; i++) {
+ if (((x[i] - barWidth) > barPtr->axes.x->tickRange.max) ||
+ ((x[i] + barWidth) < barPtr->axes.x->tickRange.min)) {
+ continue; /* Abscissa is out of range of the x-axis */
+ }
+ c1.x = x[i] - barOffset;
+ c1.y = y[i];
+ c2.x = c1.x + barWidth;
+ c2.y = baseline;
+
+ /*
+ * If the mode is "aligned" or "stacked" we need to adjust the
+ * x or y coordinates of the two corners.
+ */
+
+ if ((graphPtr->nStacks > 0) && (graphPtr->mode != MODE_INFRONT)) {
+ Blt_HashEntry *hPtr;
+
+ key.value = x[i];
+ key.axes = barPtr->axes;
+ hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
+ if (hPtr != NULL) {
+ FreqInfo *infoPtr;
+ double slice, width;
+
+ infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
+ switch (graphPtr->mode) {
+ case MODE_STACKED:
+ c2.y = infoPtr->lastY;
+ c1.y += c2.y;
+ infoPtr->lastY = c1.y;
+ break;
+
+ case MODE_ALIGNED:
+ infoPtr->count++;
+ slice = barWidth / (double)infoPtr->freq;
+ c1.x += slice * (infoPtr->freq - infoPtr->count);
+ c2.x = c1.x + slice;
+ break;
+
+ case MODE_OVERLAP:
+ infoPtr->count++;
+ slice = barWidth / (double)(infoPtr->freq * 2);
+ width = slice * (infoPtr->freq + 1);
+ c1.x += slice * (infoPtr->freq - infoPtr->count);
+ c2.x = c1.x + width;
+ break;
+ case MODE_INFRONT:
+ break;
+ }
+ }
+ }
+ invertBar = FALSE;
+ if (c1.y < c2.y) {
+ double temp;
+
+ /* Handle negative bar values by swapping ordinates */
+ temp = c1.y, c1.y = c2.y, c2.y = temp;
+ invertBar = TRUE;
+ }
+ /*
+ * Get the two corners of the bar segment and compute the rectangle
+ */
+ c1 = Blt_Map2D(graphPtr, c1.x, c1.y, &barPtr->axes);
+ c2 = Blt_Map2D(graphPtr, c2.x, c2.y, &barPtr->axes);
+
+ /* Bound the bars vertically by the size of the graph window */
+ if (c1.y < 0.0) {
+ c1.y = 0.0;
+ } else if (c1.y > (double)graphPtr->height) {
+ c1.y = (double)graphPtr->height;
+ }
+ if (c2.y < 0.0) {
+ c2.y = 0.0;
+ } else if (c2.y > (double)graphPtr->height) {
+ c2.y = (double)graphPtr->height;
+ }
+ height = (int)Round(Fabs(c1.y - c2.y));
+ if (invertBar) {
+ rectPtr->y = (short int)MIN(c1.y, c2.y);
+ } else {
+ rectPtr->y = (short int)(MAX(c1.y, c2.y)) - height;
+ }
+ rectPtr->x = (short int)MIN(c1.x, c2.x);
+ rectPtr->width = (short int)Round(Fabs(c1.x - c2.x)) + 1;
+ if (rectPtr->width < 1) {
+ rectPtr->width = 1;
+ }
+ rectPtr->height = height + 1;
+ if (rectPtr->height < 1) {
+ rectPtr->height = 1;
+ }
+ rectToData[count] = i; /* Save the data index corresponding to the
+ * rectangle */
+ rectPtr++;
+ count++;
+ }
+ barPtr->nRects = count;
+ barPtr->rectangles = rectangles;
+ barPtr->rectToData = rectToData;
+ if (barPtr->nReqActive > 0) {
+ MapActiveBars(barPtr);
+ }
+ {
+ int symbolSize;
+ Blt_ChainLink *linkPtr;
+ PenStyle *stylePtr;
+
+ symbolSize = 20;
+ if (count > 0) {
+ symbolSize = rectangles->width;
+ symbolSize |= 0x01;
+ }
+ /* Set the symbol size of all the pen styles. */
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolSize = symbolSize;
+ }
+ }
+ dataToStyle = Blt_StyleMap((Element *)barPtr);
+ if (((barPtr->yHigh.nValues > 0) && (barPtr->yLow.nValues > 0)) ||
+ ((barPtr->xHigh.nValues > 0) && (barPtr->xLow.nValues > 0)) ||
+ (barPtr->xError.nValues > 0) || (barPtr->yError.nValues > 0)) {
+ Blt_MapErrorBars(graphPtr, (Element *)barPtr, dataToStyle);
+ }
+ MergePens(barPtr, dataToStyle);
+ Blt_Free(dataToStyle);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbol --
+ *
+ * Draw a symbol centered at the given x,y window coordinate
+ * based upon the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when
+ * calculating the centered position of the symbol.
+ * -----------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr;
+ int x, y;
+ int size;
+{
+ BarPen *penPtr = ((Bar *)elemPtr)->normalPenPtr;
+ int radius;
+
+ radius = (size / 2);
+ size--;
+
+ x -= radius;
+ y -= radius;
+ XSetTSOrigin(graphPtr->display, penPtr->gc, x, y);
+ XFillRectangle(graphPtr->display, drawable, penPtr->gc, x, y,
+ size, size);
+ XSetTSOrigin(graphPtr->display, penPtr->gc, 0, 0);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSegments --
+ *
+ * Draws each of the rectangular segments for the element.
+ *
+ * Results:
+ * None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSegments(graphPtr, drawable, penPtr, rectangles, nRects)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ BarPen *penPtr;
+ XRectangle *rectangles;
+ int nRects;
+{
+ register XRectangle *rectPtr;
+
+ XFillRectangles(graphPtr->display, drawable, penPtr->gc, rectangles, nRects);
+ if ((penPtr->borderWidth > 0) && (penPtr->relief != TK_RELIEF_FLAT)) {
+ XRectangle *endPtr;
+
+ for (rectPtr = rectangles, endPtr = rectangles + nRects;
+ rectPtr < endPtr; rectPtr++) {
+ Tk_Draw3DRectangle(graphPtr->tkwin, drawable, penPtr->border,
+ rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+ penPtr->borderWidth, penPtr->relief);
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawValues --
+ *
+ * Draws the numeric value of the bar.
+ *
+ * Results:
+ * None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawValues(graphPtr, drawable, barPtr, penPtr, rectangles, nRects, rectToData)
+ Graph *graphPtr;
+ Drawable drawable;
+ Bar *barPtr;
+ BarPen *penPtr;
+ int nRects;
+ XRectangle *rectangles;
+ int *rectToData;
+{
+ XRectangle *rectPtr, *endPtr;
+ int count;
+ char *fmt;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ double x, y;
+ Point2D anchorPos;
+
+ count = 0;
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr;
+ rectPtr++) {
+ x = barPtr->x.valueArr[rectToData[count]];
+ y = barPtr->y.valueArr[rectToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ if (graphPtr->inverted) {
+ anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
+ anchorPos.x = rectPtr->x + rectPtr->width;
+ if (y < graphPtr->baseline) {
+ anchorPos.x -= rectPtr->width;
+ }
+ } else {
+ anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
+ anchorPos.y = rectPtr->y;
+ if (y < graphPtr->baseline) {
+ anchorPos.y += rectPtr->height;
+ }
+ }
+ Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle),
+ (int)anchorPos.x, (int)anchorPos.y);
+ }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawNormalBar --
+ *
+ * Draws the rectangle representing the bar element. If the
+ * relief option is set to "raised" or "sunken" and the bar
+ * borderwidth is set (borderwidth > 0), a 3D border is drawn
+ * around the bar.
+ *
+ * Don't draw bars that aren't visible (i.e. within the limits
+ * of the axis).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawNormalBar(graphPtr, drawable, elemPtr)
+ Graph *graphPtr;
+ Drawable drawable;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+ int count;
+ Blt_ChainLink *linkPtr;
+ register BarPenStyle *stylePtr;
+ BarPen *penPtr;
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if (stylePtr->nRects > 0) {
+ DrawSegments(graphPtr, drawable, penPtr, stylePtr->rectangles,
+ stylePtr->nRects);
+ }
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, barPtr, penPtr,
+ stylePtr->rectangles, stylePtr->nRects,
+ barPtr->rectToData + count);
+ }
+ count += stylePtr->nRects;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawActiveBar --
+ *
+ * Draws rectangles representing the active segments of the
+ * bar element. If the -relief option is set (other than "flat")
+ * and the borderwidth is greater than 0, a 3D border is drawn
+ * around the each bar segment.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawActiveBar(graphPtr, drawable, elemPtr)
+ Graph *graphPtr;
+ Drawable drawable;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+
+ if (barPtr->activePenPtr != NULL) {
+ BarPen *penPtr = barPtr->activePenPtr;
+
+ if (barPtr->nReqActive > 0) {
+ if (barPtr->flags & ACTIVE_PENDING) {
+ MapActiveBars(barPtr);
+ }
+ DrawSegments(graphPtr, drawable, penPtr, barPtr->activeRects,
+ barPtr->nActive);
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, barPtr, penPtr,
+ barPtr->nActive, barPtr->activeRects,
+ barPtr->activeToData);
+ }
+ } else if (barPtr->nReqActive < 0) {
+ DrawSegments(graphPtr, drawable, penPtr, barPtr->rectangles,
+ barPtr->nRects);
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, barPtr, penPtr, barPtr->nRects,
+ barPtr->rectangles, barPtr->rectToData);
+ }
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * Draw a symbol centered at the given x,y window coordinate
+ * based upon the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when
+ * calculating the centered position of the symbol.
+ *
+ * -----------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+ int size;
+ double x, y;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+
+ /*
+ * Build a PostScript procedure to draw the fill and outline of
+ * the symbol after the path of the symbol shape has been formed
+ */
+ Blt_AppendToPostScript(psToken, "\n",
+ "/DrawSymbolProc {\n",
+ " gsave\n ", (char *)NULL);
+ if (barPtr->normalPenPtr->stipple != None) {
+ Blt_BackgroundToPostScript(psToken,
+ Tk_3DBorderColor(barPtr->normalPenPtr->border));
+ Blt_AppendToPostScript(psToken, " Fill\n ", (char *)NULL);
+ Blt_ForegroundToPostScript(psToken, barPtr->normalPenPtr->fg);
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ barPtr->normalPenPtr->stipple);
+ } else {
+ Blt_ForegroundToPostScript(psToken, barPtr->normalPenPtr->fg);
+ Blt_AppendToPostScript(psToken, " fill\n", (char *)NULL);
+ }
+ Blt_AppendToPostScript(psToken, " grestore\n", (char *)NULL);
+ Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "%g %g %d Sq\n", x, y, size);
+}
+
+static void
+SegmentsToPostScript(graphPtr, psToken, penPtr, rectPtr, nRects)
+ Graph *graphPtr;
+ PsToken psToken;
+ BarPen *penPtr;
+ register XRectangle *rectPtr;
+ int nRects;
+{
+ XRectangle *endPtr;
+
+ for (endPtr = rectPtr + nRects; rectPtr < endPtr; rectPtr++) {
+ if ((rectPtr->width < 1) || (rectPtr->height < 1)) {
+ continue;
+ }
+ if (penPtr->stipple != None) {
+ Blt_RegionToPostScript(psToken,
+ (double)rectPtr->x, (double)rectPtr->y,
+ (int)rectPtr->width - 1, (int)rectPtr->height - 1);
+ Blt_BackgroundToPostScript(psToken,
+ Tk_3DBorderColor(penPtr->border));
+ Blt_AppendToPostScript(psToken, "Fill\n");
+ Blt_ForegroundToPostScript(psToken, penPtr->fg);
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ penPtr->stipple);
+ } else {
+ Blt_ForegroundToPostScript(psToken, penPtr->fg);
+ Blt_RectangleToPostScript(psToken,
+ (double)rectPtr->x, (double)rectPtr->y,
+ (int)rectPtr->width - 1, (int)rectPtr->height - 1);
+ }
+ if ((penPtr->borderWidth > 0) && (penPtr->relief != TK_RELIEF_FLAT)) {
+ Blt_Draw3DRectangleToPostScript(psToken, penPtr->border,
+ (double)rectPtr->x, (double)rectPtr->y,
+ (int)rectPtr->width, (int)rectPtr->height,
+ penPtr->borderWidth, penPtr->relief);
+ }
+ }
+}
+
+static void
+ValuesToPostScript(graphPtr, psToken, barPtr, penPtr, rectangles, nRects,
+ rectToData)
+ Graph *graphPtr;
+ PsToken psToken;
+ Bar *barPtr;
+ BarPen *penPtr;
+ int nRects;
+ XRectangle *rectangles;
+ int *rectToData;
+{
+ XRectangle *rectPtr, *endPtr;
+ int count;
+ char *fmt;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ double x, y;
+ Point2D anchorPos;
+
+ count = 0;
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr;
+ rectPtr++) {
+ x = barPtr->x.valueArr[rectToData[count]];
+ y = barPtr->y.valueArr[rectToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ if (graphPtr->inverted) {
+ anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
+ anchorPos.x = rectPtr->x + rectPtr->width;
+ if (y < graphPtr->baseline) {
+ anchorPos.x -= rectPtr->width;
+ }
+ } else {
+ anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
+ anchorPos.y = rectPtr->y;
+ if (y < graphPtr->baseline) {
+ anchorPos.y += rectPtr->height;
+ }
+ }
+ Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle),
+ anchorPos.x, anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ActiveBarToPostScript --
+ *
+ * Similar to the NormalBarToPostScript procedure, generates
+ * PostScript commands to display the rectangles representing the
+ * active bar segments of the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ActiveBarToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+
+ if (barPtr->activePenPtr != NULL) {
+ BarPen *penPtr = barPtr->activePenPtr;
+
+ if (barPtr->nReqActive > 0) {
+ if (barPtr->flags & ACTIVE_PENDING) {
+ MapActiveBars(barPtr);
+ }
+ SegmentsToPostScript(graphPtr, psToken, penPtr,
+ barPtr->activeRects, barPtr->nActive);
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(graphPtr, psToken, barPtr, penPtr,
+ barPtr->nActive, barPtr->activeRects,
+ barPtr->activeToData);
+ }
+ } else if (barPtr->nReqActive < 0) {
+ SegmentsToPostScript(graphPtr, psToken, penPtr,
+ barPtr->rectangles, barPtr->nRects);
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(graphPtr, psToken, barPtr, penPtr,
+ barPtr->nRects, barPtr->rectangles, barPtr->rectToData);
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NormalBarToPostScript --
+ *
+ * Generates PostScript commands to form the rectangles
+ * representing the segments of the bar element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+NormalBarToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+ Blt_ChainLink *linkPtr;
+ register BarPenStyle *stylePtr;
+ int count;
+ BarPen *penPtr;
+ XColor *colorPtr;
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if (stylePtr->nRects > 0) {
+ SegmentsToPostScript(graphPtr, psToken, penPtr,
+ stylePtr->rectangles, stylePtr->nRects);
+ }
+ colorPtr = penPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = penPtr->fg;
+ }
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->xErrorBars,
+ stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->yErrorBars,
+ stylePtr->yErrorBarCnt);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(graphPtr, psToken, barPtr, penPtr,
+ stylePtr->nRects, stylePtr->rectangles,
+ barPtr->rectToData + count);
+ }
+ count += stylePtr->nRects;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyBar --
+ *
+ * Release memory and resources allocated for the bar element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the bar element is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+#define FreeElemVector(v) \
+ if ((v).clientId != NULL) { \
+ Blt_FreeVectorId((v).clientId); \
+ } else if ((v).valueArr != NULL) { \
+ Blt_Free((v).valueArr); \
+ }
+
+static void
+DestroyBar(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Bar *barPtr = (Bar *)elemPtr;
+
+ if (barPtr->normalPenPtr != &(barPtr->builtinPen)) {
+ Blt_FreePen(graphPtr, (Pen *)barPtr->normalPenPtr);
+ }
+ DestroyPen(graphPtr, (Pen *)&(barPtr->builtinPen));
+ if (barPtr->activePenPtr != NULL) {
+ Blt_FreePen(graphPtr, (Pen *)barPtr->activePenPtr);
+ }
+ FreeElemVector(barPtr->x);
+ FreeElemVector(barPtr->y);
+ FreeElemVector(barPtr->w);
+ FreeElemVector(barPtr->xHigh);
+ FreeElemVector(barPtr->xLow);
+ FreeElemVector(barPtr->xError);
+ FreeElemVector(barPtr->yHigh);
+ FreeElemVector(barPtr->yLow);
+ FreeElemVector(barPtr->yError);
+
+ ResetBar(barPtr);
+ if (barPtr->reqActive != NULL) {
+ Blt_Free(barPtr->reqActive);
+ }
+ if (barPtr->palette != NULL) {
+ Blt_FreePalette(graphPtr, barPtr->palette);
+ Blt_ChainDestroy(barPtr->palette);
+ }
+ if (barPtr->tags != NULL) {
+ Blt_Free(barPtr->tags);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_BarElement --
+ *
+ * Allocate memory and initialize methods for the new bar element.
+ *
+ * Results:
+ * The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the bar element structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static ElementProcs barProcs =
+{
+ ClosestBar,
+ ConfigureBar,
+ DestroyBar,
+ DrawActiveBar,
+ DrawNormalBar,
+ DrawSymbol,
+ GetBarExtents,
+ ActiveBarToPostScript,
+ NormalBarToPostScript,
+ SymbolToPostScript,
+ MapBar,
+};
+
+
+Element *
+Blt_BarElement(graphPtr, name, type)
+ Graph *graphPtr;
+ char *name;
+ Tk_Uid type;
+{
+ register Bar *barPtr;
+
+ barPtr = Blt_Calloc(1, sizeof(Bar));
+ assert(barPtr);
+ barPtr->normalPenPtr = &(barPtr->builtinPen);
+ barPtr->procsPtr = &barProcs;
+ barPtr->configSpecs = barElemConfigSpecs;
+ barPtr->labelRelief = TK_RELIEF_FLAT;
+ barPtr->classUid = type;
+ /* By default, an element's name and label are the same. */
+ barPtr->label = Blt_Strdup(name);
+ barPtr->name = Blt_Strdup(name);
+
+ barPtr->graphPtr = graphPtr;
+ barPtr->hidden = FALSE;
+
+ InitPen(barPtr->normalPenPtr);
+ barPtr->palette = Blt_ChainCreate();
+ return (Element *)barPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InitFreqTable --
+ *
+ * Generate a table of abscissa frequencies. Duplicate
+ * x-coordinates (depending upon the bar drawing mode) indicate
+ * that something special should be done with each bar segment
+ * mapped to the same abscissa (i.e. it should be stacked,
+ * aligned, or overlay-ed with other segments)
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the bar element structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_InitFreqTable(graphPtr)
+ Graph *graphPtr;
+{
+ register Element *elemPtr;
+ Blt_ChainLink *linkPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Bar *barPtr;
+ int isNew, count;
+ int nStacks, nSegs;
+ int nPoints;
+ FreqKey key;
+ Blt_HashTable freqTable;
+ register int i;
+ double *xArr;
+ /*
+ * Free resources associated with a previous frequency table. This
+ * includes the array of frequency information and the table itself
+ */
+ if (graphPtr->freqArr != NULL) {
+ Blt_Free(graphPtr->freqArr);
+ graphPtr->freqArr = NULL;
+ }
+ if (graphPtr->nStacks > 0) {
+ Blt_DeleteHashTable(&(graphPtr->freqTable));
+ graphPtr->nStacks = 0;
+ }
+ if (graphPtr->mode == MODE_INFRONT) {
+ return; /* No frequency table is needed for
+ * "infront" mode */
+ }
+ Blt_InitHashTable(&(graphPtr->freqTable), sizeof(FreqKey) / sizeof(int));
+
+ /*
+ * Initialize a hash table and fill it with unique abscissas.
+ * Keep track of the frequency of each x-coordinate and how many
+ * abscissas have duplicate mappings.
+ */
+ Blt_InitHashTable(&freqTable, sizeof(FreqKey) / sizeof(int));
+ nSegs = nStacks = 0;
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
+ continue;
+ }
+ nSegs++;
+ barPtr = (Bar *)elemPtr;
+ xArr = barPtr->x.valueArr;
+ nPoints = NumberOfPoints(barPtr);
+ for (i = 0; i < nPoints; i++) {
+ key.value = xArr[i];
+ key.axes = barPtr->axes;
+ hPtr = Blt_CreateHashEntry(&freqTable, (char *)&key, &isNew);
+ assert(hPtr != NULL);
+ if (isNew) {
+ count = 1;
+ } else {
+ count = (int)Blt_GetHashValue(hPtr);
+ if (count == 1) {
+ nStacks++;
+ }
+ count++;
+ }
+ Blt_SetHashValue(hPtr, (ClientData)count);
+ }
+ }
+ if (nSegs == 0) {
+ return; /* No bar elements to be displayed */
+ }
+ if (nStacks > 0) {
+ FreqInfo *infoPtr;
+ FreqKey *keyPtr;
+ Blt_HashEntry *h2Ptr;
+
+ graphPtr->freqArr = Blt_Calloc(nStacks, sizeof(FreqInfo));
+ assert(graphPtr->freqArr);
+ infoPtr = graphPtr->freqArr;
+ for (hPtr = Blt_FirstHashEntry(&freqTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ count = (int)Blt_GetHashValue(hPtr);
+ keyPtr = (FreqKey *)Blt_GetHashKey(&freqTable, hPtr);
+ if (count > 1) {
+ h2Ptr = Blt_CreateHashEntry(&(graphPtr->freqTable),
+ (char *)keyPtr, &isNew);
+ count = (int)Blt_GetHashValue(hPtr);
+ infoPtr->freq = count;
+ infoPtr->axes = keyPtr->axes;
+ Blt_SetHashValue(h2Ptr, infoPtr);
+ infoPtr++;
+ }
+ }
+ }
+ Blt_DeleteHashTable(&freqTable);
+ graphPtr->nStacks = nStacks;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_ComputeStacks --
+ *
+ * Determine the height of each stack of bar segments. A stack
+ * is created by designating two or more points with the same
+ * abscissa. Each ordinate defines the height of a segment in
+ * the stack. This procedure simply looks at all the data points
+ * summing the heights of each stacked segment. The sum is saved
+ * in the frequency information table. This value will be used
+ * to calculate the y-axis limits (data limits aren't sufficient).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The heights of each stack is computed. CheckStacks will
+ * use this information to adjust the y-axis limits if necessary.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_ComputeStacks(graphPtr)
+ Graph *graphPtr;
+{
+ Element *elemPtr;
+ Bar *barPtr;
+ FreqKey key;
+ Blt_ChainLink *linkPtr;
+ Blt_HashEntry *hPtr;
+ int nPoints;
+ register int i;
+ register FreqInfo *infoPtr;
+ double *xArr, *yArr;
+
+ if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
+ return;
+ }
+ /* Reset the sums for all duplicate values to zero. */
+
+ infoPtr = graphPtr->freqArr;
+ for (i = 0; i < graphPtr->nStacks; i++) {
+ infoPtr->sum = 0.0;
+ infoPtr++;
+ }
+
+ /* Look at each bar point, adding the ordinates of duplicate abscissas */
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
+ continue;
+ }
+ barPtr = (Bar *)elemPtr;
+ xArr = barPtr->x.valueArr;
+ yArr = barPtr->y.valueArr;
+ nPoints = NumberOfPoints(barPtr);
+ for (i = 0; i < nPoints; i++) {
+ key.value = xArr[i];
+ key.axes = barPtr->axes;
+ hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
+ if (hPtr == NULL) {
+ continue;
+ }
+ infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
+ infoPtr->sum += yArr[i];
+ }
+ }
+}
+
+void
+Blt_ResetStacks(graphPtr)
+ Graph *graphPtr;
+{
+ register FreqInfo *infoPtr, *endPtr;
+
+ for (infoPtr = graphPtr->freqArr,
+ endPtr = graphPtr->freqArr + graphPtr->nStacks;
+ infoPtr < endPtr; infoPtr++) {
+ infoPtr->lastY = 0.0;
+ infoPtr->count = 0;
+ }
+}
+
diff --git a/blt/src/bltGrElem.c b/blt/src/bltGrElem.c
new file mode 100644
index 00000000000..5ecb6411ace
--- /dev/null
+++ b/blt/src/bltGrElem.c
@@ -0,0 +1,2221 @@
+
+/*
+ * bltGrElem.c --
+ *
+ * This module implements generic elements for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+
+
+static Tk_OptionParseProc StringToData;
+static Tk_OptionPrintProc DataToString;
+static Tk_OptionParseProc StringToDataPairs;
+static Tk_OptionPrintProc DataPairsToString;
+static Tk_OptionParseProc StringToAlong;
+static Tk_OptionPrintProc AlongToString;
+static Tk_CustomOption alongOption =
+{
+ StringToAlong, AlongToString, (ClientData)0
+};
+Tk_CustomOption bltDataOption =
+{
+ StringToData, DataToString, (ClientData)0
+};
+Tk_CustomOption bltDataPairsOption =
+{
+ StringToDataPairs, DataPairsToString, (ClientData)0
+};
+extern Tk_CustomOption bltDistanceOption;
+
+
+static int counter;
+
+#include "bltGrElem.h"
+
+extern Element *Blt_BarElement();
+extern Element *Blt_LineElement();
+
+#ifdef __STDC__
+static Blt_VectorChangedProc VectorChangedProc;
+#endif /* __STDC__ */
+
+EXTERN int Blt_VectorExists2 _ANSI_ARGS_((Tcl_Interp *interp, char *vecName));
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+static void
+SyncElemVector(vPtr)
+ ElemVector *vPtr;
+{
+ vPtr->nValues = Blt_VecLength(vPtr->vecPtr);
+ vPtr->valueArr = Blt_VecData(vPtr->vecPtr);
+ vPtr->min = Blt_VecMin(vPtr->vecPtr);
+ vPtr->max = Blt_VecMax(vPtr->vecPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindRange --
+ *
+ * Find the minimum, positive minimum, and maximum values in a
+ * given vector and store the results in the vector structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Minimum, positive minimum, and maximum values are stored in
+ * the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FindRange(vPtr)
+ ElemVector *vPtr;
+{
+ register int i;
+ register double *x;
+ register double min, max;
+
+ if ((vPtr->nValues < 1) || (vPtr->valueArr == NULL)) {
+ return; /* This shouldn't ever happen. */
+ }
+ x = vPtr->valueArr;
+
+ min = DBL_MAX, max = -DBL_MAX;
+ for(i = 0; i < vPtr->nValues; i++) {
+ if (finite(x[i])) {
+ min = max = x[i];
+ break;
+ }
+ }
+ /* Initialize values to track the vector range */
+ for (/* empty */; i < vPtr->nValues; i++) {
+ if (finite(x[i])) {
+ if (x[i] < min) {
+ min = x[i];
+ } else if (x[i] > max) {
+ max = x[i];
+ }
+ }
+ }
+ vPtr->min = min, vPtr->max = max;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindElemVectorMinimum --
+ *
+ * Find the minimum, positive minimum, and maximum values in a
+ * given vector and store the results in the vector structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Minimum, positive minimum, and maximum values are stored in
+ * the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+double
+Blt_FindElemVectorMinimum(vPtr, minLimit)
+ ElemVector *vPtr;
+ double minLimit;
+{
+ register int i;
+ register double *arr;
+ register double min, x;
+
+ min = DBL_MAX;
+ arr = vPtr->valueArr;
+ for (i = 0; i < vPtr->nValues; i++) {
+ x = arr[i];
+ if (x < 0.0) {
+ /* What do you do about negative values when using log
+ * scale values seems like a grey area. Mirror. */
+ x = -x;
+ }
+ if ((x > minLimit) && (min > x)) {
+ min = x;
+ }
+ }
+ if (min == DBL_MAX) {
+ min = minLimit;
+ }
+ return min;
+}
+
+static void
+FreeDataVector(vPtr)
+ ElemVector *vPtr;
+{
+ if (vPtr->clientId != NULL) {
+ Blt_FreeVectorId(vPtr->clientId); /* Free the old vector */
+ vPtr->clientId = NULL;
+ } else if (vPtr->valueArr != NULL) {
+ Blt_Free(vPtr->valueArr);
+ }
+ vPtr->valueArr = NULL;
+ vPtr->nValues = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorChangedProc --
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Graph is redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+VectorChangedProc(interp, clientData, notify)
+ Tcl_Interp *interp;
+ ClientData clientData;
+ Blt_VectorNotify notify;
+{
+ ElemVector *vPtr = clientData;
+ Element *elemPtr = vPtr->elemPtr;
+ Graph *graphPtr = elemPtr->graphPtr;
+
+ switch (notify) {
+ case BLT_VECTOR_NOTIFY_DESTROY:
+ vPtr->clientId = NULL;
+ vPtr->valueArr = NULL;
+ vPtr->nValues = 0;
+ break;
+
+ case BLT_VECTOR_NOTIFY_UPDATE:
+ default:
+ Blt_GetVectorById(interp, vPtr->clientId, &(vPtr->vecPtr));
+ SyncElemVector(vPtr);
+ break;
+ }
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ if (!elemPtr->hidden) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+static int
+EvalExprList(interp, list, nElemPtr, arrayPtr)
+ Tcl_Interp *interp;
+ char *list;
+ int *nElemPtr;
+ double **arrayPtr;
+{
+ int nElem;
+ char **elemArr;
+ double *array;
+ int result;
+
+ result = TCL_ERROR;
+ elemArr = NULL;
+ if (Tcl_SplitList(interp, list, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ array = NULL;
+ if (nElem > 0) {
+ register double *valuePtr;
+ register int i;
+
+ counter++;
+ array = Blt_Malloc(sizeof(double) * nElem);
+ if (array == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL);
+ goto badList;
+ }
+ valuePtr = array;
+ for (i = 0; i < nElem; i++) {
+ if (Tcl_ExprDouble(interp, elemArr[i], valuePtr) != TCL_OK) {
+ goto badList;
+ }
+ valuePtr++;
+ }
+ }
+ result = TCL_OK;
+
+ badList:
+ Blt_Free(elemArr);
+ *arrayPtr = array;
+ *nElemPtr = nElem;
+ if (result != TCL_OK) {
+ Blt_Free(array);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToData --
+ *
+ * Given a Tcl list of numeric expression representing the element
+ * values, convert into an array of double precision values. In
+ * addition, the minimum and maximum values are saved. Since
+ * elastic values are allow (values which translate to the
+ * min/max of the graph), we must try to get the non-elastic
+ * minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The vector is passed
+ * back via the vPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToData(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Type of axis vector to fill */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Tcl list of expressions */
+ char *widgRec; /* Element record */
+ int offset; /* Offset of vector in Element record */
+{
+ Element *elemPtr = (Element *)(widgRec);
+ ElemVector *vPtr = (ElemVector *)(widgRec + offset);
+
+ FreeDataVector(vPtr);
+ if (Blt_VectorExists2(interp, string)) {
+ Blt_VectorId clientId;
+
+ clientId = Blt_AllocVectorId(interp, string);
+ if (Blt_GetVectorById(interp, clientId, &(vPtr->vecPtr)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_SetVectorChangedProc(clientId, VectorChangedProc, vPtr);
+ vPtr->elemPtr = elemPtr;
+ vPtr->clientId = clientId;
+ SyncElemVector(vPtr);
+ } else {
+ double *newArr;
+ int nValues;
+
+ if (EvalExprList(interp, string, &nValues, &newArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nValues > 0) {
+ vPtr->valueArr = newArr;
+ }
+ vPtr->nValues = nValues;
+ FindRange(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataToString --
+ *
+ * Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DataToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Type of axis vector to print */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element record */
+ int offset; /* Offset of vector in Element record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ ElemVector *vPtr = (ElemVector *)(widgRec + offset);
+ Element *elemPtr = (Element *)(widgRec);
+ Tcl_DString dString;
+ char *result;
+ char string[TCL_DOUBLE_SPACE + 1];
+ double *p, *endPtr;
+
+ if (vPtr->clientId != NULL) {
+ return Blt_NameOfVectorId(vPtr->clientId);
+ }
+ if (vPtr->nValues == 0) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ endPtr = vPtr->valueArr + vPtr->nValues;
+ for (p = vPtr->valueArr; p < endPtr; p++) {
+ Tcl_PrintDouble(elemPtr->graphPtr->interp, *p, string);
+ Tcl_DStringAppendElement(&dString, string);
+ }
+ result = Tcl_DStringValue(&dString);
+
+ /*
+ * If memory wasn't allocated for the dynamic string, do it here (it's
+ * currently on the stack), so that Tcl can free it normally.
+ */
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDataPairs --
+ *
+ * This procedure is like StringToData except that it interprets
+ * the list of numeric expressions as X Y coordinate pairs. The
+ * minimum and maximum for both the X and Y vectors are
+ * determined.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The vectors are
+ * passed back via the widget record (elemPtr).
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDataPairs(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Tcl list of numeric expressions */
+ char *widgRec; /* Element record */
+ int offset; /* Not used. */
+{
+ Element *elemPtr = (Element *)widgRec;
+ int nElem;
+ unsigned int newSize;
+ double *newArr;
+
+ if (EvalExprList(interp, string, &nElem, &newArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem & 1) {
+ Tcl_AppendResult(interp, "odd number of data points", (char *)NULL);
+ Blt_Free(newArr);
+ return TCL_ERROR;
+ }
+ nElem /= 2;
+ newSize = nElem * sizeof(double);
+
+ FreeDataVector(&(elemPtr->x));
+ FreeDataVector(&(elemPtr->y));
+
+ elemPtr->x.valueArr = Blt_Malloc(newSize);
+ elemPtr->y.valueArr = Blt_Malloc(newSize);
+ assert(elemPtr->x.valueArr && elemPtr->y.valueArr);
+ elemPtr->x.nValues = elemPtr->y.nValues = nElem;
+
+ if (newSize > 0) {
+ register double *dataPtr;
+ register int i;
+
+ for (dataPtr = newArr, i = 0; i < nElem; i++) {
+ elemPtr->x.valueArr[i] = *dataPtr++;
+ elemPtr->y.valueArr[i] = *dataPtr++;
+ }
+ Blt_Free(newArr);
+ FindRange(&(elemPtr->x));
+ FindRange(&(elemPtr->y));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataPairsToString --
+ *
+ * Convert pairs of floating point values in the X and Y arrays
+ * into a Tcl list.
+ *
+ * Results:
+ * The return value is a string (Tcl list).
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DataPairsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Not used. */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Element *elemPtr = (Element *)widgRec;
+ Tcl_Interp *interp = elemPtr->graphPtr->interp;
+ int i;
+ int length;
+ char *result;
+ char string[TCL_DOUBLE_SPACE + 1];
+ Tcl_DString dString;
+
+ length = NumberOfPoints(elemPtr);
+ if (length < 1) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ for (i = 0; i < length; i++) {
+ Tcl_PrintDouble(interp, elemPtr->x.valueArr[i], string);
+ Tcl_DStringAppendElement(&dString, string);
+ Tcl_PrintDouble(interp, elemPtr->y.valueArr[i], string);
+ Tcl_DStringAppendElement(&dString, string);
+ }
+ result = Tcl_DStringValue(&dString);
+
+ /*
+ * If memory wasn't allocated for the dynamic string, do it here
+ * (it's currently on the stack), so that Tcl can free it
+ * normally.
+ */
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAlong --
+ *
+ * Given a Tcl list of numeric expression representing the element
+ * values, convert into an array of double precision values. In
+ * addition, the minimum and maximum values are saved. Since
+ * elastic values are allow (values which translate to the
+ * min/max of the graph), we must try to get the non-elastic
+ * minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The vector is passed
+ * back via the vPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAlong(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of value. */
+ char *widgRec; /* Widget record. */
+ int offset; /* Offset of field in widget record. */
+{
+ int *intPtr = (int *)(widgRec + offset);
+
+ if ((string[0] == 'x') && (string[1] == '\0')) {
+ *intPtr = SEARCH_X;
+ } else if ((string[0] == 'y') && (string[1] == '\0')) {
+ *intPtr = SEARCH_Y;
+ } else if ((string[0] == 'b') && (strcmp(string, "both") == 0)) {
+ *intPtr = SEARCH_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad along value \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AlongToString --
+ *
+ * Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+AlongToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in widget record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ int along = *(int *)(widgRec + offset);
+
+ switch (along) {
+ case SEARCH_X:
+ return "x";
+ case SEARCH_Y:
+ return "y";
+ case SEARCH_BOTH:
+ return "both";
+ default:
+ return "unknown along value";
+ }
+}
+
+void
+Blt_FreePalette(graphPtr, palette)
+ Graph *graphPtr;
+ Blt_Chain *palette;
+{
+ Blt_ChainLink *linkPtr;
+
+ /*
+ * Always skip the last array slot, occupied by the built-in
+ * "normal" pen of the element.
+ */
+ linkPtr = Blt_ChainFirstLink(palette);
+ if (linkPtr != NULL) {
+ register PenStyle *stylePtr;
+ Blt_ChainLink *nextPtr;
+
+ for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ Blt_FreePen(graphPtr, stylePtr->penPtr);
+ Blt_ChainDeleteLink(palette, linkPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToStyles --
+ *
+ * Parse the list of style names.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToStyles(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing style list */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+{
+ Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr = (Element *)(widgRec);
+ PenStyle *stylePtr;
+ char **elemArr;
+ int nStyles;
+ register int i;
+ size_t size = (size_t)clientData;
+
+ elemArr = NULL;
+ Blt_FreePalette(elemPtr->graphPtr, palette);
+ if ((string == NULL) || (*string == '\0')) {
+ nStyles = 0;
+ } else if (Tcl_SplitList(interp, string, &nStyles, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* The first pen is always the "normal" pen, we'll set the style later */
+ linkPtr = Blt_ChainFirstLink(palette);
+ if (linkPtr == NULL) {
+ linkPtr = Blt_ChainAllocLink(size);
+ Blt_ChainLinkBefore(palette, linkPtr, NULL);
+ }
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->penPtr = elemPtr->normalPenPtr;
+
+ for (i = 0; i < nStyles; i++) {
+ linkPtr = Blt_ChainAllocLink(size);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->weight.min = (double)i;
+ stylePtr->weight.max = (double)i + 1.0;
+ stylePtr->weight.range = 1.0;
+ if (Blt_GetPenStyle(elemPtr->graphPtr, elemArr[i], elemPtr->classUid,
+ (PenStyle *)stylePtr) != TCL_OK) {
+ Blt_Free(elemArr);
+ Blt_FreePalette(elemPtr->graphPtr, palette);
+ return TCL_ERROR;
+ }
+ Blt_ChainLinkBefore(palette, linkPtr, NULL);
+ }
+ if (elemArr != NULL) {
+ Blt_Free(elemArr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StylesToString --
+ *
+ * Convert the style information into a string.
+ *
+ * Results:
+ * The string representing the style information is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_StylesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Not used. */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
+ Tcl_DString dString;
+ char *result;
+ Blt_ChainLink *linkPtr;
+
+ Tcl_DStringInit(&dString);
+ linkPtr = Blt_ChainFirstLink(palette);
+ if (linkPtr != NULL) {
+ Element *elemPtr = (Element *)(widgRec);
+ char string[TCL_DOUBLE_SPACE];
+ Tcl_Interp *interp;
+ PenStyle *stylePtr;
+
+ interp = elemPtr->graphPtr->interp;
+ for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ Tcl_DStringStartSublist(&dString);
+ Tcl_DStringAppendElement(&dString, stylePtr->penPtr->name);
+ Tcl_PrintDouble(interp, stylePtr->weight.min, string);
+ Tcl_DStringAppendElement(&dString, string);
+ Tcl_PrintDouble(interp, stylePtr->weight.max, string);
+ Tcl_DStringAppendElement(&dString, string);
+ Tcl_DStringEndSublist(&dString);
+ }
+ }
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StyleMap --
+ *
+ * Creates an array of style indices and fills it based on the weight
+ * of each data point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+
+PenStyle **
+Blt_StyleMap(elemPtr)
+ Element *elemPtr;
+{
+ register int i;
+ int nWeights; /* Number of weights to be examined.
+ * If there are more data points than
+ * weights, they will default to the
+ * normal pen. */
+
+ PenStyle **dataToStyle; /* Directory of styles. Each array
+ * element represents the style for
+ * the data point at that index */
+ Blt_ChainLink *linkPtr;
+ PenStyle *stylePtr;
+ double *w; /* Weight vector */
+ int nPoints;
+
+ nPoints = NumberOfPoints(elemPtr);
+ nWeights = MIN(elemPtr->w.nValues, nPoints);
+ w = elemPtr->w.valueArr;
+ linkPtr = Blt_ChainFirstLink(elemPtr->palette);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+
+ /*
+ * Create a style mapping array (data point index to style),
+ * initialized to the default style.
+ */
+ dataToStyle = Blt_Malloc(nPoints * sizeof(PenStyle *));
+ assert(dataToStyle);
+ for (i = 0; i < nPoints; i++) {
+ dataToStyle[i] = stylePtr;
+ }
+
+ for (i = 0; i < nWeights; i++) {
+ for (linkPtr = Blt_ChainLastLink(elemPtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+
+ if (stylePtr->weight.range > 0.0) {
+ double norm;
+
+ norm = (w[i] - stylePtr->weight.min) / stylePtr->weight.range;
+ if (((norm - 1.0) <= DBL_EPSILON) &&
+ (((1.0 - norm) - 1.0) <= DBL_EPSILON)) {
+ dataToStyle[i] = stylePtr;
+ break; /* Done: found range that matches. */
+ }
+ }
+ }
+ }
+ return dataToStyle;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapErrorBars --
+ *
+ * Creates two arrays of points and pen indices, filled with
+ * the screen coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapErrorBars(graphPtr, elemPtr, dataToStyle)
+ Graph *graphPtr;
+ Element *elemPtr;
+ PenStyle **dataToStyle;
+{
+ int n, nPoints;
+ Extents2D exts;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ nPoints = NumberOfPoints(elemPtr);
+ if (elemPtr->xError.nValues > 0) {
+ n = MIN(elemPtr->xError.nValues, nPoints);
+ } else {
+ n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, nPoints);
+ }
+ if (n > 0) {
+ Segment2D *errorBars;
+ Segment2D *segPtr;
+ double high, low;
+ double x, y;
+ int *errorToData;
+ int *indexPtr;
+ int capWidth;
+ register int i;
+
+ segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
+ indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ capWidth = (int)(dataToStyle[i]->symbolSize * 0.33333333);
+ capWidth |= 0x01;
+ x = elemPtr->x.valueArr[i];
+ y = elemPtr->y.valueArr[i];
+ if ((finite(x)) && (finite(y))) {
+ if (elemPtr->xError.nValues > 0) {
+ high = x + elemPtr->xError.valueArr[i];
+ low = x - elemPtr->xError.valueArr[i];
+ } else {
+ high = elemPtr->xHigh.valueArr[i];
+ low = elemPtr->xLow.valueArr[i];
+ }
+ if ((finite(high)) && (finite(low))) {
+ Point2D p, q;
+
+ p = Blt_Map2D(graphPtr, high, y, &(elemPtr->axes));
+ q = Blt_Map2D(graphPtr, low, y, &(elemPtr->axes));
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Left cap */
+ segPtr->p.x = segPtr->q.x = p.x;
+ segPtr->p.y = p.y - capWidth;
+ segPtr->q.y = p.y + capWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Right cap */
+ segPtr->p.x = segPtr->q.x = q.x;
+ segPtr->p.y = q.y - capWidth;
+ segPtr->q.y = q.y + capWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->xErrorBars = errorBars;
+ elemPtr->xErrorBarCnt = segPtr - errorBars;
+ elemPtr->xErrorToData = errorToData;
+ }
+ if (elemPtr->yError.nValues > 0) {
+ n = MIN(elemPtr->yError.nValues, nPoints);
+ } else {
+ n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, nPoints);
+ }
+ if (n > 0) {
+ Segment2D *errorBars;
+ Segment2D *segPtr;
+ double high, low;
+ double x, y;
+ int *errorToData;
+ int *indexPtr;
+ int capWidth;
+ register int i;
+
+ segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
+ indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ capWidth = (int)(dataToStyle[i]->symbolSize * 0.33333333);
+ capWidth |= 0x01;
+ x = elemPtr->x.valueArr[i];
+ y = elemPtr->y.valueArr[i];
+ if ((finite(x)) && (finite(y))) {
+ if (elemPtr->yError.nValues > 0) {
+ high = y + elemPtr->yError.valueArr[i];
+ low = y - elemPtr->yError.valueArr[i];
+ } else {
+ high = elemPtr->yHigh.valueArr[i];
+ low = elemPtr->yLow.valueArr[i];
+ }
+ if ((finite(high)) && (finite(low))) {
+ Point2D p, q;
+
+ p = Blt_Map2D(graphPtr, x, high, &(elemPtr->axes));
+ q = Blt_Map2D(graphPtr, x, low, &(elemPtr->axes));
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Top cap. */
+ segPtr->p.y = segPtr->q.y = p.y;
+ segPtr->p.x = p.x - capWidth;
+ segPtr->q.x = p.x + capWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Bottom cap. */
+ segPtr->p.y = segPtr->q.y = q.y;
+ segPtr->p.x = q.x - capWidth;
+ segPtr->q.x = q.x + capWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->yErrorBars = errorBars;
+ elemPtr->yErrorBarCnt = segPtr - errorBars;
+ elemPtr->yErrorToData = errorToData;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetIndex --
+ *
+ * Given a string representing the index of a pair of x,y
+ * coordinates, return the numeric index.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetIndex(interp, elemPtr, string, indexPtr)
+ Tcl_Interp *interp;
+ Element *elemPtr;
+ char *string;
+ int *indexPtr;
+{
+ long ielem;
+ int last;
+
+ last = NumberOfPoints(elemPtr) - 1;
+ if ((*string == 'e') && (strcmp("end", string) == 0)) {
+ ielem = last;
+ } else if (Tcl_ExprLong(interp, string, &ielem) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *indexPtr = (int)ielem;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameToElement --
+ *
+ * Find the element represented the given name, returning
+ * a pointer to its data structure via elemPtrPtr.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_NameToElement(graphPtr, name, elemPtrPtr)
+ Graph *graphPtr;
+ char *name;
+ Element **elemPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ if (name == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(graphPtr->elements.table), name);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't find element \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *elemPtrPtr = (Element *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyElement --
+ *
+ * Add a new element to the graph.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyElement(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Blt_ChainLink *linkPtr;
+
+ Blt_DeleteBindings(graphPtr->bindTable, elemPtr);
+ Blt_LegendRemoveElement(graphPtr->legend, elemPtr);
+
+ Tk_FreeOptions(elemPtr->configSpecs, (char *)elemPtr, graphPtr->display, 0);
+ /*
+ * Call the element's own destructor to release the memory and
+ * resources allocated for it.
+ */
+ (*elemPtr->procsPtr->destroyProc) (graphPtr, elemPtr);
+
+ /* Remove it also from the element display list */
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+ Blt_ChainDeleteLink(graphPtr->elements.chainPtr, linkPtr);
+ if (!elemPtr->hidden) {
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ break;
+ }
+ }
+ /* Remove the element for the graph's hash table of elements */
+ if (elemPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(graphPtr->elements.table), elemPtr->hashPtr);
+ }
+ if (elemPtr->name != NULL) {
+ Blt_Free(elemPtr->name);
+ }
+ Blt_Free(elemPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateElement --
+ *
+ * Add a new element to the graph.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateElement(graphPtr, interp, argc, argv, classUid)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ Tk_Uid classUid;
+{
+ Element *elemPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (argv[3][0] == '-') {
+ Tcl_AppendResult(graphPtr->interp, "name of element \"", argv[3],
+ "\" can't start with a '-'", (char *)NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&(graphPtr->elements.table), argv[3], &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "element \"", argv[3],
+ "\" already exists in \"", argv[0], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (classUid == bltBarElementUid) {
+ elemPtr = Blt_BarElement(graphPtr, argv[3], classUid);
+ } else {
+ /* Stripcharts are line graphs with some options enabled. */
+ elemPtr = Blt_LineElement(graphPtr, argv[3], classUid);
+ }
+ elemPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, elemPtr);
+
+ if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, elemPtr->name,
+ "Element", elemPtr->configSpecs, argc - 4, argv + 4,
+ (char *)elemPtr, 0) != TCL_OK) {
+ DestroyElement(graphPtr, elemPtr);
+ return TCL_ERROR;
+ }
+ (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr);
+ Blt_ChainPrepend(graphPtr->elements.chainPtr, elemPtr);
+
+ if (!elemPtr->hidden) {
+ /* If the new element isn't hidden then redraw the graph. */
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ elemPtr->flags |= MAP_ITEM;
+ graphPtr->flags |= RESET_AXES;
+ Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildDisplayList --
+ *
+ * Given a Tcl list of element names, this procedure rebuilds the
+ * display list, ignoring invalid element names. This list describes
+ * not only only which elements to draw, but in what order. This is
+ * only important for bar and pie charts.
+ *
+ * Results:
+ * The return value is a standard Tcl result. Only if the Tcl list
+ * can not be split, a TCL_ERROR is returned and interp->result contains
+ * an error message.
+ *
+ * Side effects:
+ * The graph is eventually redrawn using the new display list.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RebuildDisplayList(graphPtr, newList)
+ Graph *graphPtr; /* Graph widget record */
+ char *newList; /* Tcl list of element names */
+{
+ int nNames; /* Number of names found in Tcl name list */
+ char **nameArr; /* Broken out array of element names */
+ Blt_HashSearch cursor;
+ register int i;
+ register Blt_HashEntry *hPtr;
+ Element *elemPtr; /* Element information record */
+
+ if (Tcl_SplitList(graphPtr->interp, newList, &nNames, &nameArr) != TCL_OK) {
+ Tcl_AppendResult(graphPtr->interp, "can't split name list \"", newList,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Clear the display list and mark all elements as hidden. */
+ Blt_ChainReset(graphPtr->elements.chainPtr);
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ elemPtr->hidden = TRUE;
+ }
+
+ /* Rebuild the display list, checking that each name it exists
+ * (currently ignoring invalid element names). */
+ for (i = 0; i < nNames; i++) {
+ if (Blt_NameToElement(graphPtr, nameArr[i], &elemPtr) == TCL_OK) {
+ elemPtr->hidden = FALSE;
+ Blt_ChainAppend(graphPtr->elements.chainPtr, elemPtr);
+ }
+ }
+ Blt_Free(nameArr);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ Tcl_ResetResult(graphPtr->interp);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyElements --
+ *
+ * Removes all the graph's elements. This routine is called when
+ * the graph is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory allocated for the graph's elements is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyElements(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Element *elemPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ elemPtr->hashPtr = NULL;
+ DestroyElement(graphPtr, elemPtr);
+ }
+ Blt_DeleteHashTable(&(graphPtr->elements.table));
+ Blt_DeleteHashTable(&(graphPtr->elements.tagTable));
+ Blt_ChainDestroy(graphPtr->elements.chainPtr);
+}
+
+void
+Blt_MapElements(graphPtr)
+ Graph *graphPtr;
+{
+ Element *elemPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (graphPtr->mode != MODE_INFRONT) {
+ Blt_ResetStacks(graphPtr);
+ }
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ if ((graphPtr->flags & MAP_ALL) || (elemPtr->flags & MAP_ITEM)) {
+ (*elemPtr->procsPtr->mapProc) (graphPtr, elemPtr);
+ elemPtr->flags &= ~MAP_ITEM;
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawElements --
+ *
+ * Calls the individual element drawing routines for each
+ * element.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Elements are drawn into the drawable (pixmap) which will
+ * eventually be displayed in the graph window.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawElements(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (!elemPtr->hidden) {
+ (*elemPtr->procsPtr->drawNormalProc) (graphPtr, drawable, elemPtr);
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawActiveElements --
+ *
+ * Calls the individual element drawing routines to display
+ * the active colors for each element.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Elements are drawn into the drawable (pixmap) which will
+ * eventually be displayed in the graph window.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawActiveElements(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+ (*elemPtr->procsPtr->drawActiveProc) (graphPtr, drawable, elemPtr);
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_ElementsToPostScript --
+ *
+ * Generates PostScript output for each graph element in the
+ * element display list.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_ElementsToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (!elemPtr->hidden) {
+ /* Comment the PostScript to indicate the start of the element */
+ Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n",
+ elemPtr->name);
+ (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken,
+ elemPtr);
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_ActiveElementsToPostScript --
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_ActiveElementsToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+ Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n",
+ elemPtr->name);
+ (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken,
+ elemPtr);
+ }
+ }
+}
+
+int
+Blt_GraphUpdateNeeded(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ /* Check if the x or y vectors have notifications pending */
+ if (((elemPtr->x.clientId != NULL) &&
+ (Blt_VectorNotifyPending(elemPtr->x.clientId))) ||
+ ((elemPtr->y.clientId != NULL) &&
+ (Blt_VectorNotifyPending(elemPtr->y.clientId)))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+Blt_GetPenStyle(graphPtr, string, type, stylePtr)
+ Graph *graphPtr;
+ char *string;
+ Tk_Uid type;
+ PenStyle *stylePtr;
+{
+ Pen *penPtr;
+ Tcl_Interp *interp = graphPtr->interp;
+ char **elemArr;
+ int nElem;
+
+ elemArr = NULL;
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((nElem != 1) && (nElem != 3)) {
+ Tcl_AppendResult(interp, "bad style \"", string, "\": should be ",
+ "\"penName\" or \"penName min max\"", (char *)NULL);
+ if (elemArr != NULL) {
+ Blt_Free(elemArr);
+ }
+ return TCL_ERROR;
+ }
+ if (Blt_GetPen(graphPtr, elemArr[0], type, &penPtr) != TCL_OK) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ if (nElem == 3) {
+ double min, max;
+
+ if ((Tcl_GetDouble(interp, elemArr[1], &min) != TCL_OK) ||
+ (Tcl_GetDouble(interp, elemArr[2], &max) != TCL_OK)) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ SetLimits(stylePtr->weight, min, max);
+ }
+ stylePtr->penPtr = penPtr;
+ Blt_Free(elemArr);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Marks data points of elements (given by their index) as active.
+ *
+ * Results:
+ * Returns TCL_OK if no errors occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ActivateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc; /* Number of element names */
+ char **argv; /* List of element names */
+{
+ Element *elemPtr;
+ register int i;
+ int *activeArr;
+ int reqNumActive;
+
+ if (argc == 3) {
+ register Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ /* List all the currently active elements */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->flags & ELEM_ACTIVE) {
+ Tcl_AppendElement(graphPtr->interp, elemPtr->name);
+ }
+ }
+ return TCL_OK;
+ }
+ if (Blt_NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ elemPtr->flags |= ELEM_ACTIVE | ACTIVE_PENDING;
+
+ activeArr = NULL;
+ reqNumActive = -1;
+ if (argc > 4) {
+ register int *activePtr;
+
+ reqNumActive = argc - 4;
+ activePtr = activeArr = Blt_Malloc(sizeof(int) * reqNumActive);
+ assert(activeArr);
+ for (i = 4; i < argc; i++) {
+ if (GetIndex(interp, elemPtr, argv[i], activePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ activePtr++;
+ }
+ }
+ if (elemPtr->reqActiveArr != NULL) {
+ Blt_Free(elemPtr->reqActiveArr);
+ }
+ elemPtr->reqNumActive = reqNumActive;
+ elemPtr->reqActiveArr = activeArr;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+ClientData
+Blt_MakeElementTag(graphPtr, tagName)
+ Graph *graphPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&(graphPtr->elements.tagTable), tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g element bind elemName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+ Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * Add a new element to the graph (using the default type of the
+ * graph).
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateOp(graphPtr, interp, argc, argv, type)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ Tk_Uid type;
+{
+ return CreateElement(graphPtr, interp, argc, argv, type);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ Element *elemPtr;
+
+ if (Blt_NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ if (Tk_ConfigureValue(interp, graphPtr->tkwin, elemPtr->configSpecs,
+ (char *)elemPtr, argv[4], 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestOp --
+ *
+ * Find the element closest to the specified screen coordinates.
+ * Options:
+ * -halo Consider points only with this maximum distance
+ * from the picked coordinate.
+ * -interpolate Find closest point along element traces, not just
+ * data points.
+ * -along
+ *
+ * Results:
+ * A standard Tcl result. If an element could be found within
+ * the halo distance, the interpreter result is "1", otherwise
+ * "0". If a closest element exists, the designated Tcl array
+ * variable will be set with the following information:
+ *
+ * 1) the element name,
+ * 2) the index of the closest point,
+ * 3) the distance (in screen coordinates) from the picked X-Y
+ * coordinate and the closest point,
+ * 4) the X coordinate (graph coordinate) of the closest point,
+ * 5) and the Y-coordinate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tk_ConfigSpec closestSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-halo", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(ClosestSearch, halo), 0, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-interpolate", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(ClosestSearch, mode), 0 },
+ {TK_CONFIG_CUSTOM, "-along", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(ClosestSearch, along), 0, &alongOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static int
+ClosestOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget */
+ Tcl_Interp *interp; /* Interpreter to report results to */
+ int argc; /* Number of element names */
+ char **argv; /* List of element names */
+{
+ Element *elemPtr;
+ ClosestSearch search;
+ int i, x, y;
+ int flags = TCL_LEAVE_ERR_MSG;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &x) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, graphPtr->tkwin, argv[4], &y) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (graphPtr->inverted) {
+ int temp;
+
+ temp = x, x = y, y = temp;
+ }
+ for (i = 6; i < argc; i += 2) { /* Count switches-value pairs */
+ if ((argv[i][0] != '-') ||
+ ((argv[i][1] == '-') && (argv[i][2] == '\0'))) {
+ break;
+ }
+ }
+ if (i > argc) {
+ i = argc;
+ }
+
+ search.mode = SEARCH_POINTS;
+ search.halo = graphPtr->halo;
+ search.index = -1;
+ search.along = SEARCH_BOTH;
+ search.x = x;
+ search.y = y;
+
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, closestSpecs, i - 6,
+ argv + 6, (char *)&search, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR; /* Error occurred processing an option. */
+ }
+ if ((i < argc) && (argv[i][0] == '-')) {
+ i++; /* Skip "--" */
+ }
+ search.dist = (double)(search.halo + 1);
+
+ if (i < argc) {
+ for ( /* empty */ ; i < argc; i++) {
+ if (Blt_NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ if (elemPtr->hidden) {
+ Tcl_AppendResult(interp, "element \"", argv[i], "\" is hidden",
+ (char *)NULL);
+ return TCL_ERROR; /* Element isn't visible */
+ }
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ } else {
+ Blt_ChainLink *linkPtr;
+
+ /*
+ * Find the closest point from the set of displayed elements,
+ * searching the display list from back to front. That way if
+ * the points from two different elements overlay each other
+ * exactly, the last one picked will be the topmost.
+ */
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (!elemPtr->hidden) {
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ }
+
+ }
+ if (search.dist < (double)search.halo) {
+ char string[200];
+ /*
+ * Return an array of 5 elements
+ */
+ if (Tcl_SetVar2(interp, argv[5], "name",
+ search.elemPtr->name, flags) == NULL) {
+ return TCL_ERROR;
+ }
+ sprintf(string, "%d", search.index);
+ if (Tcl_SetVar2(interp, argv[5], "index", string, flags) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_PrintDouble(interp, search.point.x, string);
+ if (Tcl_SetVar2(interp, argv[5], "x", string, flags) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_PrintDouble(interp, search.point.y, string);
+ if (Tcl_SetVar2(interp, argv[5], "y", string, flags) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_PrintDouble(interp, search.dist, string);
+ if (Tcl_SetVar2(interp, argv[5], "dist", string, flags) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ } else {
+ if (Tcl_SetVar2(interp, argv[5], "name", "", flags) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Sets the element specifications by the given the command line
+ * arguments and calls the element specification configuration
+ * routine. If zero or one command line options are given, only
+ * information about the option(s) is returned in interp->result.
+ * If the element configuration has changed and the element is
+ * currently displayed, the axis limits are updated and
+ * recomputed.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ Element *elemPtr;
+ int flags;
+ int numNames, numOpts;
+ char **options;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (Blt_NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ }
+ numNames = i; /* Number of element names specified */
+ numOpts = argc - i; /* Number of options specified */
+ options = argv + numNames; /* Start of options in argv */
+
+ for (i = 0; i < numNames; i++) {
+ Blt_NameToElement(graphPtr, argv[i], &elemPtr);
+ flags = TK_CONFIG_ARGV_ONLY;
+ if (numOpts == 0) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ elemPtr->configSpecs, (char *)elemPtr, (char *)NULL, flags);
+ } else if (numOpts == 1) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ elemPtr->configSpecs, (char *)elemPtr, options[0], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->configSpecs,
+ numOpts, options, (char *)elemPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((*elemPtr->procsPtr->configProc) (graphPtr, elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Failed to configure element */
+ }
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-hide", (char *)NULL)) {
+#ifdef notdef /* FIXME: Check if this is really
+ * needed anymore. Try changing -hide
+ * and see if "show" displays the right
+ * thing. */
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+ break;
+ }
+ }
+ if ((elemPtr->hidden) != (linkPtr == NULL)) {
+
+ /* The element's "hidden" variable is out of sync with
+ * the display list. [That's what you get for having
+ * two ways to do the same thing.] Update the display
+ * list by either by adding or removing the element. */
+
+ if (linkPtr == NULL) {
+ Blt_ChainPrepend(graphPtr->elements.chainPtr, elemPtr);
+ } else {
+ Blt_ChainDeleteLink(graphPtr->elements.chainPtr, linkPtr);
+ }
+ }
+#endif
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ }
+ /* If data points or axes have changed, reset the axes (may
+ * affect autoscaling) and recalculate the screen points of
+ * the element. */
+
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-*data", "-map*", "-x",
+ "-y", (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD;
+ elemPtr->flags |= MAP_ITEM;
+ }
+ /* The new label may change the size of the legend */
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-label", (char *)NULL)) {
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ }
+ }
+ /* Update the pixmap if any configuration option changed */
+ graphPtr->flags |= REDRAW_BACKING_STORE | DRAW_MARGINS;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeactivateOp --
+ *
+ * Clears the active bit for the named elements.
+ *
+ * Results:
+ * Returns TCL_OK if no errors occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeactivateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget */
+ Tcl_Interp *interp; /* Not used. */
+ int argc; /* Number of element names */
+ char **argv; /* List of element names */
+{
+ Element *elemPtr;
+ register int i;
+
+ for (i = 3; i < argc; i++) {
+ if (Blt_NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ elemPtr->flags &= ~ELEM_ACTIVE;
+ if (elemPtr->reqActiveArr != NULL) {
+ Blt_Free(elemPtr->reqActiveArr);
+ elemPtr->reqActiveArr = NULL;
+ }
+ elemPtr->reqNumActive = 0;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Delete the named elements from the graph.
+ *
+ * Results:
+ * TCL_ERROR is returned if any of the named elements can not be
+ * found. Otherwise TCL_OK is returned;
+ *
+ * Side Effects:
+ * If the element is currently displayed, the plotting area of
+ * the graph is redrawn. Memory and resources allocated by the
+ * elements are released.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget */
+ Tcl_Interp *interp; /* Not used. */
+ int argc; /* Number of element names */
+ char **argv; /* List of element names */
+{
+ Element *elemPtr;
+ register int i;
+
+ for (i = 3; i < argc; i++) {
+ if (Blt_NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ DestroyElement(graphPtr, elemPtr);
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ * Indicates if the named element exists in the graph.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The interpreter
+ * result will contain "1" or "0".
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ExistsOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->elements.table), argv[3]);
+ Blt_SetBooleanResult(interp, (hPtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Returns the name of the picked element (using the element
+ * bind operation). Right now, the only name accepted is
+ * "current".
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain
+ * the name of the element.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ register Element *elemPtr;
+
+ if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
+ elemPtr = (Element *)Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on elements. */
+ if ((elemPtr != NULL) &&
+ ((elemPtr->classUid == bltBarElementUid) ||
+ (elemPtr->classUid == bltLineElementUid) ||
+ (elemPtr->classUid == bltStripElementUid))) {
+ Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns the names of the elements is the graph matching
+ * one of more patterns provided. If no pattern arguments
+ * are given, then all element names will be returned.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The interpreter
+ * result will contain a Tcl list of the element names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NamesOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Element *elemPtr;
+ Blt_HashSearch cursor;
+ register Blt_HashEntry *hPtr;
+ register int i;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (argc == 3) {
+ Tcl_AppendElement(graphPtr->interp, elemPtr->name);
+ continue;
+ }
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(elemPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, elemPtr->name);
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ * Queries or resets the element display list.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The interpreter
+ * result will contain the new display list of element names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShowOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Element *elemPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (argc == 4) {
+ if (RebuildDisplayList(graphPtr, argv[3]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, elemPtr->name);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Returns the name of the type of the element given by some
+ * element name.
+ *
+ * Results:
+ * A standard Tcl result. Returns the type of the element in
+ * interp->result. If the identifier given doesn't represent an
+ * element, then an error message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Element name */
+{
+ Element *elemPtr;
+
+ if (Blt_NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ Tcl_SetResult(interp, elemPtr->classUid, TCL_STATIC);
+ return TCL_OK;
+}
+
+/*
+ * Global routines:
+ */
+static Blt_OpSpec elemOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?elemName? ?index...?",},
+ {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 5, 5, "elemName option",},
+ {"closest", 2, (Blt_Op)ClosestOp, 6, 0,
+ "x y varName ?option value?... ?elemName?...",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+ "elemName ?elemName?... ?option value?...",},
+ {"create", 2, (Blt_Op)CreateOp, 4, 0, "elemName ?option value?...",},
+ {"deactivate", 3, (Blt_Op)DeactivateOp, 3, 0, "?elemName?...",},
+ {"delete", 3, (Blt_Op)DeleteOp, 3, 0, "?elemName?...",},
+ {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "elemName",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+ {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+ {"show", 1, (Blt_Op)ShowOp, 3, 4, "?elemList?",},
+ {"type", 1, (Blt_Op)TypeOp, 4, 4, "elemName",},
+};
+static int numElemOps = sizeof(elemOps) / sizeof(Blt_OpSpec);
+
+
+/*
+ * ----------------------------------------------------------------
+ *
+ * Blt_ElementOp --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * ----------------------------------------------------------------
+ */
+int
+Blt_ElementOp(graphPtr, interp, argc, argv, type)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* # arguments */
+ char **argv; /* Argument list */
+ Tk_Uid type;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, numElemOps, elemOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ if (proc == CreateOp) {
+ result = CreateOp(graphPtr, interp, argc, argv, type);
+ } else {
+ result = (*proc) (graphPtr, interp, argc, argv);
+ }
+ return result;
+}
diff --git a/blt/src/bltGrElem.h b/blt/src/bltGrElem.h
new file mode 100644
index 00000000000..09eb22064d2
--- /dev/null
+++ b/blt/src/bltGrElem.h
@@ -0,0 +1,272 @@
+/*
+ * bltGrElem.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_ELEM_H
+#define _BLT_GR_ELEM_H
+
+#define SEARCH_X 0
+#define SEARCH_Y 1
+#define SEARCH_BOTH 2
+
+#define SHOW_NONE 0
+#define SHOW_X 1
+#define SHOW_Y 2
+#define SHOW_BOTH 3
+
+#define SEARCH_POINTS 0 /* Search for closest data point. */
+#define SEARCH_TRACES 1 /* Search for closest point on trace.
+ * Interpolate the connecting line segments
+ * if necessary. */
+#define SEARCH_AUTO 2 /* Automatically determine whether to search
+ * for data points or traces. Look for
+ * traces if the linewidth is > 0 and if
+ * there is more than one data point. */
+
+#define ELEM_ACTIVE (1<<8) /* Non-zero indicates that the element
+ * should be drawn in its active
+ * foreground and background
+ * colors. */
+#define ACTIVE_PENDING (1<<7)
+
+#define LABEL_ACTIVE (1<<9) /* Non-zero indicates that the
+ * element's entry in the legend
+ * should be drawn in its active
+ * foreground and background
+ * colors. */
+#define SCALE_SYMBOL (1<<10)
+
+#define NumberOfPoints(e) MIN((e)->x.nValues, (e)->y.nValues)
+
+
+/*
+ * An element has one or more vectors plus several attributes, such as
+ * line style, thickness, color, and symbol type. It has an
+ * identifier which distinguishes it among the list of all elements.
+ */
+typedef struct {
+ AxisRange weight; /* Weight range where this pen is valid. */
+
+ Pen *penPtr; /* Pen to use. */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+
+ int xErrorBarCnt; /* # of error bars for this pen. */
+
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to the
+ * current graph size. */
+} PenStyle;
+
+
+typedef struct {
+ XColor *color; /* Color of error bar */
+ int lineWidth; /* Width of the error bar segments. */
+ GC gc;
+ int show; /* Flags for errorbars: none, x, y, or both */
+
+} ErrorBarAttributes;
+
+typedef struct {
+ int halo; /* Maximal distance a candidate point
+ * can be from the sample window
+ * coordinate */
+
+ int mode; /* Indicates whether to find the closest
+ * data point or the closest point on the
+ * trace by interpolating the line segments.
+ * Can also be SEARCH_AUTO, indicating to
+ * choose how to search.*/
+
+ int x, y; /* Screen coordinates of test point */
+
+ int along; /* Indicates to let search run along a
+ * particular axis: x, y, or both. */
+
+ /* Output */
+ Element *elemPtr; /* Name of the closest element */
+
+ Point2D point; /* Graph coordinates of closest point */
+
+ int index; /* Index of closest data point */
+
+ double dist; /* Distance in screen coordinates */
+
+} ClosestSearch;
+
+typedef void (ElementDrawProc) _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+ Element *elemPtr));
+typedef void (ElementToPostScriptProc) _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken, Element *elemPtr));
+typedef void (ElementDestroyProc) _ANSI_ARGS_((Graph *graphPtr,
+ Element *elemPtr));
+typedef int (ElementConfigProc) _ANSI_ARGS_((Graph *graphPtr,
+ Element *elemPtr));
+typedef void (ElementMapProc) _ANSI_ARGS_((Graph *graphPtr,
+ Element *elemPtr));
+typedef void (ElementExtentsProc) _ANSI_ARGS_((Element *elemPtr,
+ Extents2D *extsPtr));
+typedef void (ElementClosestProc) _ANSI_ARGS_((Graph *graphPtr,
+ Element *elemPtr, ClosestSearch *searchPtr));
+typedef void (ElementDrawSymbolProc) _ANSI_ARGS_((Graph *graphPtr,
+ Drawable drawable, Element *elemPtr, int x, int y, int symbolSize));
+typedef void (ElementSymbolToPostScriptProc) _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken, Element *elemPtr, double x, double y, int symSize));
+
+typedef struct {
+ ElementClosestProc *closestProc;
+ ElementConfigProc *configProc;
+ ElementDestroyProc *destroyProc;
+ ElementDrawProc *drawActiveProc;
+ ElementDrawProc *drawNormalProc;
+ ElementDrawSymbolProc *drawSymbolProc;
+ ElementExtentsProc *extentsProc;
+ ElementToPostScriptProc *printActiveProc;
+ ElementToPostScriptProc *printNormalProc;
+ ElementSymbolToPostScriptProc *printSymbolProc;
+ ElementMapProc *mapProc;
+} ElementProcs;
+
+/*
+ * The data structure below contains information pertaining to a line
+ * vector. It consists of an array of floating point data values and
+ * for convenience, the number and minimum/maximum values.
+ */
+
+typedef struct {
+ Blt_Vector *vecPtr;
+
+ double *valueArr;
+
+ int nValues;
+
+ int arraySize;
+
+ double min, max;
+
+ Blt_VectorId clientId; /* If non-NULL, a client token identifying the
+ * external vector. */
+
+ Element *elemPtr; /* Element associated with vector. */
+
+} ElemVector;
+
+
+struct ElementStruct {
+ char *name; /* Identifier to refer the element.
+ * Used in the "insert", "delete", or
+ * "show", commands. */
+
+ Tk_Uid classUid; /* Type of element */
+
+ Graph *graphPtr; /* Graph widget of element*/
+
+ unsigned int flags; /* Indicates if the entire element is
+ * active, or if coordinates need to
+ * be calculated */
+
+ char **tags;
+
+ int hidden; /* If non-zero, don't display the element. */
+
+ Blt_HashEntry *hashPtr;
+
+ char *label; /* Label displayed in legend */
+
+ int labelRelief; /* Relief of label in legend. */
+
+ Axis2D axes; /* X-axis and Y-axis mapping the element */
+
+ ElemVector x, y, w; /* Contains array of floating point
+ * graph coordinate values. Also holds
+ * min/max and the number of
+ * coordinates */
+
+ ElemVector xError; /* Relative/symmetric X error values. */
+ ElemVector yError; /* Relative/symmetric Y error values. */
+ ElemVector xHigh, xLow; /* Absolute/asymmetric X-coordinate high/low
+ error values. */
+ ElemVector yHigh, yLow; /* Absolute/asymmetric Y-coordinate high/low
+ error values. */
+
+ int *reqActiveArr; /* Array of indices (malloc-ed) which
+ * indicate which data points are
+ * active (drawn with "active"
+ * colors). */
+
+ int reqNumActive; /* Number of active data points.
+ * Special case: if reqNumActive < 0
+ * and the active bit is set in
+ * "flags", then all data points are
+ * drawn active. */
+
+ ElementProcs *procsPtr;
+
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications. */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int *xErrorToData; /* Maps error bar segments back to the data
+ * point. */
+ int *yErrorToData; /* Maps error bar segments back to the data
+ * point. */
+
+ Pen *activePenPtr; /* Standard Pens */
+ Pen *normalPenPtr;
+
+ Blt_Chain *palette; /* Palette of pens. */
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+};
+
+
+extern double Blt_FindElemVectorMinimum _ANSI_ARGS_((ElemVector *vecPtr,
+ double minLimit));
+extern void Blt_ResizeStatusArray _ANSI_ARGS_((Element *elemPtr, int nPoints));
+extern int Blt_GetPenStyle _ANSI_ARGS_((Graph *graphPtr, char *name,
+ Tk_Uid classUid, PenStyle *stylePtr));
+extern int Blt_NameToElement _ANSI_ARGS_((Graph *graphPtr, char *name,
+ Element **elemPtrPtr));
+extern void Blt_FreePalette _ANSI_ARGS_((Graph *graphPtr, Blt_Chain *palette));
+extern PenStyle **Blt_StyleMap _ANSI_ARGS_((Element *elemPtr));
+extern void Blt_MapErrorBars _ANSI_ARGS_((Graph *graphPtr, Element *elemPtr,
+ PenStyle **dataToStyle));
+
+#endif /* _BLT_GR_ELEM_H */
diff --git a/blt/src/bltGrGrid.c b/blt/src/bltGrGrid.c
new file mode 100644
index 00000000000..7bf5677a46f
--- /dev/null
+++ b/blt/src/bltGrGrid.c
@@ -0,0 +1,517 @@
+
+/*
+ * bltGrGrid.c --
+ *
+ * This module implements grid lines for the BLT graph widget.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Graph widget created by Sani Nassif and George Howlett.
+ */
+
+#include "bltGraph.h"
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltAnyXAxisOption;
+extern Tk_CustomOption bltAnyYAxisOption;
+
+
+#define DEF_GRID_DASHES "dot"
+#define DEF_GRID_FG_COLOR RGB_GREY64
+#define DEF_GRID_FG_MONO RGB_BLACK
+#define DEF_GRID_LINE_WIDTH "0"
+#define DEF_GRID_HIDE_BARCHART "no"
+#define DEF_GRID_HIDE_GRAPH "yes"
+#define DEF_GRID_MINOR "yes"
+#define DEF_GRID_MAP_X_GRAPH "x"
+#define DEF_GRID_MAP_X_BARCHART (char *)NULL
+#define DEF_GRID_MAP_Y "y"
+#define DEF_GRID_POSITION (char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_GRID_FG_COLOR, Tk_Offset(Grid, colorPtr),
+ TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_COLOR, "-color", "color", "color",
+ DEF_GRID_FG_MONO, Tk_Offset(Grid, colorPtr),
+ TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_GRID_DASHES, Tk_Offset(Grid, dashes),
+ TK_CONFIG_NULL_OK | ALL_GRAPHS, &bltDashesOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_GRID_HIDE_BARCHART, Tk_Offset(Grid, hidden), BARCHART},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_GRID_HIDE_GRAPH, Tk_Offset(Grid, hidden), GRAPH | STRIPCHART},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "Linewidth",
+ DEF_GRID_LINE_WIDTH, Tk_Offset(Grid, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_GRID_MAP_X_GRAPH, Tk_Offset(Grid, axes.x),
+ GRAPH | STRIPCHART, &bltAnyXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_GRID_MAP_X_BARCHART, Tk_Offset(Grid, axes.x),
+ BARCHART, &bltAnyXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_GRID_MAP_Y, Tk_Offset(Grid, axes.y),
+ ALL_GRAPHS, &bltAnyYAxisOption},
+ {TK_CONFIG_BOOLEAN, "-minor", "minor", "Minor",
+ DEF_GRID_MINOR, Tk_Offset(Grid, minorGrid),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureGrid --
+ *
+ * Configures attributes of the grid such as line width,
+ * dashes, and position. The grid are first turned off
+ * before any of the attributes changes.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureGrid(graphPtr, gridPtr)
+ Graph *graphPtr;
+ Grid *gridPtr;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+
+ gcValues.background = gcValues.foreground = gridPtr->colorPtr->pixel;
+ gcValues.line_width = LineWidth(gridPtr->lineWidth);
+ gcMask = (GCForeground | GCBackground | GCLineWidth);
+ if (LineIsDashed(gridPtr->dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ gcMask |= GCLineStyle;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(gridPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &(gridPtr->dashes));
+ }
+ if (gridPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, gridPtr->gc);
+ }
+ gridPtr->gc = newGC;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapGrid --
+ *
+ * Determines the coordinates of the line segments corresponding
+ * to the grid lines for each axis.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapGrid(graphPtr)
+ Graph *graphPtr;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+ int nSegments;
+ Segment2D *segments;
+
+ if (gridPtr->x.segments != NULL) {
+ Blt_Free(gridPtr->x.segments);
+ gridPtr->x.segments = NULL;
+ }
+ if (gridPtr->y.segments != NULL) {
+ Blt_Free(gridPtr->y.segments);
+ gridPtr->y.segments = NULL;
+ }
+ gridPtr->x.nSegments = gridPtr->y.nSegments = 0;
+ /*
+ * Generate line segments to represent the grid. Line segments
+ * are calculated from the major tick intervals of each axis mapped.
+ */
+ Blt_GetAxisSegments(graphPtr, gridPtr->axes.x, &segments, &nSegments);
+ if (nSegments > 0) {
+ gridPtr->x.nSegments = nSegments;
+ gridPtr->x.segments = segments;
+ }
+ Blt_GetAxisSegments(graphPtr, gridPtr->axes.y, &segments, &nSegments);
+ if (nSegments > 0) {
+ gridPtr->y.nSegments = nSegments;
+ gridPtr->y.segments = segments;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawGrid --
+ *
+ * Draws the grid lines associated with each axis.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DrawGrid(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ if (gridPtr->hidden) {
+ return;
+ }
+ if (gridPtr->x.nSegments > 0) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, gridPtr->gc,
+ gridPtr->x.segments, gridPtr->x.nSegments);
+ }
+ if (gridPtr->y.nSegments > 0) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, gridPtr->gc,
+ gridPtr->y.segments, gridPtr->y.nSegments);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GridToPostScript --
+ *
+ * Prints the grid lines associated with each axis.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GridToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ if (gridPtr->hidden) {
+ return;
+ }
+ Blt_LineAttributesToPostScript(psToken, gridPtr->colorPtr,
+ gridPtr->lineWidth, &(gridPtr->dashes), CapButt, JoinMiter);
+ if (gridPtr->x.nSegments > 0) {
+ Blt_Segments2DToPostScript(psToken, gridPtr->x.segments,
+ gridPtr->x.nSegments);
+ }
+ if (gridPtr->y.nSegments > 0) {
+ Blt_Segments2DToPostScript(psToken, gridPtr->y.segments,
+ gridPtr->y.nSegments);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyGrid --
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Grid GC is released.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyGrid(graphPtr)
+ Graph *graphPtr;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ Tk_FreeOptions(configSpecs, (char *)gridPtr, graphPtr->display,
+ Blt_GraphType(graphPtr));
+ if (gridPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, gridPtr->gc);
+ }
+ if (gridPtr->x.segments != NULL) {
+ Blt_Free(gridPtr->x.segments);
+ }
+ if (gridPtr->y.segments != NULL) {
+ Blt_Free(gridPtr->y.segments);
+ }
+ Blt_Free(gridPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateGrid --
+ *
+ * Creates and initializes a new grid structure.
+ *
+ * Results:
+ * Returns TCL_ERROR if the configuration failed, otherwise TCL_OK.
+ *
+ * Side Effects:
+ * Memory for grid structure is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreateGrid(graphPtr)
+ Graph *graphPtr;
+{
+ Grid *gridPtr;
+
+ gridPtr = Blt_Calloc(1, sizeof(Grid));
+ assert(gridPtr);
+ gridPtr->minorGrid = TRUE;
+ graphPtr->gridPtr = gridPtr;
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin, "grid",
+ "Grid", configSpecs, 0, (char **)NULL, (char *)gridPtr,
+ Blt_GraphType(graphPtr)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureGrid(graphPtr, gridPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries configuration attributes of the grid such as line
+ * width, dashes, and position.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+ (char *)gridPtr, argv[3], Blt_GraphType(graphPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets configuration attributes of the grid
+ * such as line width, dashes, and position.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Grid attributes are reset. The graph is redrawn at the
+ * next idle point.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+ int flags;
+
+ flags = Blt_GraphType(graphPtr) | TK_CONFIG_ARGV_ONLY;
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)gridPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)gridPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
+ argc - 3, argv + 3, (char *)gridPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureGrid(graphPtr, gridPtr);
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Maps the grid.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Grid attributes are reset and the graph is redrawn if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ if (gridPtr->hidden) {
+ gridPtr->hidden = FALSE;/* Changes "-hide" configuration option */
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Maps or unmaps the grid (off or on).
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Grid attributes are reset and the graph is redrawn if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UnmapOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ if (!gridPtr->hidden) {
+ gridPtr->hidden = TRUE; /* Changes "-hide" configuration option */
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ToggleOp --
+ *
+ * Toggles the state of the grid shown/hidden.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Grid is hidden/displayed. The graph is redrawn at the next
+ * idle time.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ToggleOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+ gridPtr->hidden = (!gridPtr->hidden);
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+
+static Blt_OpSpec gridOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?options...?",},
+ {"off", 2, (Blt_Op)UnmapOp, 3, 3, "",},
+ {"on", 2, (Blt_Op)MapOp, 3, 3, "",},
+ {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "",},
+};
+static int nGridOps = sizeof(gridOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GridOp --
+ *
+ * User routine to configure grid lines. Grids are drawn
+ * at major tick intervals across the graph.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Grid may be drawn in the plotting area.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GridOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+
+ proc = Blt_GetOp(interp, nGridOps, gridOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (graphPtr, interp, argc, argv);
+}
diff --git a/blt/src/bltGrHairs.c b/blt/src/bltGrHairs.c
new file mode 100644
index 00000000000..8f084b3463b
--- /dev/null
+++ b/blt/src/bltGrHairs.c
@@ -0,0 +1,542 @@
+
+/*
+ * bltGrHairs.c --
+ *
+ * This module implements crosshairs for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Graph widget created by Sani Nassif and George Howlett.
+*/
+
+#include "bltGraph.h"
+
+extern Tk_CustomOption bltPointOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltDashesOption;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ * Contains the line segments positions and graphics context used
+ * to simulate crosshairs (by XORing) on the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+
+struct CrosshairsStruct {
+
+ XPoint hotSpot; /* Hot spot for crosshairs */
+ int visible; /* Internal state of crosshairs. If non-zero,
+ * crosshairs are displayed. */
+ int hidden; /* If non-zero, crosshairs are not displayed.
+ * This is not necessarily consistent with the
+ * internal state variable. This is true when
+ * the hot spot is off the graph. */
+ Blt_Dashes dashes; /* Dashstyle of the crosshairs. This represents
+ * an array of alternatingly drawn pixel
+ * values. If NULL, the hairs are drawn as a
+ * solid line */
+ int lineWidth; /* Width of the simulated crosshair lines */
+ XSegment segArr[2]; /* Positions of line segments representing the
+ * simulated crosshairs. */
+ XColor *colorPtr; /* Foreground color of crosshairs */
+ GC gc; /* Graphics context for crosshairs. Set to
+ * GXxor to not require redraws of graph */
+};
+
+#define DEF_HAIRS_DASHES (char *)NULL
+#define DEF_HAIRS_FG_COLOR RGB_BLACK
+#define DEF_HAIRS_FG_MONO RGB_BLACK
+#define DEF_HAIRS_LINE_WIDTH "0"
+#define DEF_HAIRS_HIDE "yes"
+#define DEF_HAIRS_POSITION (char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_HAIRS_FG_COLOR, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_HAIRS_FG_MONO, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_HAIRS_DASHES, Tk_Offset(Crosshairs, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_HAIRS_HIDE, Tk_Offset(Crosshairs, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "Linewidth",
+ DEF_HAIRS_LINE_WIDTH, Tk_Offset(Crosshairs, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_HAIRS_POSITION, Tk_Offset(Crosshairs, hotSpot),
+ 0, &bltPointOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TurnOffHairs --
+ *
+ * XOR's the existing line segments (representing the crosshairs),
+ * thereby erasing them. The internal state of the crosshairs is
+ * tracked.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshairs are erased.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TurnOffHairs(tkwin, chPtr)
+ Tk_Window tkwin;
+ Crosshairs *chPtr;
+{
+ if (Tk_IsMapped(tkwin) && (chPtr->visible)) {
+ XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin), chPtr->gc,
+ chPtr->segArr, 2);
+ chPtr->visible = FALSE;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TurnOnHairs --
+ *
+ * Draws (by XORing) new line segments, creating the effect of
+ * crosshairs. The internal state of the crosshairs is tracked.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshairs are displayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TurnOnHairs(graphPtr, chPtr)
+ Graph *graphPtr;
+ Crosshairs *chPtr;
+{
+ if (Tk_IsMapped(graphPtr->tkwin) && (!chPtr->visible)) {
+ if (!PointInGraph(graphPtr, chPtr->hotSpot.x, chPtr->hotSpot.y)) {
+ return; /* Coordinates are off the graph */
+ }
+ XDrawSegments(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ chPtr->gc, chPtr->segArr, 2);
+ chPtr->visible = TRUE;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureCrosshairs --
+ *
+ * Configures attributes of the crosshairs such as line width,
+ * dashes, and position. The crosshairs are first turned off
+ * before any of the attributes changes.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ConfigureCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ long colorValue;
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ /*
+ * Turn off the crosshairs temporarily. This is in case the new
+ * configuration changes the size, style, or position of the lines.
+ */
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+
+ gcValues.function = GXxor;
+
+ if (graphPtr->plotBg == NULL) {
+ /* The graph's color option may not have been set yet */
+ colorValue = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ colorValue = graphPtr->plotBg->pixel;
+ }
+ gcValues.background = colorValue;
+ gcValues.foreground = (colorValue ^ chPtr->colorPtr->pixel);
+
+ gcValues.line_width = LineWidth(chPtr->lineWidth);
+ gcMask = (GCForeground | GCBackground | GCFunction | GCLineWidth);
+ if (LineIsDashed(chPtr->dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ gcMask |= GCLineStyle;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(chPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &(chPtr->dashes));
+ }
+ if (chPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+ }
+ chPtr->gc = newGC;
+
+ /*
+ * Are the new coordinates on the graph?
+ */
+ chPtr->segArr[0].x2 = chPtr->segArr[0].x1 = chPtr->hotSpot.x;
+ chPtr->segArr[0].y1 = graphPtr->bottom;
+ chPtr->segArr[0].y2 = graphPtr->top;
+ chPtr->segArr[1].y2 = chPtr->segArr[1].y1 = chPtr->hotSpot.y;
+ chPtr->segArr[1].x1 = graphPtr->left;
+ chPtr->segArr[1].x2 = graphPtr->right;
+
+ if (!chPtr->hidden) {
+ TurnOnHairs(graphPtr, chPtr);
+ }
+}
+
+void
+Blt_EnableCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ if (!graphPtr->crosshairs->hidden) {
+ TurnOnHairs(graphPtr, graphPtr->crosshairs);
+ }
+}
+
+void
+Blt_DisableCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ TurnOffHairs(graphPtr->tkwin, graphPtr->crosshairs);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateCrosshairs --
+ *
+ * Update the length of the hairs (not the hot spot).
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UpdateCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ chPtr->segArr[0].y1 = graphPtr->bottom;
+ chPtr->segArr[0].y2 = graphPtr->top;
+ chPtr->segArr[1].x1 = graphPtr->left;
+ chPtr->segArr[1].x2 = graphPtr->right;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyCrosshairs --
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ Tk_FreeOptions(configSpecs, (char *)chPtr, graphPtr->display, 0);
+ if (chPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+ }
+ Blt_Free(chPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCrosshairs --
+ *
+ * Creates and initializes a new crosshair structure.
+ *
+ * Results:
+ * Returns TCL_ERROR if the crosshair structure can't be created,
+ * otherwise TCL_OK.
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreateCrosshairs(graphPtr)
+ Graph *graphPtr;
+{
+ Crosshairs *chPtr;
+
+ chPtr = Blt_Calloc(1, sizeof(Crosshairs));
+ assert(chPtr);
+ chPtr->hidden = TRUE;
+ chPtr->hotSpot.x = chPtr->hotSpot.y = -1;
+ graphPtr->crosshairs = chPtr;
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ "crosshairs", "Crosshairs", configSpecs, 0, (char **)NULL,
+ (char *)chPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries configuration attributes of the crosshairs such as
+ * line width, dashes, and position.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets configuration attributes of the crosshairs
+ * such as line width, dashes, and position.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Crosshairs are reset.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, (char *)NULL, 0);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, argv[3], 0);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)chPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureCrosshairs(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Maps the crosshairs.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Crosshairs are reset if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (chPtr->hidden) {
+ TurnOnHairs(graphPtr, chPtr);
+ chPtr->hidden = FALSE;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnmapOp --
+ *
+ * Unmaps the crosshairs.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Crosshairs are reset if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UnmapOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (!chPtr->hidden) {
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+ chPtr->hidden = TRUE;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ToggleOp --
+ *
+ * Toggles the state of the crosshairs.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Crosshairs are reset.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ToggleOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ chPtr->hidden = (chPtr->hidden == 0);
+ if (chPtr->hidden) {
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+ } else {
+ TurnOnHairs(graphPtr, chPtr);
+ }
+ return TCL_OK;
+}
+
+
+static Blt_OpSpec xhairOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?options...?",},
+ {"off", 2, (Blt_Op)UnmapOp, 3, 3, "",},
+ {"on", 2, (Blt_Op)MapOp, 3, 3, "",},
+ {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "",},
+};
+static int nXhairOps = sizeof(xhairOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CrosshairsOp --
+ *
+ * User routine to configure crosshair simulation. Crosshairs
+ * are simulated by drawing line segments parallel to both axes
+ * using the XOR drawing function. The allows the lines to be
+ * erased (by drawing them again) without redrawing the entire
+ * graph. Care must be taken to erase crosshairs before redrawing
+ * the graph and redraw them after the graph is redraw.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Crosshairs may be drawn in the plotting area.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CrosshairsOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+
+ proc = Blt_GetOp(interp, nXhairOps, xhairOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (graphPtr, interp, argc, argv);
+}
diff --git a/blt/src/bltGrLegd.c b/blt/src/bltGrLegd.c
new file mode 100644
index 00000000000..f46c2a87d2d
--- /dev/null
+++ b/blt/src/bltGrLegd.c
@@ -0,0 +1,1488 @@
+
+/*
+ * bltGrLegd.c --
+ *
+ * This module implements the legend for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltGrElem.h"
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Legend --
+ *
+ * Contains information specific to how the legend will be
+ * displayed.
+ *
+ *
+ * -------------------------------------------------------------------
+ */
+struct LegendStruct {
+ unsigned int flags;
+ Tk_Uid classUid; /* Type: Element or Marker. */
+
+ int hidden; /* If non-zero, don't display the legend. */
+
+ int raised; /* If non-zero, draw the legend last, above
+ * everything else. */
+
+ int nEntries; /* Number of element entries in table. */
+
+ short int width, height; /* Dimensions of the legend */
+
+ short int nColumns, nRows; /* Number of columns and rows in legend */
+
+ int site;
+ Point2D anchorPos; /* Says how to position the legend. Indicates
+ * the site and/or x-y screen coordinates of
+ * the legend. Used in conjunction with the
+ * anchor to determine its location. */
+
+ Tk_Anchor anchor; /* Anchor of legend. Used to interpret the
+ * positioning point of the legend in the
+ * graph*/
+
+ int x, y; /* Computed origin of legend. */
+
+ Graph *graphPtr;
+ Tcl_Command cmdToken; /* Token for graph's widget command. */
+ int reqColumns, reqRows;
+
+ Blt_Pad ipadX, ipadY; /* # of pixels padding around legend entries */
+ Blt_Pad padX, padY; /* # of pixels padding to exterior of legend */
+
+ Tk_Window tkwin; /* Optional external window to draw legend. */
+
+ TextStyle style;
+
+ int maxSymSize; /* Size of largest symbol to be displayed.
+ * Used to calculate size of legend */
+
+ Tk_3DBorder activeBorder; /* Active legend entry background color. */
+ int activeRelief; /* 3-D effect on active entry. */
+ int entryBW; /* Border width around each entry in legend. */
+
+ Tk_3DBorder border; /* 3-D effect of legend. */
+ int borderWidth; /* Width of legend 3-D border */
+ int relief; /* 3-d effect of border around the legend:
+ * TK_RELIEF_RAISED etc. */
+
+ Blt_BindTable bindTable;
+};
+
+#define padLeft padX.side1
+#define padRight padX.side2
+#define padTop padY.side1
+#define padBottom padY.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+
+#define DEF_LEGEND_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_LEGEND_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_LEGEND_ACTIVE_BORDER_WIDTH "2"
+#define DEF_LEGEND_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_LEGEND_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_LEGEND_ACTIVE_RELIEF "flat"
+#define DEF_LEGEND_ANCHOR "n"
+#define DEF_LEGEND_BG_COLOR (char *)NULL
+#define DEF_LEGEND_BG_MONO (char *)NULL
+#define DEF_LEGEND_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_LEGEND_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_LEGEND_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_LEGEND_FONT STD_FONT_SMALL
+#define DEF_LEGEND_HIDE "no"
+#define DEF_LEGEND_IPAD_X "1"
+#define DEF_LEGEND_IPAD_Y "1"
+#define DEF_LEGEND_PAD_X "1"
+#define DEF_LEGEND_PAD_Y "1"
+#define DEF_LEGEND_POSITION "rightmargin"
+#define DEF_LEGEND_RAISED "no"
+#define DEF_LEGEND_RELIEF "sunken"
+#define DEF_LEGEND_SHADOW_COLOR (char *)NULL
+#define DEF_LEGEND_SHADOW_MONO (char *)NULL
+#define DEF_LEGEND_ROWS "0"
+#define DEF_LEGEND_COLUMNS "0"
+
+static Tk_OptionParseProc StringToPosition;
+static Tk_OptionPrintProc PositionToString;
+static Tk_CustomOption legendPositionOption =
+{
+ StringToPosition, PositionToString, (ClientData)0
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltCountOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVE_BG_COLOR,
+ Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
+ Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-activeborderwidth", "activeBorderWidth",
+ "BorderWidth", DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, entryBW),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVE_FG_COLOR,
+ Tk_Offset(Legend, style.activeColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
+ Tk_Offset(Legend, style.activeColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_LEGEND_BG_COLOR, Tk_Offset(Legend, border),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-columns", "columns", "columns",
+ DEF_LEGEND_COLUMNS, Tk_Offset(Legend, reqColumns),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_LEGEND_FONT, Tk_Offset(Legend, style.font), 0},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FG_COLOR, Tk_Offset(Legend, style.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FG_MONO, Tk_Offset(Legend, style.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LEGEND_HIDE, Tk_Offset(Legend, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "Pad",
+ DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "Pad",
+ DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "Pad",
+ DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "Pad",
+ DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_LEGEND_POSITION, 0,
+ TK_CONFIG_DONT_SET_DEFAULT, &legendPositionOption},
+ {TK_CONFIG_BOOLEAN, "-raised", "raised", "Raised",
+ DEF_LEGEND_RAISED, Tk_Offset(Legend, raised),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-rows", "rows", "rows",
+ DEF_LEGEND_ROWS, Tk_Offset(Legend, reqRows),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_LEGEND_SHADOW_COLOR, Tk_Offset(Legend, style.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_LEGEND_SHADOW_MONO, Tk_Offset(Legend, style.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+#ifdef __STDC__
+static Tcl_IdleProc DisplayLegend;
+static Blt_BindPickProc PickLegendEntry;
+static Tk_EventProc LegendEventProc;
+#endif
+
+extern Tcl_CmdProc Blt_GraphInstCmdProc;
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EventuallyRedrawLegend --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at
+ * the next idle point. This request is made only if the window
+ * is displayed and no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+EventuallyRedrawLegend(legendPtr)
+ Legend *legendPtr; /* Legend record */
+{
+ if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayLegend, legendPtr);
+ legendPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LegendEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on graphs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, the graph is eventually
+ * redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+LegendEventProc(clientData, eventPtr)
+ ClientData clientData; /* Legend record */
+ register XEvent *eventPtr; /* Event which triggered call to routine */
+{
+ Legend *legendPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawLegend(legendPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Blt_DeleteWindowInstanceData(legendPtr->tkwin);
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp,
+ legendPtr->cmdToken);
+ legendPtr->cmdToken = NULL;
+ }
+ legendPtr->tkwin = graphPtr->tkwin;
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ legendPtr->site = LEGEND_RIGHT;
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ } else if (eventPtr->type == ConfigureNotify) {
+ EventuallyRedrawLegend(legendPtr);
+ }
+}
+
+static int
+CreateLegendWindow(interp, legendPtr, pathName)
+ Tcl_Interp *interp;
+ Legend *legendPtr;
+ char *pathName;
+{
+ Tk_Window tkwin;
+
+ tkwin = Tk_MainWindow(interp);
+ tkwin = Tk_CreateWindowFromPath(interp, tkwin, pathName, NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_SetWindowInstanceData(tkwin, legendPtr);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, legendPtr);
+ /* Move the legend's binding table to the new window. */
+ Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
+ if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ }
+ legendPtr->cmdToken = Tcl_CreateCommand(interp, pathName,
+ Blt_GraphInstCmdProc, legendPtr->graphPtr, NULL);
+ legendPtr->tkwin = tkwin;
+ legendPtr->site = LEGEND_WINDOW;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPosition --
+ *
+ * Convert the string representation of a legend XY position into
+ * window coordinates. The form of the string must be "@x,y" or
+ * none.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPosition(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+ char c;
+ unsigned int length;
+
+ c = string[0];
+ length = strlen(string);
+
+ if ((string == NULL) || (*string == '\0')) {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 'l') && (strncmp(string, "leftmargin", length) == 0)) {
+ legendPtr->site = LEGEND_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "rightmargin", length) == 0)) {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "topmargin", length) == 0)) {
+ legendPtr->site = LEGEND_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottommargin", length) == 0)) {
+ legendPtr->site = LEGEND_BOTTOM;
+ } else if ((c == 'p') && (strncmp(string, "plotarea", length) == 0)) {
+ legendPtr->site = LEGEND_PLOT;
+ } else if (c == '@') {
+ char *comma;
+ long x, y;
+ int result;
+
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ Tcl_AppendResult(interp, "bad screen position \"", string,
+ "\": should be @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ x = y = 0;
+ *comma = '\0';
+ result = ((Tcl_ExprLong(interp, string + 1, &x) == TCL_OK) &&
+ (Tcl_ExprLong(interp, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ return TCL_ERROR;
+ }
+ legendPtr->anchorPos.x = (int)x;
+ legendPtr->anchorPos.y = (int)y;
+ legendPtr->site = LEGEND_XY;
+ } else if (c == '.') {
+ if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ legendPtr->tkwin = legendPtr->graphPtr->tkwin;
+ }
+ if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ legendPtr->site = LEGEND_WINDOW;
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", string, "\": should be \
+\"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
+\"plotarea\", .window or @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PositionToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PositionToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of XPoint in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+
+ switch (legendPtr->site) {
+ case LEGEND_LEFT:
+ return "leftmargin";
+ case LEGEND_RIGHT:
+ return "rightmargin";
+ case LEGEND_TOP:
+ return "topmargin";
+ case LEGEND_BOTTOM:
+ return "bottommargin";
+ case LEGEND_PLOT:
+ return "plotarea";
+ case LEGEND_WINDOW:
+ return Tk_PathName(legendPtr->tkwin);
+ case LEGEND_XY:
+ {
+ char string[200];
+ char *result;
+
+ sprintf(string, "@%d,%d", (int)legendPtr->anchorPos.x,
+ (int)legendPtr->anchorPos.y);
+ result = Blt_Strdup(string);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+ }
+ default:
+ return "unknown legend position";
+ }
+}
+
+static void
+SetLegendOrigin(legendPtr)
+ Legend *legendPtr;
+{
+ Graph *graphPtr;
+ int x, y, width, height;
+
+ graphPtr = legendPtr->graphPtr;
+ x = y = width = height = 0; /* Suppress compiler warning. */
+ switch (legendPtr->site) {
+ case LEGEND_RIGHT:
+ width = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->width - (width + graphPtr->inset);
+ y = graphPtr->top;
+ break;
+ case LEGEND_LEFT:
+ width = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->inset;
+ y = graphPtr->top;
+ break;
+ case LEGEND_TOP:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
+ if (graphPtr->titleText != NULL) {
+ height -= graphPtr->titleStyle.height;
+ }
+ x = graphPtr->left;
+ y = graphPtr->inset;
+ if (graphPtr->titleText != NULL) {
+ y += graphPtr->titleStyle.height;
+ }
+ break;
+ case LEGEND_BOTTOM:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->bottomMargin.height -
+ graphPtr->bottomMargin.axesOffset;
+ x = graphPtr->left;
+ y = graphPtr->height - (height + graphPtr->inset);
+ break;
+ case LEGEND_PLOT:
+ width = graphPtr->right - graphPtr->left;
+ height = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->left;
+ y = graphPtr->top;
+ break;
+ case LEGEND_XY:
+ width = legendPtr->width;
+ height = legendPtr->height;
+ x = (int)legendPtr->anchorPos.x;
+ y = (int)legendPtr->anchorPos.y;
+ if (x < 0) {
+ x += graphPtr->width;
+ }
+ if (y < 0) {
+ y += graphPtr->height;
+ }
+ break;
+ case LEGEND_WINDOW:
+ legendPtr->anchor = TK_ANCHOR_NW;
+ legendPtr->x = legendPtr->y = 0;
+ return;
+ }
+ width = legendPtr->width - width;
+ height = legendPtr->height - height;
+ Blt_TranslateAnchor(x, y, width, height, legendPtr->anchor, &x, &y);
+
+ legendPtr->x = x + legendPtr->padLeft;
+ legendPtr->y = y + legendPtr->padTop;
+}
+
+
+static ClientData
+PickLegendEntry(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Point to be tested */
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+ int width, height;
+
+ legendPtr = graphPtr->legend;
+ width = legendPtr->width;
+ height = legendPtr->height;
+
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ width -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
+ height -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
+
+ if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
+ int row, column;
+ int n;
+
+ /*
+ * It's in the bounding box, so compute the index.
+ */
+ row = y / legendPtr->style.height;
+ column = x / legendPtr->style.width;
+ n = (column * legendPtr->nRows) + row;
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+ int count;
+
+ /* FIXME: The entry is not in numeric order since not all
+ * entries may be visible. */
+ /* Legend entries are stored in reverse. */
+ count = 0;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label != NULL) {
+ if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_MapLegend --
+ *
+ * Calculates the dimensions (width and height) needed for
+ * the legend. Also determines the number of rows and columns
+ * necessary to list all the valid element labels.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The following fields of the legend are calculated and set.
+ *
+ * nEntries - number of valid labels of elements in the
+ * display list.
+ * nRows - number of rows of entries
+ * nColumns - number of columns of entries
+ * style.height - height of each entry
+ * style.width - width of each entry
+ * height - width of legend (includes borders and padding)
+ * width - height of legend (includes borders and padding)
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_MapLegend(legendPtr, plotWidth, plotHeight)
+ Legend *legendPtr;
+ int plotWidth; /* Maximum width available in window
+ * to draw the legend. Will calculate number
+ * of columns from this. */
+ int plotHeight; /* Maximum height available in window
+ * to draw the legend. Will calculate number
+ * of rows from this. */
+{
+ Blt_ChainLink *linkPtr;
+ Element *elemPtr;
+ int nRows, nColumns;
+ int nEntries;
+ int width, height;
+ int legendWidth, legendHeight;
+ int symbolWidth;
+ int outer, inner;
+ Tk_FontMetrics fontMetrics;
+
+ /* Initialize legend values to default (no legend displayed) */
+
+ legendPtr->style.width = legendPtr->style.height = 0;
+ legendPtr->nRows = legendPtr->nColumns = 0;
+ legendPtr->nEntries = 0;
+ legendPtr->height = legendPtr->width = 0;
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ if (Tk_Width(legendPtr->tkwin) > 1) {
+ plotWidth = Tk_Width(legendPtr->tkwin);
+ }
+ if (Tk_Height(legendPtr->tkwin) > 1) {
+ plotHeight = Tk_Height(legendPtr->tkwin);
+ }
+ }
+ if ((legendPtr->hidden) || (plotWidth < 1) || (plotHeight < 1)) {
+ return; /* Legend is not being displayed */
+ }
+
+ /* Determine the number of labels and the widest label */
+
+ nEntries = 0;
+ legendWidth = legendHeight = 0;
+ for (linkPtr = Blt_ChainLastLink(legendPtr->graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ Blt_GetTextExtents(&legendPtr->style, elemPtr->label, &width, &height);
+ if (legendWidth < width) {
+ legendWidth = width;
+ }
+ if (legendHeight < height) {
+ legendHeight = height;
+ }
+ nEntries++;
+ }
+
+ if (nEntries == 0) {
+ return; /* No labels to display in legend */
+ }
+
+ /*
+ * Calculate the space need to for the legend based upon the size
+ * of a label entry and the number of rows and columns needed.
+ * Bound the height of the area by *maxHeight* which is the size
+ * of the plotting area.
+ */
+
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolWidth = 2 * fontMetrics.ascent;
+ legendPtr->nEntries = nEntries;
+
+ outer = 2 * legendPtr->borderWidth;
+ inner = 2 * legendPtr->entryBW;
+
+ legendPtr->style.height = legendHeight + inner + PADDING(legendPtr->ipadY);
+ legendPtr->style.width = legendWidth + inner + PADDING(legendPtr->ipadX) +
+ 5 + symbolWidth;
+
+ width = plotWidth - (outer + PADDING(legendPtr->padX));
+ height = plotHeight - (outer + PADDING(legendPtr->padY));
+ if (legendPtr->reqRows > 0) {
+ nRows = legendPtr->reqRows;
+ } else {
+ nRows = height / legendPtr->style.height;
+ }
+ if (legendPtr->reqColumns > 0) {
+ nColumns = legendPtr->reqColumns;
+ } else {
+ nColumns = width / legendPtr->style.width;
+ }
+ if (nRows < 1) {
+ nRows = 1;
+ }
+ if (nColumns < 1) {
+ nColumns = 1;
+ }
+ if ((legendPtr->site == LEGEND_TOP) || (legendPtr->site == LEGEND_BOTTOM)) {
+ if (nColumns > 0) {
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ if (nColumns > nEntries) {
+ nColumns = nEntries;
+ } else {
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ }
+ }
+ } else {
+ if (nRows > 0) {
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ if (nRows > nEntries) {
+ nRows = nEntries;
+ }
+ }
+ }
+ legendPtr->height = outer + PADDING(legendPtr->padY) +
+ (nRows * legendPtr->style.height);
+ legendPtr->width = outer + PADDING(legendPtr->padX) +
+ (nColumns * legendPtr->style.width);
+ legendPtr->nRows = nRows;
+ legendPtr->nColumns = nColumns;
+ if ((legendPtr->tkwin != legendPtr->graphPtr->tkwin) &&
+ ((Tk_ReqWidth(legendPtr->tkwin) != legendPtr->width) ||
+ (Tk_ReqHeight(legendPtr->tkwin) != legendPtr->height))) {
+ Tk_GeometryRequest(legendPtr->tkwin, legendPtr->width,
+ legendPtr->height);
+ }
+}
+
+void
+Blt_DrawLegend(legendPtr, drawable)
+ Legend *legendPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr;
+ Blt_ChainLink *linkPtr;
+ Pixmap pixmap;
+ Tk_3DBorder border;
+ Tk_FontMetrics fontMetrics;
+ Tk_Window tkwin;
+ int count;
+ int labelX, startY, symbolX, symbolY;
+ int symbolSize, midX, midY;
+ int width, height;
+ int x, y;
+ register Element *elemPtr;
+
+ graphPtr = legendPtr->graphPtr;
+ graphPtr->flags &= ~DRAW_LEGEND;
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ tkwin = legendPtr->tkwin;
+ width = Tk_Width(tkwin);
+ if (width < 1) {
+ width = legendPtr->width;
+ }
+ height = Tk_Height(tkwin);
+ if (height < 1) {
+ height = legendPtr->height;
+ }
+ } else {
+ tkwin = graphPtr->tkwin;
+ width = legendPtr->width;
+ height = legendPtr->height;
+ }
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+
+ symbolSize = fontMetrics.ascent;
+ midX = symbolSize + 1 + legendPtr->entryBW;
+ midY = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ labelX = 2 * symbolSize + legendPtr->entryBW + legendPtr->ipadX.side1 + 5;
+ symbolY = midY + legendPtr->ipadY.side1;
+ symbolX = midX + legendPtr->ipadX.side1;
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(legendPtr->tkwin),
+ width, height, Tk_Depth(legendPtr->tkwin));
+
+ if (legendPtr->border != NULL) {
+ /* Background color and relief. */
+ Tk_Fill3DRectangle(legendPtr->tkwin, pixmap, legendPtr->border, 0, 0,
+ width, height, 0, TK_RELIEF_FLAT);
+ } else if (legendPtr->site & LEGEND_IN_PLOT) {
+ /*
+ * Legend background is transparent and is positioned over the
+ * the plot area. Either copy the part of the background from
+ * the backing store pixmap or (if no backing store exists)
+ * just fill it with the background color of the plot.
+ */
+ if (graphPtr->backPixmap != None) {
+ XCopyArea(graphPtr->display, graphPtr->backPixmap, pixmap,
+ graphPtr->drawGC, legendPtr->x, legendPtr->y, width, height,
+ 0, 0);
+ } else {
+ XFillRectangle(graphPtr->display, pixmap, graphPtr->plotFillGC,
+ 0, 0, width, height);
+ }
+ } else {
+ /*
+ * The legend is positioned in one of the margins or the
+ * external window. Draw either the solid or tiled background
+ * with the the border.
+ */
+ if (graphPtr->tile != NULL) {
+ Blt_SetTileOrigin(legendPtr->tkwin, graphPtr->tile, legendPtr->x,
+ legendPtr->y);
+ Blt_TileRectangle(legendPtr->tkwin, pixmap, graphPtr->tile, 0, 0,
+ width, height);
+ } else {
+ XFillRectangle(graphPtr->display, pixmap, graphPtr->fillGC, 0, 0,
+ width, height);
+ }
+ }
+ x = legendPtr->padLeft + legendPtr->borderWidth;
+ y = legendPtr->padTop + legendPtr->borderWidth;
+ count = 0;
+ startY = y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this entry */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ legendPtr->style.state |= STATE_ACTIVE;
+ Tk_Fill3DRectangle(legendPtr->tkwin, pixmap,
+ legendPtr->activeBorder, x, y,
+ legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ } else {
+ legendPtr->style.state &= ~STATE_ACTIVE;
+ if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(legendPtr->tkwin, pixmap, graphPtr->border,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, elemPtr->labelRelief);
+ }
+ }
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
+ x + symbolX, y + symbolY, symbolSize);
+ Blt_DrawText(legendPtr->tkwin, pixmap, elemPtr->label,
+ &legendPtr->style,
+ x + labelX, y + legendPtr->entryBW + legendPtr->ipadY.side1);
+ count++;
+
+ /* Check when to move to the next column */
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->style.height;
+ } else {
+ x += legendPtr->style.width;
+ y = startY;
+ }
+ }
+ /*
+ * Draw the border and/or background of the legend.
+ */
+ border = legendPtr->border;
+ if (border == NULL) {
+ border = graphPtr->border;
+ }
+ Tk_Draw3DRectangle(legendPtr->tkwin, pixmap, border, 0, 0, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+
+ XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0,
+ width, height, legendPtr->x, legendPtr->y);
+ Tk_FreePixmap(graphPtr->display, pixmap);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LegendToPostScript --
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LegendToPostScript(legendPtr, psToken)
+ Legend *legendPtr;
+ PsToken psToken;
+{
+ Graph *graphPtr;
+ double x, y, startY;
+ Element *elemPtr;
+ int labelX, symbolX, symbolY;
+ int count;
+ Blt_ChainLink *linkPtr;
+ int symbolSize, midX, midY;
+ int width, height;
+ Tk_FontMetrics fontMetrics;
+
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ x = legendPtr->x, y = legendPtr->y;
+ width = legendPtr->width - PADDING(legendPtr->padX);
+ height = legendPtr->height - PADDING(legendPtr->padY);
+
+ graphPtr = legendPtr->graphPtr;
+ if (graphPtr->postscript->decorations) {
+ if (legendPtr->border != NULL) {
+ Blt_Fill3DRectangleToPostScript(psToken, legendPtr->border, x, y,
+ width, height, legendPtr->borderWidth, legendPtr->relief);
+ } else {
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, x, y,
+ width, height, legendPtr->borderWidth, legendPtr->relief);
+ }
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ Blt_RectangleToPostScript(psToken, x, y, width, height);
+ }
+ x += legendPtr->borderWidth;
+ y += legendPtr->borderWidth;
+
+ Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolSize = fontMetrics.ascent;
+ midX = symbolSize + 1 + legendPtr->entryBW;
+ midY = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ labelX = 2 * symbolSize + legendPtr->entryBW + legendPtr->ipadX.side1 + 5;
+ symbolY = midY + legendPtr->ipadY.side1;
+ symbolX = midX + legendPtr->ipadX.side1;
+
+ count = 0;
+ startY = y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ legendPtr->style.state |= STATE_ACTIVE;
+ Blt_Fill3DRectangleToPostScript(psToken, legendPtr->activeBorder,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ } else {
+ legendPtr->style.state &= ~STATE_ACTIVE;
+ if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border,
+ x, y, legendPtr->style.width, legendPtr->style.height,
+ legendPtr->entryBW, elemPtr->labelRelief);
+ }
+ }
+ (*elemPtr->procsPtr->printSymbolProc) (graphPtr, psToken, elemPtr,
+ x + symbolX, y + symbolY, symbolSize);
+ Blt_TextToPostScript(psToken, elemPtr->label, &(legendPtr->style),
+ x + labelX, y + legendPtr->entryBW + legendPtr->ipadY.side1);
+ count++;
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->style.height;
+ } else {
+ x += legendPtr->style.width;
+ y = startY;
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DisplayLegend --
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DisplayLegend(clientData)
+ ClientData clientData;
+{
+ Legend *legendPtr = clientData;
+ int width, height;
+
+ legendPtr->flags &= ~REDRAW_PENDING;
+
+ if (legendPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (legendPtr->site == LEGEND_WINDOW) {
+ width = Tk_Width(legendPtr->tkwin);
+ height = Tk_Height(legendPtr->tkwin);
+ if ((width <= 1) || (height <= 1)) {
+ return;
+ }
+ if ((width != legendPtr->width) || (height != legendPtr->height)) {
+ Blt_MapLegend(legendPtr, width, height);
+ }
+ }
+ if (!Tk_IsMapped(legendPtr->tkwin)) {
+ return;
+ }
+ Blt_DrawLegend(legendPtr, Tk_WindowId(legendPtr->tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLegend --
+ *
+ * Routine to configure the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureLegend(graphPtr, legendPtr)
+ Graph *graphPtr;
+ Legend *legendPtr;
+{
+ Blt_ResetTextStyle(graphPtr->tkwin, &(legendPtr->style));
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ EventuallyRedrawLegend(legendPtr);
+ } else {
+ /*
+ * Update the layout of the graph (and redraw the elements) if
+ * any of the following legend options (all of which affect the
+ * size of the legend) have changed.
+ *
+ * -activeborderwidth, -borderwidth
+ * -border
+ * -font
+ * -hide
+ * -ipadx, -ipady, -padx, -pady
+ * -rows
+ *
+ * If the position of the legend changed to/from the default
+ * position, also indicate that a new layout is needed.
+ *
+ */
+ if (Blt_ConfigModified(configSpecs, "-*border*", "-*pad?",
+ "-position", "-hide", "-font", "-rows", (char *)NULL)) {
+ graphPtr->flags |= MAP_WORLD;
+ }
+ graphPtr->flags |= (REDRAW_WORLD | REDRAW_BACKING_STORE);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyLegend --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources associated with the legend are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyLegend(graphPtr)
+ Graph *graphPtr;
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
+ Blt_FreeTextStyle(graphPtr->display, &(legendPtr->style));
+ Blt_DestroyBindingTable(legendPtr->bindTable);
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Tk_Window tkwin;
+
+ /* The graph may be in the process of being torn down */
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ tkwin = legendPtr->tkwin;
+ legendPtr->tkwin = NULL;
+ if (tkwin != NULL) {
+ Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, legendPtr);
+ Blt_DeleteWindowInstanceData(tkwin);
+ Tk_DestroyWindow(tkwin);
+ }
+ }
+ Blt_Free(legendPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateLegend --
+ *
+ * Creates and initializes a legend structure with default settings
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_CreateLegend(graphPtr)
+ Graph *graphPtr;
+{
+ Legend *legendPtr;
+
+ legendPtr = Blt_Calloc(1, sizeof(Legend));
+ assert(legendPtr);
+ graphPtr->legend = legendPtr;
+ legendPtr->graphPtr = graphPtr;
+ legendPtr->tkwin = graphPtr->tkwin;
+ legendPtr->hidden = FALSE;
+ legendPtr->anchorPos.x = legendPtr->anchorPos.y = -SHRT_MAX;
+ legendPtr->relief = TK_RELIEF_SUNKEN;
+ legendPtr->activeRelief = TK_RELIEF_FLAT;
+ legendPtr->entryBW = legendPtr->borderWidth = 2;
+ legendPtr->ipadX.side1 = legendPtr->ipadX.side2 = 1;
+ legendPtr->ipadY.side1 = legendPtr->ipadY.side2 = 1;
+ legendPtr->padX.side1 = legendPtr->padX.side2 = 1;
+ legendPtr->padY.side1 = legendPtr->padY.side2 = 1;
+ legendPtr->anchor = TK_ANCHOR_N;
+ legendPtr->site = LEGEND_RIGHT;
+ Blt_InitTextStyle(&(legendPtr->style));
+ legendPtr->style.justify = TK_JUSTIFY_LEFT;
+ legendPtr->style.anchor = TK_ANCHOR_NW;
+ legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
+ graphPtr->tkwin, graphPtr, PickLegendEntry, Blt_GraphTags);
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ "legend", "Legend", configSpecs, 0, (char **)NULL,
+ (char *)legendPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureLegend(graphPtr, legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument
+ * can be either a screen position "@x,y" or the name of an
+ * element.
+ *
+ * I don't know how useful it is to test with the name of an
+ * element.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ register Element *elemPtr;
+ Legend *legendPtr = graphPtr->legend;
+ int x, y;
+ char c;
+
+ if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+ return TCL_OK;
+ }
+ elemPtr = NULL;
+ c = argv[3][0];
+ if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
+ elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
+ } else if ((c == '@') &&
+ (Blt_GetXY(interp, graphPtr->tkwin, argv[3], &x, &y) == TCL_OK)) {
+ elemPtr = (Element *)PickLegendEntry(graphPtr, x, y);
+ }
+ if (elemPtr != NULL) {
+ Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Activates a particular label in the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ActivateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+ unsigned int active, redraw;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ register int i;
+
+ active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
+ redraw = 0;
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(elemPtr->name, argv[i])) {
+ break;
+ }
+ }
+ if ((i < argc) && (active != (elemPtr->flags & LABEL_ACTIVE))) {
+ elemPtr->flags ^= LABEL_ACTIVE;
+ if (elemPtr->label != NULL) {
+ redraw++;
+ }
+ }
+ }
+ if ((redraw) && (!legendPtr->hidden)) {
+ /*
+ * See if how much we need to draw. If the graph is already
+ * schedule for a redraw, just make sure the right flags are
+ * set. Otherwise redraw only the legend: it's either in an
+ * external window or it's the only thing that need updating.
+ */
+ if (graphPtr->flags & REDRAW_PENDING) {
+ if (legendPtr->site & LEGEND_IN_PLOT) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
+ } else {
+ EventuallyRedrawLegend(legendPtr);
+ }
+ }
+ /* Return the names of all the active legend entries */
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tcl_AppendElement(interp, elemPtr->name);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->legend->bindTable,
+ Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr->legend, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int flags = TK_CONFIG_ARGV_ONLY;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)legendPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureLegend(graphPtr, legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LegendOp --
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Legend is possibly redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec legendOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+ {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+ {"deactivate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "index",},
+};
+static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_LegendOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nLegendOps, legendOps, BLT_OP_ARG2, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+
+int
+Blt_LegendSite(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->site;
+}
+
+int
+Blt_LegendWidth(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->width;
+}
+
+int
+Blt_LegendHeight(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->height;
+}
+
+int
+Blt_LegendIsHidden(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->hidden;
+}
+
+int
+Blt_LegendIsRaised(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->raised;
+}
+
+int
+Blt_LegendX(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->x;
+}
+
+int
+Blt_LegendY(legendPtr)
+ Legend *legendPtr;
+{
+ return legendPtr->y;
+}
+
+void
+Blt_LegendRemoveElement(legendPtr, elemPtr)
+ Legend *legendPtr;
+ Element *elemPtr;
+{
+ Blt_DeleteBindings(legendPtr->bindTable, elemPtr);
+}
diff --git a/blt/src/bltGrLegd.h b/blt/src/bltGrLegd.h
new file mode 100644
index 00000000000..211a31737eb
--- /dev/null
+++ b/blt/src/bltGrLegd.h
@@ -0,0 +1,56 @@
+/*
+ * bltGrLegd.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_LEGEND_H
+#define _BLT_GR_LEGEND_H
+
+#define LEGEND_RIGHT (1<<0) /* Right margin */
+#define LEGEND_LEFT (1<<1) /* Left margin */
+#define LEGEND_BOTTOM (1<<2) /* Bottom margin */
+#define LEGEND_TOP (1<<3) /* Top margin, below the graph title. */
+#define LEGEND_PLOT (1<<4) /* Plot area */
+#define LEGEND_XY (1<<5) /* Screen coordinates in the plotting
+ * area. */
+#define LEGEND_WINDOW (1<<6) /* External window. */
+#define LEGEND_IN_MARGIN \
+ (LEGEND_RIGHT | LEGEND_LEFT | LEGEND_BOTTOM | LEGEND_TOP)
+#define LEGEND_IN_PLOT (LEGEND_PLOT | LEGEND_XY)
+
+extern int Blt_CreateLegend _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyLegend _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DrawLegend _ANSI_ARGS_((Legend *legendPtr, Drawable drawable));
+extern void Blt_MapLegend _ANSI_ARGS_((Legend *legendPtr, int width,
+ int height));
+extern int Blt_LegendOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_LegendSite _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendWidth _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendHeight _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendIsHidden _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendIsRaised _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendX _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendY _ANSI_ARGS_((Legend *legendPtr));
+extern void Blt_LegendRemoveElement _ANSI_ARGS_((Legend *legendPtr,
+ Element *elemPtr));
+#endif /* BLT_GR_LEGEND_H */
diff --git a/blt/src/bltGrLine.c b/blt/src/bltGrLine.c
new file mode 100644
index 00000000000..103d4eddbf0
--- /dev/null
+++ b/blt/src/bltGrLine.c
@@ -0,0 +1,5091 @@
+/*
+ * bltGrLine.c --
+ *
+ * This module implements line graph and stripchart elements for
+ * the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+#define COLOR_DEFAULT (XColor *)1
+#define PATTERN_SOLID ((Pixmap)1)
+
+#define PEN_INCREASING 1 /* Draw line segments for only those
+ * data points whose abscissas are
+ * monotonically increasing in
+ * order */
+#define PEN_DECREASING 2 /* Lines will be drawn between only
+ * those points whose abscissas are
+ * decreasing in order */
+
+#define PEN_BOTH_DIRECTIONS (PEN_INCREASING | PEN_DECREASING)
+ /* Lines will be drawn between points regardless of the ordering of
+ * the abscissas */
+
+#define BROKEN_TRACE(dir,last,next) \
+ (((((dir) & PEN_DECREASING) == 0) && ((next) < (last))) || \
+ ((((dir) & PEN_INCREASING) == 0) && ((next) > (last))))
+
+#define DRAW_SYMBOL(linePtr) \
+ (((linePtr)->symbolCounter % (linePtr)->symbolInterval) == 0)
+
+typedef enum {
+ PEN_SMOOTH_NONE, /* Line segments */
+ PEN_SMOOTH_STEP, /* Step-and-hold */
+ PEN_SMOOTH_NATURAL, /* Natural cubic spline */
+ PEN_SMOOTH_QUADRATIC, /* Quadratic spline */
+ PEN_SMOOTH_CATROM, /* Catrom parametric spline */
+ PEN_SMOOTH_LAST /* Sentinel */
+} Smoothing;
+
+typedef struct {
+ char *name;
+ Smoothing value;
+} SmoothingInfo;
+
+static SmoothingInfo smoothingInfo[] = {
+ { "linear", PEN_SMOOTH_NONE },
+ { "step", PEN_SMOOTH_STEP },
+ { "natural", PEN_SMOOTH_NATURAL },
+ { "cubic", PEN_SMOOTH_NATURAL },
+ { "quadratic", PEN_SMOOTH_QUADRATIC },
+ { "catrom", PEN_SMOOTH_CATROM },
+ { (char *)NULL, PEN_SMOOTH_LAST }
+};
+
+
+typedef struct {
+ Point2D *screenPts; /* Array of transformed coordinates */
+ int nScreenPts; /* Number of coordinates */
+ int *dataToStyle; /* Index of pen styles */
+ int *indices; /* Maps segments/traces to data points */
+
+} MapInfo;
+
+/*
+ * Symbol types for line elements
+ */
+typedef enum {
+ SYMBOL_NONE,
+ SYMBOL_SQUARE,
+ SYMBOL_CIRCLE,
+ SYMBOL_DIAMOND,
+ SYMBOL_PLUS,
+ SYMBOL_CROSS,
+ SYMBOL_SPLUS,
+ SYMBOL_SCROSS,
+ SYMBOL_TRIANGLE,
+ SYMBOL_ARROW,
+ SYMBOL_BITMAP
+} SymbolType;
+
+typedef struct {
+ SymbolType type; /* Type of symbol to be drawn/printed */
+
+ int size; /* Requested size of symbol in pixels */
+
+ XColor *outlineColor; /* Outline color */
+
+ int outlineWidth; /* Width of the outline */
+
+ GC outlineGC; /* Outline graphics context */
+
+ XColor *fillColor; /* Normal fill color */
+
+ GC fillGC; /* Fill graphics context */
+
+ /* The last two fields are used only for bitmap symbols. */
+
+ Pixmap bitmap; /* Bitmap to determine foreground/background
+ * pixels of the symbol */
+
+ Pixmap mask; /* Bitmap representing the transparent
+ * pixels of the symbol */
+
+} Symbol;
+
+typedef struct {
+ int start; /* Index into the X-Y coordinate
+ * arrays indicating where trace
+ * starts. */
+
+ int nScreenPts; /* Number of points in the continuous
+ * trace */
+
+ Point2D *screenPts; /* Array of screen coordinates
+ * (malloc-ed) representing the
+ * trace. */
+
+ int *symbolToData; /* Reverse mapping of screen
+ * coordinate indices back to their
+ * data coordinates */
+} Trace;
+
+typedef struct {
+ char *name; /* Name of pen style. If the pen was
+ * statically allocated the name will
+ * be NULL. */
+
+ Tk_Uid classUid; /* Type of pen */
+
+ char *typeId; /* String token identifying the type
+ * of pen */
+
+ unsigned int flags; /* Indicates if the pen element is
+ * active or normal */
+
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+
+ /* Symbol attributes. */
+ Symbol symbol; /* Element symbol type */
+
+ /* Trace attributes. */
+ int traceWidth; /* Width of the line segments. If
+ * lineWidth is 0, no line will be
+ * drawn, only symbols. */
+
+ Blt_Dashes traceDashes; /* Dash on-off list value */
+
+ XColor *traceColor; /* Line segment color */
+
+ XColor *traceOffColor; /* Line segment dash gap color */
+
+ GC traceGC; /* Line segment graphics context */
+
+ /* Error bar attributes. */
+ int errorShow; /* Describes which error bars to
+ * display: none, x, y, or * both. */
+
+ int errorWidth; /* Width of the error bar segments. */
+
+ XColor *errorColor; /* Color of the error bar. */
+
+ GC errorGC; /* Error bar graphics context. */
+
+ /* Show value attributes. */
+ int valueShow; /* Indicates whether to display data
+ * value. Values are x, y, both, or
+ * none. */
+ char *valueFormat; /* A printf format string. */
+
+ TextStyle valueStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the value. */
+
+} LinePen;
+
+typedef struct {
+ AxisRange weight; /* Weight range where this pen is valid. */
+
+ LinePen *penPtr; /* Pen used to draw symbols, traces, error
+ * bars, segments, etc. */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to the
+ * current graph size. */
+
+ /* Graph specific data. */
+
+ Point2D *symbolPts; /* Points to start of array for this pen. */
+
+ int nSymbolPts; /* # of points for this pen. */
+
+ /* The last two fields are used only for stripcharts. */
+
+ Segment2D *strips; /* Points to start of the line segments
+ * for this pen. */
+
+ int nStrips; /* # of line segments for this pen. */
+
+} LinePenStyle;
+
+typedef struct {
+ char *name; /* Identifier used to refer the
+ * element. Used in the "insert",
+ * "delete", or "show", operations. */
+
+ Tk_Uid classUid; /* Type of element */
+
+ Graph *graphPtr; /* Graph widget of element*/
+
+ unsigned int flags; /* Indicates if the entire element is
+ * active, or if coordinates need to
+ * be calculated */
+
+ char **tags;
+
+ int hidden; /* If non-zero, don't display the
+ * element. */
+
+ Blt_HashEntry *hashPtr;
+
+ char *label; /* Label displayed in legend */
+
+ int labelRelief; /* Relief of label in legend. */
+
+ Axis2D axes;
+
+ ElemVector x, y, w; /* Contains array of numeric values */
+
+ ElemVector xError; /* Relative/symmetric X error values. */
+ ElemVector yError; /* Relative/symmetric Y error values. */
+ ElemVector xHigh, xLow; /* Absolute/asymmetric X-coordinate high/low
+ error values. */
+ ElemVector yHigh, yLow; /* Absolute/asymmetric Y-coordinate high/low
+ error values. */
+
+ int *reqActive; /* Array of indices (malloc-ed) that
+ * indicate the data points are active
+ * (drawn with "active" colors). */
+
+ int nReqActive; /* Number of active data points.
+ * Special case: if < 0 then all data
+ * points are drawn active. */
+
+ ElementProcs *procsPtr;
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int *xErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+ int *yErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+
+ LinePen *activePenPtr; /* Pen to draw "active" elements. */
+ LinePen *normalPenPtr; /* Pen to draw elements normally. */
+ Blt_Chain *palette; /* Array of pen styles: pens are associated
+ * with specific ranges of data.*/
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+
+ /*
+ * Line specific configurable attributes
+ */
+ LinePen builtinPen;
+
+ /* Line smoothing */
+ Smoothing reqSmooth; /* Requested smoothing function to use
+ * for connecting the data points */
+
+ Smoothing smooth; /* Smoothing function used. */
+
+ double rTolerance; /* Tolerance to reduce the number of
+ * points displayed. */
+ /*
+ * Drawing related data structures.
+ */
+
+ /* Area-under-curve fill attributes. */
+ XColor *fillFgColor;
+ XColor *fillBgColor;
+ GC fillGC;
+
+ Blt_Tile fillTile; /* Tile for fill area. */
+ Pixmap fillStipple; /* Stipple for fill area. */
+
+ int nFillPts;
+ Point2D *fillPts; /* Array of points used to draw
+ * polygon to fill area under the
+ * curve */
+
+ /* Symbol points */
+ Point2D *symbolPts; /* Holds the screen coordinates of all
+ * the data points for the element. */
+ int nSymbolPts; /* Number of points */
+
+ int *symbolToData; /* Contains indices of data points.
+ * It's first used to map pens to the
+ * visible points to sort them by pen
+ * style, and later to find data
+ * points from the index of a visible
+ * point. */
+
+ /* Active symbol points */
+ Point2D *activePts; /* Array of indices representing the
+ * "active" points. */
+ int nActivePts; /* Number of indices in the above array. */
+
+ int *activeToData; /* Contains indices of data points.
+ * It's first used to map pens to the
+ * visible points to sort them by pen
+ * style, and later to find data
+ * points from the index of a visible
+ * point. */
+
+ int reqMaxSymbols;
+ int symbolInterval;
+ int symbolCounter;
+
+ /* X-Y graph-specific fields */
+
+ int penDir; /* Indicates if a change in the pen
+ * direction should be considered a
+ * retrace (line segment is not
+ * drawn). */
+
+ Blt_Chain *chainPtr; /* List of traces (a trace is a series
+ * of contiguous line segments). New
+ * traces are generated when either
+ * the next segment changes the pen
+ * direction, or the end point is
+ * clipped by the plotting area. */
+
+ /* Stripchart-specific fields */
+
+ Segment2D *strips; /* Holds the the line segments of the
+ * element trace. The segments are
+ * grouped by pen style. */
+ int nStrips; /* Number of line segments to be drawn. */
+ int *stripToData; /* Pen to visible line segment mapping. */
+
+} Line;
+
+static Tk_OptionParseProc StringToPattern;
+static Tk_OptionPrintProc PatternToString;
+static Tk_OptionParseProc StringToSmooth;
+static Tk_OptionPrintProc SmoothToString;
+extern Tk_OptionParseProc Blt_StringToStyles;
+extern Tk_OptionPrintProc Blt_StylesToString;
+static Tk_OptionParseProc StringToPenDir;
+static Tk_OptionPrintProc PenDirToString;
+static Tk_OptionParseProc StringToSymbol;
+static Tk_OptionPrintProc SymbolToString;
+
+static Tk_CustomOption patternOption =
+{
+ StringToPattern, PatternToString, (ClientData)0
+};
+static Tk_CustomOption smoothOption =
+{
+ StringToSmooth, SmoothToString, (ClientData)0
+};
+static Tk_CustomOption stylesOption =
+{
+ Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(LinePenStyle)
+};
+static Tk_CustomOption penDirOption =
+{
+ StringToPenDir, PenDirToString, (ClientData)0
+};
+static Tk_CustomOption symbolOption =
+{
+ StringToSymbol, SymbolToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDataOption;
+extern Tk_CustomOption bltDataPairsOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltLinePenOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltFillOption;
+
+#define DEF_LINE_ACTIVE_PEN "activeLine"
+#define DEF_LINE_AXIS_X "x"
+#define DEF_LINE_AXIS_Y "y"
+#define DEF_LINE_DASHES (char *)NULL
+#define DEF_LINE_DATA (char *)NULL
+#define DEF_LINE_FILL_COLOR "defcolor"
+#define DEF_LINE_FILL_MONO "defcolor"
+#define DEF_LINE_HIDE "no"
+#define DEF_LINE_LABEL (char *)NULL
+#define DEF_LINE_LABEL_RELIEF "flat"
+#define DEF_LINE_MAX_SYMBOLS "0"
+#define DEF_LINE_OFFDASH_COLOR (char *)NULL
+#define DEF_LINE_OFFDASH_MONO (char *)NULL
+#define DEF_LINE_OUTLINE_COLOR "defcolor"
+#define DEF_LINE_OUTLINE_MONO "defcolor"
+#define DEF_LINE_OUTLINE_WIDTH "1"
+#define DEF_LINE_PATTERN (char *)NULL
+#define DEF_LINE_PATTERN_BG "white"
+#define DEF_LINE_PATTERN_FG "black"
+#define DEF_LINE_PATTERN_TILE (char *)NULL
+#define DEF_LINE_PEN_COLOR RGB_NAVYBLUE
+#define DEF_LINE_PEN_DIRECTION "both"
+#define DEF_LINE_PEN_MONO RGB_BLACK
+#define DEF_LINE_PEN_WIDTH "1"
+#define DEF_LINE_PIXELS "0.125i"
+#define DEF_LINE_REDUCE "0.0"
+#define DEF_LINE_SCALE_SYMBOLS "yes"
+#define DEF_LINE_SMOOTH "linear"
+#define DEF_LINE_STIPPLE (char *)NULL
+#define DEF_LINE_STYLES ""
+#define DEF_LINE_SYMBOL "circle"
+#define DEF_LINE_TAGS "all"
+#define DEF_LINE_X_DATA (char *)NULL
+#define DEF_LINE_Y_DATA (char *)NULL
+
+#define DEF_LINE_ERRORBAR_COLOR "defcolor"
+#define DEF_LINE_ERRORBAR_WIDTH "1"
+#define DEF_LINE_SHOW_ERRORBARS "both"
+
+#define DEF_PEN_ACTIVE_COLOR RGB_BLUE
+#define DEF_PEN_ACTIVE_MONO RGB_BLACK
+#define DEF_PEN_DASHES (char *)NULL
+#define DEF_PEN_FILL_COLOR "defcolor"
+#define DEF_PEN_FILL_MONO "defcolor"
+#define DEF_PEN_LINE_WIDTH "1"
+#define DEF_PEN_NORMAL_COLOR RGB_NAVYBLUE
+#define DEF_PEN_NORMAL_MONO RGB_BLACK
+#define DEF_PEN_OFFDASH_COLOR (char *)NULL
+#define DEF_PEN_OFFDASH_MONO (char *)NULL
+#define DEF_PEN_OUTLINE_COLOR "defcolor"
+#define DEF_PEN_OUTLINE_MONO "defcolor"
+#define DEF_PEN_OUTLINE_WIDTH "1"
+#define DEF_PEN_PIXELS "0.125i"
+#define DEF_PEN_SYMBOL "circle"
+#define DEF_PEN_TYPE "line"
+#define DEF_PEN_VALUE_ANCHOR "s"
+#define DEF_PEN_VALUE_COLOR RGB_BLACK
+#define DEF_PEN_VALUE_FONT STD_FONT_SMALL
+#define DEF_PEN_VALUE_FORMAT "%g"
+#define DEF_PEN_VALUE_ROTATE (char *)NULL
+#define DEF_PEN_VALUE_SHADOW (char *)NULL
+#define DEF_PEN_SHOW_VALUES "no"
+
+static Tk_ConfigSpec lineElemConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-areapattern", "areaPattern", "AreaPattern",
+ DEF_LINE_PATTERN, Tk_Offset(Line, fillStipple),
+ TK_CONFIG_NULL_OK, &patternOption},
+ {TK_CONFIG_COLOR, "-areaforeground", "areaForeground", "areaForeground",
+ DEF_LINE_PATTERN_FG, Tk_Offset(Line, fillFgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-areabackground", "areaBackground", "areaBackground",
+ DEF_LINE_PATTERN_BG, Tk_Offset(Line, fillBgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-areatile", "areaTile", "AreaTile",
+ DEF_LINE_PATTERN_TILE, Tk_Offset(Line, fillTile),
+ TK_CONFIG_NULL_OK, &bltTileOption},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_LINE_TAGS, Tk_Offset(Line, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+ DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorColor),
+ 0, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(Line, builtinPen.errorWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ (char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+ DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+ GRAPH | STRIPCHART | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+ (char *)NULL, Tk_Offset(Line, normalPenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size),
+ GRAPH | STRIPCHART, &bltDistanceOption},
+ {TK_CONFIG_DOUBLE, "-reduce", "reduce", "Reduce",
+ DEF_LINE_REDUCE, Tk_Offset(Line, rTolerance),
+ GRAPH | STRIPCHART | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+ DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+ TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+ {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+ DEF_LINE_STYLES, Tk_Offset(Line, palette),
+ TK_CONFIG_NULL_OK, &stylesOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+ TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {TK_CONFIG_CUSTOM, "-trace", "trace", "Trace",
+ DEF_LINE_PEN_DIRECTION, Tk_Offset(Line, penDir),
+ TK_CONFIG_DONT_SET_DEFAULT, &penDirOption},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow),
+ 0, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+ (char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError",
+ (char *)NULL, Tk_Offset(Line, xError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh",
+ (char *)NULL, Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow",
+ (char *)NULL, Tk_Offset(Line, xLow), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-y", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError",
+ (char *)NULL, Tk_Offset(Line, yError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh",
+ (char *)NULL, Tk_Offset(Line, yHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow",
+ (char *)NULL, Tk_Offset(Line, yLow), 0, &bltDataOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec stripElemConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_LINE_TAGS, Tk_Offset(Line, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+ DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorColor),
+ 0, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(Line, builtinPen.errorWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ (char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+ DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+ (char *)NULL, Tk_Offset(Line, normalPenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size), 0,
+ &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+ DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+ TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+ {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+ DEF_LINE_STYLES, Tk_Offset(Line, palette),
+ TK_CONFIG_NULL_OK, &stylesOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+ TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow), 0,
+ &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+ (char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-y", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL,
+ Tk_Offset(Line, xError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL,
+ Tk_Offset(Line, yError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL,
+ Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL,
+ Tk_Offset(Line, xLow), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL,
+ Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL,
+ Tk_Offset(Line, yLow), 0, &bltDataOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tk_ConfigSpec linePenConfigSpecs[] =
+{
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_ACTIVE_COLOR, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_ACTIVE_MONO, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_NORMAL_COLOR, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_NORMAL_MONO, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_MONO_ONLY | NORMAL_PEN},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_PEN_DASHES, Tk_Offset(LinePen, traceDashes),
+ TK_CONFIG_NULL_OK | ALL_PENS, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(LinePen, errorColor),
+ ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(LinePen, errorWidth),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_PEN_FILL_COLOR, Tk_Offset(LinePen, symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_PEN_FILL_MONO, Tk_Offset(LinePen, symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ (char *)NULL, Tk_Offset(LinePen, traceWidth),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_PEN_OFFDASH_COLOR, Tk_Offset(LinePen, traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_PEN_OFFDASH_MONO, Tk_Offset(LinePen, traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_PEN_OUTLINE_COLOR, Tk_Offset(LinePen, symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_PEN_OUTLINE_MONO, Tk_Offset(LinePen, symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_PEN_OUTLINE_WIDTH, Tk_Offset(LinePen, symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_PEN_PIXELS, Tk_Offset(LinePen, symbol.size),
+ ALL_PENS, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(LinePen, errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(LinePen, valueShow),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_PEN_SYMBOL, Tk_Offset(LinePen, symbol),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &symbolOption},
+ {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
+ DEF_PEN_TYPE, Tk_Offset(Pen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Tk_Offset(LinePen, valueStyle.anchor), ALL_PENS},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(LinePen, valueStyle.color), ALL_PENS},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(LinePen, valueStyle.font), ALL_PENS},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(LinePen, valueFormat),
+ ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(LinePen, valueStyle.theta), ALL_PENS},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(LinePen, valueStyle.shadow),
+ ALL_PENS, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef double (DistanceProc) _ANSI_ARGS_((int x, int y, Point2D *p,
+ Point2D *q, Point2D *t));
+
+/* Forward declarations */
+#ifdef __STDC__
+static PenConfigureProc ConfigurePen;
+static PenDestroyProc DestroyPen;
+static ElementClosestProc ClosestLine;
+static ElementConfigProc ConfigureLine;
+static ElementDestroyProc DestroyLine;
+static ElementDrawProc DrawActiveLine;
+static ElementDrawProc DrawNormalLine;
+static ElementDrawSymbolProc DrawSymbol;
+static ElementExtentsProc GetLineExtents;
+static ElementToPostScriptProc ActiveLineToPostScript;
+static ElementToPostScriptProc NormalLineToPostScript;
+static ElementSymbolToPostScriptProc SymbolToPostScript;
+static ElementMapProc MapLine;
+static DistanceProc DistanceToY;
+static DistanceProc DistanceToX;
+static DistanceProc DistanceToLine;
+static Blt_TileChangedProc TileChangedProc;
+#endif /* __STDC__ */
+
+
+INLINE static int
+Round(x)
+ register double x;
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom configuration option (parse and print) routines
+ * ----------------------------------------------------------------------
+ */
+
+static int
+StringToBitmap(interp, tkwin, symbolPtr, string)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Symbol *symbolPtr;
+ char *string;
+{
+ Pixmap bitmap, mask;
+ char **elemArr;
+ int nElems;
+ int result;
+
+ if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bitmap = mask = None;
+
+ if (nElems > 2) {
+ Tcl_AppendResult(interp, "too many elements in bitmap list \"", string,
+ "\": should be \"bitmap mask\"", (char *)NULL);
+ result = TCL_ERROR;
+ goto error;
+ }
+ bitmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[0]));
+ if (bitmap == None) {
+ result = TCL_BREAK;
+ Tcl_ResetResult(interp);
+ goto error;
+ }
+ if ((nElems > 1) && (elemArr[1][0] != '\0')) {
+ mask = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[1]));
+ if (mask == None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), bitmap);
+ result = TCL_ERROR;
+ goto error;
+ }
+ }
+ Blt_Free(elemArr);
+ if (symbolPtr->bitmap != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->bitmap);
+ }
+ symbolPtr->bitmap = bitmap;
+ if (symbolPtr->mask != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->mask);
+ }
+ symbolPtr->mask = mask;
+ return TCL_OK;
+ error:
+ Blt_Free(elemArr);
+ return result;
+}
+
+/*ARGSUSED*/
+static char *
+PatternToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin;
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Pixmap stipple = *(Pixmap *)(widgRec + offset);
+
+ if (stipple == None) {
+ return "";
+ }
+ if (stipple == PATTERN_SOLID) {
+ return "solid";
+ }
+ return Tk_NameOfBitmap(Tk_Display(tkwin), stipple);
+}
+
+/*ARGSUSED*/
+static int
+StringToPattern(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing field */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of field in record */
+{
+ Pixmap *stipplePtr = (Pixmap *)(widgRec + offset);
+ Pixmap stipple;
+
+ if (string == NULL) {
+ stipple = None;
+ } else if (strcmp(string, "solid") == 0) {
+ stipple = PATTERN_SOLID;
+ } else {
+ stipple = Tk_GetBitmap(interp, tkwin, Tk_GetUid(string));
+ if (stipple == None) {
+ return TCL_ERROR;
+ }
+ }
+ if ((*stipplePtr != None) && (*stipplePtr != PATTERN_SOLID)) {
+ Tk_FreeBitmap(Tk_Display(tkwin), *stipplePtr);
+ }
+ *stipplePtr = stipple;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSymbol --
+ *
+ * Converts the symbol value into its string representation.
+ *
+ * Results:
+ * The static string representing the symbol type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSymbol(symbolPtr)
+ Symbol *symbolPtr;
+{
+ switch (symbolPtr->type) {
+ case SYMBOL_NONE:
+ return "none";
+ case SYMBOL_SQUARE:
+ return "square";
+ case SYMBOL_CIRCLE:
+ return "circle";
+ case SYMBOL_DIAMOND:
+ return "diamond";
+ case SYMBOL_PLUS:
+ return "plus";
+ case SYMBOL_CROSS:
+ return "cross";
+ case SYMBOL_SPLUS:
+ return "splus";
+ case SYMBOL_SCROSS:
+ return "scross";
+ case SYMBOL_TRIANGLE:
+ return "triangle";
+ case SYMBOL_ARROW:
+ return "arrow";
+ case SYMBOL_BITMAP:
+ return "bitmap";
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSymbol --
+ *
+ * Convert the string representation of a line style or symbol name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSymbol(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing symbol type */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if (c == '\0') {
+ symbolPtr->type = SYMBOL_NONE;
+ } else if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ symbolPtr->type = SYMBOL_NONE;
+ } else if ((c == 'c') && (length > 1) &&
+ (strncmp(string, "circle", length) == 0)) {
+ symbolPtr->type = SYMBOL_CIRCLE;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "square", length) == 0)) {
+ symbolPtr->type = SYMBOL_SQUARE;
+ } else if ((c == 'd') && (strncmp(string, "diamond", length) == 0)) {
+ symbolPtr->type = SYMBOL_DIAMOND;
+ } else if ((c == 'p') && (strncmp(string, "plus", length) == 0)) {
+ symbolPtr->type = SYMBOL_PLUS;
+ } else if ((c == 'c') && (length > 1) &&
+ (strncmp(string, "cross", length) == 0)) {
+ symbolPtr->type = SYMBOL_CROSS;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "splus", length) == 0)) {
+ symbolPtr->type = SYMBOL_SPLUS;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "scross", length) == 0)) {
+ symbolPtr->type = SYMBOL_SCROSS;
+ } else if ((c == 't') && (strncmp(string, "triangle", length) == 0)) {
+ symbolPtr->type = SYMBOL_TRIANGLE;
+ } else if ((c == 'a') && (strncmp(string, "arrow", length) == 0)) {
+ symbolPtr->type = SYMBOL_ARROW;
+ } else {
+ int result;
+
+ result = StringToBitmap(interp, tkwin, symbolPtr, string);
+ if (result != TCL_OK) {
+ if (result != TCL_ERROR) {
+ Tcl_AppendResult(interp, "bad symbol \"", string,
+"\": should be \"none\", \"circle\", \"square\", \"diamond\", \"plus\", \
+\"cross\", \"splus\", \"scross\", \"triangle\", \"arrow\" \
+or the name of a bitmap", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ symbolPtr->type = SYMBOL_BITMAP;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SymbolToString --
+ *
+ * Convert the symbol value into a string.
+ *
+ * Results:
+ * The string representing the symbol type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SymbolToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin;
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+ char *result;
+
+ if (symbolPtr->type == SYMBOL_BITMAP) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString,
+ Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->bitmap));
+ Tcl_DStringAppendElement(&dString, (symbolPtr->mask == None) ? "" :
+ Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->mask));
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ } else {
+ result = NameOfSymbol(symbolPtr);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSmooth --
+ *
+ * Converts the smooth value into its string representation.
+ *
+ * Results:
+ * The static string representing the smooth type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSmooth(value)
+ Smoothing value;
+{
+ if ((value < 0) || (value >= PEN_SMOOTH_LAST)) {
+ return "unknown smooth value";
+ }
+ return smoothingInfo[value].name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSmooth --
+ *
+ * Convert the string representation of a line style or smooth name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The smooth type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSmooth(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing smooth type */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of smooth type field in record */
+{
+ Smoothing *valuePtr = (Smoothing *)(widgRec + offset);
+ register SmoothingInfo *siPtr;
+
+ for (siPtr = smoothingInfo; siPtr->name != NULL; siPtr++) {
+ if (strcmp(string, siPtr->name) == 0) {
+ *valuePtr = siPtr->value;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "bad smooth value \"", string, "\": should be \
+linear, step, natural, or quadratic", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SmoothToString --
+ *
+ * Convert the smooth value into a string.
+ *
+ * Results:
+ * The string representing the smooth type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SmoothToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of smooth type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int smooth = *(int *)(widgRec + offset);
+
+ return NameOfSmooth(smooth);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPenDir --
+ *
+ * Convert the string representation of a line style or symbol name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPenDir(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing pen direction */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of pen direction field in record */
+{
+ int *penDirPtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'i') && (strncmp(string, "increasing", length) == 0)) {
+ *penDirPtr = PEN_INCREASING;
+ } else if ((c == 'd') && (strncmp(string, "decreasing", length) == 0)) {
+ *penDirPtr = PEN_DECREASING;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *penDirPtr = PEN_BOTH_DIRECTIONS;
+ } else {
+ Tcl_AppendResult(interp, "bad trace value \"", string,
+ "\" : should be \"increasing\", \"decreasing\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfPenDir --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The static string representing the pen direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfPenDir(penDir)
+ int penDir; /* Direction for pen drawing between points */
+{
+ switch (penDir) {
+ case PEN_INCREASING:
+ return "increasing";
+ case PEN_DECREASING:
+ return "decreasing";
+ case PEN_BOTH_DIRECTIONS:
+ return "both";
+ default:
+ return "unknown trace direction";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PenDirToString --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The string representing the pen drawing direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PenDirToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of pen direction field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int penDir = *(int *)(widgRec + offset);
+
+ return NameOfPenDir(penDir);
+}
+
+
+/*
+ * Clear the number of points and segments, in case there are no
+ * segments or points
+ */
+static void
+ClearPalette(palette)
+ Blt_Chain *palette;
+{
+ register LinePenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->nStrips = stylePtr->nSymbolPts = 0;
+ stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = 0;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigurePen --
+ *
+ * Sets up the appropriate configuration parameters in the GC.
+ * It is assumed the parameters have been previously set by
+ * a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ LinePen *lpPtr = (LinePen *)penPtr;
+ unsigned long gcMask;
+ GC newGC;
+ XGCValues gcValues;
+ XColor *colorPtr;
+
+ Blt_ResetTextStyle(graphPtr->tkwin, &(lpPtr->valueStyle));
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.outlineColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.foreground = colorPtr->pixel;
+ if (lpPtr->symbol.type == SYMBOL_BITMAP) {
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ /*
+ * Set a clip mask if either
+ * 1) no background color was designated or
+ * 2) a masking bitmap was specified.
+ *
+ * These aren't necessarily the bitmaps we'll be using for
+ * clipping. But this makes it unlikely that anyone else will
+ * be sharing this GC when we set the clip origin (at the time
+ * the bitmap is drawn).
+ */
+ if (colorPtr != NULL) {
+ gcValues.background = colorPtr->pixel;
+ gcMask |= GCBackground;
+ if (lpPtr->symbol.mask != None) {
+ gcValues.clip_mask = lpPtr->symbol.mask;
+ gcMask |= GCClipMask;
+ }
+ } else {
+ gcValues.clip_mask = lpPtr->symbol.bitmap;
+ gcMask |= GCClipMask;
+ }
+ }
+ gcValues.line_width = LineWidth(lpPtr->symbol.outlineWidth);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+ }
+ lpPtr->symbol.outlineGC = newGC;
+
+ /* Fills for symbols: GCForeground is fill color */
+
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ newGC = NULL;
+ if (colorPtr != NULL) {
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (lpPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+ }
+ lpPtr->symbol.fillGC = newGC;
+
+ /* Line segments */
+
+ gcMask = (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle |
+ GCJoinStyle);
+ gcValues.cap_style = CapButt;
+ gcValues.join_style = JoinRound;
+ gcValues.line_style = LineSolid;
+ gcValues.line_width = LineWidth(lpPtr->traceWidth);
+
+ colorPtr = lpPtr->traceOffColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ if (colorPtr != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = colorPtr->pixel;
+ }
+ gcValues.foreground = lpPtr->traceColor->pixel;
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ gcValues.line_width = lpPtr->traceWidth;
+ gcValues.line_style =
+ (colorPtr == NULL) ? LineOnOffDash : LineDoubleDash;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+ }
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ lpPtr->traceDashes.offset = lpPtr->traceDashes.values[0] / 2;
+ Blt_SetDashes(graphPtr->display, newGC, &(lpPtr->traceDashes));
+ }
+ lpPtr->traceGC = newGC;
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+
+ colorPtr = lpPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.line_width = LineWidth(lpPtr->errorWidth);
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->errorGC);
+ }
+ lpPtr->errorGC = newGC;
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPen --
+ *
+ * Release memory and resources allocated for the style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the pen style is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyPen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ LinePen *lpPtr = (LinePen *)penPtr;
+
+ Blt_FreeTextStyle(graphPtr->display, &(lpPtr->valueStyle));
+ if (lpPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+ }
+ if (lpPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+ }
+ if (lpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->errorGC);
+ }
+ if (lpPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+ }
+ if (lpPtr->symbol.bitmap != None) {
+ Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.bitmap);
+ lpPtr->symbol.bitmap = None;
+ }
+ if (lpPtr->symbol.mask != None) {
+ Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.mask);
+ lpPtr->symbol.mask = None;
+ }
+}
+
+
+static void
+InitPen(penPtr)
+ LinePen *penPtr;
+{
+ Blt_InitTextStyle(&(penPtr->valueStyle));
+ penPtr->symbol.outlineWidth = 1;
+ penPtr->traceWidth = 1;
+ penPtr->symbol.type = SYMBOL_CIRCLE;
+ penPtr->symbol.bitmap = None;
+ penPtr->symbol.mask = None;
+ penPtr->symbol.outlineColor = COLOR_DEFAULT;
+ penPtr->symbol.fillColor = COLOR_DEFAULT;
+ penPtr->configSpecs = linePenConfigSpecs;
+ penPtr->configProc = ConfigurePen;
+ penPtr->destroyProc = DestroyPen;
+ penPtr->flags = NORMAL_PEN;
+ penPtr->errorWidth = 1;
+ penPtr->valueShow = SHOW_NONE;
+ penPtr->errorShow = SHOW_BOTH;
+ penPtr->name = "";
+}
+
+Pen *
+Blt_LinePen(penName)
+ char *penName;
+{
+ LinePen *penPtr;
+
+ penPtr = Blt_Calloc(1, sizeof(LinePen));
+ assert(penPtr);
+ InitPen(penPtr);
+ penPtr->name = Blt_Strdup(penName);
+ if (strcmp(penName, "activeLine") == 0) {
+ penPtr->flags = ACTIVE_PEN;
+ }
+ return (Pen *) penPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * In this section, the routines deal with building and filling
+ * the element's data structures with transformed screen
+ * coordinates. They are triggered from TranformLine which is
+ * called whenever the data or coordinates axes have changed and
+ * new screen coordinates need to be calculated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScaleSymbol --
+ *
+ * Returns the scaled size for the line element. Scaling depends
+ * upon when the base line ranges for the element were set and
+ * the current range of the graph.
+ *
+ * Results:
+ * The new size of the symbol, after considering how much the
+ * graph has been scaled, is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ScaleSymbol(elemPtr, normalSize)
+ Element *elemPtr;
+ int normalSize;
+{
+ int maxSize;
+ double scale;
+ int newSize;
+
+ scale = 1.0;
+ if (elemPtr->scaleSymbols) {
+ double xRange, yRange;
+
+ xRange = (elemPtr->axes.x->max - elemPtr->axes.x->min);
+ yRange = (elemPtr->axes.y->max - elemPtr->axes.y->min);
+ if (elemPtr->flags & SCALE_SYMBOL) {
+ /* Save the ranges as a baseline for future scaling. */
+ elemPtr->xRange = xRange;
+ elemPtr->yRange = yRange;
+ elemPtr->flags &= ~SCALE_SYMBOL;
+ } else {
+ double xScale, yScale;
+
+ /* Scale the symbol by the smallest change in the X or Y axes */
+ xScale = elemPtr->xRange / xRange;
+ yScale = elemPtr->yRange / yRange;
+ scale = MIN(xScale, yScale);
+ }
+ }
+ newSize = Round(normalSize * scale);
+
+ /*
+ * Don't let the size of symbols go unbounded. Both X and Win32
+ * drawing routines assume coordinates to be a signed short int.
+ */
+ maxSize = (int)MIN(elemPtr->graphPtr->hRange, elemPtr->graphPtr->vRange);
+ if (newSize > maxSize) {
+ newSize = maxSize;
+ }
+
+ /* Make the symbol size odd so that its center is a single pixel. */
+ newSize |= 0x01;
+ return newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetScreenPoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates
+ * from the data points.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetScreenPoints(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ double *x, *y;
+ register int i, n;
+ register int count;
+ register Point2D *screenPts;
+ register int *indices;
+
+ n = NumberOfPoints(linePtr);
+ x = linePtr->x.valueArr;
+ y = linePtr->y.valueArr;
+ screenPts = Blt_Malloc(sizeof(Point2D) * n);
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * n);
+ assert(indices);
+
+ count = 0; /* Count the valid screen coordinates */
+ for (i = 0; i < n; i++) {
+ if ((finite(x[i])) && (finite(y[i]))) {
+ screenPts[count] =
+ Blt_Map2D(graphPtr, x[i], y[i], &(linePtr->axes));
+ indices[count] = i;
+ count++;
+ }
+ }
+ mapPtr->screenPts = screenPts;
+ mapPtr->nScreenPts = count;
+ mapPtr->indices = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReducePoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates
+ * from the data points.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReducePoints(mapPtr, tolerance)
+ MapInfo *mapPtr;
+ double tolerance;
+{
+ register int i, k, n;
+ Point2D *screenPts;
+ int *indices, *simple;
+
+ simple = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ screenPts = Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+ n = Blt_SimplifyLine(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1,
+ tolerance, simple);
+ for (i = 0; i < n; i++) {
+ k = simple[i];
+ screenPts[i] = mapPtr->screenPts[k];
+ indices[i] = mapPtr->indices[k];
+ }
+#ifdef notdef
+ if (n < mapPtr->nScreenPts) {
+ fprintf(stderr, "reduced from %d to %d\n", mapPtr->nScreenPts, n);
+ }
+#endif
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ Blt_Free(simple);
+ mapPtr->screenPts = screenPts;
+ mapPtr->indices = indices;
+ mapPtr->nScreenPts = n;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSteps --
+ *
+ * Resets the coordinate and pen index arrays adding new points
+ * for step-and-hold type smoothing.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and pen indices
+ * are updated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateSteps(mapPtr)
+ MapInfo *mapPtr;
+{
+ int newSize;
+ register int i, count;
+ Point2D *screenPts;
+ int *indices;
+
+ newSize = ((mapPtr->nScreenPts - 1) * 2) + 1;
+ screenPts = Blt_Malloc(newSize * sizeof(Point2D));
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * newSize);
+ assert(indices);
+
+ screenPts[0] = mapPtr->screenPts[0];
+ indices[0] = 0;
+
+ count = 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++) {
+ screenPts[count + 1] = mapPtr->screenPts[i];
+
+ /* Hold last y-coordinate, use new x-coordinate */
+ screenPts[count].x = screenPts[count + 1].x;
+ screenPts[count].y = screenPts[count - 1].y;
+
+ /* Use the same style for both the hold and the step points */
+ indices[count] = indices[count + 1] = mapPtr->indices[i];
+ count += 2;
+ }
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = screenPts;
+ mapPtr->nScreenPts = newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSpline --
+ *
+ * Computes a spline based upon the data points, returning a new
+ * (larger) coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data indices
+ * are updated based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateSpline(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ int extra;
+ register int i, j, count;
+ Point2D *origPts, *intpPts;
+ int *indices;
+ int nIntpPts, nOrigPts;
+ int result;
+ int x;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ if (origPts[j].x <= origPts[i].x) {
+ return; /* Points are not monotonically increasing */
+ }
+ }
+ if (((origPts[0].x > (double)graphPtr->right)) ||
+ ((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr->left))) {
+ return; /* All points are clipped */
+ }
+ /*
+ * The spline is computed in screen coordinates instead of data
+ * points so that we can select the abscissas of the interpolated
+ * points from each pixel horizontally across the plotting area.
+ */
+ extra = (graphPtr->right - graphPtr->left) + 1;
+ if (extra < 1) {
+ return;
+ }
+ nIntpPts = nOrigPts + extra + 1;
+ intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+ assert(intpPts);
+
+ indices = Blt_Malloc(sizeof(int) * nIntpPts);
+ assert(indices);
+
+ /* Populate the x2 array with both the original X-coordinates and
+ * extra X-coordinates for each horizontal pixel that the line
+ * segment contains. */
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+
+ /* Add the original x-coordinate */
+ intpPts[count].x = origPts[i].x;
+
+ /* Include the starting offset of the point in the offset array */
+ indices[count] = mapPtr->indices[i];
+ count++;
+
+ /* Is any part of the interval (line segment) in the plotting
+ * area? */
+ if ((origPts[j].x >= (double)graphPtr->left) ||
+ (origPts[i].x <= (double)graphPtr->right)) {
+ int last;
+
+ x = (int)(origPts[i].x + 1.0);
+
+ /*
+ * Since the line segment may be partially clipped on the
+ * left or right side, the points to interpolate are
+ * always interior to the plotting area.
+ *
+ * left right
+ * x1----|--------------------------|---x2
+ *
+ * Pick the max of the starting X-coordinate and the
+ * left edge and the min of the last X-coordinate and
+ * the right edge.
+ */
+ x = MAX(x, graphPtr->left);
+ last = (int)MIN(origPts[j].x, graphPtr->right);
+
+ /* Add the extra x-coordinates to the interval. */
+ while (x < last) {
+ indices[count] = mapPtr->indices[i];
+ intpPts[count++].x = (double)x;
+ x++;
+ }
+ }
+ }
+ nIntpPts = count;
+ result = FALSE;
+ if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalSpline(origPts, nOrigPts, intpPts, nIntpPts);
+ } else if (linePtr->smooth == PEN_SMOOTH_QUADRATIC) {
+ result = Blt_QuadraticSpline(origPts, nOrigPts, intpPts, nIntpPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We'll fallback to the
+ * current coordinates and do no smoothing (standard line
+ * segments). */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ Blt_Free(intpPts);
+ Blt_Free(indices);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = intpPts;
+ mapPtr->nScreenPts = nIntpPts;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateParametricSpline --
+ *
+ * Computes a spline based upon the data points, returning a new
+ * (larger) coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data indices
+ * are updated based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateParametricSpline(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ register int i, j, count;
+ Point2D *origPts, *intpPts;
+ int *indices;
+ int nIntpPts, nOrigPts;
+ int result;
+ double dist;
+ Point2D p, q;
+ Extents2D exts;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+
+ /* Populate the x2 array with both the original X-coordinates and
+ * extra X-coordinates for each horizontal pixel that the line
+ * segment contains. */
+ count = 1;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dist = hypot(origPts[j].x - origPts[i].x, origPts[j].y - origPts[i].y);
+ count += 1 + (int)(dist * 0.5);
+ }
+ nIntpPts = count;
+ intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+ assert(intpPts);
+
+ indices = Blt_Malloc(sizeof(int) * nIntpPts);
+ assert(indices);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dist = hypot(origPts[j].x - origPts[i].x, origPts[j].y - origPts[i].y);
+ /* Add the original x-coordinate */
+ intpPts[count].x = (double)i;
+ intpPts[count].y = 0.0;
+
+ /* Include the starting offset of the point in the offset array */
+ indices[count] = mapPtr->indices[i];
+ count++;
+
+ p = origPts[i];
+ q = origPts[j];
+
+ /* Is any part of the interval (line segment) in the plotting
+ * area? */
+
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ double distP, distQ;
+
+ distP = hypot(p.x - origPts[i].x, p.y - origPts[i].y);
+ distQ = hypot(q.x - origPts[i].x, q.y - origPts[i].y);
+ distP += 2.0;
+ while(distP <= distQ) {
+ /* Point is indicated by its interval and parameter t. */
+ intpPts[count].x = (double)i;
+ intpPts[count].y = distP / dist;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ distP += 2.0;
+ }
+ }
+ }
+ intpPts[count].x = (double)i;
+ intpPts[count].y = 0.0;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ nIntpPts = count;
+ result = FALSE;
+ if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalParametricSpline(origPts, nOrigPts, &exts, FALSE,
+ intpPts, nIntpPts);
+ } else if (linePtr->smooth == PEN_SMOOTH_CATROM) {
+ result = Blt_CatromParametricSpline(origPts, nOrigPts, intpPts,
+ nIntpPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We'll fallback to the
+ * current coordinates and do no smoothing (standard line
+ * segments). */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ Blt_Free(intpPts);
+ Blt_Free(indices);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = intpPts;
+ mapPtr->nScreenPts = nIntpPts;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapSymbols --
+ *
+ * Creates two arrays of points and pen indices, filled with
+ * the screen coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapSymbols(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Extents2D exts;
+ Point2D *symbolPts;
+ int *indices;
+ register int i, count;
+ register Point2D *pointPtr, *endPtr;
+
+ symbolPts = Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+ assert(symbolPts);
+
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ assert(indices);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of visible points */
+ i = 0;
+
+ for (pointPtr = mapPtr->screenPts, endPtr = pointPtr + mapPtr->nScreenPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (PointInRegion(&exts, pointPtr->x, pointPtr->y)) {
+ symbolPts[count].x = pointPtr->x;
+ symbolPts[count].y = pointPtr->y;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ }
+ i++;
+ }
+ linePtr->symbolPts = symbolPts;
+ linePtr->nSymbolPts = count;
+ linePtr->symbolToData = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapActiveSymbols --
+ *
+ * Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the active point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapActiveSymbols(graphPtr, linePtr)
+ Graph *graphPtr;
+ Line *linePtr;
+{
+ Extents2D exts;
+ double x, y;
+ int count;
+ Point2D *activePts;
+ register int i;
+ int pointIndex;
+ int nPoints;
+ int *activeToData;
+
+ if (linePtr->activePts != NULL) {
+ Blt_Free(linePtr->activePts);
+ linePtr->activePts = NULL;
+ }
+ if (linePtr->activeToData != NULL) {
+ Blt_Free(linePtr->activeToData);
+ linePtr->activeToData = NULL;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ activePts = Blt_Malloc(sizeof(Point2D) * linePtr->nReqActive);
+ assert(activePts);
+ activeToData = Blt_Malloc(sizeof(int) * linePtr->nReqActive);
+ nPoints = NumberOfPoints(linePtr);
+ count = 0; /* Count the visible active points */
+ for (i = 0; i < linePtr->nReqActive; i++) {
+ pointIndex = linePtr->reqActive[i];
+ if (pointIndex >= nPoints) {
+ continue; /* Index not available */
+ }
+ x = linePtr->x.valueArr[pointIndex];
+ y = linePtr->y.valueArr[pointIndex];
+ activePts[count] = Blt_Map2D(graphPtr, x, y, &(linePtr->axes));
+ activeToData[count] = pointIndex;
+ if (PointInRegion(&exts, activePts[count].x, activePts[count].y)) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ linePtr->activePts = activePts;
+ linePtr->activeToData = activeToData;
+ } else {
+ /* No active points were visible. */
+ Blt_Free(activePts);
+ Blt_Free(activeToData);
+ }
+ linePtr->nActivePts = count;
+ linePtr->flags &= ~ACTIVE_PENDING;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapStrip --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapStrip(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Extents2D exts;
+ Segment2D *strips;
+ int *indices, *indexPtr;
+ register Point2D *endPtr, *pointPtr;
+ register Segment2D *segPtr;
+ register int count;
+
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ assert(indices);
+
+ /*
+ * Create array to hold points for line segments (not polyline
+ * coordinates). So allocate twice the number of points.
+ */
+ segPtr = strips = Blt_Malloc(mapPtr->nScreenPts * sizeof(Segment2D));
+ assert(strips);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of segments. */
+ indexPtr = mapPtr->indices;
+ for (pointPtr = mapPtr->screenPts,
+ endPtr = mapPtr->screenPts + (mapPtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++, indexPtr++) {
+ segPtr->p = pointPtr[0];
+ segPtr->q = pointPtr[1];
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ indices[count] = *indexPtr;
+ count++;
+ }
+ }
+ linePtr->stripToData = indices;
+ linePtr->nStrips = count;
+ linePtr->strips = strips;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ * Reorders the both arrays of points and segments to merge pens.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The old arrays are freed and new ones allocated containing
+ * the reordered points and segments.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MergePens(linePtr, dataToStyle)
+ Line *linePtr;
+ PenStyle **dataToStyle;
+{
+ LinePenStyle *stylePtr;
+ register int i;
+ Blt_ChainLink *linkPtr;
+
+ if (Blt_ChainGetLength(linePtr->palette) < 2) {
+ linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->nStrips = linePtr->nStrips;
+ stylePtr->strips = linePtr->strips;
+ stylePtr->nSymbolPts = linePtr->nSymbolPts;
+ stylePtr->symbolPts = linePtr->symbolPts;
+ stylePtr->xErrorBarCnt = linePtr->xErrorBarCnt;
+ stylePtr->yErrorBarCnt = linePtr->yErrorBarCnt;
+ stylePtr->xErrorBars = linePtr->xErrorBars;
+ stylePtr->yErrorBars = linePtr->yErrorBars;
+ return;
+ }
+
+ /* We have more than one style. Group line segments and points of
+ * like pen styles. */
+
+ if (linePtr->nStrips > 0) {
+ Segment2D *strips;
+ int *stripToData;
+ register Segment2D *segPtr;
+ register int *indexPtr;
+ int dataIndex;
+
+ strips = Blt_Malloc(linePtr->nStrips * sizeof(Segment2D));
+ stripToData = Blt_Malloc(linePtr->nStrips * sizeof(int));
+ assert(strips && stripToData);
+ segPtr = strips, indexPtr = stripToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->strips = segPtr;
+ for (i = 0; i < linePtr->nStrips; i++) {
+ dataIndex = linePtr->stripToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->strips[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->nStrips = segPtr - stylePtr->strips;
+ }
+ Blt_Free(linePtr->strips);
+ linePtr->strips = strips;
+ Blt_Free(linePtr->stripToData);
+ linePtr->stripToData = stripToData;
+ }
+ if (linePtr->nSymbolPts > 0) {
+ int *indexPtr;
+ register Point2D *symbolPts, *pointPtr;
+ register int *symbolToData;
+ int dataIndex;
+
+ symbolPts = Blt_Malloc(linePtr->nSymbolPts * sizeof(Point2D));
+ symbolToData = Blt_Malloc(linePtr->nSymbolPts * sizeof(int));
+ assert(symbolPts && symbolToData);
+ pointPtr = symbolPts, indexPtr = symbolToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolPts = pointPtr;
+ for (i = 0; i < linePtr->nSymbolPts; i++) {
+ dataIndex = linePtr->symbolToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *pointPtr++ = linePtr->symbolPts[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->nSymbolPts = pointPtr - stylePtr->symbolPts;
+ }
+ Blt_Free(linePtr->symbolPts);
+ linePtr->symbolPts = symbolPts;
+ Blt_Free(linePtr->symbolToData);
+ linePtr->symbolToData = symbolToData;
+ }
+ if (linePtr->xErrorBarCnt > 0) {
+ Segment2D *xErrorBars, *segPtr;
+ int *xErrorToData, *indexPtr;
+ int dataIndex;
+
+ xErrorBars = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(Segment2D));
+ xErrorToData = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(int));
+ assert(xErrorBars);
+ segPtr = xErrorBars, indexPtr = xErrorToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->xErrorBars = segPtr;
+ for (i = 0; i < linePtr->xErrorBarCnt; i++) {
+ dataIndex = linePtr->xErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->xErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
+ }
+ Blt_Free(linePtr->xErrorBars);
+ linePtr->xErrorBars = xErrorBars;
+ Blt_Free(linePtr->xErrorToData);
+ linePtr->xErrorToData = xErrorToData;
+ }
+ if (linePtr->yErrorBarCnt > 0) {
+ Segment2D *errorBars, *segPtr;
+ int *errorToData, *indexPtr;
+ int dataIndex;
+
+ errorBars = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(Segment2D));
+ errorToData = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(int));
+ assert(errorBars);
+ segPtr = errorBars, indexPtr = errorToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->yErrorBars = segPtr;
+ for (i = 0; i < linePtr->yErrorBarCnt; i++) {
+ dataIndex = linePtr->yErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->yErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
+ }
+ Blt_Free(linePtr->yErrorBars);
+ linePtr->yErrorBars = errorBars;
+ Blt_Free(linePtr->yErrorToData);
+ linePtr->yErrorToData = errorToData;
+ }
+}
+
+#define CLIP_TOP (1<<0)
+#define CLIP_BOTTOM (1<<1)
+#define CLIP_RIGHT (1<<2)
+#define CLIP_LEFT (1<<3)
+
+INLINE static int
+OutCode(extsPtr, p)
+ Extents2D *extsPtr;
+ Point2D *p;
+{
+ int code;
+
+ code = 0;
+ if (p->x > extsPtr->right) {
+ code |= CLIP_RIGHT;
+ } else if (p->x < extsPtr->left) {
+ code |= CLIP_LEFT;
+ }
+ if (p->y > extsPtr->bottom) {
+ code |= CLIP_BOTTOM;
+ } else if (p->y < extsPtr->top) {
+ code |= CLIP_TOP;
+ }
+ return code;
+}
+
+static int
+ClipSegment(extsPtr, code1, code2, p, q)
+ Extents2D *extsPtr;
+ register int code1, code2;
+ register Point2D *p, *q;
+{
+ int inside, outside;
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+
+ /*
+ * In the worst case, we'll clip the line segment against each of
+ * the four sides of the bounding rectangle.
+ */
+ while ((!outside) && (!inside)) {
+ if (code1 == 0) {
+ Point2D *tmp;
+ int code;
+
+ /* Swap pointers and out codes */
+ tmp = p, p = q, q = tmp;
+ code = code1, code1 = code2, code2 = code;
+ }
+ if (code1 & CLIP_LEFT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->left - p->x) / (q->x - p->x);
+ p->x = extsPtr->left;
+ } else if (code1 & CLIP_RIGHT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->right - p->x) / (q->x - p->x);
+ p->x = extsPtr->right;
+ } else if (code1 & CLIP_BOTTOM) {
+ p->x += (q->x - p->x) *
+ (extsPtr->bottom - p->y) / (q->y - p->y);
+ p->y = extsPtr->bottom;
+ } else if (code1 & CLIP_TOP) {
+ p->x += (q->x - p->x) *
+ (extsPtr->top - p->y) / (q->y - p->y);
+ p->y = extsPtr->top;
+ }
+ code1 = OutCode(extsPtr, p);
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+ }
+ return (!inside);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SaveTrace --
+ *
+ * Creates a new trace and inserts it into the line's
+ * list of traces.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SaveTrace(linePtr, start, length, mapPtr)
+ Line *linePtr;
+ int start; /* Starting index of the trace in data point
+ * array. Used to figure out closest point */
+ int length; /* Number of points forming the trace */
+ MapInfo *mapPtr;
+{
+ Trace *tracePtr;
+ Point2D *screenPts;
+ int *indices;
+ register int i, j;
+
+ tracePtr = Blt_Malloc(sizeof(Trace));
+ assert(tracePtr);
+ screenPts = Blt_Malloc(sizeof(Point2D) * length);
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * length);
+ assert(indices);
+
+ /* Copy the screen coordinates of the trace into the point array */
+
+ if (mapPtr->indices != NULL) {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ indices[i] = mapPtr->indices[j];
+ }
+ } else {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ indices[i] = j;
+ }
+ }
+ tracePtr->nScreenPts = length;
+ tracePtr->screenPts = screenPts;
+ tracePtr->symbolToData = indices;
+ tracePtr->start = start;
+ if (linePtr->chainPtr == NULL) {
+ linePtr->chainPtr = Blt_ChainCreate();
+ }
+ Blt_ChainAppend(linePtr->chainPtr, tracePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeTraces --
+ *
+ * Deletes all the traces for the line.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeTraces(linePtr)
+ Line *linePtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(tracePtr->symbolToData);
+ Blt_Free(tracePtr->screenPts);
+ Blt_Free(tracePtr);
+ }
+ Blt_ChainDestroy(linePtr->chainPtr);
+ linePtr->chainPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapTraces --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapTraces(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ int start, count;
+ int code1, code2;
+ Point2D *p, *q;
+ Point2D s;
+ Extents2D exts;
+ register int i;
+ int broken, offscreen;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 1;
+ code1 = OutCode(&exts, mapPtr->screenPts);
+ p = mapPtr->screenPts;
+ q = p + 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++, p++, q++) {
+ code2 = OutCode(&exts, q);
+ if (code2 != 0) {
+ /* Save the coordinates of the last point, before clipping */
+ s = *q;
+ }
+ broken = BROKEN_TRACE(linePtr->penDir, p->x, q->x);
+ offscreen = ClipSegment(&exts, code1, code2, p, q);
+ if (broken || offscreen) {
+
+ /*
+ * The last line segment is either totally clipped by the plotting
+ * area or the x-direction is wrong, breaking the trace. Either
+ * way, save information about the last trace (if one exists),
+ * discarding the current line segment
+ */
+
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(linePtr, start, count, mapPtr);
+ count = 1;
+ }
+ } else {
+ count++; /* Add the point to the trace. */
+ if (code2 != 0) {
+
+ /*
+ * If the last point is clipped, this means that the trace is
+ * broken after this point. Restore the original coordinate
+ * (before clipping) after saving the trace.
+ */
+
+ start = i - (count - 1);
+ SaveTrace(linePtr, start, count, mapPtr);
+ mapPtr->screenPts[i] = s;
+ count = 1;
+ }
+ }
+ code1 = code2;
+ }
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(linePtr, start, count, mapPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapFillArea --
+ *
+ * Creates an array of points that represent a polygon that fills
+ * the area under the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapFillArea(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Point2D *origPts, *clipPts;
+ Extents2D exts;
+ double maxY;
+ register int i, n;
+
+ if (linePtr->fillPts != NULL) {
+ Blt_Free(linePtr->fillPts);
+ linePtr->fillPts = NULL;
+ linePtr->nFillPts = 0;
+ }
+ if (mapPtr->nScreenPts < 3) {
+ return;
+ }
+ n = mapPtr->nScreenPts + 3;
+ Blt_GraphExtents(graphPtr, &exts);
+
+ maxY = (double)graphPtr->bottom;
+ origPts = Blt_Malloc(sizeof(Point2D) * n);
+ for (i = 0; i < mapPtr->nScreenPts; i++) {
+ origPts[i].x = mapPtr->screenPts[i].x + 1;
+ origPts[i].y = mapPtr->screenPts[i].y;
+ if (origPts[i].y > maxY) {
+ maxY = origPts[i].y;
+ }
+ }
+ /* Add edges to make (if necessary) the polygon fill to the bottom
+ * of plotting window */
+ origPts[i].x = origPts[i - 1].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i].x = origPts[0].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i] = origPts[0];
+
+ clipPts = Blt_Malloc(sizeof(Point2D) * n * 3);
+ assert(clipPts);
+ n = Blt_PolyRectClip(&exts, origPts, n - 1, clipPts);
+
+ Blt_Free(origPts);
+ if (n < 3) {
+ Blt_Free(clipPts);
+ } else {
+ linePtr->fillPts = clipPts;
+ linePtr->nFillPts = n;
+ }
+}
+
+static void
+ResetLine(linePtr)
+ Line *linePtr;
+{
+ FreeTraces(linePtr);
+ ClearPalette(linePtr->palette);
+ if (linePtr->symbolPts != NULL) {
+ Blt_Free(linePtr->symbolPts);
+ }
+ if (linePtr->symbolToData != NULL) {
+ Blt_Free(linePtr->symbolToData);
+ }
+ if (linePtr->strips != NULL) {
+ Blt_Free(linePtr->strips);
+ }
+ if (linePtr->stripToData != NULL) {
+ Blt_Free(linePtr->stripToData);
+ }
+ if (linePtr->activePts != NULL) {
+ Blt_Free(linePtr->activePts);
+ }
+ if (linePtr->activeToData != NULL) {
+ Blt_Free(linePtr->activeToData);
+ }
+ if (linePtr->xErrorBars != NULL) {
+ Blt_Free(linePtr->xErrorBars);
+ }
+ if (linePtr->xErrorToData != NULL) {
+ Blt_Free(linePtr->xErrorToData);
+ }
+ if (linePtr->yErrorBars != NULL) {
+ Blt_Free(linePtr->yErrorBars);
+ }
+ if (linePtr->yErrorToData != NULL) {
+ Blt_Free(linePtr->yErrorToData);
+ }
+ linePtr->xErrorBars = linePtr->yErrorBars = linePtr->strips = NULL;
+ linePtr->symbolPts = linePtr->activePts = NULL;
+ linePtr->stripToData = linePtr->symbolToData = linePtr->xErrorToData =
+ linePtr->yErrorToData = linePtr->activeToData = NULL;
+ linePtr->nActivePts = linePtr->nSymbolPts = linePtr->nStrips =
+ linePtr->xErrorBarCnt = linePtr->yErrorBarCnt = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapLine --
+ *
+ * Calculates the actual window coordinates of the line element.
+ * The window coordinates are saved in an allocated point array.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is (re)allocated for the point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapLine(graphPtr, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Element *elemPtr; /* Element component record */
+{
+ Line *linePtr = (Line *)elemPtr;
+ MapInfo mapInfo;
+ int nPoints;
+ PenStyle **dataToStyle;
+ Blt_ChainLink *linkPtr;
+ LinePenStyle *stylePtr;
+
+ ResetLine(linePtr);
+ nPoints = NumberOfPoints(linePtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ GetScreenPoints(graphPtr, linePtr, &mapInfo);
+ MapSymbols(graphPtr, linePtr, &mapInfo);
+
+ if ((linePtr->flags & ACTIVE_PENDING) && (linePtr->nReqActive > 0)) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ /*
+ * Map connecting line segments if they are to be displayed.
+ */
+ if ((nPoints > 1) && ((graphPtr->classUid == bltStripElementUid) ||
+ (linePtr->builtinPen.traceWidth > 0))) {
+ linePtr->smooth = linePtr->reqSmooth;
+
+ /*
+ * Do smoothing if necessary. This can extend the coordinate array,
+ * so both mapInfo.points and mapInfo.nPoints may change.
+ */
+
+ switch (linePtr->smooth) {
+ case PEN_SMOOTH_STEP:
+ GenerateSteps(&mapInfo);
+ break;
+
+ case PEN_SMOOTH_NATURAL:
+ case PEN_SMOOTH_QUADRATIC:
+ if (mapInfo.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ } else {
+ GenerateSpline(graphPtr, linePtr, &mapInfo);
+ }
+ break;
+
+ case PEN_SMOOTH_CATROM:
+ if (mapInfo.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ } else {
+ GenerateParametricSpline(graphPtr, linePtr, &mapInfo);
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (linePtr->rTolerance > 0.0) {
+ ReducePoints(&mapInfo, linePtr->rTolerance);
+ }
+ if ((linePtr->fillTile != NULL) || (linePtr->fillStipple != None)) {
+ MapFillArea(graphPtr, linePtr, &mapInfo);
+ }
+ if (graphPtr->classUid == bltStripElementUid) {
+ MapStrip(graphPtr, linePtr, &mapInfo);
+ } else {
+ MapTraces(graphPtr, linePtr, &mapInfo);
+ }
+ }
+ Blt_Free(mapInfo.screenPts);
+ Blt_Free(mapInfo.indices);
+
+ /* Set the symbol size of all the pen styles. */
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolSize =
+ ScaleSymbol(elemPtr, stylePtr->penPtr->symbol.size);
+ }
+ dataToStyle = Blt_StyleMap((Element *)linePtr);
+ if (((linePtr->yHigh.nValues > 0) && (linePtr->yLow.nValues > 0)) ||
+ ((linePtr->xHigh.nValues > 0) && (linePtr->xLow.nValues > 0)) ||
+ (linePtr->xError.nValues > 0) || (linePtr->yError.nValues > 0)) {
+ Blt_MapErrorBars(graphPtr, (Element *)linePtr, dataToStyle);
+ }
+ MergePens(linePtr, dataToStyle);
+ Blt_Free(dataToStyle);
+}
+
+static double
+DistanceToLine(x, y, p, q, t)
+ int x, y; /* Sample X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double right, left, top, bottom;
+
+ *t = Blt_GetProjection(x, y, p, q);
+ if (p->x > q->x) {
+ right = p->x, left = q->x;
+ } else {
+ left = p->x, right = q->x;
+ }
+ if (p->y > q->y) {
+ bottom = p->y, top = q->y;
+ } else {
+ top = p->y, bottom = q->y;
+ }
+ if (t->x > right) {
+ t->x = right;
+ } else if (t->x < left) {
+ t->x = left;
+ }
+ if (t->y > bottom) {
+ t->y = bottom;
+ } else if (t->y < top) {
+ t->y = top;
+ }
+ return hypot((t->x - x), (t->y - y));
+}
+
+static double
+DistanceToX(x, y, p, q, t)
+ int x, y; /* Search X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double dist;
+
+ if (p->x > q->x) {
+ if ((x > p->x) || (x < q->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ } else {
+ if ((x > q->x) || (x < p->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->x = (double)x;
+ if (FABS(dx) < DBL_EPSILON) {
+ double d1, d2;
+ /* Same X-coordinate indicates a vertical line. Pick the
+ * closest end point. */
+ d1 = p->y - y;
+ d2 = q->y - y;
+ if (FABS(d1) < FABS(d2)) {
+ t->y = p->y, dist = d1;
+ } else {
+ t->y = q->y, dist = d2;
+ }
+ } else if (FABS(dy) < DBL_EPSILON) {
+ /* Horizontal line. */
+ t->y = p->y, dist = p->y - y;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->y = (x * m) + b;
+ dist = y - t->y;
+ }
+ return FABS(dist);
+}
+
+static double
+DistanceToY(x, y, p, q, t)
+ int x, y; /* Search X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double dist;
+
+ /* FIXME: provide a distance without overriding a valid */
+ if (p->y > q->y) {
+ if ((y > p->y) || (y < q->y)) {
+ return DBL_MAX;
+ }
+ } else {
+ if ((y > q->y) || (y < p->y)) {
+ return DBL_MAX;
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->y = y;
+ if (FABS(dy) < DBL_EPSILON) {
+ double d1, d2;
+
+ /* Save Y-coordinate indicates an horizontal line. Pick the
+ * closest end point. */
+ d1 = p->x - x;
+ d2 = q->x - x;
+ if (FABS(d1) < FABS(d2)) {
+ t->x = p->x, dist = d1;
+ } else {
+ t->x = q->x, dist = d2;
+ }
+ } else if (FABS(dx) < DBL_EPSILON) {
+ /* Vertical line. */
+ t->x = p->x, dist = p->x - x;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->x = (y - b) / m;
+ dist = x - t->x;
+ }
+ return FABS(dist);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestTrace --
+ *
+ * Find the line segment closest to the given window coordinate
+ * in the element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestTrace(graphPtr, linePtr, searchPtr, distProc)
+ Graph *graphPtr; /* Graph widget record */
+ Line *linePtr; /* Line element record */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+ DistanceProc *distProc;
+{
+ Blt_ChainLink *linkPtr;
+ Point2D closest, b;
+ Trace *tracePtr;
+ double dist, minDist;
+ register Point2D *pointPtr, *endPtr;
+ int i;
+
+ i = -1; /* Suppress compiler warning. */
+ minDist = searchPtr->dist;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ for (pointPtr = tracePtr->screenPts,
+ endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ dist = (*distProc)(searchPtr->x, searchPtr->y, pointPtr,
+ pointPtr + 1, &b);
+ if (dist < minDist) {
+ closest = b;
+ i = tracePtr->symbolToData[pointPtr - tracePtr->screenPts];
+ minDist = dist;
+ }
+ }
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->dist = minDist;
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->index = i;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &(linePtr->axes));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestStrip --
+ *
+ * Find the line segment closest to the given window coordinate
+ * in the element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestStrip(graphPtr, linePtr, searchPtr, distProc)
+ Graph *graphPtr; /* Graph widget record */
+ Line *linePtr; /* Line element record */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+ DistanceProc *distProc;
+{
+ Point2D closest, b;
+ double dist, minDist;
+ int i;
+ register Segment2D *segPtr, *endPtr;
+ int count;
+
+ i = 0;
+ minDist = searchPtr->dist;
+ count = 0;
+ for (segPtr = linePtr->strips, endPtr = segPtr + linePtr->nStrips;
+ segPtr < endPtr; segPtr++) {
+ dist = (*distProc)(searchPtr->x, searchPtr->y,
+ &(segPtr->p), &(segPtr->q), &b);
+ if (dist < minDist) {
+ closest = b;
+ i = linePtr->stripToData[count];
+ minDist = dist;
+ }
+ count++;
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->dist = minDist;
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->index = i;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &(linePtr->axes));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestPoint --
+ *
+ * Find the element whose data point is closest to the given screen
+ * coordinate.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestPoint(linePtr, searchPtr)
+ Line *linePtr; /* Line element that we are looking at */
+ ClosestSearch *searchPtr; /* Assorted information related to searching
+ * for the closest point */
+{
+ double dist, minDist;
+ double dx, dy;
+ int count, i;
+ register Point2D *endPtr, *pointPtr;
+
+ minDist = searchPtr->dist;
+ i = 0;
+
+ /*
+ * Instead of testing each data point in graph coordinates, look at
+ * the array of mapped screen coordinates. The advantages are
+ * 1) only examine points that are visible (unclipped), and
+ * 2) the computed distance is already in screen coordinates.
+ */
+ count = 0;
+ for (pointPtr = linePtr->symbolPts,
+ endPtr = linePtr->symbolPts + linePtr->nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ dx = (double)(searchPtr->x - pointPtr->x);
+ dy = (double)(searchPtr->y - pointPtr->y);
+ if (searchPtr->along == SEARCH_BOTH) {
+ dist = hypot(dx, dy);
+ } else if (searchPtr->along == SEARCH_X) {
+ dist = dx;
+ } else if (searchPtr->along == SEARCH_Y) {
+ dist = dy;
+ } else {
+ /* This can't happen */
+ continue;
+ }
+ if (dist < minDist) {
+ i = linePtr->symbolToData[count];
+ minDist = dist;
+ }
+ count++;
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->dist = minDist;
+ searchPtr->index = i;
+ searchPtr->point.x = linePtr->x.valueArr[i];
+ searchPtr->point.y = linePtr->y.valueArr[i];
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetLineExtents --
+ *
+ * Retrieves the range of the line element
+ *
+ * Results:
+ * Returns the number of data points in the element.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetLineExtents(elemPtr, extsPtr)
+ Element *elemPtr;
+ Extents2D *extsPtr;
+{
+ int nPoints;
+
+ extsPtr->top = extsPtr->left = DBL_MAX;
+ extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+ nPoints = NumberOfPoints(elemPtr);
+ if (nPoints < 1) {
+ return;
+ }
+ extsPtr->right = elemPtr->x.max;
+ if ((elemPtr->x.min <= 0.0) && (elemPtr->axes.x->logScale)) {
+ extsPtr->left = Blt_FindElemVectorMinimum(&elemPtr->x, DBL_MIN);
+ } else {
+ extsPtr->left = elemPtr->x.min;
+ }
+ extsPtr->bottom = elemPtr->y.max;
+ if ((elemPtr->y.min <= 0.0) && (elemPtr->axes.y->logScale)) {
+ extsPtr->top = Blt_FindElemVectorMinimum(&elemPtr->y, DBL_MIN);
+ } else {
+ extsPtr->top = elemPtr->y.min;
+ }
+
+ /* Correct the data limits for error bars */
+
+ if (elemPtr->xError.nValues > 0) {
+ register int i;
+ double x;
+
+ nPoints = MIN(elemPtr->xError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
+ if (x > extsPtr->right) {
+ extsPtr->right = x;
+ }
+ x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
+ if (elemPtr->axes.x->logScale) {
+ if (x < 0.0) {
+ x = -x; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((x > DBL_MIN) && (x < extsPtr->left)) {
+ extsPtr->left = x;
+ }
+ } else if (x < extsPtr->left) {
+ extsPtr->left = x;
+ }
+ }
+ } else {
+ if ((elemPtr->xHigh.nValues > 0) &&
+ (elemPtr->xHigh.max > extsPtr->right)) {
+ extsPtr->right = elemPtr->xHigh.max;
+ }
+ if (elemPtr->xLow.nValues > 0) {
+ double left;
+
+ if ((elemPtr->xLow.min <= 0.0) &&
+ (elemPtr->axes.x->logScale)) {
+ left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
+ } else {
+ left = elemPtr->xLow.min;
+ }
+ if (left < extsPtr->left) {
+ extsPtr->left = left;
+ }
+ }
+ }
+
+ if (elemPtr->yError.nValues > 0) {
+ register int i;
+ double y;
+
+ nPoints = MIN(elemPtr->yError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
+ if (y > extsPtr->bottom) {
+ extsPtr->bottom = y;
+ }
+ y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
+ if (elemPtr->axes.y->logScale) {
+ if (y < 0.0) {
+ y = -y; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((y > DBL_MIN) && (y < extsPtr->left)) {
+ extsPtr->top = y;
+ }
+ } else if (y < extsPtr->top) {
+ extsPtr->top = y;
+ }
+ }
+ } else {
+ if ((elemPtr->yHigh.nValues > 0) &&
+ (elemPtr->yHigh.max > extsPtr->bottom)) {
+ extsPtr->bottom = elemPtr->yHigh.max;
+ }
+ if (elemPtr->yLow.nValues > 0) {
+ double top;
+
+ if ((elemPtr->yLow.min <= 0.0) &&
+ (elemPtr->axes.y->logScale)) {
+ top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
+ } else {
+ top = elemPtr->yLow.min;
+ }
+ if (top < extsPtr->top) {
+ extsPtr->top = top;
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Rebuilds the designated GC with the new tile pixmap.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Line *linePtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = linePtr->graphPtr;
+ if (graphPtr->tkwin != NULL) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLine --
+ *
+ * Sets up the appropriate configuration parameters in the GC.
+ * It is assumed the parameters have been previously set by
+ * a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLine(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+ Blt_ChainLink *linkPtr;
+
+ if (ConfigurePen(graphPtr, (Pen *)&(linePtr->builtinPen)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Point to the static normal/active pens if no external pens have
+ * been selected.
+ */
+ if (linePtr->normalPenPtr == NULL) {
+ linePtr->normalPenPtr = &(linePtr->builtinPen);
+ }
+ linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ if (linkPtr != NULL) {
+ LinePenStyle *stylePtr;
+
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->penPtr = linePtr->normalPenPtr;
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_SetTileChangedProc(linePtr->fillTile, TileChangedProc, linePtr);
+ }
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = 0;
+ if (linePtr->fillFgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = linePtr->fillFgColor->pixel;
+ }
+ if (linePtr->fillBgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = linePtr->fillBgColor->pixel;
+ }
+ if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ gcMask |= (GCStipple | GCFillStyle);
+ gcValues.stipple = linePtr->fillStipple;
+ gcValues.fill_style = (linePtr->fillBgColor == NULL)
+ ? FillStippled : FillOpaqueStippled;
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (linePtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+ }
+ linePtr->fillGC = newGC;
+
+ if (Blt_ConfigModified(linePtr->configSpecs, "-scalesymbols",
+ (char *)NULL)) {
+ linePtr->flags |= (MAP_ITEM | SCALE_SYMBOL);
+ }
+ if (Blt_ConfigModified(linePtr->configSpecs, "-pixels", "-trace", "-*data",
+ "-smooth", "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
+ linePtr->flags |= MAP_ITEM;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestLine --
+ *
+ * Find the closest point or line segment (if interpolated) to
+ * the given window coordinate in the line element.
+ *
+ * Results:
+ * Returns the distance of the closest point among other
+ * information.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestLine(graphPtr, elemPtr, searchPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Element *elemPtr; /* Element to examine */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+{
+ Line *linePtr = (Line *)elemPtr;
+ int mode;
+
+ mode = searchPtr->mode;
+ if (mode == SEARCH_AUTO) {
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ mode = SEARCH_POINTS;
+ if ((NumberOfPoints(linePtr) > 1) && (penPtr->traceWidth > 0)) {
+ mode = SEARCH_TRACES;
+ }
+ }
+ if (mode == SEARCH_POINTS) {
+ ClosestPoint(linePtr, searchPtr);
+ } else {
+ DistanceProc *distProc;
+ int found;
+
+ if (searchPtr->along == SEARCH_X) {
+ distProc = DistanceToX;
+ } else if (searchPtr->along == SEARCH_Y) {
+ distProc = DistanceToY;
+ } else {
+ distProc = DistanceToLine;
+ }
+ if (elemPtr->classUid == bltStripElementUid) {
+ found = ClosestStrip(graphPtr, linePtr, searchPtr, distProc);
+ } else {
+ found = ClosestTrace(graphPtr, linePtr, searchPtr, distProc);
+ }
+ if ((!found) && (searchPtr->along != SEARCH_BOTH)) {
+ ClosestPoint(linePtr, searchPtr);
+ }
+ }
+}
+
+/*
+ * XDrawLines() points: XMaxRequestSize(dpy) - 3
+ * XFillPolygon() points: XMaxRequestSize(dpy) - 4
+ * XDrawSegments() segments: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XFillRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawArcs() or XFillArcs() arcs: (XMaxRequestSize(dpy) - 3) / 3
+ */
+
+#define MAX_DRAWLINES(d) (Blt_MaxRequestSize(d) - 3)
+#define MAX_DRAWPOLYGON(d) (Blt_MaxRequestSize(d) - 4)
+#define MAX_DRAWSEGMENTS(d) ((Blt_MaxRequestSize(d) - 3) / 2)
+#define MAX_DRAWRECTANGLES(d) ((Blt_MaxRequestSize(d) - 3) / 2)
+#define MAX_DRAWARCS(d) ((Blt_MaxRequestSize(d) - 3) / 3)
+
+#ifdef WIN32
+
+static void
+DrawCircles(
+ Display *display,
+ Drawable drawable,
+ Line *linePtr,
+ LinePen *penPtr,
+ int nSymbolPts,
+ Point2D *symbolPts,
+ int radius)
+{
+ HBRUSH brush, oldBrush;
+ HPEN pen, oldPen;
+ HDC dc;
+ TkWinDCState state;
+ register Point2D *pointPtr, *endPtr;
+
+ if (drawable == None) {
+ return; /* Huh? */
+ }
+ if ((penPtr->symbol.fillGC == NULL) &&
+ (penPtr->symbol.outlineWidth == 0)) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ /* SetROP2(dc, tkpWinRopModes[penPtr->symbol.fillGC->function]); */
+ if (penPtr->symbol.fillGC != NULL) {
+ brush = CreateSolidBrush(penPtr->symbol.fillGC->foreground);
+ } else {
+ brush = GetStockBrush(NULL_BRUSH);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ pen = Blt_GCToPen(dc, penPtr->symbol.outlineGC);
+ } else {
+ pen = GetStockPen(NULL_PEN);
+ }
+ oldPen = SelectPen(dc, pen);
+ oldBrush = SelectBrush(dc, brush);
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ Ellipse(dc, (int)pointPtr->x - radius, (int)pointPtr->y - radius,
+ (int)pointPtr->x + radius + 1, (int)pointPtr->y + radius + 1);
+ }
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ DeletePen(SelectPen(dc, oldPen));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawCircles(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, radius)
+ Display *display;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int radius;
+{
+ register int i;
+ XArc *arcArr; /* Array of arcs (circle) */
+ register XArc *arcPtr;
+ int reqSize, nArcs;
+ int s;
+ int count;
+ register Point2D *pointPtr, *endPtr;
+
+ s = radius + radius;
+ arcArr = Blt_Malloc(nSymbolPts * sizeof(XArc));
+ arcPtr = arcArr;
+
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ arcPtr->x = (short int)pointPtr->x - radius;
+ arcPtr->y = (short int)pointPtr->y - radius;
+ arcPtr->width = arcPtr->height = (unsigned short)s;
+ arcPtr->angle1 = 0;
+ arcPtr->angle2 = 23040;
+ arcPtr++, count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ arcPtr->x = (short int)pointPtr->x - radius;
+ arcPtr->y = (short int)pointPtr->y - radius;
+ arcPtr->width = arcPtr->height = (unsigned short)s;
+ arcPtr->angle1 = 0;
+ arcPtr->angle2 = 23040;
+ arcPtr++;
+ }
+ }
+ reqSize = MAX_DRAWARCS(display);
+ for (i = 0; i < count; i += reqSize) {
+ nArcs = ((i + reqSize) > count) ? (count - i) : reqSize;
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillArcs(display, drawable, penPtr->symbol.fillGC, arcArr + i,
+ nArcs);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawArcs(display, drawable, penPtr->symbol.outlineGC, arcArr + i,
+ nArcs);
+ }
+ }
+ Blt_Free(arcArr);
+}
+
+#endif
+
+static void
+DrawSquares(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, r)
+ Display *display;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ register Point2D *symbolPts;
+ int r;
+{
+ XRectangle *rectArr;
+ register Point2D *pointPtr, *endPtr;
+ register XRectangle *rectPtr;
+ int reqSize, nRects;
+ int s;
+ register int i;
+ int count;
+
+ s = r + r;
+ rectArr = Blt_Malloc(nSymbolPts * sizeof(XRectangle));
+ rectPtr = rectArr;
+
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ rectPtr->x = (short int)(pointPtr->x - r);
+ rectPtr->y = (short int)(pointPtr->y - r);
+ rectPtr->width = rectPtr->height = (unsigned short)s;
+ rectPtr++, count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ rectPtr->x = (short int)(pointPtr->x - r);
+ rectPtr->y = (short int)(pointPtr->y - r);
+ rectPtr->width = rectPtr->height = (unsigned short)s;
+ rectPtr++;
+ }
+ }
+ reqSize = MAX_DRAWRECTANGLES(display);
+ for (i = 0; i < count; i += reqSize) {
+ nRects = ((i + reqSize) > count) ? (count - i) : reqSize;
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillRectangles(display, drawable, penPtr->symbol.fillGC,
+ rectArr + i, nRects);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawRectangles(display, drawable, penPtr->symbol.outlineGC,
+ rectArr + i, nRects);
+ }
+ }
+ Blt_Free(rectArr);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbols --
+ *
+ * Draw the symbols centered at the each given x,y coordinate
+ * in the array of points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at each coordinate given. If active,
+ * only those coordinates which are currently active are
+ * drawn.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbols(graphPtr, drawable, linePtr, penPtr, size, nSymbolPts, symbolPts)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Line *linePtr;
+ LinePen *penPtr;
+ int size; /* Size of element */
+ int nSymbolPts; /* Number of coordinates in array */
+ Point2D *symbolPts; /* Array of x,y coordinates for line */
+{
+ XPoint pattern[13]; /* Template for polygon symbols */
+ int r1, r2;
+ register int i, n;
+ int count;
+ register Point2D *pointPtr, *endPtr;
+#define SQRT_PI 1.77245385090552
+#define S_RATIO 0.886226925452758
+
+ if (size < 3) {
+ if (penPtr->symbol.fillGC != NULL) {
+ XPoint *pointArr;
+
+ pointArr = Blt_Malloc(nSymbolPts * sizeof(XPoint));
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ XDrawPoints(graphPtr->display, drawable, penPtr->symbol.fillGC,
+ pointArr, nSymbolPts, CoordModeOrigin);
+ Blt_Free(pointArr);
+ }
+ return;
+ }
+ r1 = (int)ceil(size * 0.5);
+ r2 = (int)ceil(size * S_RATIO * 0.5);
+
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break;
+
+ case SYMBOL_SQUARE:
+ DrawSquares(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+ symbolPts, r2);
+ break;
+
+ case SYMBOL_CIRCLE:
+ DrawCircles(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+ symbolPts, r1);
+ break;
+
+ case SYMBOL_SPLUS:
+ case SYMBOL_SCROSS:
+ {
+ XSegment *segArr; /* Array of line segments (splus, scross) */
+ register XSegment *segPtr;
+ int reqSize, nSegs, chunk;
+
+ if (penPtr->symbol.type == SYMBOL_SCROSS) {
+ r2 = Round(r2 * M_SQRT1_2);
+ pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2;
+ pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2;
+ } else {
+ pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0;
+ pattern[0].x = pattern[2].y = -r2;
+ pattern[1].x = pattern[3].y = r2;
+ }
+ segArr = Blt_Malloc(nSymbolPts * 2 * sizeof(XSegment));
+ segPtr = segArr;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+ segPtr++;
+ segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+ segPtr++;
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+ segPtr++;
+ segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+ segPtr++;
+ }
+ }
+ nSegs = count * 2;
+ /* Always draw skinny symbols regardless of the outline width */
+ reqSize = MAX_DRAWSEGMENTS(graphPtr->display);
+ for (i = 0; i < nSegs; i += reqSize) {
+ chunk = ((i + reqSize) > nSegs) ? (nSegs - i) : reqSize;
+ XDrawSegments(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, segArr + i, chunk);
+ }
+ Blt_Free(segArr);
+ }
+ break;
+
+ case SYMBOL_PLUS:
+ case SYMBOL_CROSS:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+ int d; /* Small delta for cross/plus thickness */
+
+ d = (r2 / 3);
+
+ /*
+ *
+ * 2 3 The plus/cross symbol is a closed polygon
+ * of 12 points. The diagram to the left
+ * 0,12 1 4 5 represents the positions of the points
+ * x,y which are computed below. The extra
+ * 11 10 7 6 (thirteenth) point connects the first and
+ * last points.
+ * 9 8
+ */
+
+ pattern[0].x = pattern[11].x = pattern[12].x = -r2;
+ pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d;
+ pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d;
+ pattern[5].x = pattern[6].x = r2;
+ pattern[2].y = pattern[3].y = -r2;
+ pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y =
+ pattern[12].y = -d;
+ pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d;
+ pattern[9].y = pattern[8].y = r2;
+
+ if (penPtr->symbol.type == SYMBOL_CROSS) {
+ double dx, dy;
+
+ /* For the cross symbol, rotate the points by 45 degrees. */
+ for (n = 0; n < 12; n++) {
+ dx = (double)pattern[n].x * M_SQRT1_2;
+ dy = (double)pattern[n].y * M_SQRT1_2;
+ pattern[n].x = Round(dx - dy);
+ pattern[n].y = Round(dx + dy);
+ }
+ pattern[12] = pattern[0];
+ }
+ polygon = Blt_Malloc(nSymbolPts * 13 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 13; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 13; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 13) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 13, Complex,
+ CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 13) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 13,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_DIAMOND:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+
+ /*
+ *
+ * The plus symbol is a closed polygon
+ * 1 of 4 points. The diagram to the left
+ * represents the positions of the points
+ * 0,4 x,y 2 which are computed below. The extra
+ * (fifth) point connects the first and
+ * 3 last points.
+ *
+ */
+ pattern[1].y = pattern[0].x = -r1;
+ pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0;
+ pattern[3].y = pattern[2].x = r1;
+ pattern[4] = pattern[0];
+
+ polygon = Blt_Malloc(nSymbolPts * 5 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 5; n++, pntPtr++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 5; n++, pntPtr++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 5) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 5, Convex,
+ CoordModeOrigin);
+
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 5) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 5,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+ double b;
+ int b2, h1, h2;
+#define H_RATIO 1.1663402261671607
+#define B_RATIO 1.3467736870885982
+#define TAN30 0.57735026918962573
+#define COS30 0.86602540378443871
+
+ b = Round(size * B_RATIO * 0.7);
+ b2 = Round(b * 0.5);
+ h2 = Round(TAN30 * b2);
+ h1 = Round(b2 / COS30);
+ /*
+ *
+ * The triangle symbol is a closed polygon
+ * 0,3 of 3 points. The diagram to the left
+ * represents the positions of the points
+ * x,y which are computed below. The extra
+ * (fourth) point connects the first and
+ * 2 1 last points.
+ *
+ */
+
+ if (penPtr->symbol.type == SYMBOL_ARROW) {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = -h2;
+ pattern[2].x = -b2;
+ } else {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = -h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = h2;
+ pattern[2].x = -b2;
+ }
+ polygon = Blt_Malloc(nSymbolPts * 4 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 4; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 4; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 4) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 4, Convex,
+ CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 4) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 4,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+ case SYMBOL_BITMAP:
+ {
+ Pixmap bitmap, mask;
+ int width, height, bmWidth, bmHeight;
+ double scale, sx, sy;
+ int dx, dy;
+ register int x, y;
+
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+ &width, &height);
+ mask = None;
+
+ /*
+ * Compute the size of the scaled bitmap. Stretch the
+ * bitmap to fit a nxn bounding box.
+ */
+ sx = (double)size / (double)width;
+ sy = (double)size / (double)height;
+ scale = MIN(sx, sy);
+ bmWidth = (int)(width * scale);
+ bmHeight = (int)(height * scale);
+
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, None);
+ if (penPtr->symbol.mask != None) {
+ mask = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.mask,
+ width, height, bmWidth, bmHeight);
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC,
+ mask);
+ }
+ bitmap = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.bitmap,
+ width, height, bmWidth, bmHeight);
+ if (penPtr->symbol.fillGC == NULL) {
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC,
+ bitmap);
+ }
+ dx = bmWidth / 2;
+ dy = bmHeight / 2;
+ if (linePtr->symbolInterval > 0) {
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ x = (int)pointPtr->x - dx;
+ y = (int)pointPtr->y - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight,
+ x, y, 1);
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = (int)pointPtr->x - dx;
+ y = (int)pointPtr->y - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight,
+ x, y, 1);
+ }
+ }
+ Tk_FreePixmap(graphPtr->display, bitmap);
+ if (mask != None) {
+ Tk_FreePixmap(graphPtr->display, mask);
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbol --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Line element information */
+ int x, y; /* Center position of symbol */
+ int size; /* Size of symbol. */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to
+ * give a thicker appearance. This is only for the legend
+ * entry. This routine is never called for drawing the actual
+ * line segments.
+ */
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size,
+ y, x + size, y);
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size,
+ y + 1, x + size, y + 1);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2D point;
+
+ point.x = x, point.y = y;
+ DrawSymbols(graphPtr, drawable, linePtr, linePtr->normalPenPtr, size,
+ 1, &point);
+ }
+}
+
+#ifdef WIN32
+
+static void
+DrawTraces(
+ Graph *graphPtr,
+ Drawable drawable, /* Pixmap or window to draw into */
+ Line *linePtr,
+ LinePen *penPtr)
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ int size;
+ int n;
+ register int i;
+ int start, extra;
+
+ size = ((Blt_MaxRequestSize(graphPtr->display) * 4) / sizeof(XPoint)) - 2;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ /*
+ * If the trace has to be split into separate XDrawLines
+ * calls, then the end point of the current trace is also the
+ * starting point of the new split.
+ */
+ start = extra = 0;
+ for (i = tracePtr->nScreenPts; i > 0; i -= size) {
+ n = MIN(i, size);
+ Blt_DrawPoint2DLine(graphPtr->display, drawable, penPtr->traceGC,
+ tracePtr->screenPts + start, n + extra);
+ start += (n - 1);
+ extra = 1;
+ }
+ }
+}
+
+#else
+
+static void
+DrawTraces(graphPtr, drawable, linePtr, penPtr)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ Line *linePtr;
+ LinePen *penPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ int size;
+ int n;
+ register int i;
+ int start, extra;
+ XPoint *pointArr;
+ register Point2D *pointPtr, *endPtr;
+ int count;
+
+ size = ((Blt_MaxRequestSize(graphPtr->display) * 4) / sizeof(XPoint)) - 2;
+
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+
+ /*
+ * If the trace has to be split into separate XDrawLines
+ * calls, then the end point of the current trace is also the
+ * starting point of the new split.
+ */
+ start = extra = 0;
+ pointArr = Blt_Malloc((tracePtr->nScreenPts + 1) * sizeof(XPoint));
+ for (i = tracePtr->nScreenPts; i > 0; i -= size) {
+ n = MIN(i, size);
+ count = 0;
+ for (pointPtr = tracePtr->screenPts + start,
+ endPtr = tracePtr->screenPts + (n + extra);
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ XDrawLines(graphPtr->display, drawable, penPtr->traceGC,
+ pointArr, n + extra, CoordModeOrigin);
+ start += (n - 1);
+ extra = 1;
+ }
+ Blt_Free(pointArr);
+ }
+}
+#endif /* WIN32 */
+
+static void
+DrawValues(graphPtr, drawable, linePtr, penPtr, nSymbolPts, symbolPts,
+ pointToData)
+ Graph *graphPtr;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int *pointToData;
+{
+ Point2D *pointPtr, *endPtr;
+ int count;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ char *fmt;
+ double x, y;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = linePtr->x.valueArr[pointToData[count]];
+ y = linePtr->y.valueArr[pointToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle),
+ (int)pointPtr->x, (int)pointPtr->y);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawActiveLine --
+ *
+ * Draws the connected line(s) representing the element. If the
+ * line is made up of non-line symbols and the line width
+ * parameter has been set (linewidth > 0), the element will also
+ * be drawn as a line (with the linewidth requested). The line
+ * may consist of separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawActiveLine(graphPtr, drawable, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Element to be drawn */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, linePtr->activePenPtr->symbol.size);
+
+ /*
+ * nReqActive
+ * > 0 Some points are active. Uses activeArr.
+ * < 0 All points are active.
+ * == 0 No points are active.
+ */
+ if (linePtr->nReqActive > 0) {
+ if (linePtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+ linePtr->nActivePts, linePtr->activePts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ linePtr->nActivePts, linePtr->activePts,
+ linePtr->activeToData);
+ }
+ } else if (linePtr->nReqActive < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (linePtr->nStrips > 0) {
+ Blt_DrawSegments2D(graphPtr->display, drawable,
+ penPtr->traceGC, linePtr->strips, linePtr->nStrips);
+ } else if (Blt_ChainGetLength(linePtr->chainPtr) > 0) {
+ DrawTraces(graphPtr, drawable, linePtr, penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+ linePtr->nSymbolPts, linePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ linePtr->nSymbolPts, linePtr->symbolPts,
+ linePtr->symbolToData);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawNormalLine --
+ *
+ * Draws the connected line(s) representing the element. If the
+ * line is made up of non-line symbols and the line width parameter
+ * has been set (linewidth > 0), the element will also be drawn as
+ * a line (with the linewidth requested). The line may consist of
+ * separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawNormalLine(graphPtr, drawable, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Element to be drawn */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr;
+ Blt_ChainLink *linkPtr;
+ register LinePenStyle *stylePtr;
+ unsigned int count;
+
+ /* Fill area under the curve */
+ if (linePtr->fillPts != NULL) {
+ XPoint *pointArr;
+ Point2D *endPtr, *pointPtr;
+
+ pointArr = Blt_Malloc(sizeof(XPoint) * linePtr->nFillPts);
+ count = 0;
+ for(pointPtr = linePtr->fillPts,
+ endPtr = linePtr->fillPts + linePtr->nFillPts;
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_SetTileOrigin(graphPtr->tkwin, linePtr->fillTile, 0, 0);
+ Blt_TilePolygon(graphPtr->tkwin, drawable, linePtr->fillTile,
+ pointArr, linePtr->nFillPts);
+ } else if (linePtr->fillStipple != None) {
+ XFillPolygon(graphPtr->display, drawable, linePtr->fillGC,
+ pointArr, linePtr->nFillPts, Complex, CoordModeOrigin);
+ }
+ Blt_Free(pointArr);
+ }
+
+ /* Lines: stripchart segments or graph traces. */
+
+ if (linePtr->nStrips > 0) {
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->nStrips > 0) && (penPtr->errorWidth > 0)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable,
+ penPtr->traceGC, stylePtr->strips, stylePtr->nStrips);
+ }
+ }
+ } else if ((Blt_ChainGetLength(linePtr->chainPtr) > 0) &&
+ (linePtr->normalPenPtr->traceWidth > 0)) {
+ DrawTraces(graphPtr, drawable, linePtr, linePtr->normalPenPtr);
+ }
+
+ if (linePtr->reqMaxSymbols > 0) {
+ int total;
+
+ total = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ total += stylePtr->nSymbolPts;
+ }
+ linePtr->symbolInterval = total / linePtr->reqMaxSymbols;
+ linePtr->symbolCounter = 0;
+ }
+
+ /* Symbols, error bars, values. */
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
+ }
+ if ((stylePtr->nSymbolPts > 0) &&
+ (penPtr->symbol.type != SYMBOL_NONE)) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr,
+ stylePtr->symbolSize, stylePtr->nSymbolPts,
+ stylePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ stylePtr->nSymbolPts, stylePtr->symbolPts,
+ linePtr->symbolToData + count);
+ }
+ count += stylePtr->nSymbolPts;
+ }
+ linePtr->symbolInterval = 0;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * GetSymbolPostScriptInfo --
+ *
+ * Set up the PostScript environment with the macros and
+ * attributes needed to draw the symbols of the element.
+ *
+ * Results:
+ * None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size)
+ Graph *graphPtr;
+ PsToken psToken;
+ LinePen *penPtr;
+ int size;
+{
+ XColor *outlineColor, *fillColor, *defaultColor;
+
+ /* Set line and foreground attributes */
+ outlineColor = penPtr->symbol.outlineColor;
+ fillColor = penPtr->symbol.fillColor;
+ defaultColor = penPtr->traceColor;
+
+ if (fillColor == COLOR_DEFAULT) {
+ fillColor = defaultColor;
+ }
+ if (outlineColor == COLOR_DEFAULT) {
+ outlineColor = defaultColor;
+ }
+ if (penPtr->symbol.type == SYMBOL_NONE) {
+ Blt_LineAttributesToPostScript(psToken, defaultColor,
+ penPtr->traceWidth + 2, &(penPtr->traceDashes),
+ CapButt, JoinMiter);
+ } else {
+ Blt_LineWidthToPostScript(psToken, penPtr->symbol.outlineWidth);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ }
+
+ /*
+ * Build a PostScript procedure to draw the symbols. For bitmaps,
+ * paint both the bitmap and its mask. Otherwise fill and stroke
+ * the path formed already.
+ */
+ Blt_AppendToPostScript(psToken, "\n/DrawSymbolProc {\n", (char *)NULL);
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break; /* Do nothing */
+ case SYMBOL_BITMAP:
+ {
+ int width, height;
+ double sx, sy, scale;
+
+ /*
+ * Compute how much to scale the bitmap. Don't let the
+ * scaled bitmap exceed the bounding square for the
+ * symbol.
+ */
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+ &width, &height);
+ sx = (double)size / (double)width;
+ sy = (double)size / (double)height;
+ scale = MIN(sx, sy);
+
+ if ((penPtr->symbol.mask != None) && (fillColor != NULL)) {
+ Blt_AppendToPostScript(psToken,
+ "\n % Bitmap mask is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.mask),
+ "\"\n\n ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, fillColor);
+ Blt_BitmapToPostScript(psToken, graphPtr->display,
+ penPtr->symbol.mask, scale, scale);
+ }
+ Blt_AppendToPostScript(psToken,
+ "\n % Bitmap symbol is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.bitmap),
+ "\"\n\n ", (char *)NULL);
+ Blt_ForegroundToPostScript(psToken, outlineColor);
+ Blt_BitmapToPostScript(psToken, graphPtr->display,
+ penPtr->symbol.bitmap, scale, scale);
+ }
+ break;
+ default:
+ Blt_AppendToPostScript(psToken, " gsave\n", (char *)NULL);
+ if (fillColor != NULL) {
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, fillColor);
+ Blt_AppendToPostScript(psToken, " Fill\n", (char *)NULL);
+ }
+ if ((outlineColor != NULL) && (penPtr->symbol.outlineWidth > 0)) {
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_ForegroundToPostScript(psToken, outlineColor);
+ Blt_AppendToPostScript(psToken, " stroke\n", (char *)NULL);
+ }
+ Blt_AppendToPostScript(psToken, " grestore\n", (char *)NULL);
+ break;
+ }
+ Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolsToPostScript --
+ *
+ * Draw a symbol centered at the given x,y window coordinate
+ * based upon the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when
+ * calculating the centered position of the symbol.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolsToPostScript(graphPtr, psToken, penPtr, size, nSymbolPts, symbolPts)
+ Graph *graphPtr;
+ PsToken psToken;
+ LinePen *penPtr;
+ int size;
+ int nSymbolPts;
+ Point2D *symbolPts;
+{
+ double symbolSize;
+ register Point2D *pointPtr, *endPtr;
+ static char *symbolMacros[] =
+ {
+ "Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm",
+ (char *)NULL,
+ };
+ GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size);
+
+ symbolSize = (double)size;
+ switch (penPtr->symbol.type) {
+ case SYMBOL_SQUARE:
+ case SYMBOL_CROSS:
+ case SYMBOL_PLUS:
+ case SYMBOL_SCROSS:
+ case SYMBOL_SPLUS:
+ symbolSize = (double)Round(size * S_RATIO);
+ break;
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ symbolSize = (double)Round(size * 0.7);
+ break;
+ case SYMBOL_DIAMOND:
+ symbolSize = (double)Round(size * M_SQRT1_2);
+ break;
+
+ default:
+ break;
+ }
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ Blt_FormatToPostScript(psToken, "%g %g %g %s\n", pointPtr->x,
+ pointPtr->y, symbolSize, symbolMacros[penPtr->symbol.type]);
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
+ Graph *graphPtr; /* Graph widget record */
+ PsToken psToken;
+ Element *elemPtr; /* Line element information */
+ double x, y; /* Center position of symbol */
+ int size; /* Size of element */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to
+ * give a thicker appearance. This is only for the legend
+ * entry. This routine is never called for drawing the actual
+ * line segments.
+ */
+ Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+ penPtr->traceWidth + 2, &(penPtr->traceDashes), CapButt, JoinMiter);
+ Blt_FormatToPostScript(psToken, "%g %g %d Li\n", x, y, size + size);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2D point;
+
+ point.x = x, point.y = y;
+ SymbolsToPostScript(graphPtr, psToken, penPtr, size, 1, &point);
+ }
+}
+
+
+static void
+SetLineAttributes(psToken, penPtr)
+ PsToken psToken;
+ LinePen *penPtr;
+{
+ /* Set the attributes of the line (color, dashes, linewidth) */
+ Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+ penPtr->traceWidth, &(penPtr->traceDashes), CapButt, JoinMiter);
+ if ((LineIsDashed(penPtr->traceDashes)) &&
+ (penPtr->traceOffColor != NULL)) {
+ Blt_AppendToPostScript(psToken, "/DashesProc {\n gsave\n ",
+ (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, penPtr->traceOffColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken, "stroke\n grestore\n} def\n",
+ (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n", (char *)NULL);
+ }
+}
+
+static void
+TracesToPostScript(psToken, linePtr, penPtr)
+ PsToken psToken;
+ Line *linePtr;
+ LinePen *penPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ register Point2D *pointPtr, *endPtr;
+ int count;
+
+ SetLineAttributes(psToken, penPtr);
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ if (tracePtr->nScreenPts <= 0) {
+ continue;
+ }
+#define PS_MAXPATH 1500 /* Maximum number of components in a PostScript
+ * (level 1) path. */
+ pointPtr = tracePtr->screenPts;
+ Blt_FormatToPostScript(psToken, " newpath %g %g moveto\n",
+ pointPtr->x,
+ pointPtr->y);
+ pointPtr++;
+ count = 0;
+ for (endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ Blt_FormatToPostScript(psToken, " %g %g lineto\n",
+ pointPtr->x,
+ pointPtr->y);
+ if ((count % PS_MAXPATH) == 0) {
+ Blt_FormatToPostScript(psToken,
+ "DashesProc stroke\n newpath %g %g moveto\n",
+ pointPtr->x,
+ pointPtr->y);
+ }
+ count++;
+ }
+ Blt_FormatToPostScript(psToken, " %g %g lineto\n",
+ pointPtr->x,
+ pointPtr->y);
+ Blt_AppendToPostScript(psToken, "DashesProc stroke\n", (char *)NULL);
+ }
+}
+
+
+static void
+ValuesToPostScript(psToken, linePtr, penPtr, nSymbolPts, symbolPts,
+ pointToData)
+ PsToken psToken;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int *pointToData;
+{
+ Point2D *pointPtr, *endPtr;
+ int count;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ char *fmt;
+ double x, y;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = linePtr->x.valueArr[pointToData[count]];
+ y = linePtr->y.valueArr[pointToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle),
+ pointPtr->x, pointPtr->y);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActiveLineToPostScript --
+ *
+ * Generates PostScript commands to draw as "active" the points
+ * (symbols) and or line segments (trace) representing the
+ * element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ActiveLineToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, penPtr->symbol.size);
+ if (linePtr->nReqActive > 0) {
+ if (linePtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+ linePtr->nActivePts, linePtr->activePts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nActivePts,
+ linePtr->activePts, linePtr->activeToData);
+ }
+ } else if (linePtr->nReqActive < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (linePtr->nStrips > 0) {
+ SetLineAttributes(psToken, penPtr);
+ Blt_Segments2DToPostScript(psToken, linePtr->strips,
+ linePtr->nStrips);
+ }
+ if (Blt_ChainGetLength(linePtr->chainPtr) > 0) {
+ TracesToPostScript(psToken, linePtr, (LinePen *)penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+ linePtr->nSymbolPts, linePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nSymbolPts,
+ linePtr->symbolPts, linePtr->symbolToData);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NormalLineToPostScript --
+ *
+ * Similar to the DrawLine procedure, prints PostScript related
+ * commands to form the connected line(s) representing the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NormalLineToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ register LinePenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+ LinePen *penPtr;
+ unsigned int count;
+ XColor *colorPtr;
+
+ /* Draw fill area */
+ if (linePtr->fillPts != NULL) {
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_PathToPostScript(psToken, linePtr->fillPts, linePtr->nFillPts);
+ Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+ /* If the background fill color was specified, draw the
+ * polygon in a solid fashion with that color. */
+ if (linePtr->fillBgColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, linePtr->fillBgColor);
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ Blt_ForegroundToPostScript(psToken, linePtr->fillFgColor);
+ if (linePtr->fillTile != NULL) {
+ /* TBA: Transparent tiling is the hard part. */
+ } else if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ /* Draw the stipple in the foreground color. */
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ linePtr->fillStipple);
+ } else {
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ }
+ /* Draw lines */
+ if (linePtr->nStrips > 0) {
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->nStrips > 0) && (penPtr->traceWidth > 0)) {
+ SetLineAttributes(psToken, penPtr);
+ Blt_Segments2DToPostScript(psToken, stylePtr->strips,
+ stylePtr->nStrips);
+ }
+ }
+ } else if ((Blt_ChainGetLength(linePtr->chainPtr) > 0) &&
+ (linePtr->normalPenPtr->traceWidth > 0)) {
+ TracesToPostScript(psToken, linePtr, linePtr->normalPenPtr);
+ }
+
+ /* Draw symbols, error bars, values. */
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ colorPtr = penPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = penPtr->traceColor;
+ }
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->xErrorBars,
+ stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->yErrorBars,
+ stylePtr->yErrorBarCnt);
+ }
+ if ((stylePtr->nSymbolPts > 0) &&
+ (stylePtr->penPtr->symbol.type != SYMBOL_NONE)) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr,
+ stylePtr->symbolSize, stylePtr->nSymbolPts,
+ stylePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr,
+ stylePtr->nSymbolPts, stylePtr->symbolPts,
+ linePtr->symbolToData + count);
+ }
+ count += stylePtr->nSymbolPts;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyLine --
+ *
+ * Release memory and resources allocated for the line element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the line element is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+#define FreeVector(v) \
+ if ((v).clientId != NULL) { \
+ Blt_FreeVectorId((v).clientId); \
+ } else if ((v).valueArr != NULL) { \
+ Blt_Free((v).valueArr); \
+ }
+
+static void
+DestroyLine(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+
+ if (linePtr->normalPenPtr != &(linePtr->builtinPen)) {
+ Blt_FreePen(graphPtr, (Pen *)linePtr->normalPenPtr);
+ }
+ DestroyPen(graphPtr, (Pen *)&(linePtr->builtinPen));
+ if (linePtr->activePenPtr != NULL) {
+ Blt_FreePen(graphPtr, (Pen *)linePtr->activePenPtr);
+ }
+
+ FreeVector(linePtr->w);
+ FreeVector(linePtr->x);
+ FreeVector(linePtr->xHigh);
+ FreeVector(linePtr->xLow);
+ FreeVector(linePtr->xError);
+ FreeVector(linePtr->y);
+ FreeVector(linePtr->yHigh);
+ FreeVector(linePtr->yLow);
+ FreeVector(linePtr->yError);
+
+ ResetLine(linePtr);
+ if (linePtr->palette != NULL) {
+ Blt_FreePalette(graphPtr, linePtr->palette);
+ Blt_ChainDestroy(linePtr->palette);
+ }
+ if (linePtr->tags != NULL) {
+ Blt_Free(linePtr->tags);
+ }
+ if (linePtr->reqActive != NULL) {
+ Blt_Free(linePtr->reqActive);
+ }
+ if (linePtr->fillPts != NULL) {
+ Blt_Free(linePtr->fillPts);
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_FreeTile(linePtr->fillTile);
+ }
+ if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ Tk_FreeBitmap(graphPtr->display, linePtr->fillStipple);
+ }
+ if (linePtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineElement --
+ *
+ * Allocate memory and initialize methods for the new line element.
+ *
+ * Results:
+ * The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line element structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ElementProcs lineProcs =
+{
+ ClosestLine, /* Finds the closest element/data point */
+ ConfigureLine, /* Configures the element. */
+ DestroyLine, /* Destroys the element. */
+ DrawActiveLine, /* Draws active element */
+ DrawNormalLine, /* Draws normal element */
+ DrawSymbol, /* Draws the element symbol. */
+ GetLineExtents, /* Find the extents of the element's data. */
+ ActiveLineToPostScript, /* Prints active element. */
+ NormalLineToPostScript, /* Prints normal element. */
+ SymbolToPostScript, /* Prints the line's symbol. */
+ MapLine /* Compute element's screen coordinates. */
+};
+
+Element *
+Blt_LineElement(graphPtr, name, classUid)
+ Graph *graphPtr;
+ char *name;
+ Tk_Uid classUid;
+{
+ register Line *linePtr;
+
+ linePtr = Blt_Calloc(1, sizeof(Line));
+ assert(linePtr);
+ linePtr->procsPtr = &lineProcs;
+ if (classUid == bltLineElementUid) {
+ linePtr->configSpecs = lineElemConfigSpecs;
+ } else {
+ linePtr->configSpecs = stripElemConfigSpecs;
+ }
+ linePtr->penDir = PEN_BOTH_DIRECTIONS;
+ linePtr->reqSmooth = PEN_SMOOTH_NONE;
+ linePtr->flags = SCALE_SYMBOL;
+ linePtr->normalPenPtr = &(linePtr->builtinPen);
+ linePtr->labelRelief = TK_RELIEF_FLAT;
+ linePtr->fillStipple = None;
+
+ /* By default an element's name and label are the same. */
+ linePtr->label = Blt_Strdup(name);
+ linePtr->name = Blt_Strdup(name);
+ linePtr->graphPtr = graphPtr;
+ linePtr->hidden = FALSE;
+ linePtr->classUid = classUid;
+ InitPen(linePtr->normalPenPtr);
+ linePtr->palette = Blt_ChainCreate();
+ return (Element *)linePtr;
+}
diff --git a/blt/src/bltGrMarker.c b/blt/src/bltGrMarker.c
new file mode 100644
index 00000000000..032c6e96fcc
--- /dev/null
+++ b/blt/src/bltGrMarker.c
@@ -0,0 +1,4954 @@
+
+/*
+ * bltGrMarker.c --
+ *
+ * This module implements markers for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltChain.h"
+#include "bltGrElem.h"
+
+/* Map graph coordinates to normalized coordinates [0..1] */
+#define NORMALIZE(A,x) (((x) - (A)->tickRange.min) / (A)->tickRange.range)
+
+#define DEF_MARKER_ANCHOR "center"
+#define DEF_MARKER_BG_COLOR RGB_WHITE
+#define DEF_MARKER_BG_MONO RGB_WHITE
+#define DEF_MARKER_BITMAP (char *)NULL
+#define DEF_MARKER_CAP_STYLE "butt"
+#define DEF_MARKER_COORDS (char *)NULL
+#define DEF_MARKER_DASHES (char *)NULL
+#define DEF_MARKER_DASH_OFFSET "0"
+#define DEF_MARKER_ELEMENT (char *)NULL
+#define DEF_MARKER_FG_COLOR RGB_BLACK
+#define DEF_MARKER_FG_MONO RGB_BLACK
+#define DEF_MARKER_FILL_COLOR RGB_RED
+#define DEF_MARKER_FILL_MONO RGB_WHITE
+#define DEF_MARKER_FONT STD_FONT
+#define DEF_MARKER_GAP_COLOR RGB_PINK
+#define DEF_MARKER_GAP_MONO RGB_BLACK
+#define DEF_MARKER_HEIGHT "0"
+#define DEF_MARKER_HIDE "no"
+#define DEF_MARKER_JOIN_STYLE "miter"
+#define DEF_MARKER_JUSTIFY "left"
+#define DEF_MARKER_LINE_WIDTH "1"
+#define DEF_MARKER_MAP_X "x"
+#define DEF_MARKER_MAP_Y "y"
+#define DEF_MARKER_NAME (char *)NULL
+#define DEF_MARKER_OUTLINE_COLOR RGB_BLACK
+#define DEF_MARKER_OUTLINE_MONO RGB_BLACK
+#define DEF_MARKER_PAD "4"
+#define DEF_MARKER_ROTATE "0.0"
+#define DEF_MARKER_SCALE "1.0"
+#define DEF_MARKER_SHADOW_COLOR (char *)NULL
+#define DEF_MARKER_SHADOW_MONO (char *)NULL
+#define DEF_MARKER_STIPPLE (char *)NULL
+#define DEF_MARKER_TEXT (char *)NULL
+#define DEF_MARKER_UNDER "no"
+#define DEF_MARKER_WIDTH "0"
+#define DEF_MARKER_WINDOW (char *)NULL
+#define DEF_MARKER_XOR "no"
+#define DEF_MARKER_X_OFFSET "0"
+#define DEF_MARKER_Y_OFFSET "0"
+
+#define DEF_MARKER_TEXT_TAGS "Text all"
+#define DEF_MARKER_IMAGE_TAGS "Image all"
+#define DEF_MARKER_BITMAP_TAGS "Bitmap all"
+#define DEF_MARKER_WINDOW_TAGS "Window all"
+#define DEF_MARKER_POLYGON_TAGS "Polygon all"
+#define DEF_MARKER_LINE_TAGS "Line all"
+
+static Tk_OptionParseProc StringToCoordinates;
+static Tk_OptionPrintProc CoordinatesToString;
+static Tk_CustomOption coordsOption =
+{
+ StringToCoordinates, CoordinatesToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorPairOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+
+typedef Marker *(MarkerCreateProc) _ANSI_ARGS_((void));
+typedef void (MarkerDrawProc) _ANSI_ARGS_((Marker *markerPtr,
+ Drawable drawable));
+typedef void (MarkerFreeProc) _ANSI_ARGS_((Graph *graphPtr, Marker *markerPtr));
+typedef int (MarkerConfigProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerMapProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerPostScriptProc) _ANSI_ARGS_((Marker *markerPtr,
+ PsToken psToken));
+typedef int (MarkerPointProc) _ANSI_ARGS_((Marker *markerPtr,
+ Point2D *samplePtr));
+typedef int (MarkerRegionProc) _ANSI_ARGS_((Marker *markerPtr,
+ Extents2D *extsPtr, int enclosed));
+
+typedef struct {
+ Tk_ConfigSpec *configSpecs; /* Marker configuration specifications */
+
+ MarkerConfigProc *configProc;
+ MarkerDrawProc *drawProc;
+ MarkerFreeProc *freeProc;
+ MarkerMapProc *mapProc;
+ MarkerPointProc *pointProc;
+ MarkerRegionProc *regionProc;
+ MarkerPostScriptProc *postscriptProc;
+
+} MarkerClass;
+
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Marker --
+ *
+ * Structure defining the generic marker. In C++ parlance this
+ * would be the base type from which all markers are derived.
+ *
+ * This structure corresponds with the specific types of markers.
+ * Don't change this structure without changing the individual
+ * marker structures of each type below.
+ *
+ * -------------------------------------------------------------------
+ */
+struct MarkerStruct {
+ char *name; /* Identifier for marker in list */
+
+ Tk_Uid classUid; /* Type of marker. */
+
+ Graph *graphPtr; /* Graph widget of marker. */
+
+ unsigned int flags;
+
+ char **tags;
+
+ int hidden; /* If non-zero, don't display the marker. */
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Coordinate array to position marker */
+ int nWorldPts; /* Number of points in above array */
+
+ char *elemName; /* Element associated with marker */
+
+ Axis2D axes;
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can
+ * be a performance penalty because
+ * the graph must be redraw entirely
+ * each time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ MarkerClass *classPtr;
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * TextMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* The graph this marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* If non-zero, don't display the
+ * marker. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* pixel offset from anchor */
+
+ MarkerClass *classPtr;
+ /*
+ * Text specific fields and attributes
+ */
+#ifdef notdef
+ char *textVarName; /* Name of variable (malloc'ed) or
+ * NULL. If non-NULL, graph displays
+ * the contents of this variable. */
+#endif
+ char *string; /* Text string to be display. The string
+ * make contain newlines. */
+
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+
+ Point2D anchorPos; /* Translated anchor point. */
+
+ int width, height; /* Dimension of bounding box. */
+
+ TextStyle style; /* Text attributes (font, fg, anchor, etc) */
+
+ TextLayout *textPtr; /* Contains information about the layout
+ * of the text. */
+ Point2D outline[5];
+ XColor *fillColor;
+ GC fillGC;
+} TextMarker;
+
+
+static Tk_ConfigSpec textConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(TextMarker, anchor), 0},
+ {TK_CONFIG_COLOR, "-background", "background", "MarkerBackground",
+ (char *)NULL, Tk_Offset(TextMarker, fillColor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", "Background",
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_TEXT_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", "Foreground",
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_MARKER_FONT, Tk_Offset(TextMarker, style.font), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_COLOR, Tk_Offset(TextMarker, style.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_MONO, Tk_Offset(TextMarker, style.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_MARKER_JUSTIFY, Tk_Offset(TextMarker, style.justify),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_MARKER_ROTATE, Tk_Offset(TextMarker, style.theta),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_MARKER_SHADOW_COLOR, Tk_Offset(TextMarker, style.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_MARKER_SHADOW_MONO, Tk_Offset(TextMarker, style.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_MARKER_TEXT, Tk_Offset(TextMarker, string), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * WindowMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is
+ * currently hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset from anchor. */
+
+ MarkerClass *classPtr;
+ /*
+ * Window specific attributes
+ */
+ char *pathName; /* Name of child widget to be displayed. */
+ Tk_Window tkwin; /* Window to display. */
+ int reqWidth, reqHeight; /* If non-zero, this overrides the size
+ * requested by the child widget. */
+
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+
+ Point2D anchorPos; /* Translated anchor point. */
+ int width, height; /* Current size of the child window. */
+
+} WindowMarker;
+
+static Tk_ConfigSpec windowConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(WindowMarker, anchor), 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_WINDOW_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(WindowMarker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_MARKER_HEIGHT, Tk_Offset(WindowMarker, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_MARKER_WIDTH, Tk_Offset(WindowMarker, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_STRING, "-window", "window", "Window",
+ DEF_MARKER_WINDOW, Tk_Offset(WindowMarker, pathName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * BitmapMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker in world (graph)
+ * coordinates. If 2 pairs of X-Y
+ * coordinates are specified, then the
+ * bitmap is resized to fit this area.
+ * Otherwise if 1 pair, then the bitmap
+ * is positioned at the coordinate at its
+ * normal size. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ int xOffset, yOffset; /* Pixel offset from origin of bitmap */
+
+ MarkerClass *classPtr;
+
+ /* Bitmap specific attributes */
+ Pixmap srcBitmap; /* Original bitmap. May be further
+ * scaled or rotated. */
+ double rotate; /* Requested rotation of the bitmap */
+ double theta; /* Normalized rotation (0..360
+ * degrees) */
+ Tk_Anchor anchor; /* If only one X-Y coordinate is
+ * given, indicates how to translate
+ * the given marker position. Otherwise,
+ * if there are two X-Y coordinates, then
+ * this value is ignored. */
+ Point2D anchorPos; /* Translated anchor point. */
+
+ XColor *outlineColor; /* Foreground color */
+ XColor *fillColor; /* Background color */
+
+ GC gc; /* Private graphic context */
+ GC fillGC; /* Shared graphic context */
+ Pixmap destBitmap; /* Bitmap to be drawn. */
+ int destWidth, destHeight; /* Dimensions of the final bitmap */
+
+ Point2D outline[5]; /* Polygon representing the possibly
+ * rotated background of the
+ * bitmap. */
+} BitmapMarker;
+
+static Tk_ConfigSpec bitmapConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(BitmapMarker, anchor), 0},
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MARKER_BG_COLOR, Tk_Offset(BitmapMarker, fillColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MARKER_BG_MONO, Tk_Offset(BitmapMarker, fillColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_BITMAP_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
+ DEF_MARKER_BITMAP, Tk_Offset(BitmapMarker, srcBitmap),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_COLOR, Tk_Offset(BitmapMarker, outlineColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FG_MONO, Tk_Offset(BitmapMarker, outlineColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_MARKER_ROTATE, Tk_Offset(BitmapMarker, rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * ImageMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is
+ * currently hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+ Point2D *worldPts; /* Position of marker in world (graph)
+ * coordinates. If 2 pairs of X-Y
+ * coordinates are specified, then the
+ * image is resized to fit this area.
+ * Otherwise if 1 pair, then the image
+ * is positioned at the coordinate at its
+ * normal size. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset from anchor */
+
+ MarkerClass *classPtr;
+
+ /* Image specific attributes */
+ char *imageName; /* Name of image to be displayed. */
+ Tk_Image tkImage; /* Tk image to be displayed. */
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+ Point2D anchorPos; /* Translated anchor point. */
+ int width, height; /* Dimensions of the image */
+ Tk_Image tmpImage;
+ Pixmap pixmap; /* Pixmap containing the scaled image */
+ ColorTable colorTable; /* Pointer to color table */
+ Blt_Colorimage srcImage;
+ GC gc;
+
+} ImageMarker;
+
+static Tk_ConfigSpec imageConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MARKER_ANCHOR, Tk_Offset(ImageMarker, anchor), 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_IMAGE_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-image", "image", "Image",
+ (char *)NULL, Tk_Offset(ImageMarker, imageName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * LineMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type is "linemarker" */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (X-Y coordinates) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset */
+
+ MarkerClass *classPtr;
+
+ /* Line specific attributes */
+ XColor *fillColor;
+ XColor *outlineColor; /* Foreground and background colors */
+
+ int lineWidth; /* Line width. */
+ int capStyle; /* Cap style. */
+ int joinStyle; /* Join style.*/
+ Blt_Dashes dashes; /* Dash list values (max 11) */
+
+ GC gc; /* Private graphic context */
+
+ Segment2D *segments; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising
+ * the mapped line. The segments may
+ * not necessarily be connected after
+ * clipping. */
+ int nSegments; /* # segments in the above array. */
+
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is currently drawn. */
+} LineMarker;
+
+static Tk_ConfigSpec lineConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_LINE_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+ DEF_MARKER_CAP_STYLE, Tk_Offset(LineMarker, capStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_MARKER_DASHES, Tk_Offset(LineMarker, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-dashoffset", "dashOffset", "DashOffset",
+ DEF_MARKER_DASH_OFFSET, Tk_Offset(LineMarker, dashes.offset),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-fill", "fill", "Fill",
+ (char *)NULL, Tk_Offset(LineMarker, fillColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+ DEF_MARKER_JOIN_STYLE, Tk_Offset(LineMarker, joinStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Tk_Offset(LineMarker, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Tk_Offset(LineMarker, outlineColor),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_MONO, Tk_Offset(LineMarker, outlineColor),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+ DEF_MARKER_XOR, Tk_Offset(LineMarker, xor), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * PolygonMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Identifier for marker */
+ Tk_Uid classUid; /* Type of marker */
+ Graph *graphPtr; /* Graph marker belongs to */
+ unsigned int flags;
+ char **tags;
+ int hidden; /* Indicates if the marker is currently
+ * hidden or not. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink *linkPtr;
+
+ Point2D *worldPts; /* Position of marker (X-Y coordinates) in
+ * world (graph) coordinates. */
+ int nWorldPts; /* Number of points */
+
+ char *elemName; /* Element associated with marker */
+ Axis2D axes;
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. There can
+ * be a performance because the graph
+ * must be redraw entirely each time
+ * this marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ int xOffset, yOffset; /* Pixel offset */
+
+ MarkerClass *classPtr;
+
+ /* Polygon specific attributes and fields */
+
+ Point2D *screenPts;
+
+ ColorPair outline;
+ ColorPair fill;
+
+ Pixmap stipple; /* Stipple pattern to fill the polygon. */
+ int lineWidth; /* Width of polygon outline. */
+ int capStyle;
+ int joinStyle;
+ Blt_Dashes dashes; /* List of dash values. Indicates how
+ * draw the dashed line. If no dash
+ * values are provided, or the first value
+ * is zero, then the line is drawn solid. */
+
+ GC outlineGC; /* Graphics context to draw the outline of
+ * the polygon. */
+ GC fillGC; /* Graphics context to draw the filled
+ * polygon. */
+
+ Point2D *fillPts; /* Malloc'ed array of points used to draw
+ * the filled polygon. These points may
+ * form a degenerate polygon after clipping.
+ */
+
+ int nFillPts; /* # points in the above array. */
+
+ Segment2D *outlinePts; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising
+ * the outline of the polygon. The
+ * segments may not necessarily be
+ * closed or connected after clipping. */
+
+ int nOutlinePts; /* # points in the above array. */
+
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is visible. We have
+ * to drawn it again to erase it. */
+} PolygonMarker;
+
+static Tk_ConfigSpec polygonConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_MARKER_POLYGON_TAGS, Tk_Offset(Marker, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+ DEF_MARKER_CAP_STYLE, Tk_Offset(PolygonMarker, capStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+ DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+ TK_CONFIG_NULL_OK, &coordsOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_MARKER_DASHES, Tk_Offset(PolygonMarker, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_MARKER_FILL_COLOR, Tk_Offset(PolygonMarker, fill),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_MARKER_FILL_MONO, Tk_Offset(PolygonMarker, fill),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+ DEF_MARKER_JOIN_STYLE, Tk_Offset(PolygonMarker, joinStyle),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Tk_Offset(PolygonMarker, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+ DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Tk_Offset(PolygonMarker, outline),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_MONO, Tk_Offset(PolygonMarker, outline),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_MARKER_STIPPLE, Tk_Offset(PolygonMarker, stipple),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+ DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+ DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+ DEF_MARKER_XOR, Tk_Offset(PolygonMarker, xor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+ DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerCreateProc CreateBitmapMarker, CreateLineMarker, CreateImageMarker,
+ CreatePolygonMarker, CreateTextMarker, CreateWindowMarker;
+
+static MarkerDrawProc DrawBitmapMarker, DrawLineMarker, DrawImageMarker,
+ DrawPolygonMarker, DrawTextMarker, DrawWindowMarker;
+
+static MarkerFreeProc FreeBitmapMarker, FreeLineMarker, FreeImageMarker,
+ FreePolygonMarker, FreeTextMarker, FreeWindowMarker;
+
+static MarkerConfigProc ConfigureBitmapMarker, ConfigureLineMarker,
+ ConfigureImageMarker, ConfigurePolygonMarker, ConfigureTextMarker,
+ ConfigureWindowMarker;
+
+static MarkerMapProc MapBitmapMarker, MapLineMarker, MapImageMarker,
+ MapPolygonMarker, MapTextMarker, MapWindowMarker;
+
+static MarkerPostScriptProc BitmapMarkerToPostScript, LineMarkerToPostScript,
+ ImageMarkerToPostScript, PolygonMarkerToPostScript,
+ TextMarkerToPostScript, WindowMarkerToPostScript;
+
+static MarkerPointProc PointInBitmapMarker, PointInLineMarker,
+ PointInImageMarker, PointInPolygonMarker, PointInTextMarker,
+ PointInWindowMarker;
+
+static MarkerRegionProc RegionInBitmapMarker, RegionInLineMarker,
+ RegionInImageMarker, RegionInPolygonMarker, RegionInTextMarker,
+ RegionInWindowMarker;
+
+static Tk_ImageChangedProc ImageChangedProc;
+
+static MarkerClass bitmapMarkerClass = {
+ bitmapConfigSpecs,
+ ConfigureBitmapMarker,
+ DrawBitmapMarker,
+ FreeBitmapMarker,
+ MapBitmapMarker,
+ PointInBitmapMarker,
+ RegionInBitmapMarker,
+ BitmapMarkerToPostScript,
+};
+
+static MarkerClass imageMarkerClass = {
+ imageConfigSpecs,
+ ConfigureImageMarker,
+ DrawImageMarker,
+ FreeImageMarker,
+ MapImageMarker,
+ PointInImageMarker,
+ RegionInImageMarker,
+ ImageMarkerToPostScript,
+};
+
+static MarkerClass lineMarkerClass = {
+ lineConfigSpecs,
+ ConfigureLineMarker,
+ DrawLineMarker,
+ FreeLineMarker,
+ MapLineMarker,
+ PointInLineMarker,
+ RegionInLineMarker,
+ LineMarkerToPostScript,
+};
+
+static MarkerClass polygonMarkerClass = {
+ polygonConfigSpecs,
+ ConfigurePolygonMarker,
+ DrawPolygonMarker,
+ FreePolygonMarker,
+ MapPolygonMarker,
+ PointInPolygonMarker,
+ RegionInPolygonMarker,
+ PolygonMarkerToPostScript,
+};
+
+static MarkerClass textMarkerClass = {
+ textConfigSpecs,
+ ConfigureTextMarker,
+ DrawTextMarker,
+ FreeTextMarker,
+ MapTextMarker,
+ PointInTextMarker,
+ RegionInTextMarker,
+ TextMarkerToPostScript,
+};
+
+static MarkerClass windowMarkerClass = {
+ windowConfigSpecs,
+ ConfigureWindowMarker,
+ DrawWindowMarker,
+ FreeWindowMarker,
+ MapWindowMarker,
+ PointInWindowMarker,
+ RegionInWindowMarker,
+ WindowMarkerToPostScript,
+};
+
+#ifdef notdef
+static MarkerClass rectangleMarkerClass = {
+ rectangleConfigSpecs,
+ ConfigureRectangleMarker,
+ DrawRectangleMarker,
+ FreeRectangleMarker,
+ MapRectangleMarker,
+ PointInRectangleMarker,
+ RegionInRectangleMarker,
+ RectangleMarkerToPostScript,
+};
+
+static MarkerClass ovalMarkerClass = {
+ ovalConfigSpecs,
+ ConfigureOvalMarker,
+ DrawOvalMarker,
+ FreeOvalMarker,
+ MapOvalMarker,
+ PointInOvalMarker,
+ RegionInOvalMarker,
+ OvalMarkerToPostScript,
+};
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BoxesDontOverlap --
+ *
+ * Tests if the bounding box of a marker overlaps the plotting
+ * area in any way. If so, the marker will be drawn. Just do a
+ * min/max test on the extents of both boxes.
+ *
+ * Note: It's assumed that the extents of the bounding box lie
+ * within the area. So for a 10x10 rectangle, bottom and
+ * left would be 9.
+ *
+ * Results:
+ * Returns 0 is the marker is visible in the plotting area, and
+ * 1 otherwise (marker is clipped).
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+BoxesDontOverlap(graphPtr, extsPtr)
+ Graph *graphPtr;
+ Extents2D *extsPtr;
+{
+ assert(extsPtr->right >= extsPtr->left);
+ assert(extsPtr->bottom >= extsPtr->top);
+ assert(graphPtr->right >= graphPtr->left);
+ assert(graphPtr->bottom >= graphPtr->top);
+
+ return (((double)graphPtr->right < extsPtr->left) ||
+ ((double)graphPtr->bottom < extsPtr->top) ||
+ (extsPtr->right < (double)graphPtr->left) ||
+ (extsPtr->bottom < (double)graphPtr->top));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetCoordinate --
+ *
+ * Convert the expression string into a floating point value. The
+ * only reason we use this routine instead of Blt_ExprDouble is to
+ * handle "elastic" bounds. That is, convert the strings "-Inf",
+ * "Inf" into -(DBL_MAX) and DBL_MAX respectively.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The value of the
+ * expression is passed back via valuePtr.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetCoordinate(interp, expr, valuePtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *expr; /* Numeric expression string to parse */
+ double *valuePtr; /* Real-valued result of expression */
+{
+ char c;
+
+ c = expr[0];
+ if ((c == 'I') && (strcmp(expr, "Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) {
+ *valuePtr = -DBL_MAX; /* Elastic lower bound */
+ } else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if (Tcl_ExprDouble(interp, expr, valuePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PrintCoordinate --
+ *
+ * Convert the floating point value into its string
+ * representation. The only reason this routine is used in
+ * instead of sprintf, is to handle the "elastic" bounds. That
+ * is, convert the values DBL_MAX and -(DBL_MAX) into "+Inf" and
+ * "-Inf" respectively.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The string of the
+ * expression is passed back via string.
+ *
+ * ---------------------------------------------------------------------- */
+static char *
+PrintCoordinate(interp, x)
+ Tcl_Interp *interp;
+ double x; /* Numeric value */
+{
+ if (x == DBL_MAX) {
+ return "+Inf";
+ } else if (x == -DBL_MAX) {
+ return "-Inf";
+ } else {
+ static char string[TCL_DOUBLE_SPACE + 1];
+
+ Tcl_PrintDouble(interp, (double)x, string);
+ return string;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ParseCoordinates --
+ *
+ * The Tcl coordinate list is converted to their floating point
+ * values. It will then replace the current marker coordinates.
+ *
+ * Since different marker types require different number of
+ * coordinates this must be checked here.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side effects:
+ * If the marker coordinates are reset, the graph is eventually
+ * redrawn with at the new marker coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ParseCoordinates(interp, markerPtr, nExprs, exprArr)
+ Tcl_Interp *interp;
+ Marker *markerPtr;
+ int nExprs;
+ char **exprArr;
+{
+ int nWorldPts;
+ int minArgs, maxArgs;
+ Point2D *worldPts;
+ register int i;
+ register Point2D *pointPtr;
+ double x, y;
+
+ if (nExprs == 0) {
+ return TCL_OK;
+ }
+ if (nExprs & 1) {
+ Tcl_AppendResult(interp, "odd number of marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (markerPtr->classUid == bltLineMarkerUid) {
+ minArgs = 4, maxArgs = 0;
+ } else if (markerPtr->classUid == bltPolygonMarkerUid) {
+ minArgs = 6, maxArgs = 0;
+ } else if ((markerPtr->classUid == bltWindowMarkerUid) ||
+ (markerPtr->classUid == bltTextMarkerUid)) {
+ minArgs = 2, maxArgs = 2;
+ } else if ((markerPtr->classUid == bltImageMarkerUid) ||
+ (markerPtr->classUid == bltBitmapMarkerUid)) {
+ minArgs = 2, maxArgs = 4;
+ } else {
+ Tcl_AppendResult(interp, "unknown marker type", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if (nExprs < minArgs) {
+ Tcl_AppendResult(interp, "too few marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((maxArgs > 0) && (nExprs > maxArgs)) {
+ Tcl_AppendResult(interp, "too many marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ nWorldPts = nExprs / 2;
+ worldPts = Blt_Malloc(nWorldPts * sizeof(Point2D));
+ if (worldPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new coordinate array",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /* Don't free the old coordinate array until we've parsed the new
+ * coordinates without errors. */
+ pointPtr = worldPts;
+ for (i = 0; i < nExprs; i += 2) {
+ if ((GetCoordinate(interp, exprArr[i], &x) != TCL_OK) ||
+ (GetCoordinate(interp, exprArr[i + 1], &y) != TCL_OK)) {
+ Blt_Free(worldPts);
+ return TCL_ERROR;
+ }
+ pointPtr->x = x, pointPtr->y = y;
+ pointPtr++;
+ }
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ }
+ markerPtr->worldPts = worldPts;
+ markerPtr->nWorldPts = nWorldPts;
+ markerPtr->flags |= MAP_ITEM;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToCoordinates --
+ *
+ * Given a Tcl list of numeric expression representing the
+ * element values, convert into an array of floating point
+ * values. In addition, the minimum and maximum values are saved.
+ * Since elastic values are allow (values which translate to the
+ * min/max of the graph), we must try to get the non-elastic
+ * minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The vector is
+ * passed back via the vecPtr.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToCoordinates(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Tcl list of numeric expressions */
+ char *widgRec; /* Marker record */
+ int offset; /* Not used. */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ int nExprs;
+ char **exprArr;
+ int result;
+
+ nExprs = 0;
+ if ((string != NULL) &&
+ (Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (nExprs == 0) {
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ markerPtr->worldPts = NULL;
+ }
+ markerPtr->nWorldPts = 0;
+ return TCL_OK;
+ }
+ result = ParseCoordinates(interp, markerPtr, nExprs, exprArr);
+ Blt_Free(exprArr);
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CoordinatesToString --
+ *
+ * Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CoordinatesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Marker record */
+ int offset; /* Not used. */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ Tcl_Interp *interp;
+ Tcl_DString dString;
+ char *result;
+ register int i;
+ register Point2D *p;
+
+ if (markerPtr->nWorldPts < 1) {
+ return "";
+ }
+ interp = markerPtr->graphPtr->interp;
+
+ Tcl_DStringInit(&dString);
+ p = markerPtr->worldPts;
+ for (i = 0; i < markerPtr->nWorldPts; i++) {
+ Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->x));
+ Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->y));
+ p++;
+ }
+ result = Tcl_DStringValue(&dString);
+
+ /*
+ * If memory wasn't allocated for the dynamic string, do it here (it's
+ * currently on the stack), so that Tcl can free it normally.
+ */
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * HMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a
+ * window position.
+ *
+ * Results:
+ * Returns a floating point number representing the window
+ * coordinate position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+HMap(graphPtr, axisPtr, x)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double x;
+{
+ register double norm;
+
+ if (x == DBL_MAX) {
+ norm = 1.0;
+ } else if (x == -DBL_MAX) {
+ norm = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (x > 0.0) {
+ x = log10(x);
+ } else if (x < 0.0) {
+ x = 0.0;
+ }
+ }
+ norm = NORMALIZE(axisPtr, x);
+ }
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ /* Horizontal transformation */
+ return ((norm * (graphPtr->hRange)) + graphPtr->hOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a
+ * window position.
+ *
+ * Results:
+ * Returns a double precision number representing the window
+ * coordinate position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+VMap(graphPtr, axisPtr, y)
+ Graph *graphPtr;
+ Axis *axisPtr;
+ double y;
+{
+ register double norm;
+
+ if (y == DBL_MAX) {
+ norm = 1.0;
+ } else if (y == -DBL_MAX) {
+ norm = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (y > 0.0) {
+ y = log10(y);
+ } else if (y < 0.0) {
+ y = 0.0;
+ }
+ }
+ norm = NORMALIZE(axisPtr, y);
+ }
+ if (axisPtr->descending) {
+ norm = 1.0 - norm;
+ }
+ /* Vertical transformation */
+ return (((1.0 - norm) * (graphPtr->vRange)) + graphPtr->vOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPoint --
+ *
+ * Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ * Returns a XPoint structure containing the window coordinates
+ * of the given graph x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Point2D
+MapPoint(graphPtr, pointPtr, axesPtr)
+ Graph *graphPtr;
+ Point2D *pointPtr; /* Graph X-Y coordinate. */
+ Axis2D *axesPtr; /* Specifies which axes to use */
+{
+ Point2D result;
+
+ if (graphPtr->inverted) {
+ result.x = HMap(graphPtr, axesPtr->y, pointPtr->y);
+ result.y = VMap(graphPtr, axesPtr->x, pointPtr->x);
+ } else {
+ result.x = HMap(graphPtr, axesPtr->x, pointPtr->x);
+ result.y = VMap(graphPtr, axesPtr->y, pointPtr->y);
+ }
+ return result; /* Result is screen coordinate. */
+}
+
+static Marker *
+CreateMarker(graphPtr, name, classUid)
+ Graph *graphPtr;
+ char *name;
+ Tk_Uid classUid;
+{
+ Marker *markerPtr;
+
+ /* Create the new marker based upon the given type */
+ if (classUid == bltBitmapMarkerUid) {
+ markerPtr = CreateBitmapMarker(); /* bitmap */
+ } else if (classUid == bltLineMarkerUid) {
+ markerPtr = CreateLineMarker(); /* line */
+ } else if (classUid == bltImageMarkerUid) {
+ markerPtr = CreateImageMarker(); /* image */
+ } else if (classUid == bltTextMarkerUid) {
+ markerPtr = CreateTextMarker(); /* text */
+ } else if (classUid == bltPolygonMarkerUid) {
+ markerPtr = CreatePolygonMarker(); /* polygon */
+ } else if (classUid == bltWindowMarkerUid) {
+ markerPtr = CreateWindowMarker(); /* window */
+ } else {
+ return NULL;
+ }
+ assert(markerPtr);
+ markerPtr->graphPtr = graphPtr;
+ markerPtr->hidden = markerPtr->drawUnder = FALSE;
+ markerPtr->flags |= MAP_ITEM;
+ markerPtr->name = Blt_Strdup(name);
+ markerPtr->classUid = classUid;
+ return markerPtr;
+}
+
+static void
+DestroyMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ /* Free the resources allocated for the particular type of marker */
+ (*markerPtr->classPtr->freeProc) (graphPtr, markerPtr);
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ }
+ Blt_DeleteBindings(graphPtr->bindTable, markerPtr);
+ Tk_FreeOptions(markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ graphPtr->display, 0);
+ if (markerPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(graphPtr->markers.table), markerPtr->hashPtr);
+ }
+ if (markerPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(graphPtr->markers.chainPtr, markerPtr->linkPtr);
+ }
+ if (markerPtr->name != NULL) {
+ Blt_Free(markerPtr->name);
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_Free(markerPtr->elemName);
+ }
+ if (markerPtr->tags != NULL) {
+ Blt_Free(markerPtr->tags);
+ }
+ Blt_Free(markerPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureBitmapMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a bitmap marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as bitmap pixmap, colors,
+ * rotation, etc. get set for markerPtr; old resources get freed,
+ * if there were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ConfigureBitmapMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ if (bmPtr->srcBitmap == None) {
+ return TCL_OK;
+ }
+ if (bmPtr->destBitmap == None) {
+ bmPtr->destBitmap = bmPtr->srcBitmap;
+ }
+ bmPtr->theta = FMOD(bmPtr->rotate, 360.0);
+ if (bmPtr->theta < 0.0) {
+ bmPtr->theta += 360.0;
+ }
+ gcMask = 0;
+ if (bmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = bmPtr->outlineColor->pixel;
+ }
+ if (bmPtr->fillColor != NULL) {
+ gcValues.background = bmPtr->fillColor->pixel;
+ gcMask |= GCBackground;
+ } else {
+ gcValues.clip_mask = bmPtr->srcBitmap;
+ gcMask |= GCClipMask;
+ }
+
+ /* Note that while this is a "shared" GC, we're going to change
+ * the clip origin right before the bitmap is drawn anyways. This
+ * assumes that any drawing code using this GC (with GCClipMask
+ * set) is going to want to set the clip origin anyways. */
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ bmPtr->gc = newGC;
+
+ /* Create background GC color */
+
+ if (bmPtr->fillColor != NULL) {
+ gcValues.foreground = bmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ bmPtr->fillGC = newGC;
+ }
+ if (!bmPtr->hidden) {
+ bmPtr->flags |= MAP_ITEM;
+ if (bmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapBitmapMarker --
+ *
+ * This procedure gets called each time the layout of the graph
+ * changes. The x, y window coordinates of the bitmap marker are
+ * saved in the marker structure.
+ *
+ * Additionly, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ * the private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was
+ * set, the GC stipple origins are changed to calculated window
+ * coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapBitmapMarker(markerPtr)
+ Marker *markerPtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+ Pixmap tmpBitmap;
+ int scaledWidth, scaledHeight;
+ int srcWidth, srcHeight, tmpWidth, tmpHeight;
+ register int i;
+ Point2D corner1, corner2;
+ Point2D anchorPos;
+ Extents2D exts;
+
+ if (bmPtr->srcBitmap == None) {
+ return;
+ }
+ if (bmPtr->destBitmap != bmPtr->srcBitmap) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ bmPtr->destBitmap = bmPtr->srcBitmap;
+ }
+ Tk_SizeOfBitmap(graphPtr->display, bmPtr->srcBitmap, &srcWidth,
+ &srcHeight);
+
+ /* Start by rotating the original bitmap. */
+ tmpBitmap = bmPtr->srcBitmap;
+ tmpWidth = srcWidth, tmpHeight = srcHeight;
+ if (bmPtr->theta != 0.0) {
+ tmpBitmap = Blt_RotateBitmap(graphPtr->tkwin, bmPtr->srcBitmap,
+ srcWidth, srcHeight, bmPtr->theta, &tmpWidth, &tmpHeight);
+ }
+
+ /*
+ * Collect the coordinates. The number of coordinates will determine
+ * the calculations to be made.
+ *
+ * x1 y1 A single pair of X-Y coordinates. They represent
+ * the anchor position of the bitmap.
+ *
+ * x1 y1 x2 y2 Two pairs of X-Y coordinates. They represent
+ * two opposite corners of a bounding rectangle. The
+ * bitmap is possibly rotated and scaled to fit into
+ * this box.
+ *
+ */
+ corner1 = MapPoint(graphPtr, bmPtr->worldPts, &bmPtr->axes);
+ if (bmPtr->nWorldPts > 1) {
+ double hold;
+
+ corner2 = MapPoint(graphPtr, bmPtr->worldPts + 1, &bmPtr->axes);
+ /* Flip the corners if necessary */
+ if (corner1.x > corner2.x) {
+ hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+ }
+ if (corner1.y > corner2.y) {
+ hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+ }
+ } else {
+ corner2.x = corner1.x + tmpWidth - 1;
+ corner2.y = corner1.y + tmpHeight - 1;
+ }
+ scaledWidth = (int)(corner2.x - corner1.x) + 1;
+ scaledHeight = (int)(corner2.y - corner1.y) + 1;
+
+ if (bmPtr->nWorldPts == 1) {
+ anchorPos = Blt_TranslatePoint(&corner1, scaledWidth, scaledHeight,
+ bmPtr->anchor);
+ } else {
+ anchorPos = corner1;
+ }
+ anchorPos.x += bmPtr->xOffset;
+ anchorPos.y += bmPtr->yOffset;
+
+ /* Check if the bitmap sits at least partially in the plot area. */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + scaledWidth - 1;
+ exts.bottom = anchorPos.y + scaledHeight - 1;
+
+ bmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ if (bmPtr->clipped) {
+ if (tmpBitmap != bmPtr->srcBitmap) {
+ Tk_FreePixmap(graphPtr->display, tmpBitmap);
+ }
+ return; /* Bitmap is offscreen. Don't generate
+ * rotated or scaled bitmaps. */
+ }
+
+ /*
+ * Scale the bitmap if necessary. It's a little tricky because we
+ * only want to scale what's visible on the screen, not the entire
+ * bitmap.
+ */
+ if ((scaledWidth != tmpWidth) || (scaledHeight != tmpHeight)) {
+ Pixmap bitmap;
+ Region2D dest; /* Indicates the portion of the scaled
+ * bitmap that we want to display. */
+ double left, right, top, bottom;
+
+ /*
+ * Determine the region of the bitmap visible in the plot area.
+ */
+ left = MAX(graphPtr->left, exts.left);
+ right = MIN(graphPtr->right, exts.right);
+ top = MAX(graphPtr->top, exts.top);
+ bottom = MIN(graphPtr->bottom, exts.bottom);
+
+ dest.left = dest.top = 0;
+ if (graphPtr->left > exts.left) {
+ dest.left = (int)(graphPtr->left - exts.left);
+ }
+ if (graphPtr->top > exts.top) {
+ dest.top = (int)(graphPtr->top - exts.top);
+ }
+ dest.right = dest.left + (int)(right - left);
+ dest.bottom = dest.top + (int)(bottom - top);
+
+ anchorPos.x = left;
+ anchorPos.y = top;
+ bitmap = Blt_ScaleBitmapRegion(graphPtr->tkwin, tmpBitmap, tmpWidth,
+ tmpHeight, scaledWidth, scaledHeight, &dest);
+ if (tmpBitmap != bmPtr->srcBitmap) {
+ Tk_FreePixmap(graphPtr->display, tmpBitmap);
+ }
+ tmpBitmap = bitmap;
+ tmpWidth = RegionWidth(&dest);
+ tmpHeight = RegionHeight(&dest);
+ }
+
+ bmPtr->anchorPos = anchorPos;
+ bmPtr->destWidth = tmpWidth;
+ bmPtr->destHeight = tmpHeight;
+ bmPtr->destBitmap = tmpBitmap;
+
+ {
+ double scaleX, scaleY;
+ double transX, transY;
+ Point2D polygon[5];
+
+ /*
+ * Compute a polygon to represent the background area of the bitmap.
+ * This is needed for backgrounds of arbitrarily rotated bitmaps.
+ * We also use it to print a background in PostScript.
+ */
+ Blt_GetBoundingBox(srcWidth, srcHeight, bmPtr->theta, &tmpWidth,
+ &tmpHeight, polygon);
+ scaleX = (double)scaledWidth / (double)tmpWidth;
+ scaleY = (double)scaledHeight / (double)tmpHeight;
+
+ /*
+ * Adjust each point of the polygon. Both scale it to the new size
+ * and translate it to the actual screen position of the bitmap.
+ */
+ transX = exts.left + scaledWidth * 0.5;
+ transY = exts.top + scaledHeight * 0.5;
+ for (i = 0; i < 4; i++) {
+ bmPtr->outline[i].x = (polygon[i].x * scaleX) + transX;
+ bmPtr->outline[i].y = (polygon[i].y * scaleY) + transY;
+ }
+ /* FIXME: Outline polygon should be clipped against the plot area. */
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInBitmapMarker --
+ *
+ * Indicates if the given point is over the bitmap marker. The
+ * area of the bitmap is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the bitmap marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInBitmapMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->srcBitmap == None) {
+ return 0;
+ }
+ if (bmPtr->theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < 5; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, 5);
+ }
+ return ((samplePtr->x >= bmPtr->anchorPos.x) &&
+ (samplePtr->x < (bmPtr->anchorPos.x + bmPtr->destWidth)) &&
+ (samplePtr->y >= bmPtr->anchorPos.y) &&
+ (samplePtr->y < (bmPtr->anchorPos.y + bmPtr->destHeight)));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInBitmapMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInBitmapMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (bmPtr->theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, 4, enclosed);
+ }
+ if (enclosed) {
+ return ((bmPtr->anchorPos.x >= extsPtr->left) &&
+ (bmPtr->anchorPos.y >= extsPtr->top) &&
+ ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->right) &&
+ ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->bottom));
+ }
+ return !((bmPtr->anchorPos.x >= extsPtr->right) ||
+ (bmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->left) ||
+ ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawBitmapMarker --
+ *
+ * Draws the bitmap marker that have a transparent of filled
+ * background.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current
+ * mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawBitmapMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ double theta;
+
+ if ((bmPtr->destBitmap == None) || (bmPtr->destWidth < 1) ||
+ (bmPtr->destHeight < 1)) {
+ return;
+ }
+ theta = FMOD(bmPtr->theta, (double)90.0);
+ if ((bmPtr->fillColor == NULL) || (theta != 0.0)) {
+
+ /*
+ * If the bitmap is rotated and a filled background is
+ * required, then a filled polygon is drawn before the
+ * bitmap.
+ */
+
+ if (bmPtr->fillColor != NULL) {
+ int i;
+ XPoint polygon[4];
+
+ for (i = 0; i < 4; i++) {
+ polygon[i].x = (short int)bmPtr->outline[i].x;
+ polygon[i].y = (short int)bmPtr->outline[i].y;
+ }
+ XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+ polygon, 4, Convex, CoordModeOrigin);
+ }
+ XSetClipMask(graphPtr->display, bmPtr->gc, bmPtr->destBitmap);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x,
+ (int)bmPtr->anchorPos.y);
+ } else {
+ XSetClipMask(graphPtr->display, bmPtr->gc, None);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+ }
+
+ XCopyPlane(graphPtr->display, bmPtr->destBitmap, drawable, bmPtr->gc, 0, 0,
+ bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x,
+ (int)bmPtr->anchorPos.y, 1);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BitmapMarkerToPostScript --
+ *
+ * Generates PostScript to print a bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+BitmapMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr; /* Marker to be printed */
+ PsToken psToken;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->destBitmap == None) {
+ return;
+ }
+ if (bmPtr->fillColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, bmPtr->fillColor);
+ Blt_PolygonToPostScript(psToken, bmPtr->outline, 4);
+ }
+ Blt_ForegroundToPostScript(psToken, bmPtr->outlineColor);
+
+ Blt_FormatToPostScript(psToken,
+ " gsave\n %g %g translate\n %d %d scale\n",
+ bmPtr->anchorPos.x, bmPtr->anchorPos.y + bmPtr->destHeight,
+ bmPtr->destWidth, -bmPtr->destHeight);
+ Blt_FormatToPostScript(psToken, " %d %d true [%d 0 0 %d 0 %d] {",
+ bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth,
+ -bmPtr->destHeight, bmPtr->destHeight);
+ Blt_BitmapDataToPostScript(psToken, graphPtr->display, bmPtr->destBitmap,
+ bmPtr->destWidth, bmPtr->destHeight);
+ Blt_AppendToPostScript(psToken, " } imagemask\n",
+ "grestore\n", (char *)NULL);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeBitmapMarker --
+ *
+ * Releases the memory and attributes of the bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Bitmap attributes (GCs, colors, bitmap, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeBitmapMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ if (bmPtr->destBitmap != bmPtr->srcBitmap) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateBitmapMarker --
+ *
+ * Allocate memory and initialize methods for the new bitmap marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the bitmap marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateBitmapMarker()
+{
+ BitmapMarker *bmPtr;
+
+ bmPtr = Blt_Calloc(1, sizeof(BitmapMarker));
+ if (bmPtr != NULL) {
+ bmPtr->classPtr = &bitmapMarkerClass;
+ }
+ return (Marker *)bmPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight; /* Not used. */
+{
+ ImageMarker *imPtr = clientData;
+ Tk_PhotoHandle photo;
+
+ photo = Blt_FindPhoto(imPtr->graphPtr->interp, imPtr->imageName);
+ if (photo != NULL) {
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorimage(imPtr->srcImage);
+ }
+ /* Convert the latest incarnation of the photo image back to a
+ * color image that we can scale. */
+ imPtr->srcImage = Blt_PhotoToColorimage(photo);
+ }
+ imPtr->graphPtr->flags |= REDRAW_BACKING_STORE;
+ imPtr->flags |= MAP_ITEM;
+ Blt_EventuallyRedrawGraph(imPtr->graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureImageMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a image marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as image pixmap, colors,
+ * rotation, etc. get set for markerPtr; old resources get freed,
+ * if there were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureImageMarker(markerPtr)
+ Marker *markerPtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (Blt_ConfigModified(markerPtr->classPtr->configSpecs, "-image",
+ (char *)NULL)) {
+ Tcl_Interp *interp = graphPtr->interp;
+
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ imPtr->tkImage = NULL;
+ }
+ if (imPtr->imageName[0] != '\0') {
+ GC newGC;
+ Tk_PhotoHandle photo;
+
+ imPtr->tkImage = Tk_GetImage(interp, graphPtr->tkwin,
+ imPtr->imageName, ImageChangedProc, imPtr);
+ if (imPtr->tkImage == NULL) {
+ Tcl_AppendResult(interp, "can't find an image \"",
+ imPtr->imageName, "\"", (char *)NULL);
+ Blt_Free(imPtr->imageName);
+ imPtr->imageName = NULL;
+ return TCL_ERROR;
+ }
+ photo = Blt_FindPhoto(interp, imPtr->imageName);
+ if (photo != NULL) {
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorimage(imPtr->srcImage);
+ }
+ /* Convert the photo into a color image */
+ imPtr->srcImage = Blt_PhotoToColorimage(photo);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, 0L, (XGCValues *)NULL);
+ if (imPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, imPtr->gc);
+ }
+ imPtr->gc = newGC;
+ }
+ }
+ if (!imPtr->hidden) {
+ imPtr->flags |= MAP_ITEM;
+ if (imPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapImageMarker --
+ *
+ * This procedure gets called each time the layout of the graph
+ * changes. The x, y window coordinates of the image marker are
+ * saved in the marker structure.
+ *
+ * Additionly, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ * the private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was *
+ * set, the GC stipple origins are changed to calculated window
+ * coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapImageMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Extents2D exts;
+ Graph *graphPtr;
+ ImageMarker *imPtr;
+ Point2D anchorPos;
+ Point2D corner1, corner2;
+ int scaledWidth, scaledHeight;
+ int tmpWidth, tmpHeight;
+
+ imPtr = (ImageMarker *)markerPtr;
+ if (imPtr->tkImage == NULL) {
+ return;
+ }
+ graphPtr = imPtr->graphPtr;
+ corner1 = MapPoint(graphPtr, imPtr->worldPts, &imPtr->axes);
+ if (imPtr->srcImage == NULL) {
+ /*
+ * Don't scale or rotate non-photo images.
+ */
+ Tk_SizeOfImage(imPtr->tkImage, &tmpWidth, &tmpHeight);
+ imPtr->width = tmpWidth;
+ imPtr->height = tmpHeight;
+ imPtr->anchorPos.x = corner1.x + imPtr->xOffset;
+ imPtr->anchorPos.y = corner1.y + imPtr->yOffset;
+ exts.left = imPtr->anchorPos.x;
+ exts.top = imPtr->anchorPos.y;
+ exts.right = exts.left + tmpWidth - 1;
+ exts.bottom = exts.top + tmpHeight - 1;
+ imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ return;
+ }
+
+ tmpWidth = Blt_ColorimageWidth(imPtr->srcImage);
+ tmpHeight = Blt_ColorimageHeight(imPtr->srcImage);
+ if ((tmpWidth == 0) && (tmpHeight == 0)) {
+ imPtr->clipped = TRUE;
+ return; /* Empty image. */
+ }
+ if (imPtr->nWorldPts > 1) {
+ double hold;
+
+ corner2 = MapPoint(graphPtr, imPtr->worldPts + 1, &imPtr->axes);
+ /* Flip the corners if necessary */
+ if (corner1.x > corner2.x) {
+ hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+ }
+ if (corner1.y > corner2.y) {
+ hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+ }
+ } else {
+ corner2.x = corner1.x + tmpWidth - 1;
+ corner2.y = corner1.y + tmpHeight - 1;
+ }
+ scaledWidth = (int)(corner2.x - corner1.x) + 1;
+ scaledHeight = (int)(corner2.y - corner1.y) + 1;
+
+ if (imPtr->nWorldPts == 1) {
+ anchorPos = Blt_TranslatePoint(&corner1, scaledWidth, scaledHeight,
+ imPtr->anchor);
+ } else {
+ anchorPos = corner1;
+ }
+ anchorPos.x += imPtr->xOffset;
+ anchorPos.y += imPtr->yOffset;
+
+ /* Check if the image sits at least partially in the plot area. */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + scaledWidth - 1;
+ exts.bottom = anchorPos.y + scaledHeight - 1;
+
+ imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ if (imPtr->clipped) {
+ return; /* Image is offscreen. Don't generate
+ * rotated or scaled images. */
+ }
+
+ if ((scaledWidth != tmpWidth) || (scaledHeight != tmpHeight)) {
+ Region2D src, dest;
+ Tk_PhotoHandle photo;
+ Blt_Colorimage destImage;
+ double left, right, top, bottom;
+ double scaleX, scaleY;
+
+ scaleX = (double)scaledWidth / (double)tmpWidth;
+ scaleY = (double)scaledHeight / (double)tmpHeight;
+
+ /* Determine the reduced image area of the destination image
+ * and corresponding area in the source image. */
+ left = MAX((int)exts.left, graphPtr->left);
+ top = MAX((int)exts.top, graphPtr->top);
+ right = MIN((int)exts.right, graphPtr->right);
+ bottom = MIN((int)exts.bottom, graphPtr->bottom);
+
+ dest.left = dest.top = 0;
+ if (graphPtr->left > (int)exts.left) {
+ dest.left = graphPtr->left - (int)exts.left;
+ }
+ if (graphPtr->top > (int)exts.top) {
+ dest.top = graphPtr->top - (int)exts.top;
+ }
+ dest.right = dest.left + (int)(right - left);
+ dest.bottom = dest.top + (int)(bottom - top);
+
+ /* Compute the source region to scale */
+ src.left = (int)(((double)dest.left / scaleX) + 0.5);
+ src.right = (int)(((double)dest.right / scaleX) + 0.5);
+ src.top = (int)(((double)dest.top / scaleY) + 0.5);
+ src.bottom = (int)(((double)dest.bottom / scaleY) + 0.5);
+
+ /* Fix the computed region if necessary. */
+ if (src.top < 0) {
+ src.top = 0;
+ }
+ if (src.left < 0) {
+ src.left = 0;
+ }
+ if (src.right >= tmpWidth) {
+ src.right = tmpWidth - 1;
+ }
+ if (src.bottom >= tmpHeight) {
+ src.bottom = tmpHeight - 1;
+ }
+ tmpWidth = RegionWidth(&dest);
+ tmpHeight = RegionHeight(&dest);
+
+ destImage = Blt_ResizeColorimage(imPtr->srcImage, src.left, src.top,
+ RegionWidth(&src), RegionHeight(&src), tmpWidth, tmpHeight);
+
+ /* Reset image location and coordinates to that of the region */
+ anchorPos.x = left;
+ anchorPos.y = top;
+
+#ifdef notyet
+ /* Now convert the color image into a pixmap */
+ if (imPtr->pixmap != None) {
+ Blt_FreeColorTable(imPtr->colorTable);
+ Tk_FreePixmap(Tk_Display(graphPtr->tkwin), imPtr->pixmap);
+ imPtr->colorTable = NULL;
+ }
+ imPtr->pixmap = Blt_ColorimageToPixmap(graphPtr->interp,
+ graphPtr->tkwin, destImage, &(imPtr->colorTable));
+#else
+ imPtr->pixmap = None;
+ if (imPtr->tmpImage == NULL) {
+ imPtr->tmpImage = Blt_CreateTemporaryImage(graphPtr->interp,
+ graphPtr->tkwin, imPtr);
+ if (imPtr->tmpImage == NULL) {
+ return;
+ }
+ }
+ /* Put the scaled colorimage into the photo. */
+ photo = Blt_FindPhoto(graphPtr->interp,
+ Blt_NameOfImage(imPtr->tmpImage));
+ Blt_ColorimageToPhoto(destImage, photo);
+#endif
+ Blt_FreeColorimage(destImage);
+ }
+ imPtr->anchorPos = anchorPos;
+ imPtr->width = tmpWidth;
+ imPtr->height = tmpHeight;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ * Indicates if the given point is over the window marker. The
+ * area of the window is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the window marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInImageMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ return ((samplePtr->x >= imPtr->anchorPos.x) &&
+ (samplePtr->x < (imPtr->anchorPos.x + imPtr->width)) &&
+ (samplePtr->y >= imPtr->anchorPos.y) &&
+ (samplePtr->y < (imPtr->anchorPos.y + imPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInImageMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInImageMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ if (imPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (enclosed) {
+ return ((imPtr->anchorPos.x >= extsPtr->left) &&
+ (imPtr->anchorPos.y >= extsPtr->top) &&
+ ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->right) &&
+ ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->bottom));
+ }
+ return !((imPtr->anchorPos.x >= extsPtr->right) ||
+ (imPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->left) ||
+ ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawImageMarker --
+ *
+ * This procedure is invoked to draw a image marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawImageMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ int width, height;
+
+ if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+ return;
+ }
+ if (imPtr->pixmap == None) {
+ Pixmap pixmap;
+ Tk_Image tkImage;
+
+ tkImage = (imPtr->tmpImage != NULL) ? imPtr->tmpImage : imPtr->tkImage;
+ Tk_SizeOfImage(tkImage, &width, &height);
+ pixmap = Tk_ImageGetPhotoPixmap(tkImage);
+ pixmap = None;
+ if (pixmap == None) { /* May not be a "photo" image. */
+ Tk_RedrawImage(tkImage, 0, 0, width, height, drawable,
+ (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+ } else {
+ XCopyArea(imPtr->graphPtr->display, pixmap, drawable,
+ imPtr->gc, 0, 0, width, height, (int)imPtr->anchorPos.x,
+ (int)imPtr->anchorPos.y);
+ }
+ } else {
+ XCopyArea(imPtr->graphPtr->display, imPtr->pixmap, drawable,
+ imPtr->gc, 0, 0, imPtr->width, imPtr->height,
+ (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ImageMarkerToPostScript --
+ *
+ * This procedure is invoked to print a image marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ImageMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr; /* Marker to be printed */
+ PsToken psToken;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ char *imageName;
+ Tk_PhotoHandle photo;
+
+ if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+ return; /* Image doesn't exist anymore */
+ }
+ imageName = (imPtr->tmpImage == NULL)
+ ? Blt_NameOfImage(imPtr->tkImage) : Blt_NameOfImage(imPtr->tmpImage);
+ photo = Blt_FindPhoto(markerPtr->graphPtr->interp, imageName);
+ if (photo == NULL) {
+ return; /* Image isn't a photo image */
+ }
+ Blt_PhotoToPostScript(psToken, photo, imPtr->anchorPos.x,
+ imPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeImageMarker --
+ *
+ * Destroys the structure containing the attributes of the image
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Image attributes (GCs, colors, image, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeImageMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ if (imPtr->pixmap != None) {
+ Tk_FreePixmap(graphPtr->display, imPtr->pixmap);
+ }
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ }
+ if (imPtr->tmpImage != NULL) {
+ Blt_DestroyTemporaryImage(graphPtr->interp, imPtr->tmpImage);
+ }
+ if (imPtr->srcImage != NULL) {
+ Blt_FreeColorimage(imPtr->srcImage);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateImageMarker --
+ *
+ * Allocate memory and initialize methods for the new image marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the image marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateImageMarker()
+{
+ ImageMarker *imPtr;
+
+ imPtr = Blt_Calloc(1, sizeof(ImageMarker));
+ if (imPtr != NULL) {
+ imPtr->classPtr = &imageMarkerClass;
+ }
+ return (Marker *)imPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureTextMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a text marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureTextMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ tmPtr->style.theta = FMOD(tmPtr->style.theta, 360.0);
+ if (tmPtr->style.theta < 0.0) {
+ tmPtr->style.theta += 360.0;
+ }
+ newGC = NULL;
+ if (tmPtr->fillColor != NULL) {
+ gcMask = GCForeground;
+ gcValues.foreground = tmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (tmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, tmPtr->fillGC);
+ }
+ tmPtr->fillGC = newGC;
+ Blt_ResetTextStyle(graphPtr->tkwin, &(tmPtr->style));
+
+ if (Blt_ConfigModified(tmPtr->classPtr->configSpecs, "-text",
+ (char *)NULL)) {
+ if (tmPtr->textPtr != NULL) {
+ Blt_Free(tmPtr->textPtr);
+ tmPtr->textPtr = NULL;
+ }
+ tmPtr->width = tmPtr->height = 0;
+ if (tmPtr->string != NULL) {
+ register int i;
+
+ tmPtr->textPtr = Blt_GetTextLayout(tmPtr->string, &(tmPtr->style));
+ Blt_GetBoundingBox(tmPtr->textPtr->width, tmPtr->textPtr->height,
+ tmPtr->style.theta, &(tmPtr->width), &(tmPtr->height),
+ tmPtr->outline);
+ for (i = 0; i < 4; i++) {
+ tmPtr->outline[i].x += (tmPtr->width * 0.5);
+ tmPtr->outline[i].y += (tmPtr->height * 0.5);
+ }
+ tmPtr->outline[4].x = tmPtr->outline[0].x;
+ tmPtr->outline[4].y = tmPtr->outline[0].y;
+ }
+ }
+ if (!tmPtr->hidden) {
+ tmPtr->flags |= MAP_ITEM;
+ if (tmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapTextMarker --
+ *
+ * Calculate the layout position for a text marker. Positional
+ * information is saved in the marker. If the text is rotated,
+ * a bitmap containing the text is created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If no background color has been specified, the GC stipple
+ * origins are changed to current window coordinates. For both
+ * rotated and non-rotated text, if any old bitmap is leftover,
+ * it is freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapTextMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Extents2D exts;
+ Point2D anchorPos;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ anchorPos = MapPoint(graphPtr, tmPtr->worldPts, &tmPtr->axes);
+ anchorPos = Blt_TranslatePoint(&anchorPos, tmPtr->width, tmPtr->height,
+ tmPtr->anchor);
+ anchorPos.x += tmPtr->xOffset;
+ anchorPos.y += tmPtr->yOffset;
+ /*
+ * Determine the bounding box of the text and test to see if it
+ * is at least partially contained within the plotting area.
+ */
+ exts.left = anchorPos.x;
+ exts.top = anchorPos.y;
+ exts.right = anchorPos.x + tmPtr->width - 1;
+ exts.bottom = anchorPos.y + tmPtr->height - 1;
+ tmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+ tmPtr->anchorPos = anchorPos;
+
+}
+
+static int
+PointInTextMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return 0;
+ }
+ if (tmPtr->style.theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Figure out the bounding polygon (isolateral) for the text
+ * and see if the point is inside of it.
+ */
+
+ for (i = 0; i < 5; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, 5);
+ }
+ return ((samplePtr->x >= tmPtr->anchorPos.x) &&
+ (samplePtr->x < (tmPtr->anchorPos.x + tmPtr->width)) &&
+ (samplePtr->y >= tmPtr->anchorPos.y) &&
+ (samplePtr->y < (tmPtr->anchorPos.y + tmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInTextMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInTextMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (tmPtr->style.theta != 0.0) {
+ Point2D points[5];
+ register int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap
+ * and see if the point is inside of it.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, 4, enclosed);
+ }
+ if (enclosed) {
+ return ((tmPtr->anchorPos.x >= extsPtr->left) &&
+ (tmPtr->anchorPos.y >= extsPtr->top) &&
+ ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->right) &&
+ ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->bottom));
+ }
+ return !((tmPtr->anchorPos.x >= extsPtr->right) ||
+ (tmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->left) ||
+ ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawTextMarker --
+ *
+ * Draws the text marker on the graph.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to draw the marker in its current
+ * mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawTextMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ XPoint pointArr[4];
+ register int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by
+ * filling a bounding polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ pointArr[i].x = (short int)
+ (tmPtr->outline[i].x + tmPtr->anchorPos.x);
+ pointArr[i].y = (short int)
+ (tmPtr->outline[i].y + tmPtr->anchorPos.y);
+ }
+ XFillPolygon(graphPtr->display, drawable, tmPtr->fillGC, pointArr, 4,
+ Convex, CoordModeOrigin);
+ }
+ if (tmPtr->style.color != NULL) {
+ Blt_DrawTextLayout(graphPtr->tkwin, drawable, tmPtr->textPtr,
+ &(tmPtr->style), (int)tmPtr->anchorPos.x, (int)tmPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextMarkerToPostScript --
+ *
+ * Outputs PostScript commands to draw a text marker at a given
+ * x,y coordinate, rotation, anchor, and font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript font and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TextMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ Point2D polygon[4];
+ register int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by
+ * filling a bounding polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ polygon[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+ polygon[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+ }
+ Blt_BackgroundToPostScript(psToken, tmPtr->fillColor);
+ Blt_PolygonToPostScript(psToken, polygon, 4);
+ }
+ Blt_TextToPostScript(psToken, tmPtr->string, &(tmPtr->style),
+ tmPtr->anchorPos.x, tmPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeTextMarker --
+ *
+ * Destroys the structure containing the attributes of the text
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Text attributes (GCs, colors, stipple, font, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeTextMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ Blt_FreeTextStyle(graphPtr->display, &(tmPtr->style));
+ if (tmPtr->textPtr != NULL) {
+ Blt_Free(tmPtr->textPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTextMarker --
+ *
+ * Allocate memory and initialize methods for the new text marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the text marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateTextMarker()
+{
+ TextMarker *tmPtr;
+
+ tmPtr = Blt_Calloc(1, sizeof(TextMarker));
+ assert(tmPtr);
+
+ tmPtr->classPtr = &textMarkerClass;
+ Blt_InitTextStyle(&(tmPtr->style));
+ tmPtr->style.anchor = TK_ANCHOR_NW;
+ tmPtr->style.padLeft = tmPtr->style.padRight = 4;
+ tmPtr->style.padTop = tmPtr->style.padBottom = 4;
+
+ return (Marker *)tmPtr;
+}
+
+
+static void ChildEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static void ChildCustodyProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr winMarkerMgrInfo =
+{
+ "graph", /* Name of geometry manager used by winfo */
+ ChildGeometryProc, /* Procedure to for new geometry requests */
+ ChildCustodyProc, /* Procedure when window is taken away */
+};
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureWindowMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a window marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as window pathname, placement,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureWindowMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Tk_Window tkwin;
+
+ if (wmPtr->pathName == NULL) {
+ return TCL_OK;
+ }
+ tkwin = Tk_NameToWindow(graphPtr->interp, wmPtr->pathName,
+ graphPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_Parent(tkwin) != graphPtr->tkwin) {
+ Tcl_AppendResult(graphPtr->interp, "\"", wmPtr->pathName,
+ "\" is not a child of \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tkwin != wmPtr->tkwin) {
+ if (wmPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_UnmapWindow(wmPtr->tkwin);
+ }
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildEventProc,
+ wmPtr);
+ Tk_ManageGeometry(tkwin, &winMarkerMgrInfo, wmPtr);
+ }
+ wmPtr->tkwin = tkwin;
+
+ if (!wmPtr->hidden) {
+ wmPtr->flags |= MAP_ITEM;
+ if (wmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapWindowMarker --
+ *
+ * Calculate the layout position for a window marker. Positional
+ * information is saved in the marker.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapWindowMarker(markerPtr)
+ Marker *markerPtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->graphPtr;
+ Extents2D exts;
+ int width, height;
+
+ if (wmPtr->tkwin == (Tk_Window)NULL) {
+ return;
+ }
+ wmPtr->anchorPos = MapPoint(graphPtr, wmPtr->worldPts, &wmPtr->axes);
+
+ width = Tk_ReqWidth(wmPtr->tkwin);
+ height = Tk_ReqHeight(wmPtr->tkwin);
+ if (wmPtr->reqWidth > 0) {
+ width = wmPtr->reqWidth;
+ }
+ if (wmPtr->reqHeight > 0) {
+ height = wmPtr->reqHeight;
+ }
+ wmPtr->anchorPos = Blt_TranslatePoint(&wmPtr->anchorPos, width, height,
+ wmPtr->anchor);
+ wmPtr->anchorPos.x += wmPtr->xOffset;
+ wmPtr->anchorPos.y += wmPtr->yOffset;
+ wmPtr->width = width;
+ wmPtr->height = height;
+
+ /*
+ * Determine the bounding box of the window and test to see if it
+ * is at least partially contained within the plotting area.
+ */
+ exts.left = wmPtr->anchorPos.x;
+ exts.top = wmPtr->anchorPos.y;
+ exts.right = wmPtr->anchorPos.x + wmPtr->width - 1;
+ exts.bottom = wmPtr->anchorPos.y + wmPtr->height - 1;
+ wmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInWindowMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ return ((samplePtr->x >= wmPtr->anchorPos.x) &&
+ (samplePtr->x < (wmPtr->anchorPos.x + wmPtr->width)) &&
+ (samplePtr->y >= wmPtr->anchorPos.y) &&
+ (samplePtr->y < (wmPtr->anchorPos.y + wmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInWindowMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (enclosed) {
+ return ((wmPtr->anchorPos.x >= extsPtr->left) &&
+ (wmPtr->anchorPos.y >= extsPtr->top) &&
+ ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->right) &&
+ ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->bottom));
+ }
+ return !((wmPtr->anchorPos.x >= extsPtr->right) ||
+ (wmPtr->anchorPos.y >= extsPtr->bottom) ||
+ ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->left) ||
+ ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawWindowMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin == NULL) {
+ return;
+ }
+ if ((wmPtr->height != Tk_Height(wmPtr->tkwin)) ||
+ (wmPtr->width != Tk_Width(wmPtr->tkwin)) ||
+ ((int)wmPtr->anchorPos.x != Tk_X(wmPtr->tkwin)) ||
+ ((int)wmPtr->anchorPos.y != Tk_Y(wmPtr->tkwin))) {
+ Tk_MoveResizeWindow(wmPtr->tkwin, (int)wmPtr->anchorPos.x,
+ (int)wmPtr->anchorPos.y, wmPtr->width, wmPtr->height);
+ }
+ if (!Tk_IsMapped(wmPtr->tkwin)) {
+ Tk_MapWindow(wmPtr->tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * WindowMarkerToPostScript --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+WindowMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin == NULL) {
+ return;
+ }
+ if (Tk_IsMapped(wmPtr->tkwin)) {
+ Blt_WindowToPostScript(psToken, wmPtr->tkwin, wmPtr->anchorPos.x,
+ wmPtr->anchorPos.y);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeWindowMarker --
+ *
+ * Destroys the structure containing the attributes of the window
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window is destroyed and removed from the screen.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeWindowMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_DestroyWindow(wmPtr->tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateWindowMarker --
+ *
+ * Allocate memory and initialize methods for the new window marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the window marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateWindowMarker()
+{
+ WindowMarker *wmPtr;
+
+ wmPtr = Blt_Calloc(1, sizeof(WindowMarker));
+ if (wmPtr != NULL) {
+ wmPtr->classPtr = &windowMarkerClass;
+ }
+ return (Marker *)wmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildEventProc --
+ *
+ * This procedure is invoked whenever StructureNotify events
+ * occur for a window that's managed as part of a graph window
+ * marker. This procedure's only purpose is to clean up when
+ * windows are deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is disassociated from the window item when it is
+ * deleted.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ChildEventProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to record describing window item. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ wmPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildGeometryProc --
+ *
+ * This procedure is invoked whenever a window that's associated
+ * with a window item changes its requested dimensions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size and location on the window of the window may change,
+ * depending on the options specified for the window item.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ChildGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Pointer to record for window item. */
+ Tk_Window tkwin; /* Window that changed its desired size. */
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (wmPtr->reqWidth == 0) {
+ wmPtr->width = Tk_ReqWidth(tkwin);
+ }
+ if (wmPtr->reqHeight == 0) {
+ wmPtr->height = Tk_ReqHeight(tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildCustodyProc --
+ *
+ * This procedure is invoked when an embedded window has been
+ * stolen by another geometry manager. The information and
+ * memory associated with the widget is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the graph to be redrawn without the embedded
+ * widget at the next idle point.
+ *
+ * ----------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+ChildCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Window marker to be destroyed. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Marker *markerPtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = markerPtr->graphPtr;
+ DestroyMarker(markerPtr);
+ /*
+ * Not really needed. We should get an Expose event when the
+ * child window is unmapped.
+ */
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapLineMarker --
+ *
+ * Calculate the layout position for a line marker. Positional
+ * information is saved in the marker. The line positions are
+ * stored in an array of points (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapLineMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ Point2D *srcPtr, *endPtr;
+ Segment2D *segments, *segPtr;
+ Point2D p, q, next;
+ Extents2D exts;
+
+ lmPtr->nSegments = 0;
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+ if (lmPtr->nWorldPts < 2) {
+ return; /* Too few points */
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+
+ /*
+ * Allow twice the number of world coordinates. The line will
+ * represented as series of line segments, not one continous
+ * polyline. This is because clipping against the plot area may
+ * chop the line into several disconnected segments.
+ */
+ segments = Blt_Malloc(lmPtr->nWorldPts * sizeof(Segment2D));
+ srcPtr = lmPtr->worldPts;
+ p = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+ p.x += lmPtr->xOffset;
+ p.y += lmPtr->yOffset;
+
+ segPtr = segments;
+ for (srcPtr++, endPtr = lmPtr->worldPts + lmPtr->nWorldPts;
+ srcPtr < endPtr; srcPtr++) {
+ next = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+ next.x += lmPtr->xOffset;
+ next.y += lmPtr->yOffset;
+ q = next;
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ segPtr->p = p;
+ segPtr->q = q;
+ segPtr++;
+ }
+ p = next;
+ }
+ lmPtr->nSegments = segPtr - segments;
+ lmPtr->segments = segments;
+ lmPtr->clipped = (lmPtr->nSegments == 0);
+}
+
+static int
+PointInLineMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ return Blt_PointInSegments(samplePtr, lmPtr->segments, lmPtr->nSegments,
+ (double)lmPtr->graphPtr->halo);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInLineMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nWorldPts < 2) {
+ return FALSE;
+ }
+ if (enclosed) {
+ Point2D p;
+ Point2D *pointPtr, *endPtr;
+
+ for (pointPtr = lmPtr->worldPts,
+ endPtr = lmPtr->worldPts + lmPtr->nWorldPts;
+ pointPtr < endPtr; pointPtr++) {
+ p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+ if ((p.x < extsPtr->left) && (p.x > extsPtr->right) &&
+ (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) {
+ return FALSE;
+ }
+ }
+ return TRUE; /* All points inside bounding box. */
+ } else {
+ Point2D p, q;
+ int count;
+ Point2D *pointPtr, *endPtr;
+
+ count = 0;
+ for (pointPtr = lmPtr->worldPts,
+ endPtr = lmPtr->worldPts + (lmPtr->nWorldPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+ q = MapPoint(lmPtr->graphPtr, pointPtr + 1, &lmPtr->axes);
+ if (Blt_LineRectClip(extsPtr, &p, &q)) {
+ count++;
+ }
+ }
+ return (count > 0); /* At least 1 segment passes through region. */
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawLineMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Graph *graphPtr = markerPtr->graphPtr;
+
+ Blt_DrawSegments2D(graphPtr->display, drawable, lmPtr->gc,
+ lmPtr->segments, lmPtr->nSegments);
+ if (lmPtr->xor) { /* Toggle the drawing state */
+ lmPtr->xorState = (lmPtr->xorState == 0);
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureLineMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a line marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as line width, colors, dashes,
+ * etc. get set for markerPtr; old resources get freed, if there
+ * were any. The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLineMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
+ if (lmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = lmPtr->outlineColor->pixel;
+ }
+ if (lmPtr->fillColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = lmPtr->fillColor->pixel;
+ }
+ gcValues.cap_style = lmPtr->capStyle;
+ gcValues.join_style = lmPtr->joinStyle;
+ gcValues.line_width = LineWidth(lmPtr->lineWidth);
+ gcValues.line_style = LineSolid;
+ if (LineIsDashed(lmPtr->dashes)) {
+ gcValues.line_style =
+ (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash;
+ }
+ if (lmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = graphPtr->plotBg->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawLineMarker(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (LineIsDashed(lmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &(lmPtr->dashes));
+ }
+ lmPtr->gc = newGC;
+ if (lmPtr->xor) {
+ if (drawable != None) {
+ MapLineMarker(markerPtr);
+ DrawLineMarker(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ if (!lmPtr->hidden) {
+ lmPtr->flags |= MAP_ITEM;
+ if (lmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LineMarkerToPostScript --
+ *
+ * Prints postscript commands to display the connect line.
+ * Dashed lines need to be handled specially, especially if a
+ * background color is designated.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript output commands are saved in the interpreter
+ * (infoPtr->interp) result field.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+LineMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Blt_LineAttributesToPostScript(psToken, lmPtr->outlineColor,
+ lmPtr->lineWidth, &(lmPtr->dashes), lmPtr->capStyle,
+ lmPtr->joinStyle);
+ if ((LineIsDashed(lmPtr->dashes)) && (lmPtr->fillColor != NULL)) {
+ Blt_AppendToPostScript(psToken, "/DashesProc {\n gsave\n ",
+ (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, lmPtr->fillColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken,
+ "stroke\n",
+ " grestore\n",
+ "} def\n", (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+ (char *)NULL);
+ }
+ Blt_Segments2DToPostScript(psToken, lmPtr->segments, lmPtr->nSegments);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeLineMarker --
+ *
+ * Destroys the structure and attributes of a line marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Line attributes (GCs, colors, stipple, etc) get released.
+ * Memory is deallocated, X resources are freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeLineMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateLineMarker --
+ *
+ * Allocate memory and initialize methods for a new line marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateLineMarker()
+{
+ LineMarker *lmPtr;
+
+ lmPtr = Blt_Calloc(1, sizeof(LineMarker));
+ if (lmPtr != NULL) {
+ lmPtr->classPtr = &lineMarkerClass;
+ lmPtr->xor = FALSE;
+ lmPtr->capStyle = CapButt;
+ lmPtr->joinStyle = JoinMiter;
+ }
+ return (Marker *)lmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPolygonMarker --
+ *
+ * Calculate the layout position for a polygon marker. Positional
+ * information is saved in the polygon in an array of points
+ * (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapPolygonMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ Point2D *srcPtr, *destPtr, *endPtr;
+ Point2D *screenPts;
+ Extents2D exts;
+ int nScreenPts;
+
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ pmPtr->outlinePts = NULL;
+ pmPtr->nOutlinePts = 0;
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ pmPtr->fillPts = NULL;
+ pmPtr->nFillPts = 0;
+ }
+ if (pmPtr->screenPts != NULL) {
+ Blt_Free(pmPtr->screenPts);
+ pmPtr->screenPts = NULL;
+ }
+ if (pmPtr->nWorldPts < 3) {
+ return; /* Too few points */
+ }
+
+ /*
+ * Allocate and fill a temporary array to hold the screen
+ * coordinates of the polygon.
+ */
+ nScreenPts = pmPtr->nWorldPts + 1;
+ screenPts = Blt_Malloc((nScreenPts + 1) * sizeof(Point2D));
+ endPtr = pmPtr->worldPts + pmPtr->nWorldPts;
+ destPtr = screenPts;
+ for (srcPtr = pmPtr->worldPts; srcPtr < endPtr; srcPtr++) {
+ *destPtr = MapPoint(graphPtr, srcPtr, &pmPtr->axes);
+ destPtr->x += pmPtr->xOffset;
+ destPtr->y += pmPtr->yOffset;
+ destPtr++;
+ }
+ *destPtr = screenPts[0];
+
+ Blt_GraphExtents(graphPtr, &exts);
+ pmPtr->clipped = TRUE;
+ if (pmPtr->fill.fgColor != NULL) { /* Polygon fill required. */
+ Point2D *fillPts;
+ int n;
+
+ fillPts = Blt_Malloc(sizeof(Point2D) * nScreenPts * 3);
+ assert(fillPts);
+ n = Blt_PolyRectClip(&exts, screenPts, pmPtr->nWorldPts, fillPts);
+ if (n < 3) {
+ Blt_Free(fillPts);
+ } else {
+ pmPtr->nFillPts = n;
+ pmPtr->fillPts = fillPts;
+ pmPtr->clipped = FALSE;
+ }
+ }
+ if ((pmPtr->outline.fgColor != NULL) && (pmPtr->lineWidth > 0)) {
+ Segment2D *outlinePts;
+ register Segment2D *segPtr;
+ /*
+ * Generate line segments representing the polygon outline.
+ * The resulting outline may or may not be closed from
+ * viewport clipping.
+ */
+ outlinePts = Blt_Malloc(nScreenPts * sizeof(Segment2D));
+ if (outlinePts == NULL) {
+ return; /* Can't allocate point array */
+ }
+ /*
+ * Note that this assumes that the point array contains an
+ * extra point that closes the polygon.
+ */
+ segPtr = outlinePts;
+ for (srcPtr = screenPts, endPtr = screenPts + (nScreenPts - 1);
+ srcPtr < endPtr; srcPtr++) {
+ segPtr->p = srcPtr[0];
+ segPtr->q = srcPtr[1];
+ if (Blt_LineRectClip(&exts, &(segPtr->p), &(segPtr->q))) {
+ segPtr++;
+ }
+ }
+ pmPtr->nOutlinePts = segPtr - outlinePts;
+ pmPtr->outlinePts = outlinePts;
+ pmPtr->clipped = (pmPtr->nOutlinePts > 0);
+ }
+ pmPtr->screenPts = screenPts;
+}
+
+static int
+PointInPolygonMarker(markerPtr, samplePtr)
+ Marker *markerPtr;
+ Point2D *samplePtr;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fillPts != NULL) {
+ return Blt_PointInPolygon(samplePtr, pmPtr->fillPts, pmPtr->nFillPts);
+ }
+ return Blt_PointInSegments(samplePtr, pmPtr->outlinePts,
+ pmPtr->nOutlinePts, (double)pmPtr->graphPtr->halo);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInPolygonMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInPolygonMarker(markerPtr, extsPtr, enclosed)
+ Marker *markerPtr;
+ Extents2D *extsPtr;
+ int enclosed;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->nWorldPts >= 3) {
+ return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts,
+ enclosed);
+ }
+ return FALSE;
+}
+
+static void
+DrawPolygonMarker(markerPtr, drawable)
+ Marker *markerPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ /* Draw polygon fill region */
+ if ((pmPtr->nFillPts > 0) && (pmPtr->fill.fgColor != NULL)) {
+ XPoint *destPtr, *pointArr;
+ Point2D *srcPtr, *endPtr;
+
+ pointArr = Blt_Malloc(pmPtr->nFillPts * sizeof(XPoint));
+ if (pointArr == NULL) {
+ return;
+ }
+ destPtr = pointArr;
+ for (srcPtr = pmPtr->fillPts,
+ endPtr = pmPtr->fillPts + pmPtr->nFillPts; srcPtr < endPtr;
+ srcPtr++) {
+ destPtr->x = (short int)srcPtr->x;
+ destPtr->y = (short int)srcPtr->y;
+ destPtr++;
+ }
+ XFillPolygon(graphPtr->display, drawable, pmPtr->fillGC,
+ pointArr, pmPtr->nFillPts, Complex, CoordModeOrigin);
+ Blt_Free(pointArr);
+ }
+ /* and then the outline */
+ if ((pmPtr->nOutlinePts > 0) && (pmPtr->lineWidth > 0) &&
+ (pmPtr->outline.fgColor != NULL)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, pmPtr->outlineGC,
+ pmPtr->outlinePts, pmPtr->nOutlinePts);
+ }
+}
+
+
+static void
+PolygonMarkerToPostScript(markerPtr, psToken)
+ Marker *markerPtr;
+ PsToken psToken;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fill.fgColor != NULL) {
+
+ /*
+ * Options: fg bg
+ * Draw outline only.
+ * x Draw solid or stipple.
+ * x x Draw solid or stipple.
+ */
+
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_PathToPostScript(psToken, pmPtr->fillPts, pmPtr->nFillPts);
+ Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+ /* If the background fill color was specified, draw the
+ * polygon in a solid fashion with that color. */
+ if (pmPtr->fill.bgColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, pmPtr->fill.bgColor);
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ Blt_ForegroundToPostScript(psToken, pmPtr->fill.fgColor);
+ if (pmPtr->stipple != None) {
+ /* Draw the stipple in the foreground color. */
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ pmPtr->stipple);
+ } else {
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ }
+
+ /* Draw the outline in the foreground color. */
+ if ((pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) {
+
+ /* Set up the line attributes. */
+ Blt_LineAttributesToPostScript(psToken, pmPtr->outline.fgColor,
+ pmPtr->lineWidth, &(pmPtr->dashes), pmPtr->capStyle,
+ pmPtr->joinStyle);
+
+ /*
+ * Define on-the-fly a PostScript macro "DashesProc" that
+ * will be executed for each call to the Polygon drawing
+ * routine. If the line isn't dashed, simply make this an
+ * empty definition.
+ */
+ if ((pmPtr->outline.bgColor != NULL) && (LineIsDashed(pmPtr->dashes))) {
+ Blt_AppendToPostScript(psToken,
+ "/DashesProc {\n",
+ "gsave\n ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, pmPtr->outline.bgColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken,
+ "stroke\n",
+ " grestore\n",
+ "} def\n", (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+ (char *)NULL);
+ }
+ Blt_Segments2DToPostScript(psToken, pmPtr->outlinePts,
+ pmPtr->nOutlinePts);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigurePolygonMarker --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a polygon marker.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as polygon color, dashes,
+ * fillstyle, etc. get set for markerPtr; old resources get
+ * freed, if there were any. The marker is eventually
+ * redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePolygonMarker(markerPtr)
+ Marker *markerPtr;
+{
+ Graph *graphPtr = markerPtr->graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle);
+ if (pmPtr->outline.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->outline.fgColor->pixel;
+ }
+ if (pmPtr->outline.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->outline.bgColor->pixel;
+ }
+ gcMask |= (GCCapStyle | GCJoinStyle);
+ gcValues.cap_style = pmPtr->capStyle;
+ gcValues.join_style = pmPtr->joinStyle;
+ gcValues.line_style = LineSolid;
+ gcValues.dash_offset = 0;
+ gcValues.line_width = LineWidth(pmPtr->lineWidth);
+ if (LineIsDashed(pmPtr->dashes)) {
+ gcValues.line_style = (pmPtr->outline.bgColor == NULL)
+ ? LineOnOffDash : LineDoubleDash;
+ }
+ if (pmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ /* The graph's color option may not have been set yet */
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = graphPtr->plotBg->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawPolygonMarker(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(pmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &(pmPtr->dashes));
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ pmPtr->outlineGC = newGC;
+
+ gcMask = 0;
+ if (pmPtr->fill.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->fill.fgColor->pixel;
+ }
+ if (pmPtr->fill.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->fill.bgColor->pixel;
+ }
+ if (pmPtr->stipple != None) {
+ gcValues.stipple = pmPtr->stipple;
+ gcValues.fill_style = (pmPtr->fill.bgColor != NULL)
+ ? FillOpaqueStippled : FillStippled;
+ gcMask |= (GCStipple | GCFillStyle);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ pmPtr->fillGC = newGC;
+
+ if ((gcMask == 0) && !(graphPtr->flags & RESET_AXES) && (pmPtr->xor)) {
+ if (drawable != None) {
+ MapPolygonMarker(markerPtr);
+ DrawPolygonMarker(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ if (!pmPtr->hidden) {
+ pmPtr->flags |= MAP_ITEM;
+ if (pmPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreePolygonMarker --
+ *
+ * Release memory and resources allocated for the polygon element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the polygon element is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreePolygonMarker(graphPtr, markerPtr)
+ Graph *graphPtr;
+ Marker *markerPtr;
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ }
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ }
+ Blt_FreeColorPair(&pmPtr->outline);
+ Blt_FreeColorPair(&pmPtr->fill);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreatePolygonMarker --
+ *
+ * Allocate memory and initialize methods for the new polygon
+ * marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is
+ * returned.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreatePolygonMarker()
+{
+ PolygonMarker *pmPtr;
+
+ pmPtr = Blt_Calloc(1, sizeof(PolygonMarker));
+ if (pmPtr != NULL) {
+ pmPtr->classPtr = &polygonMarkerClass;
+ pmPtr->capStyle = CapButt;
+ pmPtr->joinStyle = JoinMiter;
+
+ }
+ return (Marker *)pmPtr;
+}
+
+int
+Blt_NameToMarker(graphPtr, name, markerPtrPtr)
+ Graph *graphPtr;
+ char *name;
+ Marker **markerPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->markers.table), name);
+ if (hPtr != NULL) {
+ *markerPtrPtr = (Marker *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendResult(graphPtr->interp, "can't find marker \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), (char *)NULL);
+ return TCL_ERROR;
+}
+
+
+static int
+RenameMarker(graphPtr, markerPtr, oldName, newName)
+ Graph *graphPtr;
+ Marker *markerPtr;
+ char *oldName, *newName;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ /* Rename the marker only if no marker already exists by that name */
+ hPtr = Blt_CreateHashEntry(&(graphPtr->markers.table), newName, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(graphPtr->interp, "can't rename marker: \"", newName,
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr->name = Blt_Strdup(newName);
+ markerPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, (char *)markerPtr);
+
+ /* Delete the old hash entry */
+ hPtr = Blt_FindHashEntry(&(graphPtr->markers.table), oldName);
+ Blt_DeleteHashEntry(&(graphPtr->markers.table), hPtr);
+ if (oldName != NULL) {
+ Blt_Free(oldName);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns a list of marker identifiers in interp->result;
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+NamesOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ Blt_ChainLink *linkPtr;
+ register int i;
+
+ Tcl_ResetResult(interp);
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if (argc == 3) {
+ Tcl_AppendElement(interp, markerPtr->name);
+ continue;
+ }
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(markerPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, markerPtr->name);
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+ClientData
+Blt_MakeMarkerTag(graphPtr, tagName)
+ Graph *graphPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&(graphPtr->markers.tagTable), tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&(graphPtr->markers.tagTable), hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g element bind elemName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tag;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->markers.tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tag = Blt_GetHashKey(&(graphPtr->markers.tagTable), hPtr);
+ Tcl_AppendElement(interp, tag);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+ Blt_MakeMarkerTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+
+ if (Blt_NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_ConfigureValue(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr, argv[4], 0)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ int flags = TK_CONFIG_ARGV_ONLY;
+ char *oldName;
+ int nNames, nOpts;
+ char **options;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (Blt_NameToMarker(graphPtr, argv[i], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of element names specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + nNames; /* Start of options in argv */
+
+ for (i = 0; i < nNames; i++) {
+ Blt_NameToMarker(graphPtr, argv[i], &markerPtr);
+ if (nOpts == 0) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ (char *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ options[0], flags);
+ }
+ /* Save the old marker. */
+ oldName = markerPtr->name;
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, nOpts, options,
+ (char *)markerPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (oldName != markerPtr->name) {
+ if (RenameMarker(graphPtr, markerPtr, oldName, markerPtr->name)
+ != TCL_OK) {
+ markerPtr->name = oldName;
+ return TCL_ERROR;
+ }
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * This procedure creates and initializes a new marker.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing
+ * the new element. If an error occurred, then the return
+ * value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated, etc.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+CreateOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ Tk_Uid classUid;
+ register int i;
+ char *name;
+ char string[200];
+ unsigned int length;
+ char c;
+ markerPtr = NULL;
+
+ c = argv[3][0];
+ /* Create the new marker based upon the given type */
+ if ((c == 't') && (strcmp(argv[3], "text") == 0)) {
+ classUid = bltTextMarkerUid;
+ } else if ((c == 'l') && (strcmp(argv[3], "line") == 0)) {
+ classUid = bltLineMarkerUid;
+ } else if ((c == 'p') && (strcmp(argv[3], "polygon") == 0)) {
+ classUid = bltPolygonMarkerUid;
+ } else if ((c == 'i') && (strcmp(argv[3], "image") == 0)) {
+ classUid = bltImageMarkerUid;
+ } else if ((c == 'b') && (strcmp(argv[3], "bitmap") == 0)) {
+ classUid = bltBitmapMarkerUid;
+ } else if ((c == 'w') && (strcmp(argv[3], "window") == 0)) {
+ classUid = bltWindowMarkerUid;
+ } else {
+ Tcl_AppendResult(interp, "unknown marker type \"", argv[3],
+ "\": should be \"text\", \"line\", \"polygon\", \"bitmap\", \"image\", or \
+\"window\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Scan for "-name" option. We need it for the component name */
+ name = NULL;
+ for (i = 4; i < argc; i += 2) {
+ length = strlen(argv[i]);
+ if ((length > 1) && (strncmp(argv[i], "-name", length) == 0)) {
+ name = argv[i + 1];
+ break;
+ }
+ }
+ /* If no name was given for the marker, make up one. */
+ if (name == NULL) {
+ sprintf(string, "marker%d", graphPtr->nextMarkerId++);
+ name = string;
+ } else if (name[0] == '-') {
+ Tcl_AppendResult(interp, "name of marker \"", name,
+ "\" can't start with a '-'", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr = CreateMarker(graphPtr, name, classUid);
+ if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, name,
+ markerPtr->classUid, markerPtr->classPtr->configSpecs,
+ argc - 4, argv + 4, (char *)markerPtr, 0) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&(graphPtr->markers.table), name, &isNew);
+ if (!isNew) {
+ Marker *oldMarkerPtr;
+ /*
+ * Marker by the same name already exists. Delete the old
+ * marker and it's list entry. But save the hash entry.
+ */
+ oldMarkerPtr = (Marker *)Blt_GetHashValue(hPtr);
+ oldMarkerPtr->hashPtr = NULL;
+ DestroyMarker(oldMarkerPtr);
+ }
+ Blt_SetHashValue(hPtr, markerPtr);
+ markerPtr->hashPtr = hPtr;
+ markerPtr->linkPtr = Blt_ChainAppend(graphPtr->markers.chainPtr, markerPtr);
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ Tcl_SetResult(interp, name, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the marker given by markerId.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+ register int i;
+
+ for (i = 3; i < argc; i++) {
+ if (Blt_NameToMarker(graphPtr, argv[i], &markerPtr) == TCL_OK) {
+ DestroyMarker(markerPtr);
+ }
+ }
+ Tcl_ResetResult(interp);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument
+ * can be either a screen position "@x,y" or the name of an
+ * element.
+ *
+ * I don't know how useful it is to test with the name of an
+ * element.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ register Marker *markerPtr;
+
+ if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
+ markerPtr = (Marker *)Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on markers. */
+ if (markerPtr == NULL) {
+ return TCL_OK;
+ }
+ if ((markerPtr->classUid == bltBitmapMarkerUid) ||
+ (markerPtr->classUid == bltLineMarkerUid) ||
+ (markerPtr->classUid == bltWindowMarkerUid) ||
+ (markerPtr->classUid == bltPolygonMarkerUid) ||
+ (markerPtr->classUid == bltTextMarkerUid) ||
+ (markerPtr->classUid == bltImageMarkerUid)) {
+ Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RelinkOp --
+ *
+ * Reorders the marker (given by the first name) before/after
+ * the another marker (given by the second name) in the
+ * marker display list. If no second name is given, the
+ * marker is placed at the beginning/end of the list.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RelinkOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Blt_ChainLink *linkPtr, *placeLinkPtr;
+ Marker *markerPtr;
+
+ /* Find the new marker to be inserted into the display list */
+ if (Blt_NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Now use the marker to find the entry in the display list */
+ linkPtr = markerPtr->linkPtr;
+
+ placeLinkPtr = NULL;
+ if (argc == 5) {
+ if (Blt_NameToMarker(graphPtr, argv[4], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ placeLinkPtr = markerPtr->linkPtr;
+ }
+ /* Unlink the list link and relink it at the new location */
+ Blt_ChainUnlinkLink(graphPtr->markers.chainPtr, linkPtr);
+
+ if (argv[2][0] == 'a') {
+ Blt_ChainLinkAfter(graphPtr->markers.chainPtr, linkPtr, placeLinkPtr);
+ } else {
+ Blt_ChainLinkBefore(graphPtr->markers.chainPtr, linkPtr, placeLinkPtr);
+ }
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_ChainLink *linkPtr;
+ Extents2D exts;
+ Marker *markerPtr;
+ int mode;
+ int left, right, top, bottom;
+ int enclosed;
+
+#define FIND_ENCLOSED (1<<0)
+#define FIND_OVERLAPPING (1<<1)
+ mode = 0;
+ if (strcmp(argv[3], "enclosed") == 0) {
+ mode = FIND_ENCLOSED;
+ } else if (strcmp(argv[3], "overlapping") == 0) {
+ mode = FIND_OVERLAPPING;
+ } else {
+ Tcl_AppendResult(interp, "bad search type \"", argv[3],
+ ": should be \"enclosed\", or \"overlapping\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if ((Tcl_GetInt(interp, argv[4], &left) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[5], &top) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[6], &right) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[7], &bottom) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (left < right) {
+ exts.left = (double)left;
+ exts.right = (double)right;
+ } else {
+ exts.left = (double)right;
+ exts.right = (double)left;
+ }
+ if (top < bottom) {
+ exts.top = (double)top;
+ exts.bottom = (double)bottom;
+ } else {
+ exts.top = (double)bottom;
+ exts.bottom = (double)top;
+ }
+ enclosed = (mode == FIND_ENCLOSED);
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if (markerPtr->hidden) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->elements.table),
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+ if ((*markerPtr->classPtr->regionProc)(markerPtr, &exts, enclosed)) {
+ Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+ }
+ }
+ Tcl_SetResult(interp, "", TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->markers.table), argv[3]);
+ Blt_SetBooleanResult(interp, (hPtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Returns a symbolic name for the type of the marker whose ID is
+ * given.
+ *
+ * Results:
+ * A standard Tcl result. interp->result will contain the symbolic
+ * type of the marker.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Marker *markerPtr;
+
+ if (Blt_NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, markerPtr->classUid, TCL_STATIC);
+ return TCL_OK;
+}
+
+/* Public routines */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_MarkerOp --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec markerOps[] =
+{
+ {"after", 1, (Blt_Op)RelinkOp, 4, 5, "marker ?afterMarker?",},
+ {"before", 2, (Blt_Op)RelinkOp, 4, 5, "marker ?beforeMarker?",},
+ {"bind", 2, (Blt_Op)BindOp, 3, 6, "marker sequence command",},
+ {"cget", 2, (Blt_Op)CgetOp, 5, 5, "marker option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+ "marker ?marker?... ?option value?...",},
+ {"create", 2, (Blt_Op)CreateOp, 4, 0,
+ "type ?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteOp, 3, 0, "?marker?...",},
+ {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "marker",},
+ {"find", 1, (Blt_Op)FindOp, 8, 8, "enclosed|overlapping x1 y1 x2 y2",},
+ {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+ {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+ {"type", 1, (Blt_Op)TypeOp, 4, 4, "marker",},
+};
+static int nMarkerOps = sizeof(markerOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_MarkerOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nMarkerOps, markerOps, BLT_OP_ARG2, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_MarkersToPostScript --
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_MarkersToPostScript(graphPtr, psToken, under)
+ Graph *graphPtr;
+ PsToken psToken;
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ register Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if ((markerPtr->classPtr->postscriptProc == NULL) ||
+ (markerPtr->nWorldPts == 0)) {
+ continue;
+ }
+ if (markerPtr->drawUnder != under) {
+ continue;
+ }
+ if (markerPtr->hidden) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->elements.table),
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+ Blt_AppendToPostScript(psToken, "\n% Marker \"", markerPtr->name,
+ "\" is a ", markerPtr->classUid, " marker\n", (char *)NULL);
+ (*markerPtr->classPtr->postscriptProc) (markerPtr, psToken);
+ }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_DrawMarkers --
+ *
+ * Calls the individual drawing routines (based on marker type)
+ * for each marker in the display list.
+ *
+ * A marker will not be drawn if
+ *
+ * 1) An element linked to the marker (indicated by elemName)
+ * is currently hidden.
+ *
+ * 2) No coordinates have been specified for the marker.
+ *
+ * 3) The marker is requesting to be drawn at a different level
+ * (above/below the elements) from the current mode.
+ *
+ * 4) The marker is configured as hidden (-hide option).
+ *
+ * 5) The marker isn't visible in the current viewport
+ * (i.e. clipped).
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Markers are drawn into the drawable (pixmap) which will eventually
+ * be displayed in the graph window.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_DrawMarkers(graphPtr, drawable, under)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+
+ if ((markerPtr->nWorldPts == 0) ||
+ (markerPtr->drawUnder != under) ||
+ (markerPtr->hidden) ||
+ (markerPtr->clipped)) {
+ continue;
+ }
+ if (markerPtr->elemName != NULL) {
+ Blt_HashEntry *hPtr;
+
+ /* Look up the named element and see if it's hidden */
+ hPtr = Blt_FindHashEntry(&(graphPtr->elements.table),
+ markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = (Element *)Blt_GetHashValue(hPtr);
+ if (elemPtr->hidden) {
+ continue;
+ }
+ }
+ }
+
+ (*markerPtr->classPtr->drawProc) (markerPtr, drawable);
+ }
+}
+
+void
+Blt_MapMarkers(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if ((markerPtr->nWorldPts == 0) || (markerPtr->hidden)) {
+ continue;
+ }
+ if ((graphPtr->flags & MAP_ALL) || (markerPtr->flags & MAP_ITEM)) {
+ (*markerPtr->classPtr->mapProc) (markerPtr);
+ markerPtr->flags &= ~MAP_ITEM;
+ }
+ }
+}
+
+
+void
+Blt_DestroyMarkers(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Marker *markerPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->markers.table), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ markerPtr = (Marker *)Blt_GetHashValue(hPtr);
+ /*
+ * Dereferencing the pointer to the hash table prevents the
+ * hash table entry from being automatically deleted.
+ */
+ markerPtr->hashPtr = NULL;
+ DestroyMarker(markerPtr);
+ }
+ Blt_DeleteHashTable(&(graphPtr->markers.table));
+ Blt_DeleteHashTable(&(graphPtr->markers.tagTable));
+ Blt_ChainDestroy(graphPtr->markers.chainPtr);
+}
+
+Marker *
+Blt_NearestMarker(graphPtr, x, y, under)
+ Graph *graphPtr;
+ int x, y; /* Screen coordinates */
+ int under;
+{
+ Blt_ChainLink *linkPtr;
+ Marker *markerPtr;
+ Point2D point;
+
+ point.x = (double)x;
+ point.y = (double)y;
+ for (linkPtr = Blt_ChainLastLink(graphPtr->markers.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ markerPtr = Blt_ChainGetValue(linkPtr);
+ if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) &&
+ (!markerPtr->hidden)) {
+ if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
+ return markerPtr;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/blt/src/bltGrMisc.c b/blt/src/bltGrMisc.c
new file mode 100644
index 00000000000..d4eb9d09a9c
--- /dev/null
+++ b/blt/src/bltGrMisc.c
@@ -0,0 +1,1372 @@
+
+/*
+ * bltGrMisc.c --
+ *
+ * This module implements miscellaneous routines for the BLT
+ * graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+
+static Tk_OptionParseProc StringToPoint;
+static Tk_OptionPrintProc PointToString;
+static Tk_OptionParseProc StringToColorPair;
+static Tk_OptionPrintProc ColorPairToString;
+Tk_CustomOption bltPointOption =
+{
+ StringToPoint, PointToString, (ClientData)0
+};
+Tk_CustomOption bltColorPairOption =
+{
+ StringToColorPair, ColorPairToString, (ClientData)0
+};
+
+/* ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetXY --
+ *
+ * Converts a string in the form "@x,y" into an XPoint structure
+ * of the x and y coordinates.
+ *
+ * Results:
+ * A standard Tcl result. If the string represents a valid position
+ * *pointPtr* will contain the converted x and y coordinates and
+ * TCL_OK is returned. Otherwise, TCL_ERROR is returned and
+ * interp->result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetXY(interp, tkwin, string, xPtr, yPtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *string;
+ int *xPtr, *yPtr;
+{
+ char *comma;
+ int result;
+ int x, y;
+
+ if ((string == NULL) || (*string == '\0')) {
+ *xPtr = *yPtr = -SHRT_MAX;
+ return TCL_OK;
+ }
+ if (*string != '@') {
+ goto badFormat;
+ }
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ goto badFormat;
+ }
+ *comma = '\0';
+ result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
+ (Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *xPtr = x, *yPtr = y;
+ return TCL_OK;
+
+ badFormat:
+ Tcl_AppendResult(interp, "bad position \"", string,
+ "\": should be \"@x,y\"", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPoint --
+ *
+ * Convert the string representation of a legend XY position into
+ * window coordinates. The form of the string must be "@x,y" or
+ * none.
+ *
+ * Results:
+ * A standard Tcl result. The symbol type is written into the
+ * widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPoint(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+ int x, y;
+
+ if (Blt_GetXY(interp, tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ pointPtr->x = x, pointPtr->y = y;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PointToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PointToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of XPoint in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ char *result;
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+
+ result = "";
+ if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
+ char string[200];
+
+ sprintf(string, "@%d,%d", pointPtr->x, pointPtr->y);
+ result = Blt_Strdup(string);
+ assert(result);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ }
+ return result;
+}
+
+/*LINTLIBRARY*/
+static int
+GetColorPair(interp, tkwin, fgStr, bgStr, pairPtr, allowDefault)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *fgStr, *bgStr;
+ ColorPair *pairPtr;
+ int allowDefault;
+{
+ unsigned int length;
+ XColor *fgColor, *bgColor;
+
+ fgColor = bgColor = NULL;
+ length = strlen(fgStr);
+ if (fgStr[0] == '\0') {
+ fgColor = NULL;
+ } else if ((allowDefault) && (fgStr[0] == 'd') &&
+ (strncmp(fgStr, "defcolor", length) == 0)) {
+ fgColor = COLOR_DEFAULT;
+ } else {
+ fgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(fgStr));
+ if (fgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ length = strlen(bgStr);
+ if (bgStr[0] == '\0') {
+ bgColor = NULL;
+ } else if ((allowDefault) && (bgStr[0] == 'd') &&
+ (strncmp(bgStr, "defcolor", length) == 0)) {
+ bgColor = COLOR_DEFAULT;
+ } else {
+ bgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(bgStr));
+ if (bgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ pairPtr->fgColor = fgColor;
+ pairPtr->bgColor = bgColor;
+ return TCL_OK;
+}
+
+void
+Blt_FreeColorPair(pairPtr)
+ ColorPair *pairPtr;
+{
+ if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->bgColor);
+ }
+ if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->fgColor);
+ }
+ pairPtr->bgColor = pairPtr->fgColor = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorPair --
+ *
+ * Convert the color names into pair of XColor pointers.
+ *
+ * Results:
+ * A standard Tcl result. The color pointer is written into the
+ * widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorPair(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing color */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of color field in record */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ ColorPair sample;
+ int allowDefault = (int)clientData;
+
+ sample.fgColor = sample.bgColor = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ int nColors;
+ char **colors;
+ int result;
+
+ if (Tcl_SplitList(interp, string, &nColors, &colors) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+ switch (nColors) {
+ case 0:
+ result = TCL_OK;
+ break;
+ case 1:
+ result = GetColorPair(interp, tkwin, colors[0], "", &sample,
+ allowDefault);
+ break;
+ case 2:
+ result = GetColorPair(interp, tkwin, colors[0], colors[1],
+ &sample, allowDefault);
+ break;
+ default:
+ result = TCL_ERROR;
+ Tcl_AppendResult(interp, "too many names in colors list",
+ (char *)NULL);
+ }
+ Blt_Free(colors);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ Blt_FreeColorPair(pairPtr);
+ *pairPtr = sample;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ * Convert the color option value into a string.
+ *
+ * Results:
+ * The static string representing the color option is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColor(colorPtr)
+ XColor *colorPtr;
+{
+ if (colorPtr == NULL) {
+ return "";
+ } else if (colorPtr == COLOR_DEFAULT) {
+ return "defcolor";
+ } else {
+ return Tk_NameOfColor(colorPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorPairToString --
+ *
+ * Convert the color pairs into color names.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorPairToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ Tcl_DString dString;
+ char *result;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->fgColor));
+ Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->bgColor));
+ result = Tcl_DStringValue(&dString);
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+int
+Blt_PointInSegments(samplePtr, segments, nSegments, halo)
+ Point2D *samplePtr;
+ Segment2D *segments;
+ int nSegments;
+ double halo;
+{
+ register Segment2D *segPtr, *endPtr;
+ double left, right, top, bottom;
+ Point2D p, t;
+ double dist, minDist;
+
+ minDist = DBL_MAX;
+ for (segPtr = segments, endPtr = segments + nSegments; segPtr < endPtr;
+ segPtr++) {
+ t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y,
+ &segPtr->p, &segPtr->q);
+ if (segPtr->p.x > segPtr->q.x) {
+ right = segPtr->p.x, left = segPtr->q.x;
+ } else {
+ right = segPtr->q.x, left = segPtr->p.x;
+ }
+ if (segPtr->p.y > segPtr->q.y) {
+ bottom = segPtr->p.y, top = segPtr->q.y;
+ } else {
+ bottom = segPtr->q.y, top = segPtr->p.y;
+ }
+ p.x = BOUND(t.x, left, right);
+ p.y = BOUND(t.y, top, bottom);
+ dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
+ if (dist < minDist) {
+ minDist = dist;
+ }
+ }
+ return (minDist < halo);
+}
+
+int
+Blt_PointInPolygon(samplePtr, points, nPoints)
+ Point2D *samplePtr;
+ Point2D *points;
+ int nPoints;
+{
+ double b;
+ register Point2D *p, *q, *endPtr;
+ register int count;
+
+ count = 0;
+ for (p = points, q = p + 1, endPtr = p + nPoints; q < endPtr; p++, q++) {
+ if (((p->y <= samplePtr->y) && (samplePtr->y < q->y)) ||
+ ((q->y <= samplePtr->y) && (samplePtr->y < p->y))) {
+ b = (q->x - p->x) * (samplePtr->y - p->y) / (q->y - p->y) + p->x;
+ if (samplePtr->x < b) {
+ count++; /* Count the number of intersections. */
+ }
+ }
+ }
+ return (count & 0x01);
+}
+
+int
+Blt_RegionInPolygon(extsPtr, points, nPoints, enclosed)
+ Extents2D *extsPtr;
+ Point2D *points;
+ int nPoints;
+ int enclosed;
+{
+ register Point2D *pointPtr, *endPtr;
+
+ if (enclosed) {
+ /*
+ * All points of the polygon must be inside the rectangle.
+ */
+ for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
+ pointPtr++) {
+ if ((pointPtr->x < extsPtr->left) ||
+ (pointPtr->x > extsPtr->right) ||
+ (pointPtr->y < extsPtr->top) ||
+ (pointPtr->y > extsPtr->bottom)) {
+ return FALSE; /* One point is exterior. */
+ }
+ }
+ return TRUE;
+ } else {
+ Point2D p, q;
+
+ /*
+ * If any segment of the polygon clips the bounding region, the
+ * polygon overlaps the rectangle.
+ */
+ points[nPoints] = points[0];
+ for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
+ pointPtr++) {
+ p = *pointPtr;
+ q = *(pointPtr + 1);
+ if (Blt_LineRectClip(extsPtr, &p, &q)) {
+ return TRUE;
+ }
+ }
+ /*
+ * Otherwise the polygon and rectangle are either disjoint
+ * or enclosed. Check if one corner of the rectangle is
+ * inside the polygon.
+ */
+ p.x = extsPtr->left;
+ p.y = extsPtr->top;
+ return Blt_PointInPolygon(&p, points, nPoints);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GraphExtents --
+ *
+ * Generates a bounding box representing the plotting area of
+ * the graph. This data structure is used to clip the points and
+ * line segments of the line element.
+ *
+ * The clip region is the plotting area plus such arbitrary extra
+ * space. The reason we clip with a bounding box larger than the
+ * plot area is so that symbols will be drawn even if their center
+ * point isn't in the plotting area.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The bounding box is filled with the dimensions of the plotting
+ * area.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GraphExtents(graphPtr, extsPtr)
+ Graph *graphPtr;
+ Extents2D *extsPtr;
+{
+ extsPtr->left = (double)(graphPtr->hOffset - graphPtr->padX.side1);
+ extsPtr->top = (double)(graphPtr->vOffset - graphPtr->padY.side1);
+ extsPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange +
+ graphPtr->padX.side2);
+ extsPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange +
+ graphPtr->padY.side2);
+}
+
+static int
+ClipTest (double ds, double dr, double *t1, double *t2)
+{
+ double t;
+
+ if (ds < 0.0) {
+ t = dr / ds;
+ if (t > *t2) {
+ return FALSE;
+ }
+ if (t > *t1) {
+ *t1 = t;
+ }
+ } else if (ds > 0.0) {
+ t = dr / ds;
+ if (t < *t1) {
+ return FALSE;
+ }
+ if (t < *t2) {
+ *t2 = t;
+ }
+ } else {
+ /* d = 0, so line is parallel to this clipping edge */
+ if (dr < 0.0) { /* Line is outside clipping edge */
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineRectClip --
+ *
+ * Clips the given line segment to a rectangular region. The
+ * coordinates of the clipped line segment are returned. The
+ * original coordinates are overwritten.
+ *
+ * Reference: Liang-Barsky Line Clipping Algorithm.
+ *
+ * Results:
+ * Returns if line segment is visible within the region. The
+ * coordinates of the original line segment are overwritten
+ * by the clipped coordinates.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_LineRectClip(extsPtr, p, q)
+ Extents2D *extsPtr; /* Rectangular region to clip. */
+ Point2D *p, *q; /* (in/out) Coordinates of original
+ * and clipped line segment. */
+{
+ double t1, t2;
+ double dx, dy;
+
+ t1 = 0.0;
+ t2 = 1.0;
+ dx = q->x - p->x;
+ if ((ClipTest (-dx, p->x - extsPtr->left, &t1, &t2)) &&
+ (ClipTest (dx, extsPtr->right - p->x, &t1, &t2))) {
+ dy = q->y - p->y;
+ if ((ClipTest (-dy, p->y - extsPtr->top, &t1, &t2)) &&
+ (ClipTest (dy, extsPtr->bottom - p->y, &t1, &t2))) {
+ if (t2 < 1.0) {
+ q->x = p->x + t2 * dx;
+ q->y = p->y + t2 * dy;
+ }
+ if (t1 > 0.0) {
+ p->x += t1 * dx;
+ p->y += t1 * dy;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PolyRectClip --
+ *
+ * Clips the given polygon to a rectangular region. The resulting
+ * polygon is returned. Note that the resulting polyon may be
+ * complex, connected by zero width/height segments. The drawing
+ * routine (such as XFillPolygon) will not draw a connecting
+ * segment.
+ *
+ * Reference: Liang-Barsky Polygon Clipping Algorithm
+ *
+ * Results:
+ * Returns the number of points in the clipped polygon. The
+ * points of the clipped polygon are stored in *outputPts*.
+ *
+ *----------------------------------------------------------------------
+ */
+#define EPSILON FLT_EPSILON
+#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++
+
+int
+Blt_PolyRectClip(extsPtr, points, nPoints, clipPts)
+ Extents2D *extsPtr;
+ Point2D *points;
+ int nPoints;
+ Point2D *clipPts;
+{
+ Point2D *endPtr;
+ double dx, dy;
+ double tin1, tin2;
+ double tinx, tiny;
+ double xin, yin, xout, yout;
+ int count;
+ register Point2D *p; /* First vertex of input polygon edge. */
+ register Point2D *q; /* Last vertex of input polygon edge. */
+ register Point2D *r;
+
+ r = clipPts;
+ count = 0; /* Counts # of vertices in output polygon. */
+
+ points[nPoints] = points[0];
+
+ for (p = points, q = p + 1, endPtr = p + nPoints; p < endPtr; p++, q++) {
+ dx = q->x - p->x; /* X-direction */
+ dy = q->y - p->y; /* Y-direction */
+
+ if (FABS(dx) < EPSILON) {
+ dx = (p->x > extsPtr->left) ? -EPSILON : EPSILON ;
+ }
+ if (FABS(dy) < EPSILON) {
+ dy = (p->y > extsPtr->top) ? -EPSILON : EPSILON ;
+ }
+
+ if (dx > 0.0) { /* Left */
+ xin = extsPtr->left;
+ xout = extsPtr->right + 1.0;
+ } else { /* Right */
+ xin = extsPtr->right + 1.0;
+ xout = extsPtr->left;
+ }
+ if (dy > 0.0) { /* Top */
+ yin = extsPtr->top;
+ yout = extsPtr->bottom + 1.0;
+ } else { /* Bottom */
+ yin = extsPtr->bottom + 1.0;
+ yout = extsPtr->top;
+ }
+
+ tinx = (xin - p->x) / dx;
+ tiny = (yin - p->y) / dy;
+
+ if (tinx < tiny) { /* Hits x first */
+ tin1 = tinx;
+ tin2 = tiny;
+ } else { /* Hits y first */
+ tin1 = tiny;
+ tin2 = tinx;
+ }
+
+ if (tin1 <= 1.0) {
+ if (tin1 > 0.0) {
+ AddVertex(xin, yin);
+ }
+ if (tin2 <= 1.0) {
+ double toutx, touty, tout1;
+
+ toutx = (xout - p->x) / dx;
+ touty = (yout - p->y) / dy;
+ tout1 = MIN(toutx, touty);
+
+ if ((tin2 > 0.0) || (tout1 > 0.0)) {
+ if (tin2 <= tout1) {
+ if (tin2 > 0.0) {
+ if (tinx > tiny) {
+ AddVertex(xin, p->y + tinx * dy);
+ } else {
+ AddVertex(p->x + tiny * dx, yin);
+ }
+ }
+ if (tout1 < 1.0) {
+ if (toutx < touty) {
+ AddVertex(xout, p->y + toutx * dy);
+ } else {
+ AddVertex(p->x + touty * dx, yout);
+ }
+ } else {
+ AddVertex(q->x, q->y);
+ }
+ } else {
+ if (tinx > tiny) {
+ AddVertex(xin, yout);
+ } else {
+ AddVertex(xout, yin);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (count > 0) {
+ AddVertex(clipPts[0].x, clipPts[0].y);
+ }
+ return count;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetProjection --
+ *
+ * Computes the projection of a point on a line. The line (given
+ * by two points), is assumed the be infinite.
+ *
+ * Compute the slope (angle) of the line and rotate it 90 degrees.
+ * Using the slope-intercept method (we know the second line from
+ * the sample test point and the computed slope), then find the
+ * intersection of both lines. This will be the projection of the
+ * sample point on the first line.
+ *
+ * Results:
+ * Returns the coordinates of the projection on the line.
+ *
+ *----------------------------------------------------------------------
+ */
+Point2D
+Blt_GetProjection(x, y, p, q)
+ int x, y; /* Screen coordinates of the sample point. */
+ Point2D *p, *q; /* Line segment to project point onto */
+{
+ double dx, dy;
+ Point2D t;
+
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+
+ /* Test for horizontal and vertical lines */
+ if (FABS(dx) < DBL_EPSILON) {
+ t.x = p->x, t.y = (double)y;
+ } else if (FABS(dy) < DBL_EPSILON) {
+ t.x = (double)x, t.y = p->y;
+ } else {
+ double m1, m2; /* Slope of both lines */
+ double b1, b2; /* y-intercepts */
+ double midX, midY; /* Midpoint of line segment. */
+ double ax, ay, bx, by;
+
+ /* Compute the slop and intercept of the line segment. */
+ m1 = (dy / dx);
+ b1 = p->y - (p->x * m1);
+
+ /*
+ * Compute the slope and intercept of a second line segment:
+ * one that intersects through sample X-Y coordinate with a
+ * slope perpendicular to original line.
+ */
+
+ /* Find midpoint of original segment. */
+ midX = (p->x + q->x) * 0.5;
+ midY = (p->y + q->y) * 0.5;
+
+ /* Rotate the line 90 degrees */
+ ax = midX - (0.5 * dy);
+ ay = midY - (0.5 * -dx);
+ bx = midX + (0.5 * dy);
+ by = midY + (0.5 * -dx);
+
+ m2 = (ay - by) / (ax - bx);
+ b2 = y - (x * m2);
+
+ /*
+ * Given the equations of two lines which contain the same point,
+ *
+ * y = m1 * x + b1
+ * y = m2 * x + b2
+ *
+ * solve for the intersection.
+ *
+ * x = (b2 - b1) / (m1 - m2)
+ * y = m1 * x + b1
+ *
+ */
+
+ t.x = (b2 - b1) / (m1 - m2);
+ t.y = m1 * t.x + b1;
+ }
+ return t;
+}
+
+
+#define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
+ (c)->green = (int)((g) * 65535.0), \
+ (c)->blue = (int)((b) * 65535.0))
+
+void
+Blt_HSV(colorPtr, huePtr, valPtr, satPtr)
+ XColor *colorPtr;
+ double *huePtr, *valPtr, *satPtr;
+{
+ unsigned short iMax, iMin;
+ double range;
+ unsigned short *colorValues;
+ register int i;
+ double hue, sat, val;
+
+ /* Find the minimum and maximum RGB intensities */
+ colorValues = (unsigned short *)&colorPtr->red;
+ iMax = iMin = colorValues[0];
+ for (i = 1; i < 3; i++) {
+ if (iMax < colorValues[i]) {
+ iMax = colorValues[i];
+ } else if (iMin > colorValues[i]) {
+ iMin = colorValues[i];
+ }
+ }
+
+ val = (double)iMax / 65535.0;
+ hue = 0.0, sat = 0.0;
+
+ range = (double)iMax - (double)iMin;
+ if (iMax != iMin) {
+ sat = range / (double)iMax;
+ }
+ if (sat > 0.0) {
+ double r, g, b;
+
+ /* Normalize the RGB values */
+ r = ((double)iMax - (double)colorPtr->red) / range;
+ g = ((double)iMax - (double)colorPtr->green) / range;
+ b = ((double)iMax - (double)colorPtr->blue) / range;
+
+ if (colorPtr->red == iMax) {
+ hue = (double)(b - g);
+ } else if (colorPtr->green == iMax) {
+ hue = (double)(2 + (r - b));
+ } else if (colorPtr->blue == iMax) {
+ hue = (double)(4 + (g - r));
+ }
+ hue *= 60.0;
+ } else {
+ sat = 0.5;
+ }
+ if (hue < 0.0) {
+ hue += 360.0;
+ }
+ *huePtr = hue;
+ *valPtr = val;
+ *satPtr = sat;
+}
+
+void
+Blt_RGB(hue, sat, val, colorPtr)
+ double hue, sat, val;
+ XColor *colorPtr;
+{
+ double p, q, t;
+ double frac;
+ int ihue;
+
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > 1.0) {
+ val = 1.0;
+ }
+ if (sat == 0.0) {
+ SetColor(colorPtr, val, val, val);
+ return;
+ }
+ hue = FMOD(hue, 360.0) / 60.0;
+ ihue = (int)floor(hue);
+ frac = hue - ihue;
+ p = val * (1 - sat);
+ q = val * (1 - (sat * frac));
+ t = val * (1 - (sat * (1 - frac)));
+
+ switch (ihue) {
+ case 0:
+ SetColor(colorPtr, val, t, p);
+ break;
+ case 1:
+ SetColor(colorPtr, q, val, p);
+ break;
+ case 2:
+ SetColor(colorPtr, p, val, t);
+ break;
+ case 3:
+ SetColor(colorPtr, p, q, val);
+ break;
+ case 4:
+ SetColor(colorPtr, t, p, val);
+ break;
+ case 5:
+ SetColor(colorPtr, val, p, q);
+ break;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_AdjustViewport --
+ *
+ * Adjusts the offsets of the viewport according to the scroll mode.
+ * This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ * "canvas" The viewport scrolls within the range of world
+ * coordinates. This way the viewport always displays
+ * a full page of the world. If the world is smaller
+ * than the viewport, then (bizarrely) the world and
+ * viewport are inverted so that the world moves up
+ * and down within the viewport.
+ *
+ * "listbox" The viewport can scroll beyond the range of world
+ * coordinates. Every entry can be displayed at the
+ * top of the viewport. This also means that the
+ * scrollbar thumb weirdly shrinks as the last entry
+ * is scrolled upward.
+ *
+ * Results:
+ * The corrected offset is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode)
+ int offset, worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ switch (scrollMode) {
+ case BLT_SCROLL_MODE_CANVAS:
+
+ /*
+ * Canvas-style scrolling allows the world to be scrolled
+ * within the window.
+ */
+
+ if (worldSize < windowSize) {
+ if ((worldSize - offset) > windowSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset > 0) {
+ offset = 0;
+ }
+ } else {
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ }
+ break;
+
+ case BLT_SCROLL_MODE_LISTBOX:
+ if (offset < 0) {
+ offset = 0;
+ }
+ if (offset >= worldSize) {
+ offset = worldSize - scrollUnits;
+ }
+ break;
+
+ case BLT_SCROLL_MODE_HIERBOX:
+
+ /*
+ * Hierbox-style scrolling allows the world to be scrolled
+ * within the window.
+ */
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ break;
+ }
+ return offset;
+}
+
+int
+Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize,
+ scrollUnits, scrollMode)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ int *offsetPtr;
+ int worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ char c;
+ unsigned int length;
+ int offset;
+ int count;
+ double fract;
+
+ offset = *offsetPtr;
+ c = argv[0][0];
+ length = strlen(argv[0]);
+ if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
+ if (argc != 3) {
+ return TCL_ERROR;
+ }
+ /* scroll number unit/page */
+ if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
+ fract = (double)count *scrollUnits;
+ } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count *windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += (int)fract;
+ } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
+ if (argc != 2) {
+ return TCL_ERROR;
+ }
+ /* moveto fraction */
+ if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = (int)(worldSize * fract);
+ } else {
+ /* Treat like "scroll units" */
+ if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count *scrollUnits;
+ offset += (int)fract;
+ return TCL_OK;
+ }
+ *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+ scrollMode);
+ return TCL_OK;
+}
+
+#if (TCL_MAJOR_VERSION >= 8)
+int
+Blt_GetScrollInfoFromObj(interp, objc, objv, offsetPtr, worldSize, windowSize,
+ scrollUnits, scrollMode)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+ int *offsetPtr;
+ int worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ char c;
+ unsigned int length;
+ int offset;
+ int count;
+ double fract;
+ char *string;
+
+ offset = *offsetPtr;
+
+ string = Tcl_GetString(objv[0]);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
+ if (objc != 3) {
+ return TCL_ERROR;
+ }
+ /* scroll number unit/page */
+ if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[2]);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
+ fract = (double)count *scrollUnits;
+ } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count *windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"",
+ Tcl_GetString(objv[2]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += (int)fract;
+ } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
+ if (objc != 2) {
+ return TCL_ERROR;
+ }
+ /* moveto fraction */
+ if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = (int)(worldSize * fract);
+ } else {
+ /* Treat like "scroll units" */
+ if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count *scrollUnits;
+ offset += (int)fract;
+ return TCL_OK;
+ }
+ *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+ scrollMode);
+ return TCL_OK;
+}
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_UpdateScrollbar --
+ *
+ * Invoke a Tcl command to the scrollbar, defining the new
+ * position and length of the scroll. See the Tk documentation
+ * for further information on the scrollbar. It is assumed the
+ * scrollbar command prefix is valid.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Scrollbar is commanded to change position and/or size.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract)
+ Tcl_Interp *interp;
+ char *scrollCmd; /* scrollbar command */
+ double firstFract, lastFract;
+{
+ char string[200];
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, scrollCmd, -1);
+ sprintf(string, " %f %f", firstFract, lastFract);
+ Tcl_DStringAppend(&dString, string, -1);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&dString);
+}
+
+/* -------------- */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGCFromDrawable --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget.
+ * This is needed because the certain GC parameters (like dashes)
+ * can not be set via XCreateGC, therefore there is no way for
+ * Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr)
+ Display *display;
+ Drawable drawable;
+ unsigned long gcMask;
+ XGCValues *valuePtr;
+{
+ GC newGC;
+
+#ifdef WIN32
+ newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
+#else
+ newGC = XCreateGC(display, drawable, gcMask, valuePtr);
+#endif
+ return newGC;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGC --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget.
+ * This is needed because the certain GC parameters (like dashes)
+ * can not be set via XCreateGC, therefore there is no way for
+ * Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGC(tkwin, gcMask, valuePtr)
+ Tk_Window tkwin;
+ unsigned long gcMask;
+ XGCValues *valuePtr;
+{
+ GC gc;
+ Pixmap pixmap;
+ Drawable drawable;
+ Display *display;
+
+ pixmap = None;
+ drawable = Tk_WindowId(tkwin);
+ display = Tk_Display(tkwin);
+
+ if (drawable == None) {
+ Drawable root;
+ int depth;
+
+ root = RootWindow(display, Tk_ScreenNumber(tkwin));
+ depth = Tk_Depth(tkwin);
+
+ if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
+ drawable = root;
+ } else {
+ pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
+ drawable = pixmap;
+ }
+ }
+ gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
+ if (pixmap != None) {
+ Tk_FreePixmap(display, pixmap);
+ }
+ return gc;
+}
+
+void
+Blt_FreePrivateGC(display, gc)
+ Display *display;
+ GC gc;
+{
+ Tk_FreeXId(display, (XID) XGContextFromGC(gc));
+ XFreeGC(display, gc);
+}
+
+#ifndef WIN32
+void
+Blt_SetDashes(display, gc, dashesPtr)
+ Display *display;
+ GC gc;
+ Blt_Dashes *dashesPtr;
+{
+ XSetDashes(display, gc, dashesPtr->offset,
+ (CONST char *)dashesPtr->values, strlen((char *)dashesPtr->values));
+}
+#endif
+
+
+static double
+FindSplit(points, i, j, split)
+ Point2D points[];
+ int i, j; /* Indices specifying the range of points. */
+ int *split; /* (out) Index of next split. */
+{
+ double maxDist;
+
+ maxDist = -1.0;
+ if ((i + 1) < j) {
+ register int k;
+ double a, b, c;
+ double sqDist;
+
+ /*
+ *
+ * sqDist P(k) = | 1 P(i).x P(i).y |
+ * | 1 P(j).x P(j).y |
+ * | 1 P(k).x P(k).y |
+ * ---------------------------
+ * (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
+ */
+
+ a = points[i].y - points[j].y;
+ b = points[j].x - points[i].x;
+ c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
+ for (k = (i + 1); k < j; k++) {
+ sqDist = (points[k].x * a) + (points[k].y * b) + c;
+ if (sqDist < 0.0) {
+ sqDist = -sqDist;
+ }
+ if (sqDist > maxDist) {
+ maxDist = sqDist; /* Track the maximum. */
+ *split = k;
+ }
+ }
+ /* Correction for segment length---should be redone if can == 0 */
+ maxDist *= maxDist / (a * a + b * b);
+ }
+ return maxDist;
+}
+
+
+/* Douglas-Peucker line simplification algorithm */
+int
+Blt_SimplifyLine(inputPts, low, high, tolerance, indices)
+ Point2D inputPts[];
+ int low, high;
+ double tolerance;
+ int indices[];
+{
+#define StackPush(a) s++, stack[s] = (a)
+#define StackPop(a) (a) = stack[s], s--
+#define StackEmpty() (s < 0)
+#define StackTop() stack[s]
+ int *stack;
+ int split = -1;
+ double sqDist, sqTolerance;
+ int s = -1; /* Points to top stack item. */
+ int count;
+
+ stack = Blt_Malloc(sizeof(int) * (high - low + 1));
+ StackPush(high);
+ count = 0;
+ indices[count++] = 0;
+ sqTolerance = tolerance * tolerance;
+ while (!StackEmpty()) {
+ sqDist = FindSplit(inputPts, low, StackTop(), &split);
+ if (sqDist > sqTolerance) {
+ StackPush(split);
+ } else {
+ indices[count++] = StackTop();
+ StackPop(low);
+ }
+ }
+ Blt_Free(stack);
+ return count;
+}
+
+void
+Blt_DrawSegments2D(display, drawable, gc, segPtr, nSegments)
+ Display *display;
+ Drawable drawable;
+ GC gc;
+ register Segment2D *segPtr;
+ int nSegments;
+{
+ XSegment *xSegPtr, *xSegArr;
+ Segment2D *endPtr;
+
+ xSegArr = Blt_Malloc(nSegments * sizeof(XSegment));
+ if (xSegArr == NULL) {
+ return;
+ }
+ xSegPtr = xSegArr;
+ for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
+ xSegPtr->x1 = (short int)segPtr->p.x;
+ xSegPtr->y1 = (short int)segPtr->p.y;
+ xSegPtr->x2 = (short int)segPtr->q.x;
+ xSegPtr->y2 = (short int)segPtr->q.y;
+ xSegPtr++;
+ }
+ XDrawSegments(display, drawable, gc, xSegArr, nSegments);
+ Blt_Free(xSegArr);
+}
+
diff --git a/blt/src/bltGrPen.c b/blt/src/bltGrPen.c
new file mode 100644
index 00000000000..3a08ecfe662
--- /dev/null
+++ b/blt/src/bltGrPen.c
@@ -0,0 +1,700 @@
+
+/*
+ * bltGrPen.c --
+ *
+ * This module implements pens for the BLT graph widget.
+ *
+ * Copyright 1996-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+static Tk_OptionParseProc StringToColor;
+static Tk_OptionPrintProc ColorToString;
+static Tk_OptionParseProc StringToPen;
+static Tk_OptionPrintProc PenToString;
+Tk_CustomOption bltColorOption =
+{
+ StringToColor, ColorToString, (ClientData)0
+};
+Tk_CustomOption bltPenOption =
+{
+ StringToPen, PenToString, (ClientData)0
+};
+Tk_CustomOption bltBarPenOption =
+{
+ StringToPen, PenToString, (ClientData)&bltBarElementUid
+};
+Tk_CustomOption bltLinePenOption =
+{
+ StringToPen, PenToString, (ClientData)&bltLineElementUid
+};
+
+/*
+ *----------------------------------------------------------------------
+
+ * StringToColor --
+ *
+ * Convert the string representation of a color into a XColor pointer.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The color pointer is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColor(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing color */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of color field in record */
+{
+ XColor **colorPtrPtr = (XColor **)(widgRec + offset);
+ XColor *colorPtr;
+ unsigned int length;
+ char c;
+
+ if ((string == NULL) || (*string == '\0')) {
+ *colorPtrPtr = NULL;
+ return TCL_OK;
+ }
+ c = string[0];
+ length = strlen(string);
+
+ if ((c == 'd') && (strncmp(string, "defcolor", length) == 0)) {
+ colorPtr = COLOR_DEFAULT;
+ } else {
+ colorPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(string));
+ if (colorPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ *colorPtrPtr = colorPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ * Convert the color option value into a string.
+ *
+ * Results:
+ * The static string representing the color option is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColor(colorPtr)
+ XColor *colorPtr;
+{
+ if (colorPtr == NULL) {
+ return "";
+ } else if (colorPtr == COLOR_DEFAULT) {
+ return "defcolor";
+ } else {
+ return Tk_NameOfColor(colorPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorToString --
+ *
+ * Convert the color value into a string.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget information record */
+ int offset; /* Offset of symbol type in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ XColor *colorPtr = *(XColor **)(widgRec + offset);
+
+ return NameOfColor(colorPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPen --
+ *
+ * Convert the color value into a string.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPen(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing pen */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of pen field in record */
+{
+ Tk_Uid classUid = *(Tk_Uid *)clientData; /* Element type. */
+ Pen **penPtrPtr = (Pen **)(widgRec + offset);
+ Pen *penPtr;
+ Graph *graphPtr;
+
+ penPtr = NULL;
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+
+ if (classUid == NULL) {
+ classUid = graphPtr->classUid;
+ }
+ if ((string != NULL) && (string[0] != '\0')) {
+ if (Blt_GetPen(graphPtr, string, classUid, &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ /* Release the old pen */
+ if (*penPtrPtr != NULL) {
+ Blt_FreePen(graphPtr, *penPtrPtr);
+ }
+ *penPtrPtr = penPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PenToString --
+ *
+ * Parse the name of the name.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PenToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget information record */
+ int offset; /* Offset of pen in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Pen *penPtr = *(Pen **)(widgRec + offset);
+
+ return penPtr->name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameToPen --
+ *
+ * Find and return the pen style from a given name.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pen *
+NameToPen(graphPtr, name)
+ Graph *graphPtr;
+ char *name;
+{
+ Blt_HashEntry *hPtr;
+ Pen *penPtr;
+
+ hPtr = Blt_FindHashEntry(&(graphPtr->penTable), name);
+ if (hPtr == NULL) {
+ notFound:
+ Tcl_AppendResult(graphPtr->interp, "can't find pen \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ return NULL;
+ }
+ penPtr = (Pen *)Blt_GetHashValue(hPtr);
+ if (penPtr->flags & PEN_DELETE_PENDING) {
+ goto notFound;
+ }
+ return penPtr;
+}
+
+static void
+DestroyPen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ Tk_FreeOptions(penPtr->configSpecs, (char *)penPtr, graphPtr->display, 0);
+ (*penPtr->destroyProc) (graphPtr, penPtr);
+ if ((penPtr->name != NULL) && (penPtr->name[0] != '\0')) {
+ Blt_Free(penPtr->name);
+ }
+ if (penPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(graphPtr->penTable), penPtr->hashPtr);
+ }
+ Blt_Free(penPtr);
+}
+
+void
+Blt_FreePen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ penPtr->refCount--;
+ if ((penPtr->refCount == 0) && (penPtr->flags & PEN_DELETE_PENDING)) {
+ DestroyPen(graphPtr, penPtr);
+ }
+}
+
+Pen *
+Blt_CreatePen(graphPtr, penName, classUid, nOpts, options)
+ Graph *graphPtr;
+ char *penName;
+ Tk_Uid classUid;
+ int nOpts;
+ char **options;
+{
+
+ Pen *penPtr;
+ Blt_HashEntry *hPtr;
+ unsigned int length, configFlags;
+ int isNew;
+ register int i;
+
+ /*
+ * Scan the option list for a "-type" entry. This will indicate
+ * what type of pen we are creating. Otherwise we'll default to the
+ * suggested type. Last -type option wins.
+ */
+ for (i = 0; i < nOpts; i += 2) {
+ length = strlen(options[i]);
+ if ((length > 2) && (strncmp(options[i], "-type", length) == 0)) {
+ char *arg;
+
+ arg = options[i + 1];
+ if (strcmp(arg, "bar") == 0) {
+ classUid = bltBarElementUid;
+ } else if (strcmp(arg, "line") != 0) {
+ classUid = bltLineElementUid;
+ } else if (strcmp(arg, "strip") != 0) {
+ classUid = bltStripElementUid;
+ } else {
+ Tcl_AppendResult(graphPtr->interp, "unknown pen type \"",
+ arg, "\" specified", (char *)NULL);
+ return NULL;
+ }
+ }
+ }
+ hPtr = Blt_CreateHashEntry(&(graphPtr->penTable), penName, &isNew);
+ if (!isNew) {
+ penPtr = (Pen *)Blt_GetHashValue(hPtr);
+ if (!(penPtr->flags & PEN_DELETE_PENDING)) {
+ Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+ "\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ if (penPtr->classUid != classUid) {
+ Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+ "\" in-use: can't change pen type from \"", penPtr->classUid,
+ "\" to \"", classUid, "\"", (char *)NULL);
+ return NULL;
+ }
+ penPtr->flags &= ~PEN_DELETE_PENDING;
+ } else {
+ if (classUid == bltBarElementUid) {
+ penPtr = Blt_BarPen(penName);
+ } else {
+ penPtr = Blt_LinePen(penName);
+ }
+ penPtr->classUid = classUid;
+ penPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, penPtr);
+ }
+
+ configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ penPtr->name, "Pen", penPtr->configSpecs, nOpts, options,
+ (char *)penPtr, configFlags) != TCL_OK) {
+ if (isNew) {
+ DestroyPen(graphPtr, penPtr);
+ }
+ return NULL;
+ }
+ (*penPtr->configProc) (graphPtr, penPtr);
+ return penPtr;
+}
+
+int
+Blt_GetPen(graphPtr, name, classUid, penPtrPtr)
+ Graph *graphPtr;
+ char *name;
+ Tk_Uid classUid;
+ Pen **penPtrPtr;
+{
+ Pen *penPtr;
+
+ penPtr = NameToPen(graphPtr, name);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (penPtr->classUid != classUid) {
+ Tcl_AppendResult(graphPtr->interp, "pen \"", name,
+ "\" is the wrong type (is \"", penPtr->classUid, "\"",
+ ", wanted \"", classUid, "\")", (char *)NULL);
+ return TCL_ERROR;
+ }
+ penPtr->refCount++;
+ *penPtrPtr = penPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyPens --
+ *
+ * Release memory and resources allocated for the style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the pen style is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyPens(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Pen *penPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->penTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ penPtr = (Pen *)Blt_GetHashValue(hPtr);
+ penPtr->hashPtr = NULL;
+ DestroyPen(graphPtr, penPtr);
+ }
+ Blt_DeleteHashTable(&(graphPtr->penTable));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * A standard Tcl result. If querying configuration values,
+ * interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc; /* Not used. */
+ char *argv[];
+{
+ Pen *penPtr;
+ unsigned int configFlags;
+
+ penPtr = NameToPen(graphPtr, argv[3]);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, penPtr->configSpecs,
+ (char *)penPtr, argv[4], configFlags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets pen attributes (font, line width, color, etc).
+ *
+ * Results:
+ * A standard Tcl result. If querying configuration values,
+ * interp->result will contain the results.
+ *
+ * Side Effects:
+ * Pen resources are possibly allocated (GC, font).
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc;
+ char *argv[];
+{
+ int flags;
+ Pen *penPtr;
+ int nNames, nOpts;
+ int redraw;
+ char **options;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (NameToPen(graphPtr, argv[i]) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of pen names specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + i; /* Start of options in argv */
+
+ redraw = 0;
+ for (i = 0; i < nNames; i++) {
+ penPtr = NameToPen(graphPtr, argv[i]);
+ flags = TK_CONFIG_ARGV_ONLY | (penPtr->flags & (ACTIVE_PEN|NORMAL_PEN));
+ if (nOpts == 0) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ penPtr->configSpecs, (char *)penPtr, (char *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+ penPtr->configSpecs, (char *)penPtr, options[0], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, penPtr->configSpecs,
+ nOpts, options, (char *)penPtr, flags) != TCL_OK) {
+ break;
+ }
+ (*penPtr->configProc) (graphPtr, penPtr);
+ if (penPtr->refCount > 0) {
+ redraw++;
+ }
+ }
+ if (redraw) {
+ graphPtr->flags |= REDRAW_BACKING_STORE | DRAW_MARGINS;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ if (i < nNames) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * Adds a new penstyle to the graph.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Pen *penPtr;
+
+ penPtr = Blt_CreatePen(graphPtr, argv[3], graphPtr->classUid, argc - 4,
+ argv + 4);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, penPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Delete the given pen.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+DeleteOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Pen *penPtr;
+ int i;
+
+ for (i = 3; i < argc; i++) {
+ penPtr = NameToPen(graphPtr, argv[i]);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (penPtr->flags & PEN_DELETE_PENDING) {
+ Tcl_AppendResult(graphPtr->interp, "can't find pen \"", argv[i],
+ "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ penPtr->flags |= PEN_DELETE_PENDING;
+ if (penPtr->refCount == 0) {
+ DestroyPen(graphPtr, penPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Return a list of the names of all the axes.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NamesOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Blt_HashSearch cursor;
+ Pen *penPtr;
+ register int i;
+ register Blt_HashEntry *hPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(graphPtr->penTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ penPtr = (Pen *)Blt_GetHashValue(hPtr);
+ if (penPtr->flags & PEN_DELETE_PENDING) {
+ continue;
+ }
+ if (argc == 3) {
+ Tcl_AppendElement(interp, penPtr->name);
+ continue;
+ }
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(penPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, penPtr->name);
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Return the type of pen.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(interp, graphPtr, argc, argv)
+ Tcl_Interp *interp;
+ Graph *graphPtr;
+ int argc;
+ char **argv;
+{
+ Pen *penPtr;
+
+ penPtr = NameToPen(graphPtr, argv[3]);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, penPtr->classUid, TCL_STATIC);
+ return TCL_OK;
+}
+
+static Blt_OpSpec penOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 5, 5, "penName option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+ "penName ?penName?... ?option value?...",},
+ {"create", 2, (Blt_Op)CreateOp, 4, 0, "penName ?option value?...",},
+ {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "?penName?...",},
+ {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+ {"type", 1, (Blt_Op)TypeOp, 4, 4, "penName",},
+};
+static int nPenOps = sizeof(penOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PenOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+
+ proc = Blt_GetOp(interp, nPenOps, penOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (interp, graphPtr, argc, argv);
+}
diff --git a/blt/src/bltGrPs.c b/blt/src/bltGrPs.c
new file mode 100644
index 00000000000..a8e2c11b188
--- /dev/null
+++ b/blt/src/bltGrPs.c
@@ -0,0 +1,1271 @@
+/*
+ * bltGrPs.c --
+ *
+ * This module implements the "postscript" operation for BLT
+ * graph widget.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * PostScript routines to print a graph
+ *
+ * -----------------------------------------------------------------
+ */
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define PS_PREVIEW_EPSI 0
+#define PS_PREVIEW_WMF 1
+#define PS_PREVIEW_TIFF 2
+
+static Tk_OptionParseProc StringToColorMode;
+static Tk_OptionPrintProc ColorModeToString;
+static Tk_CustomOption colorModeOption =
+{
+ StringToColorMode, ColorModeToString, (ClientData)0,
+};
+static Tk_OptionParseProc StringToFormat;
+static Tk_OptionPrintProc FormatToString;
+static Tk_CustomOption formatOption =
+{
+ StringToFormat, FormatToString, (ClientData)0,
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPadOption;
+
+#define DEF_PS_CENTER "yes"
+#define DEF_PS_COLOR_MAP (char *)NULL
+#define DEF_PS_COLOR_MODE "color"
+#define DEF_PS_DECORATIONS "yes"
+#define DEF_PS_FONT_MAP (char *)NULL
+#define DEF_PS_FOOTER "no"
+#define DEF_PS_HEIGHT "0"
+#define DEF_PS_LANDSCAPE "no"
+#define DEF_PS_MAXPECT "no"
+#define DEF_PS_PADX "1.0i"
+#define DEF_PS_PADY "1.0i"
+#define DEF_PS_PAPERHEIGHT "11.0i"
+#define DEF_PS_PAPERWIDTH "8.5i"
+#define DEF_PS_PREVIEW "no"
+#define DEF_PS_PREVIEW_FORMAT "epsi"
+#define DEF_PS_WIDTH "0"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BOOLEAN, "-center", "center", "Center",
+ DEF_PS_CENTER, Tk_Offset(PostScript, center),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-colormap", "colorMap", "ColorMap",
+ DEF_PS_COLOR_MAP, Tk_Offset(PostScript, colorVarName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-colormode", "colorMode", "ColorMode",
+ DEF_PS_COLOR_MODE, Tk_Offset(PostScript, colorMode),
+ TK_CONFIG_DONT_SET_DEFAULT, &colorModeOption},
+ {TK_CONFIG_BOOLEAN, "-decorations", "decorations", "Decorations",
+ DEF_PS_DECORATIONS, Tk_Offset(PostScript, decorations),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-fontmap", "fontMap", "FontMap",
+ DEF_PS_FONT_MAP, Tk_Offset(PostScript, fontVarName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-footer", "footer", "Footer",
+ DEF_PS_FOOTER, Tk_Offset(PostScript, footer),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_PS_HEIGHT, Tk_Offset(PostScript, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-landscape", "landscape", "Landscape",
+ DEF_PS_LANDSCAPE, Tk_Offset(PostScript, landscape),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-maxpect", "maxpect", "Maxpect",
+ DEF_PS_MAXPECT, Tk_Offset(PostScript, maxpect),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_PS_PADX, Tk_Offset(PostScript, padX), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_PS_PADY, Tk_Offset(PostScript, padY), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-paperheight", "paperHeight", "PaperHeight",
+ DEF_PS_PAPERHEIGHT, Tk_Offset(PostScript, reqPaperHeight),
+ 0, &bltPositiveDistanceOption},
+ {TK_CONFIG_CUSTOM, "-paperwidth", "paperWidth", "PaperWidth",
+ DEF_PS_PAPERWIDTH, Tk_Offset(PostScript, reqPaperWidth),
+ 0, &bltPositiveDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-preview", "preview", "Preview",
+ DEF_PS_PREVIEW, Tk_Offset(PostScript, addPreview),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-previewformat", "previewFormat", "PreviewFormat",
+ DEF_PS_PREVIEW_FORMAT, Tk_Offset(PostScript, previewFormat),
+ TK_CONFIG_DONT_SET_DEFAULT, &formatOption},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_PS_WIDTH, Tk_Offset(PostScript, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+extern void Blt_MarkersToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken, int under));
+extern void Blt_ElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_ActiveElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_LegendToPostScript _ANSI_ARGS_((Legend *legendPtr,
+ PsToken psToken));
+extern void Blt_GridToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_AxesToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+extern void Blt_AxisLimitsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+ PsToken psToken));
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorMode --
+ *
+ * Convert the string representation of a PostScript color mode
+ * into the enumerated type representing the color level:
+ *
+ * PS_MODE_COLOR - Full color
+ * PS_MODE_GREYSCALE - Color converted to greyscale
+ * PS_MODE_MONOCHROME - Only black and white
+ *
+ * Results:
+ * A standard Tcl result. The color level is written into the
+ * page layout information structure.
+ *
+ * Side Effects:
+ * Future invocations of the "postscript" option will use this
+ * variable to determine how color information will be displayed
+ * in the PostScript output it produces.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorMode(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New value. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+{
+ PsColorMode *modePtr = (PsColorMode *) (widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'c') && (strncmp(string, "color", length) == 0)) {
+ *modePtr = PS_MODE_COLOR;
+ } else if ((c == 'g') && (strncmp(string, "grayscale", length) == 0)) {
+ *modePtr = PS_MODE_GREYSCALE;
+ } else if ((c == 'g') && (strncmp(string, "greyscale", length) == 0)) {
+ *modePtr = PS_MODE_GREYSCALE;
+ } else if ((c == 'm') && (strncmp(string, "monochrome", length) == 0)) {
+ *modePtr = PS_MODE_MONOCHROME;
+ } else {
+ Tcl_AppendResult(interp, "bad color mode \"", string, "\": should be \
+\"color\", \"greyscale\", or \"monochrome\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColorMode --
+ *
+ * Convert the PostScript mode value into the string representing
+ * a valid color mode.
+ *
+ * Results:
+ * The static string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColorMode(colorMode)
+ PsColorMode colorMode;
+{
+ switch (colorMode) {
+ case PS_MODE_COLOR:
+ return "color";
+ case PS_MODE_GREYSCALE:
+ return "greyscale";
+ case PS_MODE_MONOCHROME:
+ return "monochrome";
+ default:
+ return "unknown color mode";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorModeToString --
+ *
+ * Convert the current color mode into the string representing a
+ * valid color mode.
+ *
+ * Results:
+ * The string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record. */
+ int offset; /* field of colorMode in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ PsColorMode mode = *(PsColorMode *) (widgRec + offset);
+
+ return NameOfColorMode(mode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ * Convert the string of the PostScript preview format into
+ * an enumerated type representing the desired format. The
+ * available formats are:
+ *
+ * PS_PREVIEW_WMF - Windows Metafile.
+ * PS_PREVIEW_TIFF - TIFF bitmap image.
+ * PS_PREVIEW_EPSI - Device independent ASCII preview
+ *
+ * Results:
+ * A standard Tcl result. The format is written into the
+ * page layout information structure.
+ *
+ * Side Effects:
+ * Future invocations of the "postscript" option will use this
+ * variable to determine how to format a preview image (if one
+ * is selected) when the PostScript output is produced.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New value. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+{
+ int *formatPtr = (int *) (widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'c') && (strncmp(string, "epsi", length) == 0)) {
+ *formatPtr = PS_PREVIEW_EPSI;
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+ } else if ((c == 't') && (strncmp(string, "tiff", length) == 0)) {
+ *formatPtr = PS_PREVIEW_TIFF;
+#endif /* HAVE_TIFF_H */
+ } else if ((c == 'w') && (strncmp(string, "wmf", length) == 0)) {
+ *formatPtr = PS_PREVIEW_WMF;
+#endif /* WIN32 */
+ } else {
+ Tcl_AppendResult(interp, "bad format \"", string, "\": should be ",
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+ "\"tiff\" or ",
+#endif /* HAVE_TIFF_H */
+ "\"wmf\" or ",
+#endif /* WIN32 */
+ "\"epsi\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatToString --
+ *
+ * Convert the preview format into the string representing its
+ * type.
+ *
+ * Results:
+ * The string representing the preview format is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* PostScript structure record */
+ int offset; /* field of colorMode in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int format = *(int *)(widgRec + offset);
+
+ switch (format) {
+ case PS_PREVIEW_EPSI:
+ return "epsi";
+ case PS_PREVIEW_WMF:
+ return "wmf";
+ case PS_PREVIEW_TIFF:
+ return "tiff";
+ }
+ return "?unknown preview format?";
+}
+
+void
+Blt_DestroyPostScript(graphPtr)
+ Graph *graphPtr;
+{
+ Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript,
+ graphPtr->display, 0);
+ Blt_Free(graphPtr->postscript);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+ if (Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs, (char *)psPtr,
+ argv[3], 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * A new PostScript file is created.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Number of options in argv vector */
+ char **argv; /* Option vector */
+{
+ int flags = TK_CONFIG_ARGV_ONLY;
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)psPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)psPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)psPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * ComputeBoundingBox --
+ *
+ * Computes the bounding box for the PostScript plot. First get
+ * the size of the plot (by default, it's the size of graph's X
+ * window). If the plot plus the page border is bigger than the
+ * designated paper size, or if the "-maxpect" option is turned
+ * on, scale the plot to the page.
+ *
+ * Note: All coordinates/sizes are in screen coordinates, not
+ * PostScript coordinates. This includes the computed
+ * bounding box and paper size. They will be scaled to
+ * printer points later.
+ *
+ * Results:
+ * Returns the height of the paper in screen coordinates.
+ *
+ * Side Effects:
+ * The graph dimensions (width and height) are changed to the
+ * requested PostScript plot size.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+ComputeBoundingBox(graphPtr, psPtr)
+ Graph *graphPtr;
+ PostScript *psPtr;
+{
+ int paperWidth, paperHeight;
+ int x, y, hSize, vSize, hBorder, vBorder;
+ double hScale, vScale, scale;
+
+ x = psPtr->padLeft;
+ y = psPtr->padTop;
+ hBorder = PADDING(psPtr->padX);
+ vBorder = PADDING(psPtr->padY);
+
+ if (psPtr->reqWidth > 0) {
+ graphPtr->width = psPtr->reqWidth;
+ }
+ if (psPtr->reqHeight > 0) {
+ graphPtr->height = psPtr->reqHeight;
+ }
+ if (psPtr->landscape) {
+ hSize = graphPtr->height;
+ vSize = graphPtr->width;
+ } else {
+ hSize = graphPtr->width;
+ vSize = graphPtr->height;
+ }
+ /*
+ * If the paper size wasn't specified, set it to the graph size plus
+ * the paper border.
+ */
+ paperWidth = psPtr->reqPaperWidth;
+ paperHeight = psPtr->reqPaperHeight;
+ if (paperWidth < 1) {
+ paperWidth = hSize + hBorder;
+ }
+ if (paperHeight < 1) {
+ paperHeight = vSize + vBorder;
+ }
+ hScale = vScale = 1.0;
+ /*
+ * Scale the plot size (the graph itself doesn't change size) if
+ * it's bigger than the paper or if -maxpect was set.
+ */
+ if ((psPtr->maxpect) || ((hSize + hBorder) > paperWidth)) {
+ hScale = (double)(paperWidth - hBorder) / (double)hSize;
+ }
+ if ((psPtr->maxpect) || ((vSize + vBorder) > paperHeight)) {
+ vScale = (double)(paperHeight - vBorder) / (double)vSize;
+ }
+ scale = MIN(hScale, vScale);
+ if (scale != 1.0) {
+ hSize = (int)((hSize * scale) + 0.5);
+ vSize = (int)((vSize * scale) + 0.5);
+ }
+ psPtr->pageScale = scale;
+ if (psPtr->center) {
+ if (paperWidth > hSize) {
+ x = (paperWidth - hSize) / 2;
+ }
+ if (paperHeight > vSize) {
+ y = (paperHeight - vSize) / 2;
+ }
+ }
+ psPtr->left = x;
+ psPtr->bottom = y;
+ psPtr->right = x + hSize - 1;
+ psPtr->top = y + vSize - 1;
+
+ graphPtr->flags |= LAYOUT_NEEDED | MAP_WORLD;
+ Blt_LayoutGraph(graphPtr);
+ return paperHeight;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PreviewImage --
+ *
+ * Generates a EPSI thumbnail of the graph. The thumbnail is
+ * restricted to a certain size. This is to keep the size of the
+ * PostScript file small and the processing time low.
+ *
+ * The graph is drawn into a pixmap. We then take a snapshot
+ * of that pixmap, and rescale it to a smaller image. Finally,
+ * the image is dumped to PostScript.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void
+PreviewImage(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ int noBackingStore = 0;
+ Pixmap drawable;
+ Blt_Colorimage image;
+ int nLines;
+ Tcl_DString dString;
+
+ /* Create a pixmap and draw the graph into it. */
+
+ drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
+ Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+
+ /* Get a color image from the pixmap */
+ image = Blt_DrawableToColorimage(graphPtr->tkwin, drawable, 0, 0,
+ graphPtr->width, graphPtr->height, 1.0);
+ Tk_FreePixmap(graphPtr->display, drawable);
+ if (image == NULL) {
+ return; /* Can't grab pixmap? */
+ }
+#ifdef THUMBNAIL_PREVIEW
+ {
+ double scale, xScale, yScale;
+ int width, height;
+ Blt_Colorimage destImage;
+
+ /* Scale the source image into a size appropriate for a thumbnail. */
+#define PS_MAX_PREVIEW_WIDTH 300.0
+#define PS_MAX_PREVIEW_HEIGHT 300.0
+ xScale = PS_MAX_PREVIEW_WIDTH / (double)graphPtr->width;
+ yScale = PS_MAX_PREVIEW_HEIGHT / (double)graphPtr->height;
+ scale = MIN(xScale, yScale);
+
+ width = (int)(scale * graphPtr->width + 0.5);
+ height = (int)(scale * graphPtr->height + 0.5);
+ destImage = Blt_ResampleColorimage(image, width, height,
+ bltBoxFilterPtr, bltBoxFilterPtr);
+ Blt_FreeColorimage(image);
+ image = destImage;
+ }
+#endif /* THUMBNAIL_PREVIEW */
+ Blt_ColorimageToGreyscale(image);
+ if (psPtr->landscape) {
+ Blt_Colorimage oldImage;
+
+ oldImage = image;
+ image = Blt_RotateColorimage(image, 90.0);
+ Blt_FreeColorimage(oldImage);
+ }
+ Tcl_DStringInit(&dString);
+ /* Finally, we can generate PostScript for the image */
+ nLines = Blt_ColorimageToPsData(image, 1, &dString, "%");
+
+ Blt_AppendToPostScript(psToken, "%%BeginPreview: ", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "%d %d 8 %d\n", Blt_ColorimageWidth(image),
+ Blt_ColorimageHeight(image), nLines);
+ Blt_AppendToPostScript(psToken, Tcl_DStringValue(&dString), (char *)NULL);
+ Blt_AppendToPostScript(psToken, "%%EndPreview\n\n", (char *)NULL);
+ Tcl_DStringFree(&dString);
+ Blt_FreeColorimage(image);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PostScriptPreamble
+ *
+ * The PostScript preamble calculates the needed translation and scaling
+ * to make X11 coordinates compatible with PostScript.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+static int
+PostScriptPreamble(graphPtr, fileName, psToken)
+ Graph *graphPtr;
+ char *fileName;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ time_t ticks;
+ char date[200]; /* Hold the date string from ctime() */
+ char *version;
+ double dpiX, dpiY;
+ double xPixelsToPica, yPixelsToPica; /* Scales to convert pixels to pica */
+ Screen *screenPtr;
+ char *nl;
+ int paperHeightPixels;
+
+ paperHeightPixels = ComputeBoundingBox(graphPtr, psPtr);
+ if (fileName == NULL) {
+ fileName = Tk_PathName(graphPtr->tkwin);
+ }
+ Blt_AppendToPostScript(psToken, "%!PS-Adobe-3.0 EPSF-3.0\n",
+ (char *)NULL);
+
+ /*
+ * Compute the scale factors to convert PostScript to X11 coordinates.
+ * Round the pixels per inch (dpi) to an integral value before computing
+ * the scale.
+ */
+#define MM_INCH 25.4
+#define PICA_INCH 72.0
+ screenPtr = Tk_Screen(graphPtr->tkwin);
+ dpiX = (WidthOfScreen(screenPtr) * MM_INCH) / WidthMMOfScreen(screenPtr);
+ xPixelsToPica = PICA_INCH / dpiX;
+ dpiY = (HeightOfScreen(screenPtr) * MM_INCH) / HeightMMOfScreen(screenPtr);
+ yPixelsToPica = PICA_INCH / dpiY;
+
+ /*
+ * The "BoundingBox" comment is required for EPS files. The box
+ * coordinates are integers, so we need round away from the
+ * center of the box.
+ */
+ Blt_FormatToPostScript(psToken, "%%%%BoundingBox: %d %d %d %d\n",
+ (int)floor(psPtr->left * xPixelsToPica),
+ (int)floor((paperHeightPixels - psPtr->top) * yPixelsToPica),
+ (int)ceil(psPtr->right * xPixelsToPica),
+ (int)ceil((paperHeightPixels - psPtr->bottom) * yPixelsToPica));
+
+ Blt_AppendToPostScript(psToken, "%%Pages: 0\n", (char *)NULL);
+
+ version = Tcl_GetVar(graphPtr->interp, "blt_version", TCL_GLOBAL_ONLY);
+ if (version == NULL) {
+ version = "???";
+ }
+ Blt_FormatToPostScript(psToken, "%%%%Creator: (BLT %s %s)\n", version,
+ Tk_Class(graphPtr->tkwin));
+
+ ticks = time((time_t *) NULL);
+ strcpy(date, ctime(&ticks));
+ nl = date + strlen(date) - 1;
+ if (*nl == '\n') {
+ *nl = '\0';
+ }
+ Blt_FormatToPostScript(psToken, "%%%%CreationDate: (%s)\n", date);
+ Blt_FormatToPostScript(psToken, "%%%%Title: (%s)\n", fileName);
+ Blt_AppendToPostScript(psToken, "%%DocumentData: Clean7Bit\n",
+ (char *)NULL);
+ if (psPtr->landscape) {
+ Blt_AppendToPostScript(psToken, "%%Orientation: Landscape\n",
+ (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "%%Orientation: Portrait\n",
+ (char *)NULL);
+ }
+ Blt_AppendToPostScript(psToken,
+ "%%DocumentNeededResources: font Helvetica Courier\n", (char *)NULL);
+ Blt_AppendToPostScript(psToken, "%%EndComments\n\n", (char *)NULL);
+ if ((psPtr->addPreview) && (psPtr->previewFormat == PS_PREVIEW_EPSI)) {
+ PreviewImage(graphPtr, psToken);
+ }
+ if (Blt_FileToPostScript(psToken, "bltGraph.pro") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (psPtr->footer) {
+ char *who;
+
+ who = getenv("LOGNAME");
+ if (who == NULL) {
+ who = "???";
+ }
+ Blt_AppendToPostScript(psToken,
+ "8 /Helvetica SetFont\n",
+ "10 30 moveto\n",
+ "(Date: ", date, ") show\n",
+ "10 20 moveto\n",
+ "(File: ", fileName, ") show\n",
+ "10 10 moveto\n",
+ "(Created by: ", who, "@", Tcl_GetHostName(), ") show\n",
+ "0 0 moveto\n",
+ (char *)NULL);
+ }
+ /*
+ * Set the conversion from PostScript to X11 coordinates. Scale
+ * pica to pixels and flip the y-axis (the origin is the upperleft
+ * corner).
+ */
+ Blt_AppendToPostScript(psToken,
+ "% Transform coordinate system to use X11 coordinates\n\n",
+ "% 1. Flip y-axis over by reversing the scale,\n",
+ "% 2. Translate the origin to the other side of the page,\n",
+ "% making the origin the upper left corner\n", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "%g -%g scale\n", xPixelsToPica,
+ yPixelsToPica);
+ /* Papersize is in pixels. Translate the new origin *after*
+ * changing the scale. */
+ Blt_FormatToPostScript(psToken, "0 %d translate\n\n",
+ -paperHeightPixels);
+ Blt_AppendToPostScript(psToken, "% User defined page layout\n\n",
+ "% Set color level\n", (char *)NULL);
+ Blt_FormatToPostScript(psToken, "/CL %d def\n\n", psPtr->colorMode);
+ Blt_FormatToPostScript(psToken, "%% Set origin\n%d %d translate\n\n",
+ psPtr->left, psPtr->bottom);
+ if (psPtr->landscape) {
+ Blt_FormatToPostScript(psToken,
+ "%% Landscape orientation\n0 %g translate\n-90 rotate\n",
+ ((double)graphPtr->width * psPtr->pageScale));
+ }
+ if (psPtr->pageScale != 1.0) {
+ Blt_AppendToPostScript(psToken, "\n% Setting graph scale factor\n",
+ (char *)NULL);
+ Blt_FormatToPostScript(psToken, " %g %g scale\n", psPtr->pageScale,
+ psPtr->pageScale);
+ }
+ Blt_AppendToPostScript(psToken, "\n%%EndSetup\n\n", (char *)NULL);
+ return TCL_OK;
+}
+
+
+static void
+MarginsToPostScript(graphPtr, psToken)
+ Graph *graphPtr;
+ PsToken psToken;
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ XRectangle margin[4];
+
+ margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0;
+ margin[0].width = margin[3].width = graphPtr->width;
+ margin[0].height = graphPtr->top;
+ margin[3].y = graphPtr->bottom;
+ margin[3].height = graphPtr->height - graphPtr->bottom;
+ margin[2].y = margin[1].y = graphPtr->top;
+ margin[1].width = graphPtr->left;
+ margin[2].height = margin[1].height = graphPtr->bottom - graphPtr->top;
+ margin[2].x = graphPtr->right;
+ margin[2].width = graphPtr->width - graphPtr->right;
+
+ /* Clear the surrounding margins and clip the plotting surface */
+ if (psPtr->decorations) {
+ Blt_BackgroundToPostScript(psToken,
+ Tk_3DBorderColor(graphPtr->border));
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ }
+ Blt_RectanglesToPostScript(psToken, margin, 4);
+
+ /* Interior 3D border */
+ if ((psPtr->decorations) && (graphPtr->plotBW > 0)) {
+ int x, y, width, height;
+
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+ width = (graphPtr->right - graphPtr->left) + (2 * graphPtr->plotBW);
+ height = (graphPtr->bottom - graphPtr->top) + (2 * graphPtr->plotBW);
+ Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, (double)x,
+ (double)y, width, height, graphPtr->plotBW, graphPtr->plotRelief);
+ }
+ if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
+ /*
+ * Print the legend if we're using a site which lies in one
+ * of the margins (left, right, top, or bottom) of the graph.
+ */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ if (graphPtr->titleText != NULL) {
+ Blt_TextToPostScript(psToken, graphPtr->titleText,
+ &(graphPtr->titleStyle),
+ (double)graphPtr->titleX,
+ (double)graphPtr->titleY);
+ }
+ Blt_AxesToPostScript(graphPtr, psToken);
+}
+
+
+static int
+GraphToPostScript(graphPtr, ident, psToken)
+ Graph *graphPtr;
+ char *ident; /* Identifier string (usually the filename) */
+ PsToken psToken;
+{
+ int x, y, width, height;
+ int result = TCL_ERROR;
+
+ /*
+ * We need to know how big a graph to print. If the graph hasn't
+ * been drawn yet, the width and height will be 1. Instead use
+ * the requested size of the widget. The user can still override
+ * this with the -width and -height postscript options.
+ */
+ if (graphPtr->height <= 1) {
+ graphPtr->height = Tk_ReqHeight(graphPtr->tkwin);
+ }
+ if (graphPtr->width <= 1) {
+ graphPtr->width = Tk_ReqWidth(graphPtr->tkwin);
+ }
+ result = PostScriptPreamble(graphPtr, ident, psToken);
+ if (result != TCL_OK) {
+ goto error;
+ }
+ /*
+ * Determine rectangle of the plotting area for the graph window
+ */
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+
+ width = (graphPtr->right - graphPtr->left + 1) + (2 * graphPtr->plotBW);
+ height = (graphPtr->bottom - graphPtr->top + 1) + (2 * graphPtr->plotBW);
+
+ Blt_FontToPostScript(psToken, graphPtr->titleStyle.font);
+ Blt_RegionToPostScript(psToken, (double)x, (double)y, width, height);
+ if (graphPtr->postscript->decorations) {
+ Blt_BackgroundToPostScript(psToken, graphPtr->plotBg);
+ } else {
+ Blt_ClearBackgroundToPostScript(psToken);
+ }
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ Blt_AppendToPostScript(psToken, "gsave clip\n\n", (char *)NULL);
+ /* Draw the grid, elements, and markers in the plotting area. */
+ if (!graphPtr->gridPtr->hidden) {
+ Blt_GridToPostScript(graphPtr, psToken);
+ }
+ Blt_MarkersToPostScript(graphPtr, psToken, TRUE);
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (!Blt_LegendIsRaised(graphPtr->legend))) {
+ /* Print legend underneath elements and markers */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ Blt_AxisLimitsToPostScript(graphPtr, psToken);
+ Blt_ElementsToPostScript(graphPtr, psToken);
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (Blt_LegendIsRaised(graphPtr->legend))) {
+ /* Print legend above elements (but not markers) */
+ Blt_LegendToPostScript(graphPtr->legend, psToken);
+ }
+ Blt_MarkersToPostScript(graphPtr, psToken, FALSE);
+ Blt_ActiveElementsToPostScript(graphPtr, psToken);
+ Blt_AppendToPostScript(psToken, "\n",
+ "% Unset clipping\n",
+ "grestore\n\n", (char *)NULL);
+ MarginsToPostScript(graphPtr, psToken);
+ Blt_AppendToPostScript(psToken,
+ "showpage\n",
+ "%Trailer\n",
+ "grestore\n",
+ "end\n",
+ "%EOF\n", (char *)NULL);
+ error:
+ /* Reset height and width of graph window */
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ graphPtr->flags = MAP_WORLD;
+
+ /*
+ * Redraw the graph in order to re-calculate the layout as soon as
+ * possible. This is in the case the crosshairs are active.
+ */
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+#ifdef WIN32
+
+static void
+InitAPMHeader(
+ Tk_Window tkwin,
+ int width, int height,
+ APMHEADER *headerPtr)
+{
+ unsigned int *p;
+ unsigned int sum;
+ Screen *screen;
+#define MM_INCH 25.4
+ double dpiX, dpiY;
+
+ headerPtr->key = 0x9ac6cdd7L;
+ headerPtr->hmf = 0;
+ headerPtr->inch = 1440;
+
+ screen = Tk_Screen(tkwin);
+ dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
+ dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
+
+ headerPtr->bbox.Left = headerPtr->bbox.Top = 0;
+ headerPtr->bbox.Bottom = (SHORT)((width * 1440) / dpiX);
+ headerPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
+ headerPtr->reserved = 0;
+ sum = 0;
+ for (p = (unsigned int *)headerPtr;
+ p < (unsigned int *)&(headerPtr->checksum); p++) {
+ sum ^= *p;
+ }
+ headerPtr->checksum = sum;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * CreateWindowEPS --
+ *
+ * Generates an EPS file with a Window metafile preview.
+ *
+ * Windows metafiles aren't very robust. Including exactly the
+ * same metafile (one embedded in a DOS EPS, the other as .wmf
+ * file) will play back differently.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+CreateWindowsEPS(
+ Graph *graphPtr,
+ PsToken psToken,
+ FILE *f)
+{
+ DWORD size;
+ DOSEPSHEADER epsHeader;
+ HANDLE hMem;
+ HDC hRefDC, hDC;
+ HENHMETAFILE hMetaFile;
+ Tcl_DString dString;
+ TkWinDC drawableDC;
+ TkWinDCState state;
+ int result;
+ unsigned char *buffer, *psBuffer;
+
+ result = TCL_ERROR;
+
+ Blt_AppendToPostScript(psToken, "\n", (char *)NULL);
+ psBuffer = Blt_PostScriptFromToken(psToken);
+ /*
+ * Fill out as much information as we can into the DOS EPS header.
+ * We won't know the start of the length of the WMF segment until
+ * we create the metafile.
+ */
+ epsHeader.magic[0] = 0xC5;
+ epsHeader.magic[1] = 0xD0;
+ epsHeader.magic[2] = 0xD3;
+ epsHeader.magic[3] = 0xC6;
+ epsHeader.psStart = sizeof(epsHeader);
+ epsHeader.psLength = strlen(psBuffer) + 1;
+ epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+ epsHeader.wmfLength = 0L; /* Fill in later. */
+ epsHeader.tiffStart = 0L;
+ epsHeader.tiffLength = 0L;
+ epsHeader.checksum = 0xFFFF;
+
+ hMem = NULL;
+ result = TCL_ERROR;
+ hRefDC = TkWinGetDrawableDC(graphPtr->display,
+ Tk_WindowId(graphPtr->tkwin), &state);
+
+ /* Build a description string. */
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+ Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+
+ hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+
+ if (hDC == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't create metafile: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Assemble a Tk drawable that points to the metafile and let the
+ * graph's drawing routine draw into it. */
+ drawableDC.hdc = hDC;
+ drawableDC.type = TWD_WINDC;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_LayoutGraph(graphPtr);
+ Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
+ GdiFlush();
+ hMetaFile = CloseEnhMetaFile(hDC);
+
+ size = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hRefDC);
+ hMem = GlobalAlloc(GHND, size);
+ if (hMem == NULL) {
+ Tcl_AppendResult(graphPtr->interp, "can't allocate global memory:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ if (!GetWinMetaFileBits(hMetaFile, size, buffer, MM_ANISOTROPIC, hRefDC)) {
+ Tcl_AppendResult(graphPtr->interp, "can't get metafile data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+
+ /*
+ * Fix up the EPS header with the correct metafile length and PS
+ * offset (now that we what they are).
+ */
+ epsHeader.wmfLength = size;
+ epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+
+ /* Write out the eps header, */
+ if (fwrite(&epsHeader, 1, sizeof(epsHeader), f) != sizeof(epsHeader)) {
+ Tcl_AppendResult(graphPtr->interp, "error writing eps header:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* the PostScript, */
+ if (fwrite(psBuffer, 1, epsHeader.psLength, f) != epsHeader.psLength) {
+ Tcl_AppendResult(graphPtr->interp, "error writing PostScript data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* and finally the metadata itself. */
+ if (fwrite(buffer, 1, size, f) != size) {
+ Tcl_AppendResult(graphPtr->interp, "error writing metafile data:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ result = TCL_OK;
+
+ error:
+ DeleteEnhMetaFile(hMetaFile);
+ TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hRefDC, &state);
+ fclose(f);
+ if (hMem != NULL) {
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ graphPtr->flags = MAP_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+#endif /*WIN32*/
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OutputOp --
+ *
+ * This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ * Standard TCL result. TCL_OK if plot was successfully printed,
+ * TCL_ERROR otherwise.
+ *
+ * Side effects:
+ * A new PostScript file is created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+OutputOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Number of options in argv vector */
+ char **argv; /* Option vector */
+{
+ PostScript *psPtr = (PostScript *)graphPtr->postscript;
+ int result = TCL_ERROR;
+ FILE *f = NULL;
+ PsToken psToken;
+ char *fileName; /* Name of file to write PostScript output
+ * If NULL, output is returned via
+ * interp->result. */
+ fileName = NULL;
+ if (argc > 3) {
+ if (argv[3][0] != '-') {
+ fileName = argv[3]; /* First argument is the file name. */
+ argv++, argc--;
+ }
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)psPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (fileName != NULL) {
+#ifdef WIN32
+ f = fopen(fileName, "wb");
+#else
+ f = fopen(fileName, "w");
+#endif
+ if (f == NULL) {
+ Tcl_AppendResult(interp, "can't create \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+ psToken = Blt_GetPsToken(graphPtr->interp, graphPtr->tkwin);
+ psToken->fontVarName = psPtr->fontVarName;
+ psToken->colorVarName = psPtr->colorVarName;
+ psToken->colorMode = psPtr->colorMode;
+
+ if (GraphToPostScript(graphPtr, fileName, psToken) != TCL_OK) {
+ goto error;
+ }
+ /*
+ * If a file name was given, write the results to that file
+ */
+ if (f != NULL) {
+#ifdef WIN32
+ if ((psPtr->addPreview) && (psPtr->previewFormat != PS_PREVIEW_EPSI)) {
+ if (CreateWindowsEPS(graphPtr, psToken, f)) {
+ return TCL_ERROR;
+ }
+ } else {
+ fputs(Blt_PostScriptFromToken(psToken), f);
+ if (ferror(f)) {
+ Tcl_AppendResult(interp, "error writing file \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ fclose(f);
+ }
+#else
+ fputs(Blt_PostScriptFromToken(psToken), f);
+ if (ferror(f)) {
+ Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ fclose(f);
+#endif /* WIN32 */
+ } else {
+ Tcl_SetResult(interp, Blt_PostScriptFromToken(psToken), TCL_VOLATILE);
+ }
+ result = TCL_OK;
+
+ error:
+ if (f != NULL) {
+ fclose(f);
+ }
+ Blt_ReleasePsToken(psToken);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreatePostScript --
+ *
+ * Creates a postscript structure.
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ * Side effects:
+ * A new PostScript structure is created.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreatePostScript(graphPtr)
+ Graph *graphPtr;
+{
+ PostScript *psPtr;
+
+ psPtr = Blt_Calloc(1, sizeof(PostScript));
+ assert(psPtr);
+ psPtr->colorMode = PS_MODE_COLOR;
+ psPtr->center = TRUE;
+ psPtr->decorations = TRUE;
+ graphPtr->postscript = psPtr;
+
+ if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+ "postscript", "Postscript", configSpecs, 0, (char **)NULL,
+ (char *)psPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_PostScriptOp --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec psOps[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+ {"output", 1, (Blt_Op)OutputOp, 3, 0,
+ "?fileName? ?option value?...",},
+};
+
+static int nPsOps = sizeof(psOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PostScriptOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* # arguments */
+ char **argv; /* Argument list */
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nPsOps, psOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, argc, argv);
+ return result;
+}
+
diff --git a/blt/src/bltGraph.c b/blt/src/bltGraph.c
new file mode 100644
index 00000000000..d9d3bdc1d46
--- /dev/null
+++ b/blt/src/bltGraph.c
@@ -0,0 +1,2362 @@
+
+/*
+ * bltGraph.c --
+ *
+ * This module implements a graph widget for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The graph widget was created by Sani Nassif and George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 2) Update manual pages.
+ *
+ * 3) Update comments.
+ *
+ * 5) Surface, contour, and flow graphs
+ *
+ * 7) Arrows for line markers
+ *
+ */
+
+#include "bltGraph.h"
+#include "bltBind.h"
+#include "bltGrElem.h"
+#include "bltSwitch.h"
+#include <X11/Xutil.h>
+
+Tk_Uid bltXAxisUid;
+Tk_Uid bltYAxisUid;
+Tk_Uid bltBarElementUid;
+Tk_Uid bltLineElementUid;
+Tk_Uid bltStripElementUid;
+Tk_Uid bltContourElementUid;
+Tk_Uid bltLineMarkerUid;
+Tk_Uid bltBitmapMarkerUid;
+Tk_Uid bltImageMarkerUid;
+Tk_Uid bltTextMarkerUid;
+Tk_Uid bltPolygonMarkerUid;
+Tk_Uid bltWindowMarkerUid;
+
+extern Tk_CustomOption bltLinePenOption;
+extern Tk_CustomOption bltBarPenOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltBarModeOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltShadowOption;
+
+#define DEF_GRAPH_ASPECT_RATIO "0.0"
+#define DEF_GRAPH_BAR_BASELINE "0.0"
+#define DEF_GRAPH_BAR_MODE "normal"
+#define DEF_GRAPH_BAR_WIDTH "0.8"
+#define DEF_GRAPH_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_GRAPH_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_GRAPH_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_GRAPH_BUFFER_ELEMENTS "1"
+#define DEF_GRAPH_BUFFER_GRAPH "1"
+#define DEF_GRAPH_CURSOR "crosshair"
+#define DEF_GRAPH_FONT STD_FONT_LARGE
+#define DEF_GRAPH_HALO "2m"
+#define DEF_GRAPH_HALO_BAR "0.1i"
+#define DEF_GRAPH_HEIGHT "4i"
+#define DEF_GRAPH_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_GRAPH_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_GRAPH_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_GRAPH_HIGHLIGHT_WIDTH "2"
+#define DEF_GRAPH_INVERT_XY "0"
+#define DEF_GRAPH_JUSTIFY "center"
+#define DEF_GRAPH_MARGIN "0"
+#define DEF_GRAPH_MARGIN_VAR (char *)NULL
+#define DEF_GRAPH_PLOT_BG_COLOR RGB_WHITE
+#define DEF_GRAPH_PLOT_BG_MONO RGB_WHITE
+#define DEF_GRAPH_PLOT_BW_COLOR STD_BORDERWIDTH
+#define DEF_GRAPH_PLOT_BW_MONO "0"
+#define DEF_GRAPH_PLOT_PADX "8"
+#define DEF_GRAPH_PLOT_PADY "8"
+#define DEF_GRAPH_PLOT_RELIEF "sunken"
+#define DEF_GRAPH_RELIEF "flat"
+#define DEF_GRAPH_SHADOW_COLOR (char *)NULL
+#define DEF_GRAPH_SHADOW_MONO (char *)NULL
+#define DEF_GRAPH_SHOW_VALUES "no"
+#define DEF_GRAPH_TAKE_FOCUS ""
+#define DEF_GRAPH_TITLE (char *)NULL
+#define DEF_GRAPH_TITLE_COLOR STD_COLOR_NORMAL_FG
+#define DEF_GRAPH_TITLE_MONO STD_MONO_NORMAL_FG
+#define DEF_GRAPH_WIDTH "5i"
+#define DEF_GRAPH_DATA (char *)NULL
+#define DEF_GRAPH_DATA_COMMAND (char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_DOUBLE, "-aspect", "aspect", "Aspect",
+ DEF_GRAPH_ASPECT_RATIO, Tk_Offset(Graph, aspect),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_GRAPH_BG_COLOR, Tk_Offset(Graph, border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-barmode", "barMode", "BarMode",
+ DEF_GRAPH_BAR_MODE, Tk_Offset(Graph, mode),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltBarModeOption},
+ {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
+ DEF_GRAPH_BAR_WIDTH, Tk_Offset(Graph, barWidth), 0},
+ {TK_CONFIG_DOUBLE, "-baseline", "baseline", "Baseline",
+ DEF_GRAPH_BAR_BASELINE, Tk_Offset(Graph, baseline), 0},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bm", "bottomMargin",
+ (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_GRAPH_BORDER_WIDTH, Tk_Offset(Graph, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-bottommargin", "bottomMargin", "Margin",
+ DEF_GRAPH_MARGIN, Tk_Offset(Graph, bottomMargin.reqSize), 0,
+ &bltDistanceOption},
+ {TK_CONFIG_STRING, "-bottomvariable", "bottomVariable", "BottomVariable",
+ DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, bottomMargin.varName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
+ DEF_GRAPH_BUFFER_ELEMENTS, Tk_Offset(Graph, backingStore),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-buffergraph", "bufferGraph", "BufferGraph",
+ DEF_GRAPH_BUFFER_GRAPH, Tk_Offset(Graph, doubleBuffer),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-data", "data", "Data",
+ (char *)NULL, Tk_Offset(Graph, data), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-datacommand", "dataCommand", "DataCommand",
+ (char *)NULL, Tk_Offset(Graph, dataCmd), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_GRAPH_FONT, Tk_Offset(Graph, titleStyle.font), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_GRAPH_TITLE_COLOR, Tk_Offset(Graph, titleStyle.color),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleStyle.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-halo", "halo", "Halo",
+ DEF_GRAPH_HALO, Tk_Offset(Graph, halo), 0, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_GRAPH_HEIGHT, Tk_Offset(Graph, reqHeight), 0, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_GRAPH_HIGHLIGHT_BG_COLOR, Tk_Offset(Graph, highlightBgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_GRAPH_HIGHLIGHT_BG_MONO, Tk_Offset(Graph, highlightBgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_GRAPH_HIGHLIGHT_COLOR, Tk_Offset(Graph, highlightColor), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_GRAPH_HIGHLIGHT_WIDTH, Tk_Offset(Graph, highlightWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
+ DEF_GRAPH_INVERT_XY, Tk_Offset(Graph, inverted),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_GRAPH_JUSTIFY, Tk_Offset(Graph, titleStyle.justify),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-leftmargin", "leftMargin", "Margin",
+ DEF_GRAPH_MARGIN, Tk_Offset(Graph, leftMargin.reqSize),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-leftvariable", "leftVariable", "LeftVariable",
+ DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, leftMargin.varName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-lm", "leftMargin", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
+ DEF_GRAPH_PLOT_BG_MONO, Tk_Offset(Graph, plotBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
+ DEF_GRAPH_PLOT_BG_COLOR, Tk_Offset(Graph, plotBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
+ DEF_GRAPH_PLOT_BW_COLOR, Tk_Offset(Graph, plotBW),
+ TK_CONFIG_COLOR_ONLY, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
+ DEF_GRAPH_PLOT_BW_MONO, Tk_Offset(Graph, plotBW),
+ TK_CONFIG_MONO_ONLY, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-plotpadx", "plotPadX", "PlotPad",
+ DEF_GRAPH_PLOT_PADX, Tk_Offset(Graph, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-plotpady", "plotPadY", "PlotPad",
+ DEF_GRAPH_PLOT_PADY, Tk_Offset(Graph, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
+ DEF_GRAPH_PLOT_RELIEF, Tk_Offset(Graph, plotRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-rightmargin", "rightMargin", "Margin",
+ DEF_GRAPH_MARGIN, Tk_Offset(Graph, rightMargin.reqSize),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-rightvariable", "rightVariable", "RightVariable",
+ DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, rightMargin.varName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-rm", "rightMargin", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_GRAPH_SHADOW_COLOR, Tk_Offset(Graph, titleStyle.shadow),
+ TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_GRAPH_SHADOW_MONO, Tk_Offset(Graph, titleStyle.shadow),
+ TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleStyle.color),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_GRAPH_TAKE_FOCUS, Tk_Offset(Graph, takeFocus), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Graph, tile),
+ TK_CONFIG_NULL_OK, &bltTileOption},
+ {TK_CONFIG_STRING, "-title", "title", "Title",
+ DEF_GRAPH_TITLE, Tk_Offset(Graph, titleText), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-tm", "topMargin", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-topmargin", "topMargin", "Margin",
+ DEF_GRAPH_MARGIN, Tk_Offset(Graph, topMargin.reqSize),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-topvariable", "topVariable", "TopVariable",
+ DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, topMargin.varName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_GRAPH_WIDTH, Tk_Offset(Graph, reqWidth),
+ 0, &bltDistanceOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToFormat;
+static Blt_SwitchCustom formatSwitch =
+{
+ StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+ char *name;
+ int width, height;
+ int format;
+} SnapData;
+
+enum SnapFormats { FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
+
+static Blt_SwitchSpec snapSwitches[] =
+{
+ {BLT_SWITCH_INT_POSITIVE, "-width", Blt_Offset(SnapData, width), 0},
+ {BLT_SWITCH_INT_POSITIVE, "-height", Blt_Offset(SnapData, height), 0},
+ {BLT_SWITCH_CUSTOM, "-format", Blt_Offset(SnapData, format), 0,
+ &formatSwitch},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Tcl_IdleProc DisplayGraph;
+static Tcl_FreeProc DestroyGraph;
+static Tk_EventProc GraphEventProc;
+Tcl_CmdProc Blt_GraphInstCmdProc;
+
+#ifdef __STDC__
+static Blt_BindPickProc PickEntry;
+static Tcl_CmdProc StripchartCmd;
+static Tcl_CmdProc BarchartCmd;
+static Tcl_CmdProc GraphCmd;
+static Tcl_CmdDeleteProc GraphInstCmdDeleteProc;
+static Blt_TileChangedProc TileChangedProc;
+#endif /* __STDC__ */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_EventuallyRedrawGraph --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at
+ * the next idle point. This request is made only if the window
+ * is displayed and no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_EventuallyRedrawGraph(graphPtr)
+ Graph *graphPtr; /* Graph widget record */
+{
+ if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayGraph, graphPtr);
+ graphPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GraphEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on graphs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, the graph is eventually
+ * redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+GraphEventProc(clientData, eventPtr)
+ ClientData clientData; /* Graph widget record */
+ register XEvent *eventPtr; /* Event which triggered call to routine */
+{
+ Graph *graphPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ graphPtr->flags |= GRAPH_FOCUS;
+ } else {
+ graphPtr->flags &= ~GRAPH_FOCUS;
+ }
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (graphPtr->tkwin != NULL) {
+ Blt_DeleteWindowInstanceData(graphPtr->tkwin);
+ graphPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(graphPtr->interp, graphPtr->cmdToken);
+ }
+ if (graphPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayGraph, graphPtr);
+ }
+ Tcl_EventuallyFree(graphPtr, DestroyGraph);
+ } else if (eventPtr->type == ConfigureNotify) {
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GraphInstCmdDeleteProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *---------------------------------------------------------------------- */
+static void
+GraphInstCmdDeleteProc(clientData)
+ ClientData clientData; /* Pointer to widget record. */
+{
+ Graph *graphPtr = clientData;
+
+ if (graphPtr->tkwin != NULL) { /* NULL indicates window has
+ * already been destroyed. */
+ Tk_Window tkwin;
+
+ tkwin = graphPtr->tkwin;
+ graphPtr->tkwin = NULL;
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ Blt_DeleteWindowInstanceData(tkwin);
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Rebuilds the designated GC with the new tile pixmap.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Graph *graphPtr = clientData;
+
+ if (graphPtr->tkwin != NULL) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_AdjustAxisPointers --
+ *
+ * Sets the axis pointers according to whether the axis is
+ * inverted on not. The axis sites are also reset.
+ *
+ * Results:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_AdjustAxisPointers(graphPtr)
+ Graph *graphPtr; /* Graph widget record */
+{
+ if (graphPtr->inverted) {
+ graphPtr->leftMargin.chainPtr = graphPtr->axisChain[0];
+ graphPtr->bottomMargin.chainPtr = graphPtr->axisChain[1];
+ graphPtr->rightMargin.chainPtr = graphPtr->axisChain[2];
+ graphPtr->topMargin.chainPtr = graphPtr->axisChain[3];
+ } else {
+ graphPtr->leftMargin.chainPtr = graphPtr->axisChain[1];
+ graphPtr->bottomMargin.chainPtr = graphPtr->axisChain[0];
+ graphPtr->rightMargin.chainPtr = graphPtr->axisChain[3];
+ graphPtr->topMargin.chainPtr = graphPtr->axisChain[2];
+ }
+}
+
+static int
+InitPens(graphPtr)
+ Graph *graphPtr;
+{
+ Blt_InitHashTable(&(graphPtr->penTable), BLT_STRING_KEYS);
+ if (Blt_CreatePen(graphPtr, "activeLine", bltLineElementUid, 0,
+ (char **)NULL) == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_CreatePen(graphPtr, "activeBar", bltBarElementUid, 0,
+ (char **)NULL) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GraphTags --
+ *
+ * Sets the binding tags for a graph object. This routine is
+ * called by Tk when an event occurs in the graph. It fills
+ * an array of pointers with bind tag addresses.
+ *
+ * The object addresses are strings hashed in one of two tag
+ * tables: one for elements and the another for markers. Note
+ * that there's only one binding table for elements and markers.
+ * [We don't want to trigger both a marker and element bind
+ * command for the same event.] But we don't want a marker and
+ * element with the same tag name to activate the others
+ * bindings. A tag "all" for markers should mean all markers, not
+ * all markers and elements. As a result, element and marker
+ * tags are stored in separate hash tables, which means we can't
+ * generate the same tag address for both an elements and marker,
+ * even if they have the same name.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * This information will be used by the binding code in bltUtil.c
+ * to determine what graph objects match the current event. The
+ * tags are placed in tagArr and *nTagsPtr is set with the
+ * number of tags found.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GraphTags(table, object, list)
+ Blt_BindTable table;
+ ClientData object;
+ Blt_List list;
+{
+ Element *elemPtr;
+ MakeTagProc *tagProc;
+ Graph *graphPtr;
+
+ graphPtr = (Graph *)Blt_GetBindingData(table);
+ /*
+ * Trick: Markers, elements, and axes have the same first few
+ * fields in their structures, such as "type", "name", or
+ * "tags". This is so we can look at graph objects
+ * interchangably. It doesn't matter what we cast the
+ * object to.
+ */
+ elemPtr = (Element *)object;
+
+ if ((elemPtr->classUid == bltLineElementUid) ||
+ (elemPtr->classUid == bltStripElementUid) ||
+ (elemPtr->classUid == bltBarElementUid)) {
+ tagProc = Blt_MakeElementTag;
+ } else if ((elemPtr->classUid == bltXAxisUid) ||
+ (elemPtr->classUid == bltYAxisUid)) {
+ tagProc = Blt_MakeAxisTag;
+ } else {
+ tagProc = Blt_MakeMarkerTag;
+ }
+ /*
+ * Always add the name of the object to the tag array.
+ */
+ Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->name), 0);
+ Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->classUid), 0);
+ if (elemPtr->tags != NULL) {
+ register char **p;
+
+ for (p = elemPtr->tags; *p != NULL; p++) {
+ Blt_ListAppend(list, (*tagProc) (graphPtr, *p), 0);
+ }
+ }
+}
+
+/*
+ * Find the closest point from the set of displayed elements,
+ * searching the display list from back to front. That way, if
+ * the points from two different elements overlay each other exactly,
+ * the one that's on top (visible) is picked.
+ */
+static ClientData
+PickEntry(clientData, x, y)
+ ClientData clientData;
+ int x, y;
+{
+ Graph *graphPtr = clientData;
+ Blt_ChainLink *linkPtr;
+ ClosestSearch search;
+ Element *elemPtr;
+ Marker *markerPtr;
+ Extents2D exts;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ if ((x > exts.right) || (x < exts.left) || (y > exts.bottom) ||
+ (y < exts.top)) {
+ return Blt_NearestAxis(graphPtr, x, y);
+ }
+ markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, FALSE);
+ if (markerPtr != NULL) {
+ return markerPtr;
+ }
+ search.along = SEARCH_BOTH;
+ search.halo = graphPtr->halo + 1;
+ search.index = -1;
+ search.x = x;
+ search.y = y;
+ search.dist = (double)(search.halo + 1);
+ search.mode = SEARCH_AUTO;
+
+ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ elemPtr = Blt_ChainGetValue(linkPtr);
+ if (!elemPtr->hidden) {
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ }
+ if (search.dist <= (double)search.halo) {
+ return search.elemPtr;
+ }
+ markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, TRUE);
+ if (markerPtr != NULL) {
+ return markerPtr;
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureGraph --
+ *
+ * Allocates resources for the graph.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for graphPtr; old resources get freed, if there
+ * were any. The graph is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureGraph(graphPtr)
+ Graph *graphPtr; /* Graph widget record */
+{
+ XColor *colorPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ /* Don't allow negative bar widths. Reset to an arbitrary value (0.1) */
+ if (graphPtr->barWidth <= 0.0) {
+ graphPtr->barWidth = 0.1;
+ }
+ graphPtr->inset = graphPtr->borderWidth + graphPtr->highlightWidth + 1;
+ if ((graphPtr->reqHeight != Tk_ReqHeight(graphPtr->tkwin)) ||
+ (graphPtr->reqWidth != Tk_ReqWidth(graphPtr->tkwin))) {
+ Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
+ graphPtr->reqHeight);
+ }
+ Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
+ colorPtr = Tk_3DBorderColor(graphPtr->border);
+
+ if (graphPtr->titleText != NULL) {
+ int textWidth, textHeight;
+
+ Blt_GetTextExtents(&(graphPtr->titleStyle), graphPtr->titleText,
+ &textWidth, &textHeight);
+ graphPtr->titleStyle.height = textHeight + 10;
+ } else {
+ graphPtr->titleStyle.width = graphPtr->titleStyle.height = 0;
+ }
+
+ /*
+ * Create GCs for interior and exterior regions, and a background
+ * GC for clearing the margins with XFillRectangle
+ */
+
+ /* Margin GC */
+
+ gcValues.foreground = graphPtr->titleStyle.color->pixel;
+ gcValues.background = colorPtr->pixel;
+ gcMask = (GCForeground | GCBackground);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (graphPtr->drawGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+ }
+ graphPtr->drawGC = newGC;
+
+ /* Plot fill GC (Background = Foreground) */
+
+ gcValues.foreground = graphPtr->plotBg->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (graphPtr->plotFillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
+ }
+ graphPtr->plotFillGC = newGC;
+
+ /* Margin fill GC (Background = Foreground) */
+
+ gcValues.foreground = colorPtr->pixel;
+ gcValues.background = graphPtr->titleStyle.color->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (graphPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
+ }
+ graphPtr->fillGC = newGC;
+ if (graphPtr->tile != NULL) {
+ Blt_SetTileChangedProc(graphPtr->tile, TileChangedProc, graphPtr);
+ }
+
+ Blt_ResetTextStyle(graphPtr->tkwin, &(graphPtr->titleStyle));
+
+ if (Blt_ConfigModified(configSpecs, "-invertxy", (char *)NULL)) {
+
+ /*
+ * If the -inverted option changed, we need to readjust the pointers
+ * to the axes and recompute the their scales.
+ */
+
+ Blt_AdjustAxisPointers(graphPtr);
+ graphPtr->flags |= RESET_AXES;
+ }
+ if ((!graphPtr->backingStore) && (graphPtr->backPixmap != None)) {
+
+ /*
+ * Free the pixmap if we're not buffering the display of elements
+ * anymore.
+ */
+
+ Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+ graphPtr->backPixmap = None;
+ }
+ /*
+ * Reconfigure the crosshairs, just in case the background color of
+ * the plotarea has been changed.
+ */
+ Blt_ConfigureCrosshairs(graphPtr);
+
+ /*
+ * Update the layout of the graph (and redraw the elements) if
+ * any of the following graph options which affect the size of
+ * the plotting area has changed.
+ *
+ * -aspect
+ * -borderwidth, -plotborderwidth
+ * -font, -title
+ * -width, -height
+ * -invertxy
+ * -bottommargin, -leftmargin, -rightmargin, -topmargin,
+ * -barmode, -barwidth
+ */
+ if (Blt_ConfigModified(configSpecs, "-invertxy", "-title", "-font",
+ "-*margin", "-*width", "-height", "-barmode", "-*pad*", "-aspect",
+ (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD;
+ }
+ if (Blt_ConfigModified(configSpecs, "-plotbackground", (char *)NULL)) {
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyGraph --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a graph at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyGraph(dataPtr)
+ DestroyData dataPtr;
+{
+ Graph *graphPtr = (Graph *)dataPtr;
+
+ Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
+ /*
+ * Destroy the individual components of the graph: elements, markers,
+ * X and Y axes, legend, display lists etc.
+ */
+ Blt_DestroyMarkers(graphPtr);
+ Blt_DestroyElements(graphPtr);
+
+ Blt_DestroyAxes(graphPtr);
+ Blt_DestroyPens(graphPtr);
+
+ if (graphPtr->legend != NULL) {
+ Blt_DestroyLegend(graphPtr);
+ }
+ if (graphPtr->postscript != NULL) {
+ Blt_DestroyPostScript(graphPtr);
+ }
+ if (graphPtr->crosshairs != NULL) {
+ Blt_DestroyCrosshairs(graphPtr);
+ }
+ if (graphPtr->gridPtr != NULL) {
+ Blt_DestroyGrid(graphPtr);
+ }
+ if (graphPtr->bindTable != NULL) {
+ Blt_DestroyBindingTable(graphPtr->bindTable);
+ }
+
+ /* Release allocated X resources and memory. */
+ if (graphPtr->drawGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+ }
+ if (graphPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
+ }
+ if (graphPtr->plotFillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
+ }
+ Blt_FreeTextStyle(graphPtr->display, &(graphPtr->titleStyle));
+ if (graphPtr->backPixmap != None) {
+ Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+ }
+ if (graphPtr->freqArr != NULL) {
+ Blt_Free(graphPtr->freqArr);
+ }
+ if (graphPtr->nStacks > 0) {
+ Blt_DeleteHashTable(&(graphPtr->freqTable));
+ }
+ if (graphPtr->tile != NULL) {
+ Blt_FreeTile(graphPtr->tile);
+ }
+ Blt_Free(graphPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateGraph --
+ *
+ * This procedure creates and initializes a new widget.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing
+ * the new widget. If an error occurred, then the return
+ * value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated, a Tk_Window is created, etc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Graph *
+CreateGraph(interp, argc, argv, classUid)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ Tk_Uid classUid;
+{
+ Graph *graphPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ graphPtr = Blt_Calloc(1, sizeof(Graph));
+ assert(graphPtr);
+
+ /* Initialize the graph data structure. */
+
+ graphPtr->tkwin = tkwin;
+ graphPtr->display = Tk_Display(tkwin);
+ graphPtr->interp = interp;
+ graphPtr->classUid = classUid;
+ graphPtr->backingStore = TRUE;
+ graphPtr->doubleBuffer = TRUE;
+ graphPtr->highlightWidth = 2;
+ graphPtr->plotRelief = TK_RELIEF_SUNKEN;
+ graphPtr->relief = TK_RELIEF_FLAT;
+ graphPtr->flags = (RESET_WORLD);
+ graphPtr->nextMarkerId = 1;
+ graphPtr->padLeft = graphPtr->padRight = 8;
+ graphPtr->padTop = graphPtr->padBottom = 8;
+ graphPtr->bottomMargin.site = MARGIN_BOTTOM;
+ graphPtr->leftMargin.site = MARGIN_LEFT;
+ graphPtr->topMargin.site = MARGIN_TOP;
+ graphPtr->rightMargin.site = MARGIN_RIGHT;
+ Blt_InitTextStyle(&(graphPtr->titleStyle));
+
+ Blt_InitHashTable(&(graphPtr->axes.table), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(graphPtr->axes.tagTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(graphPtr->elements.table), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(graphPtr->elements.tagTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(graphPtr->markers.table), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(graphPtr->markers.tagTable), BLT_STRING_KEYS);
+ graphPtr->elements.chainPtr = Blt_ChainCreate();
+ graphPtr->markers.chainPtr = Blt_ChainCreate();
+ graphPtr->axes.chainPtr = Blt_ChainCreate();
+
+ if (classUid == bltLineElementUid) {
+ Tk_SetClass(tkwin, "Graph");
+ } else if (classUid == bltBarElementUid) {
+ Tk_SetClass(tkwin, "Barchart");
+ } else if (classUid == bltStripElementUid) {
+ Tk_SetClass(tkwin, "Stripchart");
+ }
+ Blt_SetWindowInstanceData(tkwin, graphPtr);
+
+ if (InitPens(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 2, argv + 2,
+ (char *)graphPtr, 0) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ Blt_AdjustAxisPointers(graphPtr);
+
+ if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_CreateLegend(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_CreateGrid(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ Tk_CreateEventHandler(graphPtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc,
+ graphPtr);
+
+ graphPtr->cmdToken = Tcl_CreateCommand(interp, argv[1],
+ Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(graphPtr->tkwin, graphPtr->cmdToken);
+#endif
+ ConfigureGraph(graphPtr);
+ graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr,
+ PickEntry, Blt_GraphTags);
+ return graphPtr;
+
+ error:
+ DestroyGraph((DestroyData)graphPtr);
+ return NULL;
+}
+
+/* Widget sub-commands */
+
+/*ARGSUSED*/
+static int
+XAxisOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
+ return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+X2AxisOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
+ return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+YAxisOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
+ return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+Y2AxisOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
+ return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+BarOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ return Blt_ElementOp(graphPtr, interp, argc, argv, bltBarElementUid);
+}
+
+/*ARGSUSED*/
+static int
+LineOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ return Blt_ElementOp(graphPtr, interp, argc, argv, bltLineElementUid);
+}
+
+/*ARGSUSED*/
+static int
+ElementOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ return Blt_ElementOp(graphPtr, interp, argc, argv, graphPtr->classUid);
+}
+
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int flags;
+
+ flags = TK_CONFIG_ARGV_ONLY;
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, (char *)NULL, flags);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, argv[2], flags);
+ } else {
+ if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 2,
+ argv + 2, (char *)graphPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureGraph(graphPtr);
+ return TCL_OK;
+ }
+}
+
+/* ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, argv[2], 0);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExtentsOp --
+ *
+ * Reports the size of one of several items within the graph.
+ * The following are valid items:
+ *
+ * "bottommargin" Height of the bottom margin
+ * "leftmargin" Width of the left margin
+ * "legend" x y w h of the legend
+ * "plotarea" x y w h of the plotarea
+ * "plotheight" Height of the plot area
+ * "rightmargin" Width of the right margin
+ * "topmargin" Height of the top margin
+ * "plotwidth" Width of the plot area
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+ExtentsOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ char c;
+ unsigned int length;
+ char string[200];
+
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'p') && (length > 4) &&
+ (strncmp("plotheight", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottom - graphPtr->top + 1),
+ TCL_VOLATILE);
+ } else if ((c == 'p') && (length > 4) &&
+ (strncmp("plotwidth", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->right - graphPtr->left + 1),
+ TCL_VOLATILE);
+ } else if ((c == 'p') && (length > 4) &&
+ (strncmp("plotarea", argv[2], length) == 0)) {
+ sprintf(string, "%d %d %d %d", graphPtr->left, graphPtr->top,
+ graphPtr->right - graphPtr->left + 1,
+ graphPtr->bottom - graphPtr->top + 1);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ } else if ((c == 'l') && (length > 2) &&
+ (strncmp("legend", argv[2], length) == 0)) {
+ sprintf(string, "%d %d %d %d", Blt_LegendX(graphPtr->legend),
+ Blt_LegendY(graphPtr->legend),
+ Blt_LegendWidth(graphPtr->legend),
+ Blt_LegendHeight(graphPtr->legend));
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ } else if ((c == 'l') && (length > 2) &&
+ (strncmp("leftmargin", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->leftMargin.width),
+ TCL_VOLATILE);
+ } else if ((c == 'r') && (length > 1) &&
+ (strncmp("rightmargin", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->rightMargin.width),
+ TCL_VOLATILE);
+ } else if ((c == 't') && (length > 1) &&
+ (strncmp("topmargin", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->topMargin.height), TCL_VOLATILE);
+ } else if ((c == 'b') && (length > 1) &&
+ (strncmp("bottommargin", argv[2], length) == 0)) {
+ Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottomMargin.height),
+ TCL_VOLATILE);
+ } else {
+ Tcl_AppendResult(interp, "bad extent item \"", argv[2],
+ "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
+topmargin, bottommargin, plotarea, or legend", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InsideOp --
+ *
+ * Returns true of false whether the given point is inside
+ * the plotting area (defined by left,bottom right, top).
+ *
+ * Results:
+ * Always returns TCL_OK. interp->result will contain
+ * the boolean string representation.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+InsideOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y;
+ Extents2D exts;
+ int result;
+
+ if (Tk_GetPixels(interp, graphPtr->tkwin, argv[2], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ result = PointInRegion(&exts, x, y);
+ Blt_SetBooleanResult(interp, result);
+ return TCL_OK;
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * InvtransformOp --
+ *
+ * This procedure returns a list of the graph coordinate
+ * values corresponding with the given window X and Y
+ * coordinate positions.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred while
+ * parsing the window positions, TCL_ERROR is returned, and
+ * interp->result will contain the error message. Otherwise
+ * interp->result will contain a Tcl list of the x and y
+ * coordinates.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvtransformOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ double x, y;
+ Point2D point;
+ Axis2D axes;
+
+ if (Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK ||
+ Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ /* Perform the reverse transformation, converting from window
+ * coordinates to graph data coordinates. Note that the point is
+ * always mapped to the bottom and left axes (which may not be
+ * what the user wants). */
+
+ /* Pick the first pair of axes */
+ axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+ axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+ point = Blt_InvMap2D(graphPtr, x, y, &axes);
+
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, point.x));
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, point.y));
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ * This procedure returns a list of the window coordinates
+ * corresponding with the given graph x and y coordinates.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the list of the graph coordinates. If an error occurred
+ * while parsing the window positions, TCL_ERROR is returned,
+ * then interp->result will contain an error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ double x, y;
+ Point2D point;
+ Axis2D axes;
+
+ if ((Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK) ||
+ (Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ /*
+ * Perform the transformation from window to graph coordinates.
+ * Note that the points are always mapped onto the bottom and left
+ * axes (which may not be the what the user wants).
+ */
+ axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+ axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+
+ point = Blt_Map2D(graphPtr, x, y, &axes);
+ Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.x)));
+ Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.y)));
+ return TCL_OK;
+}
+
+#ifndef NO_PRINTER
+/*
+ * --------------------------------------------------------------------------
+ *
+ * Print1Op --
+ *
+ * Prints the equivalent of a screen snapshot of the graph
+ * to the designated printer.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred
+ * TCL_ERROR is returned and interp->result will contain an
+ * error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+Print1Op(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int noBackingStore = 0;
+ BITMAPINFO info;
+ void *data;
+ TkWinDCState state;
+ TkWinBitmap bd;
+ DIBSECTION ds;
+ Drawable drawable;
+ HBITMAP hBitmap;
+ HDC hDC;
+ DOCINFO di;
+ double pageWidth, pageHeight;
+ int result;
+ double scale, sx, sy;
+ int jobId;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+ graphPtr->width = graphPtr->reqWidth;
+ }
+ if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+ graphPtr->height = graphPtr->reqHeight;
+ }
+ if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * This is a taken from Blt_SnapPhoto. The difference is that
+ * here we're using the DIBSection directly, without converting
+ * the section into a ColorImage.
+ */
+ ZeroMemory(&info, sizeof(info));
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = graphPtr->width;
+ info.bmiHeader.biHeight = graphPtr->height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ hDC = TkWinGetDrawableDC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ &state);
+ hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+ TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
+
+ /*
+ * Create our own drawable by hand using the DIB we just created.
+ * We'll then draw into it using the standard drawing functions.
+ */
+ bd.type = TWD_BITMAP;
+ bd.handle = hBitmap;
+ bd.colormap = DefaultColormap(graphPtr->display,
+ DefaultScreen(graphPtr->display));
+ bd.depth = Tk_Depth(graphPtr->tkwin);
+
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, (Drawable)&bd, noBackingStore);
+
+ /*
+ * Now that the DIB contains the image of the graph, get the the
+ * data bits and write them to the printer device, stretching the
+ * image to the fit the printer's resolution.
+ */
+ result = TCL_ERROR;
+ if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+ Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ hDC = ((TkWinDC *) drawable)->hdc;
+ /* Get the resolution of the printer device. */
+ sx = (double)GetDeviceCaps(hDC, HORZRES) / (double)graphPtr->width;
+ sy = (double)GetDeviceCaps(hDC, VERTRES) / (double)graphPtr->height;
+ scale = MIN(sx, sy);
+ pageWidth = scale * graphPtr->width;
+ pageHeight = scale * graphPtr->height;
+
+ ZeroMemory(&di, sizeof(di));
+ di.cbSize = sizeof(di);
+ di.lpszDocName = "Graph Contents";
+ jobId = StartDoc(hDC, &di);
+ if (jobId <= 0) {
+ Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ if (StartPage(hDC) <= 0) {
+ Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ StretchDIBits(hDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0,
+ graphPtr->width, graphPtr->height, ds.dsBm.bmBits,
+ (LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
+ EndPage(hDC);
+ EndDoc(hDC);
+ result = TCL_OK;
+ done:
+ DeleteBitmap(hBitmap);
+ return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * Print2Op --
+ *
+ * Prints directly to the designated printer device.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred,
+ * TCL_ERROR is returned and interp->result will contain an
+ * error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+Print2Op(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Drawable drawable;
+ int noBackingStore = 0;
+ int width, height;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+ graphPtr->width = graphPtr->reqWidth;
+ }
+ if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+ graphPtr->height = graphPtr->reqHeight;
+ }
+ if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
+ return TCL_ERROR;
+ } {
+ int oldMode;
+ HDC hDC;
+ double xRatio, yRatio;
+ TkWinDC *drawPtr;
+ double vportWidth, vportHeight;
+
+ drawPtr = (TkWinDC *) drawable;
+ hDC = drawPtr->hdc;
+ Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
+ oldMode = SetMapMode(hDC, MM_ISOTROPIC);
+ if (oldMode == 0) {
+ Tcl_AppendResult(interp, "can't set mode for printer DC: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ vportWidth = graphPtr->width * xRatio;
+ vportHeight = graphPtr->height * yRatio;
+ SetViewportExtEx(hDC, ROUND(vportWidth), ROUND(vportHeight), NULL);
+ SetWindowExtEx(hDC, graphPtr->width, graphPtr->height, NULL);
+ }
+ Blt_StartPrintJob(interp, argv[2]);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+ width = graphPtr->width, height = graphPtr->height;
+ Blt_EndPrintJob(interp, argv[2]);
+ return TCL_OK;
+}
+
+#endif /* NO_PRINTER */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ * Convert a string represent a node number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Contains a pointer to the tabset containing
+ * this image. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ int *formatPtr = (int *)(record + offset);
+ char c;
+
+ c = string[0];
+ if ((c == 'p') && (strcmp(string, "photo") == 0)) {
+ *formatPtr = FORMAT_PHOTO;
+#ifdef WIN32
+ } else if ((c == 'e') && (strcmp(string, "emf") == 0)) {
+ *formatPtr = FORMAT_EMF;
+ } else if ((c == 'w') && (strcmp(string, "wmf") == 0)) {
+ *formatPtr = FORMAT_WMF;
+#endif /* WIN32 */
+ } else {
+#ifdef WIN32
+ Tcl_AppendResult(interp, "bad format \"", string,
+ "\": should be photo, emf, or wmf.", (char *)NULL);
+#else
+ Tcl_AppendResult(interp, "bad format \"", string,
+ "\": should be photo.", (char *)NULL);
+#endif /* WIN32 */
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#ifdef WIN32
+static int InitMetaFileHeader(
+ Tk_Window tkwin,
+ int width, int height,
+ APMHEADER *mfhPtr)
+{
+ unsigned int *p;
+ unsigned int sum;
+ Screen *screen;
+#define MM_INCH 25.4
+ double dpiX, dpiY;
+
+ mfhPtr->key = 0x9ac6cdd7L;
+ mfhPtr->hmf = 0;
+ mfhPtr->inch = 1440;
+
+ screen = Tk_Screen(tkwin);
+ dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
+ dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
+
+ mfhPtr->bbox.Left = mfhPtr->bbox.Top = 0;
+ mfhPtr->bbox.Bottom = (SHORT)((width * 1440)/ dpiX);
+ mfhPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
+ mfhPtr->reserved = 0;
+ sum = 0;
+ for (p = (unsigned int *)mfhPtr;
+ p < (unsigned int *)&(mfhPtr->checksum); p++) {
+ sum ^= *p;
+ }
+ mfhPtr->checksum = sum;
+ return TCL_OK;
+}
+
+static int
+CreateAPMetaFile(
+ Tcl_Interp *interp,
+ HANDLE hMetaFile,
+ HDC hDC,
+ APMHEADER *mfhPtr,
+ char *fileName)
+{
+ HANDLE hFile;
+ HANDLE hMem;
+ LPVOID buffer;
+ int result;
+ DWORD count, nBytes;
+
+ result = TCL_ERROR;
+ hMem = NULL;
+ hFile = CreateFile(
+ fileName, /* File path */
+ GENERIC_WRITE, /* Access mode */
+ 0, /* No sharing. */
+ NULL, /* Security attributes */
+ CREATE_ALWAYS, /* Overwrite any existing file */
+ FILE_ATTRIBUTE_NORMAL,
+ NULL); /* No template file */
+ if (hFile == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't create metafile \"", fileName,
+ "\":", Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((!WriteFile(hFile, (LPVOID)mfhPtr, sizeof(APMHEADER), &count,
+ NULL)) || (count != sizeof(APMHEADER))) {
+ Tcl_AppendResult(interp, "can't create metafile header to \"",
+ fileName, "\":", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
+ hMem = GlobalAlloc(GHND, nBytes);
+ if (hMem == NULL) {
+ Tcl_AppendResult(interp, "can't create allocate global memory:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ if (!GetWinMetaFileBits(hMetaFile, nBytes, buffer, MM_ANISOTROPIC, hDC)) {
+ Tcl_AppendResult(interp, "can't get metafile bits:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ if ((!WriteFile(hFile, buffer, nBytes, &count, NULL)) ||
+ (count != nBytes)) {
+ Tcl_AppendResult(interp, "can't write metafile bits:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ result = TCL_OK;
+ error:
+ CloseHandle(hFile);
+ if (hMem != NULL) {
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ return result;
+}
+#endif /*WIN32*/
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ * Snaps a picture of the graph and stores it in the specified image
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the list of the graph coordinates. If an error occurred
+ * while parsing the window positions, TCL_ERROR is returned,
+ * then interp->result will contain an error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SnapOp(graphPtr, interp, argc, argv)
+ Graph *graphPtr; /* Graph widget record */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int result;
+ Pixmap drawable;
+ int noBackingStore = 0;
+ register int i;
+ SnapData data;
+
+ /* .g snap ?switches? name */
+ data.height = Tk_Height(graphPtr->tkwin);
+ data.width = Tk_Width(graphPtr->tkwin);
+ data.format = FORMAT_PHOTO;
+ /* Process switches */
+ i = Blt_ProcessSwitches(interp, snapSwitches, argc - 2, argv + 2,
+ (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
+ if (i < 0) {
+ return TCL_ERROR;
+ }
+ i += 2;
+ if (i >= argc) {
+ Tcl_AppendResult(interp, "missing name argument: should be \"",
+ argv[0], "snap ?switches? name\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ data.name = argv[i];
+ if (data.width < 2) {
+ data.width = 400;
+ }
+ if (data.height < 2) {
+ data.height = 400;
+ }
+ /* Always re-compute the layout of the graph before snapping the photo. */
+ graphPtr->width = data.width;
+ graphPtr->height = data.height;
+ Blt_LayoutGraph(graphPtr);
+
+ result = TCL_ERROR;
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ if (data.format == FORMAT_PHOTO) {
+ drawable = Tk_GetPixmap(graphPtr->display, drawable, graphPtr->width,
+ graphPtr->height, Tk_Depth(graphPtr->tkwin));
+#ifdef WIN32
+ assert(drawable != None);
+#endif
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+ result = Blt_SnapPhoto(interp, graphPtr->tkwin, drawable, 0, 0,
+ data.width, data.height, data.width, data.height, data.name, 1.0);
+ Tk_FreePixmap(graphPtr->display, drawable);
+#ifdef WIN32
+ } else {
+ TkWinDC drawableDC;
+ TkWinDCState state;
+ HDC hRefDC, hDC;
+ HENHMETAFILE hMetaFile;
+ Tcl_DString dString;
+ char *title;
+
+ hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+ Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ title = Tcl_DStringValue(&dString);
+ hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, title);
+ Tcl_DStringFree(&dString);
+
+ if (hDC == NULL) {
+ Tcl_AppendResult(interp, "can't create metafile: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ drawableDC.hdc = hDC;
+ drawableDC.type = TWD_WINDC;
+
+ Blt_LayoutGraph(graphPtr);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
+
+ hMetaFile = CloseEnhMetaFile(hDC);
+ if (strcmp(data.name, "CLIPBOARD") == 0) {
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(drawable);
+ OpenClipboard(hWnd);
+ EmptyClipboard();
+ SetClipboardData(CF_ENHMETAFILE, hMetaFile);
+ CloseClipboard();
+ result = TCL_OK;
+ } else {
+ result = TCL_ERROR;
+ if (data.format == FORMAT_WMF) {
+ APMHEADER mfh;
+
+ assert(sizeof(mfh) == 22);
+ InitMetaFileHeader(graphPtr->tkwin, data.width, data.height,
+ &mfh);
+ result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh,
+ data.name);
+ } else {
+ HENHMETAFILE hMetaFile2;
+
+ hMetaFile2 = CopyEnhMetaFile(hMetaFile, data.name);
+ if (hMetaFile2 != NULL) {
+ result = TCL_OK;
+ DeleteEnhMetaFile(hMetaFile2);
+ }
+ }
+ DeleteEnhMetaFile(hMetaFile);
+ }
+ TkWinReleaseDrawableDC(drawable, hRefDC, &state);
+#endif /*WIN32*/
+ }
+ graphPtr->flags = MAP_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * GraphWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+static Blt_OpSpec graphOps[] =
+{
+ {"axis", 1, (Blt_Op)Blt_VirtualAxisOp, 2, 0, "oper ?args?",},
+ {"bar", 2, (Blt_Op)BarOp, 2, 0, "oper ?args?",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+ {"crosshairs", 2, (Blt_Op)Blt_CrosshairsOp, 2, 0, "oper ?args?",},
+ {"element", 2, (Blt_Op)ElementOp, 2, 0, "oper ?args?",},
+ {"extents", 2, (Blt_Op)ExtentsOp, 3, 3, "item",},
+ {"grid", 1, (Blt_Op)Blt_GridOp, 2, 0, "oper ?args?",},
+ {"inside", 3, (Blt_Op)InsideOp, 4, 4, "winX winY",},
+ {"invtransform", 3, (Blt_Op)InvtransformOp, 4, 4, "winX winY",},
+ {"legend", 2, (Blt_Op)Blt_LegendOp, 2, 0, "oper ?args?",},
+ {"line", 2, (Blt_Op)LineOp, 2, 0, "oper ?args?",},
+ {"marker", 2, (Blt_Op)Blt_MarkerOp, 2, 0, "oper ?args?",},
+ {"pen", 2, (Blt_Op)Blt_PenOp, 2, 0, "oper ?args?",},
+ {"postscript", 2, (Blt_Op)Blt_PostScriptOp, 2, 0, "oper ?args?",},
+#ifndef NO_PRINTER
+ {"print1", 2, (Blt_Op)Print1Op, 3, 3, "printerId",},
+ {"print2", 2, (Blt_Op)Print2Op, 3, 3, "printerId",},
+#endif /*NO_PRINTER*/
+ {"snap", 1, (Blt_Op)SnapOp, 3, 0, "?switches? name",},
+ {"transform", 1, (Blt_Op)TransformOp, 4, 4, "x y",},
+ {"x2axis", 2, (Blt_Op)X2AxisOp, 2, 0, "oper ?args?",},
+ {"xaxis", 2, (Blt_Op)XAxisOp, 2, 0, "oper ?args?",},
+ {"y2axis", 2, (Blt_Op)Y2AxisOp, 2, 0, "oper ?args?",},
+ {"yaxis", 2, (Blt_Op)YAxisOp, 2, 0, "oper ?args?",},
+};
+static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_GraphInstCmdProc(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+ Graph *graphPtr = clientData;
+
+ proc = Blt_GetOp(interp, nGraphOps, graphOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(graphPtr);
+ result = (*proc) (graphPtr, interp, argc, argv);
+ Tcl_Release(graphPtr);
+ return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * NewGraph --
+ *
+ * Creates a new window and Tcl command representing an
+ * instance of a graph widget.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+NewGraph(interp, argc, argv, classUid)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ Tk_Uid classUid;
+{
+ Graph *graphPtr;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ graphPtr = CreateGraph(interp, argc, argv, classUid);
+ if (graphPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(graphPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * GraphCmd --
+ *
+ * Creates a new window and Tcl command representing an
+ * instance of a graph widget.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GraphCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return NewGraph(interp, argc, argv, bltLineElementUid);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BarchartCmd --
+ *
+ * Creates a new window and Tcl command representing an
+ * instance of a barchart widget.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BarchartCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return NewGraph(interp, argc, argv, bltBarElementUid);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * StripchartCmd --
+ *
+ * Creates a new window and Tcl command representing an
+ * instance of a barchart widget.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StripchartCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return NewGraph(interp, argc, argv, bltStripElementUid);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DrawMargins --
+ *
+ * Draws the exterior region of the graph (axes, ticks, titles, etc)
+ * onto a pixmap. The interior region is defined by the given
+ * rectangle structure.
+ *
+ * ---------------------------------
+ * | |
+ * | rectArr[0] |
+ * | |
+ * ---------------------------------
+ * | |top right| |
+ * | | | |
+ * | | | |
+ * | [1] | | [2] |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | |left bottom| |
+ * ---------------------------------
+ * | |
+ * | rectArr[3] |
+ * | |
+ * ---------------------------------
+ *
+ * X coordinate axis
+ * Y coordinate axis
+ * legend
+ * interior border
+ * exterior border
+ * titles (X and Y axis, graph)
+ *
+ * Returns:
+ * None.
+ *
+ * Side Effects:
+ * Exterior of graph is displayed in its window.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+DrawMargins(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ XRectangle rects[4];
+ /*
+ * Draw the four outer rectangles which encompass the plotting
+ * surface. This clears the surrounding area and clips the plot.
+ */
+ rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0;
+ rects[0].width = rects[3].width = (short int)graphPtr->width;
+ rects[0].height = (short int)graphPtr->top;
+ rects[3].y = graphPtr->bottom;
+ rects[3].height = graphPtr->height - graphPtr->bottom;
+ rects[2].y = rects[1].y = graphPtr->top;
+ rects[1].width = graphPtr->left;
+ rects[2].height = rects[1].height = graphPtr->bottom - graphPtr->top;
+ rects[2].x = graphPtr->right;
+ rects[2].width = graphPtr->width - graphPtr->right;
+
+ if (graphPtr->tile != NULL) {
+ Blt_SetTileOrigin(graphPtr->tkwin, graphPtr->tile, 0, 0);
+ Blt_TileRectangles(graphPtr->tkwin, drawable, graphPtr->tile, rects, 4);
+ } else {
+ XFillRectangles(graphPtr->display, drawable, graphPtr->fillGC, rects,
+ 4);
+ }
+
+ /* Draw 3D border around the plotting area */
+
+ if (graphPtr->plotBW > 0) {
+ int x, y, width, height;
+
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+ width = (graphPtr->right - graphPtr->left) + (2 * graphPtr->plotBW);
+ height = (graphPtr->bottom - graphPtr->top) + (2 * graphPtr->plotBW);
+ Tk_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border,
+ x, y, width, height, graphPtr->plotBW, graphPtr->plotRelief);
+ }
+ if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
+ /* Legend is drawn on one of the graph margins */
+ Blt_DrawLegend(graphPtr->legend, drawable);
+ }
+ if (graphPtr->titleText != NULL) {
+ Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->titleText,
+ &(graphPtr->titleStyle), graphPtr->titleX, graphPtr->titleY);
+ }
+ Blt_DrawAxes(graphPtr, drawable);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawPlotRegion --
+ *
+ * Draws the contents of the plotting area. This consists of
+ * the elements, markers (draw under elements), axis limits,
+ * grid lines, and possibly the legend. Typically, the output
+ * will be cached into a backing store pixmap, so that redraws
+ * can occur quickly.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawPlotRegion(graphPtr, drawable)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+{
+ /* Clear the background of the plotting area. */
+ XFillRectangle(graphPtr->display, drawable, graphPtr->plotFillGC,
+ graphPtr->left, graphPtr->top, graphPtr->right - graphPtr->left + 1,
+ graphPtr->bottom - graphPtr->top + 1);
+
+ /* Draw the elements, markers, legend, and axis limits. */
+
+ if (!graphPtr->gridPtr->hidden) {
+ Blt_DrawGrid(graphPtr, drawable);
+ }
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (!Blt_LegendIsRaised(graphPtr->legend))) {
+ Blt_DrawLegend(graphPtr->legend, drawable);
+ }
+ Blt_DrawAxisLimits(graphPtr, drawable);
+ Blt_DrawElements(graphPtr, drawable);
+}
+
+void
+Blt_LayoutGraph(graphPtr)
+ Graph *graphPtr;
+{
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (graphPtr->flags & LAYOUT_NEEDED) {
+ Blt_LayoutMargins(graphPtr);
+ graphPtr->flags &= ~LAYOUT_NEEDED;
+ }
+ /* Compute coordinate transformations for graph components */
+ if ((graphPtr->vRange < 1) || (graphPtr->hRange < 1)) {
+ return;
+ }
+ if (graphPtr->flags & MAP_WORLD) {
+ Blt_MapAxes(graphPtr);
+ }
+ Blt_MapElements(graphPtr);
+ Blt_MapMarkers(graphPtr);
+ Blt_MapGrid(graphPtr);
+ graphPtr->flags &= ~(MAP_ALL);
+}
+
+void
+Blt_DrawGraph(graphPtr, drawable, backingStore)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ int backingStore; /* If non-zero, use backing store for
+ * plotting area. */
+{
+ if (backingStore) {
+ /*
+ * Create another pixmap to save elements if one doesn't
+ * already exist or the size of the window has changed.
+ */
+ if ((graphPtr->backPixmap == None) ||
+ (graphPtr->backWidth != graphPtr->width) ||
+ (graphPtr->backHeight != graphPtr->height)) {
+
+ if (graphPtr->backPixmap != None) {
+ Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+ }
+ graphPtr->backPixmap = Tk_GetPixmap(graphPtr->display,
+ Tk_WindowId(graphPtr->tkwin), graphPtr->width,
+ graphPtr->height, Tk_Depth(graphPtr->tkwin));
+ graphPtr->backWidth = graphPtr->width;
+ graphPtr->backHeight = graphPtr->height;
+ graphPtr->flags |= REDRAW_BACKING_STORE;
+ }
+ if (graphPtr->flags & REDRAW_BACKING_STORE) {
+
+ /* The backing store is new or out-of-date. */
+
+ DrawPlotRegion(graphPtr, graphPtr->backPixmap);
+ graphPtr->flags &= ~REDRAW_BACKING_STORE;
+ }
+
+ /* Copy the pixmap to the one used for drawing the entire graph. */
+
+ XCopyArea(graphPtr->display, graphPtr->backPixmap, drawable,
+ graphPtr->drawGC, graphPtr->left, graphPtr->top,
+ (graphPtr->right - graphPtr->left + 1),
+ (graphPtr->bottom - graphPtr->top + 1),
+ graphPtr->left, graphPtr->top);
+ } else {
+ DrawPlotRegion(graphPtr, drawable);
+ }
+
+ /* Draw markers above elements */
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
+ Blt_DrawActiveElements(graphPtr, drawable);
+
+ if (graphPtr->flags & DRAW_MARGINS) {
+ DrawMargins(graphPtr, drawable);
+ }
+ if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) &&
+ (Blt_LegendIsRaised(graphPtr->legend))) {
+ Blt_DrawLegend(graphPtr->legend, drawable);
+ }
+ /* Draw 3D border just inside of the focus highlight ring. */
+ if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border,
+ graphPtr->highlightWidth, graphPtr->highlightWidth,
+ graphPtr->width - 2 * graphPtr->highlightWidth,
+ graphPtr->height - 2 * graphPtr->highlightWidth,
+ graphPtr->borderWidth, graphPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & GRAPH_FOCUS)) {
+ GC gc;
+
+ gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
+ Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
+ drawable);
+ }
+}
+
+static void
+UpdateMarginTraces(graphPtr)
+ Graph *graphPtr;
+{
+ Margin *marginPtr;
+ int size;
+ register int i;
+
+ for (i = 0; i < 4; i++) {
+ marginPtr = graphPtr->margins + i;
+ if (marginPtr->varName != NULL) { /* Trigger variable traces */
+ if ((marginPtr->site == MARGIN_LEFT) ||
+ (marginPtr->site == MARGIN_RIGHT)) {
+ size = marginPtr->width;
+ } else {
+ size = marginPtr->height;
+ }
+ Tcl_SetVar(graphPtr->interp, marginPtr->varName, Blt_Itoa(size),
+ TCL_GLOBAL_ONLY);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayGraph --
+ *
+ * This procedure is invoked to display a graph widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the graph in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DisplayGraph(clientData)
+ ClientData clientData;
+{
+ Graph *graphPtr = clientData;
+ Pixmap drawable;
+
+ graphPtr->flags &= ~REDRAW_PENDING;
+ if (graphPtr->tkwin == NULL) {
+ return; /* Window destroyed (should not get here) */
+ }
+#ifdef notdef
+ fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(graphPtr->tkwin));
+#endif
+ if (Blt_GraphUpdateNeeded(graphPtr)) {
+ /*
+ * One of the elements of the graph has a vector notification
+ * pending. This means that the vector will eventually notify
+ * the graph that its data has changed. Since the graph uses
+ * the actual vector (not a copy) we need to keep in-sync.
+ * Therefore don't draw right now but wait until we've been
+ * notified before redrawing.
+ */
+ return;
+ }
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ Blt_LayoutGraph(graphPtr);
+ Blt_UpdateCrosshairs(graphPtr);
+ if (!Tk_IsMapped(graphPtr->tkwin)) {
+ /* The graph's window isn't displayed, so don't bother
+ * drawing anything. By getting this far, we've at least
+ * computed the coordinates of the graph's new layout. */
+ return;
+ }
+
+ /* Disable crosshairs before redisplaying to the screen */
+ Blt_DisableCrosshairs(graphPtr);
+ /*
+ * Create a pixmap the size of the window for double buffering.
+ */
+ if (graphPtr->doubleBuffer) {
+ drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
+ } else {
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ }
+#ifdef WIN32
+ assert(drawable != None);
+#endif
+ Blt_DrawGraph(graphPtr, drawable,
+ graphPtr->backingStore && graphPtr->doubleBuffer);
+ if (graphPtr->flags & DRAW_MARGINS) {
+ XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
+ graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0);
+ } else {
+ XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
+ graphPtr->drawGC, graphPtr->left, graphPtr->top,
+ (graphPtr->right - graphPtr->left + 1),
+ (graphPtr->bottom - graphPtr->top + 1),
+ graphPtr->left, graphPtr->top);
+ }
+ if (graphPtr->doubleBuffer) {
+ Tk_FreePixmap(graphPtr->display, drawable);
+ }
+ Blt_EnableCrosshairs(graphPtr);
+ graphPtr->flags &= ~RESET_WORLD;
+ UpdateMarginTraces(graphPtr);
+}
+
+/*LINTLIBRARY*/
+int
+Blt_GraphInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpecs[] =
+ {
+ {"graph", GraphCmd,},
+ {"barchart", BarchartCmd,},
+ {"stripchart", StripchartCmd,},
+ };
+ bltBarElementUid = Tk_GetUid("BarElement");
+ bltLineElementUid = Tk_GetUid("LineElement");
+ bltStripElementUid = Tk_GetUid("StripElement");
+ bltContourElementUid = Tk_GetUid("ContourElement");
+
+ bltLineMarkerUid = Tk_GetUid("LineMarker");
+ bltBitmapMarkerUid = Tk_GetUid("BitmapMarker");
+ bltImageMarkerUid = Tk_GetUid("ImageMarker");
+ bltTextMarkerUid = Tk_GetUid("TextMarker");
+ bltPolygonMarkerUid = Tk_GetUid("PolygonMarker");
+ bltWindowMarkerUid = Tk_GetUid("WindowMarker");
+
+ bltXAxisUid = Tk_GetUid("X");
+ bltYAxisUid = Tk_GetUid("Y");
+
+ return Blt_InitCmds(interp, "blt", cmdSpecs, 3);
+}
+
+Graph *
+Blt_GetGraphFromWindowData(tkwin)
+ Tk_Window tkwin;
+{
+ Graph *graphPtr;
+
+ while (tkwin != NULL) {
+ graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
+ if (graphPtr != NULL) {
+ return graphPtr;
+ }
+ tkwin = Tk_Parent(tkwin);
+ }
+ return NULL;
+}
+
+int
+Blt_GraphType(graphPtr)
+ Graph *graphPtr;
+{
+ if (graphPtr->classUid == bltLineElementUid) {
+ return GRAPH;
+ } else if (graphPtr->classUid == bltBarElementUid) {
+ return BARCHART;
+ } else if (graphPtr->classUid == bltStripElementUid) {
+ return STRIPCHART;
+ }
+ return 0;
+}
diff --git a/blt/src/bltGraph.h b/blt/src/bltGraph.h
new file mode 100644
index 00000000000..c3519ab59b7
--- /dev/null
+++ b/blt/src/bltGraph.h
@@ -0,0 +1,680 @@
+/*
+ * bltGraph.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GRAPH_H
+#define _BLT_GRAPH_H
+
+#include "bltInt.h"
+#include "bltHash.h"
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltPs.h"
+#include "bltTile.h"
+
+typedef struct GraphStruct Graph;
+typedef struct ElementStruct Element;
+typedef struct LegendStruct Legend;
+
+#include "bltGrAxis.h"
+#include "bltGrLegd.h"
+
+#define MARKER_UNDER 1 /* Draw markers designated to lie underneath
+ * elements, grids, legend, etc. */
+#define MARKER_ABOVE 0 /* Draw markers designated to rest above
+ * elements, grids, legend, etc. */
+
+#define PADX 2 /* Padding between labels/titles */
+#define PADY 2 /* Padding between labels */
+
+#define MINIMUM_MARGIN 20 /* Minimum margin size */
+
+
+#define BOUND(x, lo, hi) \
+ (((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x))
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Graph component structure definitions
+ *
+ * -------------------------------------------------------------------
+ */
+#define PointInGraph(g,x,y) \
+ (((x) <= (g)->right) && ((x) >= (g)->left) && \
+ ((y) <= (g)->bottom) && ((y) >= (g)->top))
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * ClassType --
+ *
+ * Enumerates the different types of graph elements this program
+ * produces. An element can be either a line or a bar.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef enum {
+ CLASS_UNKNOWN,
+ CLASS_LINE_ELEMENT,
+ CLASS_STRIP_ELEMENT,
+ CLASS_BAR_ELEMENT,
+ CLASS_BITMAP_MARKER,
+ CLASS_IMAGE_MARKER,
+ CLASS_LINE_MARKER,
+ CLASS_POLYGON_MARKER,
+ CLASS_TEXT_MARKER,
+ CLASS_WINDOW_MARKER
+
+} ClassType;
+
+/*
+ * Mask values used to selectively enable GRAPH or BARCHART entries in
+ * the various configuration specs.
+ */
+#define GRAPH (TK_CONFIG_USER_BIT << 1)
+#define STRIPCHART (TK_CONFIG_USER_BIT << 2)
+#define BARCHART (TK_CONFIG_USER_BIT << 3)
+#define LINE_GRAPHS (GRAPH | STRIPCHART)
+#define ALL_GRAPHS (GRAPH | BARCHART | STRIPCHART)
+
+#define PEN_DELETE_PENDING (1<<0)
+#define ACTIVE_PEN (TK_CONFIG_USER_BIT << 6)
+#define NORMAL_PEN (TK_CONFIG_USER_BIT << 7)
+#define ALL_PENS (NORMAL_PEN | ACTIVE_PEN)
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * FreqInfo --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ int freq; /* Number of occurrences of x-coordinate */
+ Axis2D axes; /* Indicates which x and y axis are mapped to
+ * the x-value */
+ double sum; /* Sum of the ordinates of each duplicate
+ * abscissa */
+ int count;
+ double lastY;
+
+} FreqInfo;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * FreqKey --
+ *
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ double value; /* Duplicated abscissa */
+ Axis2D axes; /* Axis mapping of element */
+} FreqKey;
+
+/*
+ * BarModes --
+ *
+ * Bar elements are displayed according to their x-y coordinates.
+ * If two bars have the same abscissa (x-coordinate), the bar
+ * segments will be drawn according to one of the following
+ * modes:
+ */
+
+typedef enum BarModes {
+ MODE_INFRONT, /* Each successive segment is drawn in
+ * front of the previous. */
+ MODE_STACKED, /* Each successive segment is drawn
+ * stacked above the previous. */
+ MODE_ALIGNED, /* Each successive segment is drawn
+ * aligned to the previous from
+ * right-to-left. */
+ MODE_OVERLAP /* Like "aligned", each successive segment
+ * is drawn from right-to-left. In addition
+ * the segments will overlap each other
+ * by a small amount */
+} BarMode;
+
+typedef struct PenStruct Pen;
+typedef struct MarkerStruct Marker;
+
+typedef Pen *(PenCreateProc) _ANSI_ARGS_((void));
+typedef int (PenConfigureProc) _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+typedef void (PenDestroyProc) _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+
+struct PenStruct {
+ char *name; /* Pen style identifier. If NULL pen
+ * was statically allocated. */
+ Tk_Uid classUid; /* Type of pen */
+ char *typeId; /* String token identifying the type of pen */
+ unsigned int flags; /* Indicates if the pen element is active or
+ * normal */
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+
+};
+
+typedef enum {
+ PS_MONO_BACKGROUND,
+ PS_MONO_FOREGROUND
+} MonoAttribute;
+
+/*
+ * PostScript --
+ *
+ * Structure contains information specific to the outputting of
+ * PostScript commands to print the graph.
+ *
+ */
+typedef struct {
+ /* User configurable fields */
+
+ int decorations; /* If non-zero, print graph with
+ * color background and 3D borders */
+
+ int reqWidth, reqHeight; /* If greater than zero, represents the
+ * requested dimensions of the printed graph */
+ int reqPaperWidth;
+ int reqPaperHeight; /* Requested dimensions for the PostScript
+ * page. Can constrain the size of the graph
+ * if the graph (plus padding) is larger than
+ * the size of the page. */
+ Blt_Pad padX, padY; /* Requested padding on the exterior of the
+ * graph. This forms the bounding box for
+ * the page. */
+ PsColorMode colorMode; /* Selects the color mode for PostScript page
+ * (0=monochrome, 1=greyscale, 2=color) */
+ char *colorVarName; /* If non-NULL, is the name of a Tcl array
+ * variable containing X to PostScript color
+ * translations */
+ char *fontVarName; /* If non-NULL, is the name of a Tcl array
+ * variable containing X to PostScript font
+ * translations */
+ int landscape; /* If non-zero, orient the page 90 degrees */
+ int center; /* If non-zero, center the graph on the page */
+ int maxpect; /* If non-zero, indicates to scale the graph
+ * so that it fills the page (maintaining the
+ * aspect ratio of the graph) */
+ int addPreview; /* If non-zero, generate a preview image and
+ * add it to the PostScript output */
+ int footer; /* If non-zero, a footer with the title, date
+ * and user will be added to the PostScript
+ * output outside of the bounding box. */
+ int previewFormat; /* Format of EPS preview:
+ * PS_PREVIEW_WMF, PS_PREVIEW_EPSI, or
+ * PS_PREVIEW_TIFF. */
+
+ /* Computed fields */
+
+ int left, bottom; /* Bounding box of PostScript plot. */
+ int right, top;
+
+ double pageScale; /* Scale of page. Set if "-maxpect" option
+ * is set, otherwise 1.0. */
+} PostScript;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Grid
+ *
+ * Contains attributes of describing how to draw grids (at major
+ * ticks) in the graph. Grids may be mapped to either/both x and
+ * y axis.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ GC gc; /* Graphics context for the grid. */
+ Axis2D axes;
+ int hidden; /* If non-zero, grid isn't displayed. */
+ int minorGrid; /* If non-zero, draw grid line for minor
+ * axis ticks too */
+ Blt_Dashes dashes; /* Dashstyle of the grid. This represents
+ * an array of alternatingly drawn pixel
+ * values. */
+ int lineWidth; /* Width of the grid lines */
+ XColor *colorPtr; /* Color of the grid lines */
+
+ struct GridSegments {
+ Segment2D *segments; /* Array of line segments representing the
+ * x or y grid lines */
+ int nSegments; /* # of axis segments. */
+ } x, y;
+
+} Grid;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ * Contains the line segments positions and graphics context used
+ * to simulate crosshairs (by XOR-ing) on the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct CrosshairsStruct Crosshairs;
+
+typedef struct {
+ short int width, height; /* Extents of the margin */
+
+ short int axesOffset;
+ short int axesTitleLength; /* Width of the widest title to be shown.
+ * Multiple titles are displayed in
+ * another margin. This is the minimum
+ * space requirement. */
+ unsigned int nAxes; /* Number of axes to be displayed */
+ Blt_Chain *chainPtr; /* Extra axes associated with this margin */
+
+ char *varName; /* If non-NULL, name of variable to be
+ * updated when the margin size changes */
+
+ int reqSize; /* Requested size of margin */
+ int site; /* Indicates where margin is located:
+ * left/right/top/bottom. */
+} Margin;
+
+#define MARGIN_NONE -1
+#define MARGIN_BOTTOM 0
+#define MARGIN_LEFT 1
+#define MARGIN_TOP 2
+#define MARGIN_RIGHT 3
+
+#define rightMargin margins[MARGIN_RIGHT]
+#define leftMargin margins[MARGIN_LEFT]
+#define topMargin margins[MARGIN_TOP]
+#define bottomMargin margins[MARGIN_BOTTOM]
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Graph --
+ *
+ * Top level structure containing everything pertaining to
+ * the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+struct GraphStruct {
+ unsigned int flags; /* Flags; see below for definitions. */
+ Tcl_Interp *interp; /* Interpreter associated with graph */
+ Tk_Window tkwin; /* Window that embodies the graph. NULL
+ * means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up. */
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already gone
+ * away. */
+ Tcl_Command cmdToken; /* Token for graph's widget command. */
+
+ char *data; /* This value isn't used in C code.
+ * It may be used in Tcl bindings to
+ * associate extra data. */
+
+ Tk_Cursor cursor;
+
+ int inset; /* Sum of focus highlight and 3-D
+ * border. Indicates how far to
+ * offset the graph from outside
+ * edge of the window. */
+
+ int borderWidth; /* Width of the exterior border */
+ int relief; /* Relief of the exterior border */
+ Tk_3DBorder border; /* 3-D border used to delineate the plot
+ * surface and outer edge of window */
+
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ TextStyle titleStyle; /* Graph title */
+ char *titleText;
+ short int titleX, titleY;
+
+ char *takeFocus;
+
+ int reqWidth, reqHeight; /* Requested size of graph window */
+ int width, height; /* Size of graph window or PostScript
+ * page */
+
+ Blt_HashTable penTable; /* Table of pens */
+
+ struct Component {
+ Blt_HashTable table; /* Hash table of ids. */
+ Blt_Chain *chainPtr; /* Display list. */
+ Blt_HashTable tagTable; /* Table of bind tags. */
+ } elements, markers, axes;
+
+ Tk_Uid classUid; /* Default element type */
+
+ Blt_BindTable bindTable;
+ int nextMarkerId; /* Tracks next marker identifier available */
+
+ Blt_Chain *axisChain[4]; /* Chain of axes for each of the
+ * margins. They're separate from the
+ * margin structures to make it easier
+ * to invert the X-Y axes by simply
+ * switching chain pointers.
+ */
+ Margin margins[4];
+
+ PostScript *postscript; /* PostScript options: see bltGrPS.c */
+ Legend *legend; /* Legend information: see bltGrLegd.c */
+ Crosshairs *crosshairs; /* Crosshairs information: see bltGrHairs.c */
+ Grid *gridPtr; /* Grid attribute information */
+
+ int halo; /* Maximum distance allowed between points
+ * when searching for a point */
+ int inverted; /* If non-zero, indicates the x and y axis
+ * positions should be inverted. */
+ Blt_Tile tile;
+ GC drawGC; /* Used for drawing on the margins. This
+ * includes the axis lines */
+ GC fillGC; /* Used to fill the background of the
+ * margins. The fill is governed by
+ * the background color or the tiled
+ * pixmap. */
+ int plotBW; /* Width of interior 3-D border. */
+ int plotRelief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+ XColor *plotBg; /* Color of plotting surface */
+
+ GC plotFillGC; /* Used to fill the plotting area with a
+ * solid background color. The fill color
+ * is stored in "plotBg". */
+
+ /* If non-zero, force plot to conform to aspect ratio W/H */
+ double aspect;
+
+ short int left, right; /* Coordinates of plot bbox */
+ short int top, bottom;
+
+ Blt_Pad padX; /* Vertical padding for plotarea */
+ int vRange, vOffset; /* Vertical axis range and offset from the
+ * left side of the graph window. Used to
+ * transform coordinates to vertical
+ * axes. */
+ Blt_Pad padY; /* Horizontal padding for plotarea */
+ int hRange, hOffset; /* Horizontal axis range and offset from
+ * the top of the graph window. Used to
+ * transform horizontal axes */
+
+ int doubleBuffer; /* If non-zero, draw the graph into a pixmap
+ * first to reduce flashing. */
+ int backingStore; /* If non-zero, cache elements by drawing
+ * them into a pixmap */
+ Pixmap backPixmap; /* Pixmap used to cache elements
+ * displayed. If *backingStore* is
+ * non-zero, each element is drawn
+ * into this pixmap before it is
+ * copied onto the screen. The pixmap
+ * then acts as a cache (only the
+ * pixmap is redisplayed if the none
+ * of elements have changed). This is
+ * done so that markers can be redrawn
+ * quickly over elements without
+ * redrawing each element. */
+ int backWidth, backHeight; /* Size of element backing store pixmap. */
+
+ /*
+ * barchart specific information
+ */
+ double baseline; /* Baseline from bar chart. */
+ double barWidth; /* Default width of each bar in graph units.
+ * The default width is 1.0 units. */
+ BarMode mode; /* Mode describing how to display bars
+ * with the same x-coordinates. Mode can
+ * be "stack", "align", or "normal" */
+ FreqInfo *freqArr; /* Contains information about duplicate
+ * x-values in bar elements (malloc-ed).
+ * This information can also be accessed
+ * by the frequency hash table */
+ Blt_HashTable freqTable; /* */
+ int nStacks; /* Number of entries in frequency array.
+ * If zero, indicates nothing special needs
+ * to be done for "stack" or "align" modes */
+ char *dataCmd; /* New data callback? */
+
+};
+
+/*
+ * Bit flags definitions:
+ *
+ * All kinds of state information kept here. All these
+ * things happen when the window is available to draw into
+ * (DisplayGraph). Need the window width and height before
+ * we can calculate graph layout (i.e. the screen coordinates
+ * of the axes, elements, titles, etc). But we want to do this
+ * only when we have to, not every time the graph is redrawn.
+ *
+ * Same goes for maintaining a pixmap to double buffer graph
+ * elements. Need to mark when the pixmap needs to updated.
+ *
+ *
+ * MAP_ITEM Indicates that the element/marker/axis
+ * configuration has changed such that
+ * its layout of the item (i.e. its
+ * position in the graph window) needs
+ * to be recalculated.
+ *
+ * MAP_ALL Indicates that the layout of the axes and
+ * all elements and markers and the graph need
+ * to be recalculated. Otherwise, the layout
+ * of only those markers and elements that
+ * have changed will be reset.
+ *
+ * GET_AXIS_GEOMETRY Indicates that the size of the axes needs
+ * to be recalculated.
+ *
+ * RESET_AXES Flag to call to Blt_ResetAxes routine.
+ * This routine recalculates the scale offset
+ * (used for mapping coordinates) of each axis.
+ * If an axis limit has changed, then it sets
+ * flags to re-layout and redraw the entire
+ * graph. This needs to happend before the axis
+ * can compute transformations between graph and
+ * screen coordinates.
+ *
+ * LAYOUT_NEEDED
+ *
+ * REDRAW_BACKING_STORE If set, redraw all elements into the pixmap
+ * used for buffering elements.
+ *
+ * REDRAW_PENDING Non-zero means a DoWhenIdle handler has
+ * already been queued to redraw this window.
+ *
+ * DRAW_LEGEND Non-zero means redraw the legend. If this is
+ * the only DRAW_* flag, the legend display
+ * routine is called instead of the graph
+ * display routine.
+ *
+ * DRAW_MARGINS Indicates that the margins bordering
+ * the plotting area need to be redrawn.
+ * The possible reasons are:
+ *
+ * 1) an axis configuration changed
+ * 2) an axis limit changed
+ * 3) titles have changed
+ * 4) window was resized.
+ *
+ * GRAPH_FOCUS
+ */
+
+#define MAP_ITEM (1<<0) /* 0x0001 */
+#define MAP_ALL (1<<1) /* 0x0002 */
+#define GET_AXIS_GEOMETRY (1<<2) /* 0x0004 */
+#define RESET_AXES (1<<3) /* 0x0008 */
+#define LAYOUT_NEEDED (1<<4) /* 0x0010 */
+
+#define REDRAW_PENDING (1<<8) /* 0x0100 */
+#define DRAW_LEGEND (1<<9) /* 0x0200 */
+#define DRAW_MARGINS (1<<10)/* 0x0400 */
+#define REDRAW_BACKING_STORE (1<<11)/* 0x0800 */
+
+#define GRAPH_FOCUS (1<<12)/* 0x1000 */
+#define DATA_CHANGED (1<<13)/* 0x2000 */
+
+#define MAP_WORLD (MAP_ALL|RESET_AXES|GET_AXIS_GEOMETRY)
+#define REDRAW_WORLD (DRAW_MARGINS | DRAW_LEGEND)
+#define RESET_WORLD (REDRAW_WORLD | MAP_WORLD)
+
+/*
+ * ---------------------- Forward declarations ------------------------
+ */
+
+extern int Blt_CreatePostScript _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_CreateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_CreateGrid _ANSI_ARGS_((Graph *graphPtr));
+extern Point2D Blt_InvMap2D _ANSI_ARGS_((Graph *graphPtr, double x,
+ double y, Axis2D *pairPtr));
+extern Point2D Blt_Map2D _ANSI_ARGS_((Graph *graphPtr, double x,
+ double y, Axis2D *pairPtr));
+extern Graph *Blt_GetGraphFromWindowData _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_AdjustAxisPointers _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_LineRectClip _ANSI_ARGS_((Extents2D *extsPtr, Point2D *p,
+ Point2D *q));
+extern int Blt_PolyRectClip _ANSI_ARGS_((Extents2D *extsPtr, Point2D *inputPts,
+ int nInputPts, Point2D *outputPts));
+
+extern void Blt_ComputeStacks _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ConfigureCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyGrid _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyElements _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyMarkers _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyPostScript _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DrawAxes _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawAxisLimits _ANSI_ARGS_((Graph *graphPtr,
+ Drawable drawable));
+extern void Blt_DrawElements _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawActiveElements _ANSI_ARGS_((Graph *graphPtr,
+ Drawable drawable));
+extern void Blt_DrawGraph _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+ int backingStore));
+extern void Blt_DrawGrid _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawMarkers _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+ int under));
+extern void Blt_DrawSegments2D _ANSI_ARGS_((Display *display,
+ Drawable drawable, GC gc, Segment2D *segments, int nSegments));
+extern int Blt_GetCoordinate _ANSI_ARGS_((Tcl_Interp *interp,
+ char *expr, double *valuePtr));
+extern void Blt_InitFreqTable _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_LayoutGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_LayoutMargins _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_EventuallyRedrawGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ResetAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ResetStacks _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_GraphExtents _ANSI_ARGS_((Graph *graphPtr, Extents2D *extsPtr));
+extern void Blt_DisableCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_EnableCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapElements _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapMarkers _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapGrid _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_UpdateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyPens _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_GetPen _ANSI_ARGS_((Graph *graphPtr, char *name,
+ Tk_Uid classUid, Pen **penPtrPtr));
+extern Pen *Blt_BarPen _ANSI_ARGS_((char *penName));
+extern Pen *Blt_LinePen _ANSI_ARGS_((char *penName));
+extern Pen *Blt_CreatePen _ANSI_ARGS_((Graph *graphPtr, char *penName,
+ Tk_Uid classUid, int nOpts, char **options));
+extern int Blt_InitLinePens _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_InitBarPens _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_FreePen _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+
+extern int Blt_VirtualAxisOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_AxisOp _ANSI_ARGS_((Graph *graphPtr, int margin, int argc,
+ char **argv));
+extern int Blt_ElementOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv, Tk_Uid classUid));
+extern int Blt_GridOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_CrosshairsOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_MarkerOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_PenOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_PointInPolygon _ANSI_ARGS_((Point2D *samplePtr,
+ Point2D *screenPts, int nScreenPts));
+extern int Blt_RegionInPolygon _ANSI_ARGS_((Extents2D *extsPtr, Point2D *points,
+ int nPoints, int enclosed));
+extern int Blt_PointInSegments _ANSI_ARGS_((Point2D *samplePtr,
+ Segment2D *segments, int nSegments, double halo));
+extern int Blt_PostScriptOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+ int argc, char **argv));
+extern int Blt_GraphUpdateNeeded _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_DefaultAxes _ANSI_ARGS_((Graph *graphPtr));
+extern Axis *Blt_GetFirstAxis _ANSI_ARGS_((Blt_Chain *chainPtr));
+extern void Blt_UpdateAxisBackgrounds _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_GetAxisSegments _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr,
+ Segment2D **segPtrPtr, int *nSegmentsPtr));
+extern Marker *Blt_NearestMarker _ANSI_ARGS_((Graph *graphPtr, int x, int y,
+ int under));
+extern int Blt_NameToMarker _ANSI_ARGS_((Graph *graphPtr, char *name,
+ Marker **markerPtrPtr));
+
+extern Axis *Blt_NearestAxis _ANSI_ARGS_((Graph *graphPtr, int x, int y));
+
+
+typedef ClientData (MakeTagProc) _ANSI_ARGS_((Graph *graphPtr, char *tagName));
+extern MakeTagProc Blt_MakeElementTag;
+extern MakeTagProc Blt_MakeMarkerTag;
+extern MakeTagProc Blt_MakeAxisTag;
+
+extern Blt_BindTagProc Blt_GraphTags;
+extern Blt_BindTagProc Blt_AxisTags;
+
+extern int Blt_GraphType _ANSI_ARGS_((Graph *graphPtr));
+
+/* ---------------------- Global declarations ------------------------ */
+
+extern Tk_Uid bltBarElementUid;
+extern Tk_Uid bltLineElementUid;
+extern Tk_Uid bltStripElementUid;
+extern Tk_Uid bltLineMarkerUid;
+extern Tk_Uid bltBitmapMarkerUid;
+extern Tk_Uid bltImageMarkerUid;
+extern Tk_Uid bltTextMarkerUid;
+extern Tk_Uid bltPolygonMarkerUid;
+extern Tk_Uid bltWindowMarkerUid;
+extern Tk_Uid bltXAxisUid;
+extern Tk_Uid bltYAxisUid;
+
+#endif /* _BLT_GRAPH_H */
diff --git a/blt/src/bltHash.c b/blt/src/bltHash.c
new file mode 100644
index 00000000000..3f89e7a9fc9
--- /dev/null
+++ b/blt/src/bltHash.c
@@ -0,0 +1,1349 @@
+
+/*
+ * bltHash.c --
+ *
+ *
+ * This module implements an in-memory hash table for the BLT
+ * toolkit. Built upon the Tcl hash table, it adds pool
+ * allocation 64-bit address handling, improved array hash
+ * function.
+ *
+ * Copyright 2001 Silicon Metrics Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Bob Jenkins, 1996. hash.c. Public Domain.
+ * Bob Jenkins, 1997. lookup8.c. Public Domain.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include <bltInt.h>
+
+#include <stdio.h>
+#include <string.h>
+/* The following header is required for LP64 compilation */
+#include <stdlib.h>
+
+#include "bltHash.h"
+
+/*
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+
+#define REBUILD_MULTIPLIER 3
+
+#if (SIZEOF_VOID_P == 8)
+#define RANDOM_INDEX HashOneWord
+#define DOWNSHIFT_START 62
+#else
+
+/*
+ * The following macro takes a preliminary integer hash value and
+ * produces an index into a hash tables bucket list. The idea is
+ * to make it so that preliminary values that are arbitrarily similar
+ * will end up in different buckets. The hash function was taken
+ * from a random-number generator.
+ */
+#define RANDOM_INDEX(tablePtr, i) \
+ (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
+#define DOWNSHIFT_START 28
+#endif
+
+/*
+ * Procedure prototypes for static procedures in this file:
+ */
+
+static Blt_Hash HashArray _ANSI_ARGS_((CONST void *key, size_t length));
+static Blt_HashEntry *ArrayFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key));
+static Blt_HashEntry *ArrayCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key, int *newPtr));
+static Blt_HashEntry *BogusFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key));
+static Blt_HashEntry *BogusCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key, int *newPtr));
+static Blt_Hash HashString _ANSI_ARGS_((CONST char *string));
+static void RebuildTable _ANSI_ARGS_((Blt_HashTable *tablePtr));
+static Blt_HashEntry *StringFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key));
+static Blt_HashEntry *StringCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key, int *newPtr));
+static Blt_HashEntry *OneWordFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key));
+static Blt_HashEntry *OneWordCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key, int *newPtr));
+
+#if (SIZEOF_VOID_P == 8)
+static Blt_Hash HashOneWord _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ CONST void *key));
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashString --
+ *
+ * Compute a one-word summary of a text string, which can be
+ * used to generate a hash index.
+ *
+ * Results:
+ * The return value is a one-word summary of the information in
+ * string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_Hash
+HashString(string)
+ register CONST char *string;/* String from which to compute hash value. */
+{
+ register Blt_Hash result;
+ register Blt_Hash c;
+
+ /*
+ * I tried a zillion different hash functions and asked many other
+ * people for advice. Many people had their own favorite functions,
+ * all different, but no-one had much idea why they were good ones.
+ * I chose the one below (multiply by 9 and add new character)
+ * because of the following reasons:
+ *
+ * 1. Multiplying by 10 is perfect for keys that are decimal strings,
+ * and multiplying by 9 is just about as good.
+ * 2. Times-9 is (shift-left-3) plus (old). This means that each
+ * character's bits hang around in the low-order bits of the
+ * hash value for ever, plus they spread fairly rapidly up to
+ * the high-order bits to fill out the hash value. This seems
+ * to work well both for decimal and non-decimal strings.
+ */
+
+ result = 0;
+ while ((c = *string++) != 0) {
+ result += (result << 3) + c;
+ }
+ return (Blt_Hash)result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringFind --
+ *
+ * Given a hash table with string keys, and a string key, find
+ * the entry with a matching key.
+ *
+ * Results:
+ * The return value is a token for the matching entry in the
+ * hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+StringFind(tablePtr, key)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find matching entry. */
+{
+ Blt_Hash hval;
+ register Blt_HashEntry *hPtr;
+ size_t hindex;
+
+ hval = HashString((char *)key);
+ hindex = hval & tablePtr->mask;
+
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->hval == hval) {
+ register CONST char *p1, *p2;
+
+ for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+ if (*p1 != *p2) {
+ break;
+ }
+ if (*p1 == '\0') {
+ return hPtr;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringCreate --
+ *
+ * Given a hash table with string keys, and a string key, find
+ * the entry with a matching key. If there is no matching entry,
+ * then create a new entry that does match.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry. If this
+ * is a newly-created entry, then *newPtr will be set to a non-zero
+ * value; otherwise *newPtr will be set to 0. If this is a new
+ * entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ * A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+StringCreate(tablePtr, key, newPtr)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find or create matching
+ * entry. */
+ int *newPtr; /* Store info here telling whether a new
+ * entry was created. */
+{
+ Blt_Hash hval;
+ Blt_HashEntry **bucketPtr;
+ register Blt_HashEntry *hPtr;
+ size_t size, hindex;
+
+ hval = HashString(key);
+ hindex = hval & tablePtr->mask;
+
+ /*
+ * Search all of the entries in this bucket.
+ */
+
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->hval == hval) {
+ register CONST char *p1, *p2;
+
+ for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+ if (*p1 != *p2) {
+ break;
+ }
+ if (*p1 == '\0') {
+ *newPtr = FALSE;
+ return hPtr;
+ }
+ }
+ }
+ }
+
+ /*
+ * Entry not found. Add a new one to the bucket.
+ */
+
+ *newPtr = TRUE;
+ size = sizeof(Blt_HashEntry) + strlen(key) - sizeof(Blt_HashKey) + 1;
+ if (tablePtr->hPool != NULL) {
+ hPtr = Blt_PoolAllocItem(tablePtr->hPool, size);
+ } else {
+ hPtr = Blt_Malloc(size);
+ }
+ bucketPtr = tablePtr->buckets + hindex;
+ hPtr->nextPtr = *bucketPtr;
+ hPtr->hval = hval;
+ hPtr->clientData = 0;
+ strcpy(hPtr->key.string, key);
+ *bucketPtr = hPtr;
+ tablePtr->numEntries++;
+
+ /*
+ * If the table has exceeded a decent size, rebuild it with many
+ * more buckets.
+ */
+
+ if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+ RebuildTable(tablePtr);
+ }
+ return hPtr;
+}
+
+#if (SIZEOF_VOID_P == 8)
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashOneWord --
+ *
+ * Compute a one-word hash value of a 64-bit word, which then can
+ * be used to generate a hash index.
+ *
+ * From Knuth, it's a multiplicative hash. Multiplies an unsigned
+ * 64-bit value with the golden ratio (sqrt(5) - 1) / 2. The
+ * downshift value is 64 - n, when n is the log2 of the size of
+ * the hash table.
+ *
+ * Results:
+ * The return value is a one-word summary of the information in
+ * 64 bit word.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_Hash
+HashOneWord(tablePtr, key)
+ Blt_HashTable *tablePtr;
+ CONST void *key;
+{
+ uint64_t a0, a1;
+ uint64_t y0, y1;
+ uint64_t y2, y3;
+ uint64_t p1, p2;
+ uint64_t result;
+ /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
+ a0 = (uint64_t)key & 0x00000000FFFFFFFF;
+ a1 = (uint64_t)key >> 32;
+
+ y0 = a0 * 0x000000007f4a7c13;
+ y1 = a0 * 0x000000009e3779b9;
+ y2 = a1 * 0x000000007f4a7c13;
+ y3 = a1 * 0x000000009e3779b9;
+ y1 += y0 >> 32; /* Can't carry */
+ y1 += y2; /* Might carry */
+ if (y1 < y2) {
+ y3 += (1LL << 32); /* Propagate */
+ }
+
+ /* 128-bit product: p1 = loword, p2 = hiword */
+ p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
+ p2 = y3 + (y1 >> 32);
+
+ /* Left shift the value downward by the size of the table */
+ if (tablePtr->downShift > 0) {
+ if (tablePtr->downShift < 64) {
+ result = ((p2 << (64 - tablePtr->downShift)) |
+ (p1 >> (tablePtr->downShift & 63)));
+ } else {
+ result = p2 >> (tablePtr->downShift & 63);
+ }
+ } else {
+ result = p1;
+ }
+ /* Finally mask off the high bits */
+ return (Blt_Hash)(result & tablePtr->mask);
+}
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordFind --
+ *
+ * Given a hash table with one-word keys, and a one-word key, find
+ * the entry with a matching key.
+ *
+ * Results:
+ * The return value is a token for the matching entry in the
+ * hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+OneWordFind(tablePtr, key)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ register CONST void *key; /* Key to use to find matching entry. */
+{
+ register Blt_HashEntry *hPtr;
+ size_t hindex;
+
+ hindex = RANDOM_INDEX(tablePtr, key);
+
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->key.oneWordValue == key) {
+ return hPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordCreate --
+ *
+ * Given a hash table with one-word keys, and a one-word key, find
+ * the entry with a matching key. If there is no matching entry,
+ * then create a new entry that does match.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry. If this
+ * is a newly-created entry, then *newPtr will be set to a non-zero
+ * value; otherwise *newPtr will be set to 0. If this is a new
+ * entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ * A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+OneWordCreate(tablePtr, key, newPtr)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find or create matching
+ * entry. */
+ int *newPtr; /* Store info here telling whether a new
+ * entry was created. */
+{
+ Blt_HashEntry **bucketPtr;
+ register Blt_HashEntry *hPtr;
+ size_t hindex;
+
+ hindex = RANDOM_INDEX(tablePtr, key);
+
+ /*
+ * Search all of the entries in this bucket.
+ */
+
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->key.oneWordValue == key) {
+ *newPtr = FALSE;
+ return hPtr;
+ }
+ }
+
+ /*
+ * Entry not found. Add a new one to the bucket.
+ */
+
+ *newPtr = TRUE;
+ if (tablePtr->hPool != NULL) {
+ hPtr = Blt_PoolAllocItem(tablePtr->hPool, sizeof(Blt_HashEntry));
+ } else {
+ hPtr = Blt_Malloc(sizeof(Blt_HashEntry));
+ }
+ bucketPtr = tablePtr->buckets + hindex;
+ hPtr->nextPtr = *bucketPtr;
+ hPtr->hval = (Blt_Hash)key;
+ hPtr->clientData = 0;
+ hPtr->key.oneWordValue = (void *)key; /* CONST XXXX */
+ *bucketPtr = hPtr;
+ tablePtr->numEntries++;
+
+ /*
+ * If the table has exceeded a decent size, rebuild it with many
+ * more buckets.
+ */
+
+ if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+ RebuildTable(tablePtr);
+ }
+ return hPtr;
+}
+
+
+#if (SIZEOF_VOID_P == 4)
+/*
+ * --------------------------------------------------------------------
+ *
+ * MIX32 --
+ *
+ * Bob Jenkins, 1996. Public Domain.
+ *
+ * Mix 3 32/64-bit values reversibly. For every delta with one or
+ * two bit set, and the deltas of all three high bits or all
+ * three low bits, whether the original value of a,b,c is almost
+ * all zero or is uniformly distributed, If mix() is run
+ * forward or backward, at least 32 bits in a,b,c have at least
+ * 1/4 probability of changing. * If mix() is run forward, every
+ * bit of c will change between 1/3 and 2/3 of the time. (Well,
+ * 22/100 and 78/100 for some 2-bit deltas.) mix() was built out
+ * of 36 single-cycle latency instructions in a structure that
+ * could supported 2x parallelism, like so:
+ *
+ * a -= b;
+ * a -= c; x = (c>>13);
+ * b -= c; a ^= x;
+ * b -= a; x = (a<<8);
+ * c -= a; b ^= x;
+ * c -= b; x = (b>>13);
+ * ...
+ *
+ * Unfortunately, superscalar Pentiums and Sparcs can't take
+ * advantage of that parallelism. They've also turned some of
+ * those single-cycle latency instructions into multi-cycle
+ * latency instructions. Still, this is the fastest good hash I
+ * could find. There were about 2^^68 to choose from. I only
+ * looked at a billion or so.
+ *
+ * --------------------------------------------------------------------
+ */
+#define MIX32(a,b,c) \
+ a -= b, a -= c, a ^= (c >> 13), \
+ b -= c, b -= a, b ^= (a << 8), \
+ c -= a, c -= b, c ^= (b >> 13), \
+ a -= b, a -= c, a ^= (c >> 12), \
+ b -= c, b -= a, b ^= (a << 16), \
+ c -= a, c -= b, c ^= (b >> 5), \
+ a -= b, a -= c, a ^= (c >> 3), \
+ b -= c, b -= a, b ^= (a << 10), \
+ c -= a, c -= b, c ^= (b >> 15)
+
+#define GOLDEN_RATIO32 0x9e3779b9 /* An arbitrary value */
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * HashArray --
+ *
+ * Bob Jenkins, 1996. Public Domain.
+ *
+ * This works on all machines. Length has to be measured in
+ * unsigned longs instead of bytes. It requires that
+ *
+ * o The key be an array of unsigned ints.
+ * o All your machines have the same endianness
+ * o The length be the number of unsigned ints in the key.
+ *
+ * --------------------------------------------------------------------
+ */
+static Blt_Hash
+HashArray(key, length)
+ CONST void *key;
+ size_t length; /* Length of the key in 32-bit words */
+{
+ register uint32_t a, b, c, len;
+ register uint32_t *arrayPtr = (uint32_t *)key;
+ /* Set up the internal state */
+ len = length;
+ a = b = GOLDEN_RATIO32; /* An arbitrary value */
+ c = 0; /* Previous hash value */
+
+ while (len >= 3) { /* Handle most of the key */
+ a += arrayPtr[0];
+ b += arrayPtr[1];
+ c += arrayPtr[2];
+ MIX32(a, b, c);
+ arrayPtr += 3; len -= 3;
+ }
+ c += length;
+ /* And now the last 2 words */
+ /* Note that all the case statements fall through */
+ switch(len) {
+ /* c is reserved for the length */
+ case 2 : b += arrayPtr[1];
+ case 1 : a += arrayPtr[0];
+ /* case 0: nothing left to add */
+ }
+ MIX32(a, b, c);
+ return (Blt_Hash)c;
+}
+#endif /* SIZEOF_VOID_P == 4 */
+
+#if (SIZEOF_VOID_P == 8)
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * MIX64 --
+ *
+ * Bob Jenkins, January 4 1997, Public Domain. You can use
+ * this free for any purpose. It has no warranty.
+ *
+ * Returns a 64-bit value. Every bit of the key affects every
+ * bit of the return value. No funnels. Every 1-bit and 2-bit
+ * delta achieves avalanche. About 41+5len instructions.
+ *
+ * The best hash table sizes are powers of 2. There is no need
+ * to do mod a prime (mod is sooo slow!). If you need less than
+ * 64 bits, use a bitmask. For example, if you need only 10
+ * bits, do h = (h & hashmask(10)); In which case, the hash table
+ * should have hashsize(10) elements.
+ *
+ * By Bob Jenkins, Jan 4 1997. bob_jenkins@burtleburtle.net.
+ * You may use this code any way you wish, private, educational,
+ * or commercial, as long as this whole comment accompanies it.
+ *
+ * See http://burtleburtle.net/bob/hash/evahash.html
+ * Use for hash table lookup, or anything where one collision in
+ * 2^^64 * is acceptable. Do NOT use for cryptographic purposes.
+ *
+ * --------------------------------------------------------------------
+ */
+
+#define MIX64(a,b,c) \
+ a -= b, a -= c, a ^= (c >> 43), \
+ b -= c, b -= a, b ^= (a << 9), \
+ c -= a, c -= b, c ^= (b >> 8), \
+ a -= b, a -= c, a ^= (c >> 38), \
+ b -= c, b -= a, b ^= (a << 23), \
+ c -= a, c -= b, c ^= (b >> 5), \
+ a -= b, a -= c, a ^= (c >> 35), \
+ b -= c, b -= a, b ^= (a << 49), \
+ c -= a, c -= b, c ^= (b >> 11), \
+ a -= b, a -= c, a ^= (c >> 12), \
+ b -= c, b -= a, b ^= (a << 18), \
+ c -= a, c -= b, c ^= (b >> 22)
+
+#define GOLDEN_RATIO64 0x9e3779b97f4a7c13LL
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * HashArray --
+ *
+ * Bob Jenkins, January 4 1997, Public Domain. You can use
+ * this free for any purpose. It has no warranty.
+ *
+ * This works on all machines. The length has to be measured in
+ * 64 bit words, instead of bytes. It requires that
+ *
+ * o The key be an array of 64 bit words (unsigned longs).
+ * o All your machines have the same endianness.
+ * o The length be the number of 64 bit words in the key.
+ *
+ * --------------------------------------------------------------------
+ */
+static Blt_Hash
+HashArray(key, length)
+ CONST void *key;
+ size_t length; /* Length of key in 32-bit words. */
+{
+ register uint64_t a, b, c, len;
+ register uint32_t *iPtr = (uint32_t *)key;
+
+#ifdef WORDS_BIGENDIAN
+#define PACK(a,b) ((uint64_t)(b) | ((uint64_t)(a) << 32))
+#else
+#define PACK(a,b) ((uint64_t)(a) | ((uint64_t)(b) << 32))
+#endif
+ /* Set up the internal state */
+ len = length; /* Length is the number of 64-bit words. */
+ a = b = GOLDEN_RATIO64; /* An arbitrary value */
+ c = 0; /* Previous hash value */
+
+ while (len >= 6) { /* Handle most of the key */
+ a += PACK(iPtr[0], iPtr[1]);
+ b += PACK(iPtr[2], iPtr[3]);
+ c += PACK(iPtr[4], iPtr[5]);
+ MIX64(a,b,c);
+ iPtr += 6; len -= 6;
+ }
+ c += length;
+ /* And now the last 2 words */
+ /* Note that all the case statements fall through */
+ switch(len) {
+ /* c is reserved for the length */
+ case 5 :
+ case 4 :
+ a += PACK(iPtr[0], iPtr[1]);
+ b += PACK(iPtr[2], iPtr[3]);
+ iPtr += 4; len -= 4;
+ break;
+ case 3 :
+ case 2 :
+ a += PACK(iPtr[0], iPtr[1]);
+ iPtr += 2; len -= 2;
+ /* case 0: nothing left to add */
+ }
+ if (len > 0) {
+ b += iPtr[0];
+ }
+ MIX64(a,b,c);
+ return (Blt_Hash)c;
+}
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayFind --
+ *
+ * Given a hash table with array-of-int keys, and a key, find
+ * the entry with a matching key.
+ *
+ * Results:
+ * The return value is a token for the matching entry in the
+ * hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+ArrayFind(tablePtr, key)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find matching entry. */
+{
+ Blt_Hash hval;
+ register Blt_HashEntry *hPtr;
+ size_t hindex;
+
+ hval = HashArray(key, tablePtr->keyType);
+ hindex = hval & tablePtr->mask;
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->hval == hval) {
+ register unsigned int *iPtr1, *iPtr2;
+ unsigned int count;
+
+ for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words,
+ count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+ if (count == 0) {
+ return hPtr;
+ }
+ if (*iPtr1 != *iPtr2) {
+ break;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayCreate --
+ *
+ * Given a hash table with one-word keys, and a one-word key, find
+ * the entry with a matching key. If there is no matching entry,
+ * then create a new entry that does match.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry. If this
+ * is a newly-created entry, then *newPtr will be set to a non-zero
+ * value; otherwise *newPtr will be set to 0. If this is a new
+ * entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ * A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+ArrayCreate(tablePtr, key, newPtr)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ register CONST void *key; /* Key to use to find or create matching
+ * entry. */
+ int *newPtr; /* Store info here telling whether a new
+ * entry was created. */
+{
+ Blt_Hash hval;
+ Blt_HashEntry **bucketPtr;
+ int count;
+ register Blt_HashEntry *hPtr;
+ register uint32_t *iPtr1, *iPtr2;
+ size_t size, hindex;
+
+ hval = HashArray(key, tablePtr->keyType);
+ hindex = hval & tablePtr->mask;
+
+ /*
+ * Search all of the entries in the appropriate bucket.
+ */
+ for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+ hPtr = hPtr->nextPtr) {
+ if (hPtr->hval == hval) {
+ for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words,
+ count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+ if (count == 0) {
+ *newPtr = FALSE;
+ return hPtr;
+ }
+ if (*iPtr1 != *iPtr2) {
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Entry not found. Add a new one to the bucket.
+ */
+ *newPtr = TRUE;
+ /* We assume here that the size of the key is at least 2 words */
+ size = sizeof(Blt_HashEntry) + tablePtr->keyType * sizeof(uint32_t) -
+ sizeof(Blt_HashKey);
+ if (tablePtr->hPool != NULL) {
+ hPtr = Blt_PoolAllocItem(tablePtr->hPool, size);
+ } else {
+ hPtr = Blt_Malloc(size);
+ }
+ bucketPtr = tablePtr->buckets + hindex;
+ hPtr->nextPtr = *bucketPtr;
+ hPtr->hval = hval;
+ hPtr->clientData = 0;
+ count = tablePtr->keyType;
+ for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words;
+ count > 0; count--, iPtr1++, iPtr2++) {
+ *iPtr2 = *iPtr1;
+ }
+ *bucketPtr = hPtr;
+ tablePtr->numEntries++;
+
+ /*
+ * If the table has exceeded a decent size, rebuild it with many
+ * more buckets.
+ */
+ if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+ RebuildTable(tablePtr);
+ }
+ return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusFind --
+ *
+ * This procedure is invoked when an Blt_FindHashEntry is called
+ * on a table that has been deleted.
+ *
+ * Results:
+ * If panic returns (which it shouldn't) this procedure returns
+ * NULL.
+ *
+ * Side effects:
+ * Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static Blt_HashEntry *
+BogusFind(tablePtr, key)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find matching entry. */
+{
+ Blt_Panic("called Blt_FindHashEntry on deleted table");
+ return NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusCreate --
+ *
+ * This procedure is invoked when an Blt_CreateHashEntry is called
+ * on a table that has been deleted.
+ *
+ * Results:
+ * If panic returns (which it shouldn't) this procedure returns
+ * NULL.
+ *
+ * Side effects:
+ * Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static Blt_HashEntry *
+BogusCreate(tablePtr, key, newPtr)
+ Blt_HashTable *tablePtr; /* Table in which to lookup entry. */
+ CONST void *key; /* Key to use to find or create matching
+ * entry. */
+ int *newPtr; /* Store info here telling whether a new
+ * entry was created. */
+{
+ Blt_Panic("called Blt_CreateHashEntry on deleted table");
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildTable --
+ *
+ * This procedure is invoked when the ratio of entries to hash
+ * buckets becomes too large. It creates a new table with a
+ * larger bucket array and moves all of the entries into the
+ * new table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets reallocated and entries get re-hashed to new
+ * buckets.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+RebuildTable(tablePtr)
+ register Blt_HashTable *tablePtr; /* Table to enlarge. */
+{
+ Blt_HashEntry **bucketPtr, **oldBuckets;
+ register Blt_HashEntry **oldChainPtr, **endPtr;
+ register Blt_HashEntry *hPtr, *nextPtr;
+ size_t hindex;
+
+ oldBuckets = tablePtr->buckets;
+ endPtr = tablePtr->buckets + tablePtr->numBuckets;
+ /*
+ * Allocate and initialize the new bucket array, and set up
+ * hashing constants for new array size.
+ */
+ tablePtr->numBuckets <<= 2;
+ tablePtr->buckets = Blt_Calloc(tablePtr->numBuckets,
+ sizeof(Blt_HashEntry *));
+ tablePtr->rebuildSize <<= 2;
+ tablePtr->downShift -= 2;
+ tablePtr->mask = tablePtr->numBuckets - 1;
+
+ /*
+ * Move all of the existing entries into the new bucket array,
+ * based on their hash values.
+ */
+ if (tablePtr->keyType == BLT_ONE_WORD_KEYS) {
+ /*
+ * BLT_ONE_WORD_KEYS are handled slightly differently because
+ * they use the current table size (number of buckets) to be
+ * distributed.
+ */
+ for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
+ for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
+ nextPtr = hPtr->nextPtr;
+ hindex = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
+ bucketPtr = tablePtr->buckets + hindex;
+ hPtr->nextPtr = *bucketPtr;
+ *bucketPtr = hPtr;
+ }
+ }
+ } else {
+ for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
+ for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
+ nextPtr = hPtr->nextPtr;
+ hindex = hPtr->hval & tablePtr->mask;
+ bucketPtr = tablePtr->buckets + hindex;
+ hPtr->nextPtr = *bucketPtr;
+ *bucketPtr = hPtr;
+ }
+ }
+ }
+
+ /*
+ * Free up the old bucket array, if it was dynamically allocated.
+ */
+ if (oldBuckets != tablePtr->staticBuckets) {
+ Blt_Free(oldBuckets);
+ }
+}
+
+
+/* Public hash table routines */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitHashTable --
+ *
+ * Given storage for a hash table, set up the fields to prepare
+ * the hash table for use.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TablePtr is now ready to be passed to Blt_FindHashEntry and
+ * Blt_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_InitHashTable(tablePtr, keyType)
+ register Blt_HashTable *tablePtr; /* Pointer to table record, which
+ * is supplied by the caller. */
+ size_t keyType; /* Type of keys to use in table. */
+{
+#if (BLT_SMALL_HASH_TABLE != 4)
+ Blt_Panic("Blt_InitHashTable: BLT_SMALL_HASH_TABLE is %d, not 4\n",
+ BLT_SMALL_HASH_TABLE);
+#endif
+ tablePtr->buckets = tablePtr->staticBuckets;
+ tablePtr->numBuckets = BLT_SMALL_HASH_TABLE;
+ tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
+ tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
+ tablePtr->numEntries = 0;
+ tablePtr->rebuildSize = BLT_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
+ tablePtr->downShift = DOWNSHIFT_START;
+
+ /* The number of buckets is always a power of 2, so we can
+ * generate the mask by simply subtracting 1 from the number of
+ * buckets. */
+ tablePtr->mask = (Blt_Hash)(tablePtr->numBuckets - 1);
+ tablePtr->keyType = keyType;
+
+ switch (keyType) {
+ case BLT_STRING_KEYS: /* NUL terminated string keys. */
+ tablePtr->findProc = StringFind;
+ tablePtr->createProc = StringCreate;
+ break;
+
+ case BLT_ONE_WORD_KEYS: /* 32 or 64 bit atomic keys. */
+ tablePtr->findProc = OneWordFind;
+ tablePtr->createProc = OneWordCreate;
+ break;
+
+ default: /* Structures/arrays. */
+ if (keyType == 0) {
+ Blt_Panic("Blt_InitHashTable: Key size can't be %d, must be > 0\n",
+ keyType);
+ }
+ tablePtr->findProc = ArrayFind;
+ tablePtr->createProc = ArrayCreate;
+ break;
+ }
+ tablePtr->hPool = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitHashTableWithPool --
+ *
+ * Given storage for a hash table, set up the fields to prepare
+ * the hash table for use. The only difference between this
+ * routine and Blt_InitHashTable is that is uses a pool allocator
+ * to allocate memory for hash table entries. The type of pool
+ * is either fixed or variable size (string) keys.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TablePtr is now ready to be passed to Blt_FindHashEntry and
+ * Blt_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_InitHashTableWithPool(tablePtr, keyType)
+ register Blt_HashTable *tablePtr; /* Pointer to table record, which
+ * is supplied by the caller. */
+ size_t keyType; /* Type of keys to use in table. */
+{
+ Blt_InitHashTable(tablePtr, keyType);
+ if (keyType == BLT_STRING_KEYS) {
+ tablePtr->hPool = Blt_PoolCreate(BLT_VARIABLE_SIZE_ITEMS);
+ } else {
+ tablePtr->hPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DeleteHashEntry --
+ *
+ * Remove a single entry from a hash table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The entry given by entryPtr is deleted from its table and
+ * should never again be used by the caller. It is up to the
+ * caller to free the clientData field of the entry, if that
+ * is relevant.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DeleteHashEntry(tablePtr, entryPtr)
+ Blt_HashTable *tablePtr;
+ Blt_HashEntry *entryPtr;
+{
+ register Blt_HashEntry *prevPtr;
+ Blt_HashEntry **bucketPtr;
+ size_t hindex;
+
+ if (tablePtr->keyType == BLT_ONE_WORD_KEYS) {
+ hindex = RANDOM_INDEX(tablePtr, (CONST void *)entryPtr->hval);
+ } else {
+ hindex = (entryPtr->hval & tablePtr->mask);
+ }
+ bucketPtr = tablePtr->buckets + hindex;
+ if (*bucketPtr == entryPtr) {
+ *bucketPtr = entryPtr->nextPtr;
+ } else {
+ for (prevPtr = *bucketPtr;/*empty*/; prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ Blt_Panic("malformed bucket chain in Blt_DeleteHashEntry");
+ }
+ if (prevPtr->nextPtr == entryPtr) {
+ prevPtr->nextPtr = entryPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ tablePtr->numEntries--;
+ if (tablePtr->hPool != NULL) {
+ Blt_PoolFreeItem(tablePtr->hPool, (char *)entryPtr);
+ } else {
+ Blt_Free(entryPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DeleteHashTable --
+ *
+ * Free up everything associated with a hash table except for
+ * the record for the table itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The hash table is no longer useable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DeleteHashTable(tablePtr)
+ register Blt_HashTable *tablePtr; /* Table to delete. */
+{
+ /*
+ * Free up all the entries in the table.
+ */
+
+ if (tablePtr->hPool != NULL) {
+ Blt_PoolDestroy(tablePtr->hPool);
+ tablePtr->hPool = NULL;
+ } else {
+ register Blt_HashEntry *hPtr, *nextPtr;
+ size_t i;
+
+ for (i = 0; i < tablePtr->numBuckets; i++) {
+ hPtr = tablePtr->buckets[i];
+ while (hPtr != NULL) {
+ nextPtr = hPtr->nextPtr;
+ Blt_Free(hPtr);
+ hPtr = nextPtr;
+ }
+ }
+ }
+
+ /*
+ * Free up the bucket array, if it was dynamically allocated.
+ */
+
+ if (tablePtr->buckets != tablePtr->staticBuckets) {
+ Blt_Free(tablePtr->buckets);
+ }
+
+ /*
+ * Arrange for panics if the table is used again without
+ * re-initialization.
+ */
+
+ tablePtr->findProc = BogusFind;
+ tablePtr->createProc = BogusCreate;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FirstHashEntry --
+ *
+ * Locate the first entry in a hash table and set up a record
+ * that can be used to step through all the remaining entries
+ * of the table.
+ *
+ * Results:
+ * The return value is a pointer to the first entry in tablePtr,
+ * or NULL if tablePtr has no entries in it. The memory at
+ * *searchPtr is initialized so that subsequent calls to
+ * Blt_NextHashEntry will return all of the entries in the table,
+ * one at a time.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashEntry *
+Blt_FirstHashEntry(tablePtr, searchPtr)
+ Blt_HashTable *tablePtr; /* Table to search. */
+ Blt_HashSearch *searchPtr; /* Place to store information about
+ * progress through the table. */
+{
+ searchPtr->tablePtr = tablePtr;
+ searchPtr->nextIndex = 0;
+ searchPtr->nextEntryPtr = NULL;
+ return Blt_NextHashEntry(searchPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NextHashEntry --
+ *
+ * Once a hash table enumeration has been initiated by calling
+ * Blt_FirstHashEntry, this procedure may be called to return
+ * successive elements of the table.
+ *
+ * Results:
+ * The return value is the next entry in the hash table being
+ * enumerated, or NULL if the end of the table is reached.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashEntry *
+Blt_NextHashEntry(searchPtr)
+ register Blt_HashSearch *searchPtr; /* Place to store information about
+ * progress through the table. Must
+ * have been initialized by calling
+ * Blt_FirstHashEntry. */
+{
+ Blt_HashEntry *hPtr;
+
+ while (searchPtr->nextEntryPtr == NULL) {
+ if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {
+ return NULL;
+ }
+ searchPtr->nextEntryPtr =
+ searchPtr->tablePtr->buckets[searchPtr->nextIndex];
+ searchPtr->nextIndex++;
+ }
+ hPtr = searchPtr->nextEntryPtr;
+ searchPtr->nextEntryPtr = hPtr->nextPtr;
+ return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_HashStats --
+ *
+ * Return statistics describing the layout of the hash table
+ * in its hash buckets.
+ *
+ * Results:
+ * The return value is a malloc-ed string containing information
+ * about tablePtr. It is the caller's responsibility to free
+ * this string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_HashStats(tablePtr)
+ Blt_HashTable *tablePtr; /* Table for which to produce stats. */
+{
+#define NUM_COUNTERS 10
+ size_t count[NUM_COUNTERS], overflow, i, j, max;
+ double average, tmp;
+ register Blt_HashEntry *hPtr;
+ Blt_HashEntry **bucketPtr, **endPtr;
+ char *result, *p;
+
+ /*
+ * Compute a histogram of bucket usage.
+ */
+
+ for (i = 0; i < NUM_COUNTERS; i++) {
+ count[i] = 0;
+ }
+ overflow = 0;
+ average = 0.0;
+ max = 0;
+ endPtr = tablePtr->buckets + tablePtr->numBuckets;
+ for (bucketPtr = tablePtr->buckets; bucketPtr < endPtr; bucketPtr++) {
+ j = 0;
+ for (hPtr = *bucketPtr; hPtr != NULL; hPtr = hPtr->nextPtr) {
+ j++;
+ }
+ if (j > max) {
+ max = j;
+ }
+ if (j < NUM_COUNTERS) {
+ count[j]++;
+ } else {
+ overflow++;
+ }
+ tmp = j;
+ average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
+ }
+
+ /*
+ * Print out the histogram and a few other pieces of information.
+ */
+ result = Blt_Malloc((unsigned) ((NUM_COUNTERS*60) + 300));
+#if SIZEOF_VOID_P == 8
+ sprintf(result, "%ld entries in table, %ld buckets\n",
+ tablePtr->numEntries, tablePtr->numBuckets);
+#else
+ sprintf(result, "%d entries in table, %d buckets\n",
+ tablePtr->numEntries, tablePtr->numBuckets);
+#endif
+ p = result + strlen(result);
+ for (i = 0; i < NUM_COUNTERS; i++) {
+#if SIZEOF_VOID_P == 8
+ sprintf(p, "number of buckets with %ld entries: %ld\n",
+ i, count[i]);
+#else
+ sprintf(p, "number of buckets with %d entries: %d\n",
+ i, count[i]);
+#endif
+ p += strlen(p);
+ }
+#if SIZEOF_VOID_P == 8
+ sprintf(p, "number of buckets with %d or more entries: %ld\n",
+ NUM_COUNTERS, overflow);
+#else
+ sprintf(p, "number of buckets with %d or more entries: %d\n",
+ NUM_COUNTERS, overflow);
+#endif
+ p += strlen(p);
+ sprintf(p, "average search distance for entry: %.2f\n", average);
+ p += strlen(p);
+#if SIZEOF_VOID_P == 8
+ sprintf(p, "maximum search distance for entry: %ld", max);
+#else
+ sprintf(p, "maximum search distance for entry: %d", max);
+#endif
+ return result;
+}
diff --git a/blt/src/bltHash.h.in b/blt/src/bltHash.h.in
new file mode 100644
index 00000000000..4fb9931bab2
--- /dev/null
+++ b/blt/src/bltHash.h.in
@@ -0,0 +1,223 @@
+
+/*
+ * bltHash.h --
+ *
+ * Copyright 2001 Silicon Metrics Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Bob Jenkins, 1996. hash.c. Public Domain.
+ * Bob Jenkins, 1997. lookup8.c. Public Domain.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#ifndef BLT_HASH_H
+#define BLT_HASH_H 1
+
+#ifndef BLT_INT_H
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG @SIZEOF_LONG@
+#endif
+#ifndef SIZEOF_LONG_LONG
+#define SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@
+#endif
+#ifndef SIZEOF_INT
+#define SIZEOF_INT @SIZEOF_INT@
+#endif
+#ifndef SIZEOF_VOID_P
+#define SIZEOF_VOID_P @SIZEOF_VOID_P@
+#endif
+#ifndef HAVE_INTTYPES_H
+#if @HAVE_INTTYPES_H@
+#define HAVE_INTTYPES_H 1
+#endif
+#endif
+#endif /* !BLT_INT_H */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#if (SIZEOF_VOID_P == 8)
+#if (SIZEOF_LONG == 8)
+typedef signed long int64_t;
+typedef unsigned long uint64_t;
+#else
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#endif /* SIZEOF_LONG == 8 */
+#else
+#ifndef __CYGWIN__
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+#endif /* __CYGWIN__ */
+#endif /* SIZEOF_VOID_P == 8 */
+#endif /* HAVE_INTTYPES_H */
+
+#if (SIZEOF_VOID_P == 8)
+typedef uint64_t Blt_Hash;
+#else
+typedef uint32_t Blt_Hash;
+#endif /* SIZEOF_VOID_P == 8 */
+
+#include "bltPool.h"
+
+/*
+ * Acceptable key types for hash tables:
+ */
+#define BLT_STRING_KEYS 0
+#define BLT_ONE_WORD_KEYS ((size_t)-1)
+
+/*
+ * Forward declaration of Blt_HashTable. Needed by some C++ compilers
+ * to prevent errors when the forward reference to Blt_HashTable is
+ * encountered in the Blt_HashEntry structure.
+ */
+
+#ifdef __cplusplus
+struct Blt_HashTable;
+#endif
+
+typedef union { /* Key has one of these forms: */
+ void *oneWordValue; /* One-word value for key. */
+ unsigned long words[1]; /* Multiple integer words for key.
+ * The actual size will be as large
+ * as necessary for this table's
+ * keys. */
+ char string[4]; /* String for key. The actual size
+ * will be as large as needed to hold
+ * the key. */
+} Blt_HashKey;
+
+/*
+ * Structure definition for an entry in a hash table. No-one outside
+ * Blt should access any of these fields directly; use the macros
+ * defined below.
+ */
+typedef struct Blt_HashEntry {
+ struct Blt_HashEntry *nextPtr; /* Pointer to next entry in this
+ * hash bucket, or NULL for end of
+ * chain. */
+ Blt_Hash hval;
+
+ ClientData clientData; /* Application stores something here
+ * with Blt_SetHashValue. */
+ Blt_HashKey key; /* MUST BE LAST FIELD IN RECORD!! */
+} Blt_HashEntry;
+
+/*
+ * Structure definition for a hash table. Must be in blt.h so clients
+ * can allocate space for these structures, but clients should never
+ * access any fields in this structure.
+ */
+#define BLT_SMALL_HASH_TABLE 4
+typedef struct Blt_HashTable {
+ Blt_HashEntry **buckets; /* Pointer to bucket array. Each
+ * element points to first entry in
+ * bucket's hash chain, or NULL. */
+ Blt_HashEntry *staticBuckets[BLT_SMALL_HASH_TABLE];
+ /* Bucket array used for small tables
+ * (to avoid mallocs and frees). */
+ size_t numBuckets; /* Total number of buckets allocated
+ * at **buckets. */
+ size_t numEntries; /* Total number of entries present
+ * in table. */
+ size_t rebuildSize; /* Enlarge table when numEntries gets
+ * to be this large. */
+ Blt_Hash mask; /* Mask value used in hashing
+ * function. */
+ unsigned int downShift; /* Shift count used in hashing
+ * function. Designed to use high-
+ * order bits of randomized keys. */
+ size_t keyType; /* Type of keys used in this table.
+ * It's either BLT_STRING_KEYS,
+ * BLT_ONE_WORD_KEYS, or an integer
+ * giving the number of ints that
+ * is the size of the key.
+ */
+ Blt_HashEntry *(*findProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+ CONST void *key));
+ Blt_HashEntry *(*createProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+ CONST void *key, int *newPtr));
+
+ Blt_Pool hPool; /* Pointer to the pool allocator used
+ * for entries in this hash table. If
+ * NULL, the standard Tcl_Alloc,
+ * Tcl_Free routines will be used
+ * instead.
+ */
+} Blt_HashTable;
+
+/*
+ * Structure definition for information used to keep track of searches
+ * through hash tables:
+ */
+
+typedef struct {
+ Blt_HashTable *tablePtr; /* Table being searched. */
+ unsigned long nextIndex; /* Index of next bucket to be
+ * enumerated after present one. */
+ Blt_HashEntry *nextEntryPtr; /* Next entry to be enumerated in the
+ * the current bucket. */
+} Blt_HashSearch;
+
+/*
+ * Macros for clients to use to access fields of hash entries:
+ */
+
+#define Blt_GetHashValue(h) ((h)->clientData)
+#define Blt_SetHashValue(h, value) ((h)->clientData = (ClientData)(value))
+#define Blt_GetHashKey(tablePtr, h) \
+ ((void *) (((tablePtr)->keyType == BLT_ONE_WORD_KEYS) ? \
+ (void *)(h)->key.oneWordValue : (h)->key.string))
+
+/*
+ * Macros to use for clients to use to invoke find and create procedures
+ * for hash tables:
+ */
+#define Blt_FindHashEntry(tablePtr, key) \
+ (*((tablePtr)->findProc))(tablePtr, key)
+#define Blt_CreateHashEntry(tablePtr, key, newPtr) \
+ (*((tablePtr)->createProc))(tablePtr, key, newPtr)
+
+EXTERN void Blt_InitHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ size_t keyType));
+
+EXTERN void Blt_InitHashTableWithPool _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ size_t keyType));
+
+EXTERN void Blt_DeleteHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+EXTERN void Blt_DeleteHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ Blt_HashEntry *entryPtr));
+
+EXTERN Blt_HashEntry *Blt_FirstHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+ Blt_HashSearch *searchPtr));
+
+EXTERN Blt_HashEntry *Blt_NextHashEntry _ANSI_ARGS_((Blt_HashSearch *srchPtr));
+
+EXTERN char *Blt_HashStats _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+#endif /* BLT_HASH_H */
diff --git a/blt/src/bltHierbox.c b/blt/src/bltHierbox.c
new file mode 100644
index 00000000000..0212f0beea3
--- /dev/null
+++ b/blt/src/bltHierbox.c
@@ -0,0 +1,8672 @@
+
+/*
+ * bltHierbox.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright -1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "hierbox" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_HIERBOX
+#include "bltBind.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include "bltChain.h"
+#include "bltList.h"
+#include "bltTile.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#if HAVE_UTF
+#else
+#define Tcl_NumUtfChars(s,n) (((n) == -1) ? strlen((s)) : (n))
+#define Tcl_UtfAtIndex(s,i) ((s) + (i))
+#endif
+
+#define SEPARATOR_NONE ((char *)-1)
+#define SEPARATOR_LIST ((char *)NULL)
+#define APPEND (-1)
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define UCHAR(c) ((unsigned char) (c))
+
+#define APPLY_BEFORE (1<<0)
+#define APPLY_OPEN_ONLY (1<<1)
+#define APPLY_RECURSE (1<<2)
+
+#define BUTTON_IPAD 1
+#define BUTTON_SIZE 7
+#define INSET_PAD 2
+#define ICON_PADX 2
+#define ICON_PADY 1
+#define LABEL_PADX 3
+#define LABEL_PADY 0
+#define FOCUS_WIDTH 1
+
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+#define ODD(x) ((x) | 0x01)
+#define TOGGLE(x, mask) \
+ (((x) & (mask)) ? ((x) & ~(mask)) : ((x) | (mask)))
+
+#define VPORTWIDTH(h) (Tk_Width((h)->tkwin) - 2 * (h)->inset)
+#define VPORTHEIGHT(h) (Tk_Height((h)->tkwin) - 2 * (h)->inset)
+
+#define WORLDX(h, sx) ((sx) - (h)->inset + (h)->xOffset)
+#define WORLDY(h, sy) ((sy) - (h)->inset + (h)->yOffset)
+
+#define SCREENX(h, wx) ((wx) - (h)->xOffset + (h)->inset)
+#define SCREENY(h, wy) ((wy) - (h)->yOffset + (h)->inset)
+
+#define LEVELWIDTH(d) (hboxPtr->levelInfo[(d)].width)
+#define LEVELX(d) (hboxPtr->levelInfo[(d)].x)
+
+#define GETFONT(h, f) (((f) == NULL) ? (h)->defFont : (f))
+#define GETCOLOR(h, c) (((c) == NULL) ? (h)->defColor : (c))
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Internal hierarchy widget flags:
+ *
+ * HIERBOX_LAYOUT The layout of the hierarchy needs to be
+ * recomputed.
+ *
+ * HIERBOX_REDRAW A redraw request is pending for the widget.
+ *
+ * HIERBOX_XSCROLL X-scroll request is pending.
+ * HIERBOX_YSCROLL Y-scroll request is pending.
+ * HIERBOX_SCROLL Both X-scroll and Y-scroll requests are
+ * pending.
+ *
+ * HIERBOX_FOCUS The widget is receiving keyboard events.
+ * Draw the focus highlight border around the
+ * widget.
+ *
+ * HIERBOX_DIRTY The hierarchy has changed, possibly invalidating
+ * locations and pointers to entries. This widget
+ * need to recompute its layout.
+ *
+ * HIERBOX_BORDERS The borders of the widget (highlight ring and
+ * 3-D border) need to be redrawn.
+ *
+ *
+ * Selection related flags:
+ *
+ * SELECTION_EXPORT Export the selection to X.
+ *
+ * SELECTION_PENDING A selection command idle task is pending.
+ *
+ * SELECTION_CLEAR Entry's selection flag is to be cleared.
+ *
+ * SELECTION_SET Entry's selection flag is to be set.
+ *
+ * SELECTION_TOGGLE Entry's selection flag is to be toggled.
+ *
+ * SELECTION_MASK Mask of selection set/clear/toggle flags.
+ *
+ * ---------------------------------------------------------------------------
+ */
+#define HIERBOX_LAYOUT (1<<0)
+#define HIERBOX_REDRAW (1<<1)
+#define HIERBOX_XSCROLL (1<<2)
+#define HIERBOX_YSCROLL (1<<3)
+#define HIERBOX_SCROLL (HIERBOX_XSCROLL | HIERBOX_YSCROLL)
+#define HIERBOX_FOCUS (1<<4)
+#define HIERBOX_DIRTY (1<<5)
+#define HIERBOX_BORDERS (1<<6)
+
+#define SELECTION_PENDING (1<<15)
+#define SELECTION_EXPORT (1<<16)
+#define SELECTION_CLEAR (1<<17)
+#define SELECTION_SET (1<<18)
+#define SELECTION_TOGGLE (SELECTION_SET | SELECTION_CLEAR)
+#define SELECTION_MASK (SELECTION_SET | SELECTION_CLEAR)
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Internal entry flags:
+ *
+ * ENTRY_BUTTON Indicates that a button needs to be
+ * drawn for this entry.
+ *
+ * ENTRY_OPEN Indicates that the entry is open and
+ * its subentries should also be displayed.
+ *
+ * ENTRY_MAPPED Indicates that the entry is mapped (i.e.
+ * can be viewed by opening or scrolling.
+ *
+ * BUTTON_AUTO
+ * BUTTON_SHOW
+ * BUTTON_MASK
+ *
+ * -------------------------------------------------------------------------
+ */
+#define ENTRY_BUTTON (1<<0)
+#define ENTRY_OPEN (1<<2)
+#define ENTRY_MAPPED (1<<3)
+#define BUTTON_AUTO (1<<8)
+#define BUTTON_SHOW (1<<9)
+#define BUTTON_MASK (BUTTON_AUTO | BUTTON_SHOW)
+
+#define DEF_ENTRY_BG_COLOR (char *)NULL
+#define DEF_ENTRY_BG_MONO (char *)NULL
+#define DEF_ENTRY_BIND_TAGS "Entry all"
+#define DEF_ENTRY_BUTTON "auto"
+#define DEF_ENTRY_COMMAND (char *)NULL
+#define DEF_ENTRY_DATA (char *)NULL
+#define DEF_ENTRY_FG_COLOR (char *)NULL
+#define DEF_ENTRY_FG_MONO (char *)NULL
+#define DEF_ENTRY_FONT (char *)NULL
+#define DEF_ENTRY_ICONS (char *)NULL
+#define DEF_ENTRY_ACTIVE_ICONS (char *)NULL
+#define DEF_ENTRY_IMAGES (char *)NULL
+#define DEF_ENTRY_LABEL (char *)NULL
+#define DEF_ENTRY_SHADOW_COLOR (char *)NULL
+#define DEF_ENTRY_SHADOW_MONO (char *)NULL
+#define DEF_ENTRY_TEXT (char *)NULL
+
+#define DEF_BUTTON_ACTIVE_BG_COLOR RGB_WHITE
+#define DEF_BUTTON_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_BUTTON_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_BUTTON_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_BUTTON_BORDER_WIDTH "1"
+#if (TK_MAJOR_VERSION == 4)
+#define DEF_BUTTON_CLOSE_RELIEF "flat"
+#define DEF_BUTTON_OPEN_RELIEF "flat"
+#else
+#define DEF_BUTTON_CLOSE_RELIEF "solid"
+#define DEF_BUTTON_OPEN_RELIEF "solid"
+#endif
+#define DEF_BUTTON_IMAGES (char *)NULL
+#define DEF_BUTTON_NORMAL_BG_COLOR RGB_WHITE
+#define DEF_BUTTON_NORMAL_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_BUTTON_NORMAL_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_BUTTON_NORMAL_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_BUTTON_SIZE "7"
+
+#define DEF_HIERBOX_ACTIVE_BG_COLOR RGB_LIGHTBLUE0
+#define DEF_HIERBOX_ACTIVE_SELECT_BG_COLOR RGB_LIGHTBLUE1
+#define DEF_HIERBOX_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_HIERBOX_ACTIVE_FG_COLOR RGB_BLACK
+#define DEF_HIERBOX_ACTIVE_RELIEF "flat"
+#define DEF_HIERBOX_ACTIVE_STIPPLE "gray25"
+#define DEF_HIERBOX_ALLOW_DUPLICATES "yes"
+#define DEF_HIERBOX_BACKGROUND RGB_WHITE
+#define DEF_HIERBOX_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_HIERBOX_COMMAND (char *)NULL
+#define DEF_HIERBOX_CURSOR (char *)NULL
+#define DEF_HIERBOX_DASHES "dot"
+#define DEF_HIERBOX_EXPORT_SELECTION "no"
+#define DEF_HIERBOX_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_HIERBOX_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_HIERBOX_FOCUS_DASHES "dot"
+#define DEF_HIERBOX_FOCUS_EDIT "no"
+#define DEF_HIERBOX_FOCUS_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_HIERBOX_FOCUS_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_HIERBOX_FONT STD_FONT
+#define DEF_HIERBOX_HEIGHT "400"
+#define DEF_HIERBOX_HIDE_ROOT "no"
+#define DEF_HIERBOX_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_HIERBOX_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_HIERBOX_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_HIERBOX_HIGHLIGHT_WIDTH "2"
+#define DEF_HIERBOX_LINE_COLOR RGB_GREY50
+#define DEF_HIERBOX_LINE_MONO STD_MONO_NORMAL_FG
+#define DEF_HIERBOX_LINE_SPACING "0"
+#define DEF_HIERBOX_LINE_WIDTH "1"
+#define DEF_HIERBOX_MAKE_PATH "no"
+#define DEF_HIERBOX_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_HIERBOX_NORMAL_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_HIERBOX_RELIEF "sunken"
+#define DEF_HIERBOX_SCROLL_INCREMENT "0"
+#define DEF_HIERBOX_SCROLL_MODE "hierbox"
+#define DEF_HIERBOX_SCROLL_TILE "yes"
+#define DEF_HIERBOX_SELECT_BG_COLOR RGB_LIGHTSKYBLUE1
+#define DEF_HIERBOX_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_HIERBOX_SELECT_BORDER_WIDTH "1"
+#define DEF_HIERBOX_SELECT_CMD (char *)NULL
+#define DEF_HIERBOX_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_HIERBOX_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_HIERBOX_SELECT_MODE "single"
+#define DEF_HIERBOX_SELECT_RELIEF "flat"
+#define DEF_HIERBOX_SEPARATOR (char *)NULL
+#define DEF_HIERBOX_SHOW_ROOT "yes"
+#define DEF_HIERBOX_SORT_SELECTION "no"
+#define DEF_HIERBOX_TAKE_FOCUS "1"
+#define DEF_HIERBOX_TEXT_COLOR STD_COLOR_NORMAL_FG
+#define DEF_HIERBOX_TEXT_MONO STD_MONO_NORMAL_FG
+#define DEF_HIERBOX_TILE (char *)NULL
+#define DEF_HIERBOX_TRIMLEFT ""
+#define DEF_HIERBOX_WIDTH "200"
+
+typedef struct HierboxStruct Hierbox;
+typedef struct EntryStruct Entry;
+typedef struct TreeStruct Tree;
+
+typedef int (CompareProc) _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ char *pattern));
+typedef int (ApplyProc) _ANSI_ARGS_((Hierbox *hboxPtr, Tree * treePtr));
+typedef Tree *(IterProc) _ANSI_ARGS_((Tree * treePtr, unsigned int mask));
+
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltUidOption;
+
+static Tk_OptionParseProc StringToButton;
+static Tk_OptionPrintProc ButtonToString;
+static Tk_OptionParseProc StringToImages;
+static Tk_OptionPrintProc ImagesToString;
+static Tk_OptionParseProc StringToScrollMode;
+static Tk_OptionPrintProc ScrollModeToString;
+static Tk_OptionParseProc StringToSeparator;
+static Tk_OptionPrintProc SeparatorToString;
+/*
+ * Contains a pointer to the widget that's currently being configured.
+ * This is used in the custom configuration parse routine for images.
+ */
+static Hierbox *hierBox;
+
+static Tk_CustomOption imagesOption =
+{
+ StringToImages, ImagesToString, (ClientData)&hierBox,
+};
+static Tk_CustomOption buttonOption =
+{
+ StringToButton, ButtonToString, (ClientData)0,
+};
+static Tk_CustomOption scrollModeOption =
+{
+ StringToScrollMode, ScrollModeToString, (ClientData)0,
+};
+static Tk_CustomOption separatorOption =
+{
+ StringToSeparator, SeparatorToString, (ClientData)0,
+};
+/*
+ * CachedImage --
+ *
+ * Since instances of the same Tk image can be displayed in
+ * different windows with possibly different color palettes, Tk
+ * internally stores each instance in a linked list. But if
+ * the instances are used in the same widget and therefore use
+ * the same color palette, this adds a lot of overhead,
+ * especially when deleting instances from the linked list.
+ *
+ * For the hierbox widget, we never need more than a single
+ * instance of an image, regardless of how many times it's used.
+ * So one solution is to cache the image, maintaining a reference
+ * count for each image used in the widget. It's likely that the
+ * hierarchy widget will use many instances of the same image
+ * (for example the open/close icons).
+ */
+
+typedef struct CachedImageStruct {
+ Tk_Image tkImage; /* The Tk image being cached. */
+ int refCount; /* Reference count for this image. */
+ short int width, height; /* Dimensions of the cached image. */
+ Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */
+} *CachedImage;
+
+#define ImageHeight(image) ((image)->height)
+#define ImageWidth(image) ((image)->width)
+#define ImageData(image) ((image)->tkImage)
+
+/*
+ * Tree --
+ *
+ * Structure representing a general order tree.
+ *
+ */
+struct TreeStruct {
+ Tk_Uid nameId; /* String identifying the node within this
+ * hierarchy. Does not necessarily have to
+ * be unique. */
+
+ Entry *entryPtr; /* Points to the entry structure at this
+ * node. */
+
+ Tree *parentPtr; /* Pointer to the parent node.
+ * If NULL, this is the root node. */
+
+ Blt_Chain *chainPtr; /* Pointer to list of child nodes. The list
+ * isn't allocated until the child nodes are
+ * to be inserted. */
+
+ Blt_ChainLink *linkPtr; /* List link containing this node in parent.
+ * This is used to remove the node from
+ * its parent when destroying the node. */
+
+ short int level; /* The level of this node in the tree. */
+};
+
+/*
+ * Entry --
+ *
+ * Contains data-specific information how to represent the data
+ * of a node of the hierarchy.
+ *
+ */
+struct EntryStruct {
+ int worldX, worldY; /* X-Y position in world coordinates
+ * where the entry is positioned. */
+
+ short int width, height; /* Dimensions of the entry. */
+
+ int lineHeight; /* Length of the vertical line segment. */
+
+ unsigned int flags; /* Flags for this entry. For the
+ * definitions of the various bit
+ * fields see below. */
+
+ Tk_Uid dataUid; /* This value isn't used in C code.
+ * It may be used in Tcl bindings to
+ * associate extra data (other than
+ * the label, image, or text) with the
+ * entry. */
+
+ Tk_Uid tags; /* List of binding tags for this
+ * entry. Tk_Uid, not a string,
+ * because in the typical case most
+ * entries will have the same
+ * bintags. */
+ Blt_HashEntry *hashPtr; /* Points the hash entry containing this
+ * entry. This is how the node index is
+ * stored--as the key of the hash entry. */
+ Hierbox *hboxPtr;
+
+ Tk_Uid openCmd, closeCmd; /* Tcl commands to invoke when entries
+ * are opened or closed. They override
+ * those specified globally. */
+ /*
+ * Button information:
+ */
+ short int buttonX, buttonY; /* X-Y coordinate offsets from to
+ * upper left corner of the entry to
+ * the upper-left corner of the
+ * button. Used to pick the
+ * button quickly */
+
+ CachedImage *icons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+
+ GC iconGC; /* GC for drawing default bitmaps. */
+
+ short int iconWidth, iconHeight;
+ /* Maximum dimensions for icons and
+ * buttons for this entry. This is
+ * used to align the button, icon, and
+ * text. */
+
+ CachedImage *activeIcons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+
+ short int levelX; /* X-coordinate offset of data image
+ * or text for children nodes. */
+ /*
+ * Label information:
+ */
+ short int labelWidth, labelHeight;
+ char *labelText; /* Text displayed right of the icon. */
+
+
+ Tk_Font labelFont; /* Font of label. Overrides global font
+ * specification. */
+
+ XColor *labelColor; /* Color of label. Overrides default text color
+ * specification. */
+
+ GC labelGC;
+
+ Shadow labelShadow;
+
+ /*
+ * Data (text or image) information:
+ */
+ Tk_Uid dataText;
+
+ Tk_Font dataFont;
+
+ XColor *dataColor;
+
+ Shadow dataShadow;
+
+ GC dataGC;
+
+ CachedImage *images;
+
+};
+
+
+static Tk_ConfigSpec entryConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activeicons", "activeIcons", "Icons",
+ DEF_ENTRY_ICONS, Tk_Offset(Entry, activeIcons),
+ TK_CONFIG_NULL_OK, &imagesOption},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_ENTRY_BIND_TAGS, Tk_Offset(Entry, tags),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-closecommand", "entryCloseCommand",
+ "EntryCloseCommand",
+ DEF_ENTRY_COMMAND, Tk_Offset(Entry, closeCmd),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "data",
+ DEF_ENTRY_DATA, Tk_Offset(Entry, dataUid),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-button", "button", "Button",
+ DEF_ENTRY_BUTTON, Tk_Offset(Entry, flags),
+ TK_CONFIG_DONT_SET_DEFAULT, &buttonOption},
+ {TK_CONFIG_CUSTOM, "-icons", "icons", "Icons",
+ DEF_ENTRY_ICONS, Tk_Offset(Entry, icons),
+ TK_CONFIG_NULL_OK, &imagesOption},
+ {TK_CONFIG_CUSTOM, "-images", "images", "Images",
+ DEF_ENTRY_IMAGES, Tk_Offset(Entry, images),
+ TK_CONFIG_NULL_OK, &imagesOption},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ DEF_ENTRY_LABEL, Tk_Offset(Entry, labelText), 0},
+ {TK_CONFIG_COLOR, "-labelcolor", "labelColor", "LabelColor",
+ DEF_ENTRY_FG_COLOR, Tk_Offset(Entry, labelColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-labelcolor", "labelColor", "LabelColor",
+ DEF_ENTRY_FG_MONO, Tk_Offset(Entry, labelColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_FONT, "-labelfont", "labelFont", "LabelFont",
+ DEF_ENTRY_FONT, Tk_Offset(Entry, labelFont), 0},
+ {TK_CONFIG_CUSTOM, "-labelshadow", "labelShadow", "LabelShadow",
+ DEF_ENTRY_SHADOW_COLOR, Tk_Offset(Entry, labelShadow),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-labelshadow", "labelShadow", "LabelShadow",
+ DEF_ENTRY_SHADOW_MONO, Tk_Offset(Entry, labelShadow),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-opencommand", "entryOpenCommand", "EntryOpenCommand",
+ DEF_ENTRY_COMMAND, Tk_Offset(Entry, openCmd),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-text", "text", "Text",
+ DEF_ENTRY_LABEL, Tk_Offset(Entry, dataText),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_COLOR, "-textcolor", "textColor", "TextColor",
+ DEF_ENTRY_FG_COLOR, Tk_Offset(Entry, dataColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_FONT, "-textfont", "textFont", "TextFont",
+ DEF_ENTRY_FONT, Tk_Offset(Entry, dataFont), 0},
+ {TK_CONFIG_CUSTOM, "-textshadow", "textShadow", "Shadow",
+ DEF_ENTRY_SHADOW_COLOR, Tk_Offset(Entry, dataShadow),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-textShadow", "textShadow", "Shadow",
+ DEF_ENTRY_SHADOW_MONO, Tk_Offset(Entry, dataShadow),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltShadowOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * ButtonAttributes --
+ *
+ * A button is the open/close indicator at the far left of the
+ * entry. It is displayed as a plus or minus in a solid
+ * colored box with optionally an border. It has both "active"
+ * and "normal" colors.
+ */
+
+typedef struct {
+ XColor *fgColor; /* Foreground color. */
+ Tk_3DBorder border; /* Background color. */
+ XColor *activeFgColor; /* Active foreground color. */
+ Tk_3DBorder activeBorder; /* Active background color. */
+ GC lineGC, normalGC, activeGC;
+ int reqSize;
+ int borderWidth;
+ int openRelief, closeRelief;
+ int width, height;
+ CachedImage *images;
+
+} ButtonAttributes;
+
+/*
+ * TextEdit --
+ *
+ * This structure is shared by entries when their labels are
+ * edited via the keyboard. It maintains the location of the
+ * insertion cursor and the text selection for the editted entry.
+ * The structure is shared since we need only one. The "focus"
+ * entry should be the only entry receiving KeyPress/KeyRelease
+ * events at any time. Information from the previously editted
+ * entry is overwritten.
+ *
+ * Note that all the indices internally are in terms of bytes,
+ * not characters. This is because UTF-8 strings may encode a
+ * single character into a multi-byte sequence. To find the
+ * respective character position
+ *
+ * n = Tcl_NumUtfChars(string, index);
+ *
+ * where n is the resulting character number.
+ */
+typedef struct {
+ int active;
+ int insertPos; /* Position of the cursor within the
+ * array of bytes of the entry's label. */
+ int x, y; /* Offsets of the insertion cursolsr from the
+ * start of the label. Used to draw the
+ * the cursor relative to the entry. */
+ int width, height; /* Size of the insertion cursor symbol. */
+
+ int selAnchor; /* Fixed end of selection. Used to extend
+ * the selection while maintaining the
+ * other end of the selection. */
+ int selFirst; /* Position of the first character in the
+ * selection. */
+ int selLast; /* Position of the last character in the
+ * selection. */
+
+ int cursorOn; /* Indicates if the cursor is displayed. */
+ int onTime, offTime; /* Time in milliseconds to wait before
+ * changing the cursor from off-to-on
+ * and on-to-off. Setting offTime to 0 makes
+ * the cursor steady. */
+ Tcl_TimerToken timerToken; /* Handle for a timer event called periodically
+ * to blink the cursor. */
+
+} TextEdit;
+
+/*
+ * LevelInfo --
+ *
+ */
+
+typedef struct {
+ int x;
+ int width;
+} LevelInfo;
+
+/*
+ * Hierbox --
+ *
+ * Represents the hierarchy widget that contains one or more
+ * entries.
+ *
+ * Entries are positioned in "world" coordinates, refering to
+ * the virtual hierbox space. Coordinate 0,0 is the upper-left
+ * of the hierarchy box and the bottom is the end of the last
+ * entry. The widget's Tk window acts as view port into this
+ * virtual space. The hierbox's xOffset and yOffset fields specify
+ * the location of the view port in the virtual world. Scrolling
+ * the viewport is therefore simply changing the xOffset and/or
+ * yOffset fields and redrawing.
+ *
+ * Note that world coordinates are integers, not signed short
+ * integers like X11 screen coordinates. It's very easy to
+ * create a hierarchy that is more than 0x7FFF pixels high.
+ *
+ */
+struct HierboxStruct {
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+
+ Tcl_Command cmdToken; /* Token for widget's command. */
+
+ int flags; /* For bitfield definitions, see below */
+
+ int allowDuplicates; /* Allow duplicate entries. */
+
+ int autoCreate; /* Automatically create path entries
+ * as needed. */
+ int exportSelection; /* Export the selection to X. */
+
+ int hideRoot; /* Don't display the root entry. */
+
+ int scrollTile; /* Adjust the tile along with viewport
+ * offsets as the widget is
+ * scrolled. */
+
+ int inset; /* Total width of all borders,
+ * including traversal highlight and
+ * 3-D border. Indicates how much
+ * interior stuff must be offset from
+ * outside edges to leave room for
+ * borders. */
+
+ Tk_3DBorder border; /* 3D border surrounding the window
+ * (viewport). */
+ int borderWidth; /* Width of 3D border. */
+ int relief; /* 3D border relief. */
+
+
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ Blt_Tile tile; /* Tiled background */
+
+ char *separator; /* Pathname separators */
+ char *trimLeft; /* Leading characters to trim from
+ * pathnames */
+
+ /*
+ * Entries are connected by horizontal and vertical lines. They
+ * may be drawn dashed or solid.
+ */
+ int lineWidth; /* Width of lines connecting entries */
+ int dashes; /* Dash on-off value. */
+ XColor *lineColor; /* Color of connecting lines. */
+
+ /*
+ * Button Information:
+ *
+ * The button is the open/close indicator at the far left of the
+ * entry. It is usually displayed as a plus or minus in a solid
+ * colored box with optionally an border. It has both "active"
+ * and "normal" colors.
+ */
+ ButtonAttributes button;
+
+
+ /*
+ * Selection Information:
+ *
+ * The selection is the rectangle that contains a selected entry.
+ * There may be many selected entries. It is displayed as a
+ * solid colored box with optionally a 3D border.
+ */
+ Tk_3DBorder selBorder; /* Background color of an highlighted entry.*/
+
+ int selRelief; /* Relief of selected items. Currently
+ * is always raised. */
+
+ int selBorderWidth; /* Border width of a selected entry.*/
+
+ XColor *selFgColor; /* Text color of a selected entry. */
+
+ Tree *selAnchorPtr; /* Fixed end of selection (i.e. node
+ * at which selection was started.) */
+
+ Blt_HashTable selectTable;
+ Blt_Chain selectChain;
+
+ int sortSelection; /* Indicates if the entries in the
+ * selection should be sorted or
+ * displayed in the order they were
+ * selected. */
+
+ char *selectMode; /* Selection style. This value isn't
+ * used in C code (it's just a place
+ * holder), but for the widget's Tcl
+ * bindings. */
+
+ char *selectCmd; /* Tcl script that's invoked whenever
+ * the selection changes. */
+
+ int leader; /* Number of pixels padding between entries */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ int reqWidth, reqHeight; /* Requested dimensions of the widget's
+ * window. */
+
+ GC lineGC; /* GC for drawing dotted line between
+ * entries. */
+
+ XColor *activeFgColor;
+ Tk_3DBorder activeBorder;
+
+ XColor *focusColor;
+ Blt_Dashes focusDashes; /* Dash on-off value. */
+ GC focusGC; /* Graphics context for the active label */
+ int focusEdit; /* Indicates if the label of the "focus" entry
+ * can be editted. */
+ TextEdit labelEdit;
+
+ Tree *activePtr;
+ Tree *focusPtr; /* Pointer to node that's currently active. */
+ Tree *activeButtonPtr; /* Pointer to last active button */
+
+ /* Number of pixels to move for 1 scroll unit. */
+ int reqScrollX, reqScrollY;
+ int xScrollUnits, yScrollUnits;
+
+ /* Command strings to control horizontal and vertical scrollbars. */
+ char *xScrollCmdPrefix, *yScrollCmdPrefix;
+
+ int scrollMode; /* Selects mode of scrolling: either
+ * BLT_SCROLL_MODE_HIERBOX,
+ * BLT_SCROLL_MODE_LISTBOX, or
+ * BLT_SCROLL_MODE_CANVAS. */
+
+ /*
+ * Total size of all "open" entries. This represents the range of
+ * world coordinates.
+ */
+ int worldWidth, worldHeight;
+ int xOffset, yOffset; /* Translation between view port and
+ * world origin. */
+ int minHeight; /* Minimum entry height. Used to
+ * to compute what the y-scroll unit
+ * should be. */
+ LevelInfo *levelInfo;
+ /*
+ * Scanning information:
+ */
+ int scanAnchorX, scanAnchorY;
+ /* Scan anchor in screen coordinates. */
+ int scanX, scanY; /* X-Y world coordinate where the scan
+ * started. */
+
+
+ Blt_HashTable nodeTable; /* Table of node identifiers */
+ Blt_HashTable imageTable; /* Table of Tk images */
+ Tree *rootPtr; /* Root of hierarchy */
+ int depth; /* Maximum depth of the hierarchy. */
+
+ Tree **visibleArr; /* Array of visible entries */
+ int nVisible; /* Number of entries in the above array */
+
+ int nextSerial;
+
+ char *openCmd, *closeCmd; /* Tcl commands to invoke when entries
+ * are opened or closed. */
+
+ Tk_Font defFont; /* Specifies a default font for labels and
+ * text data. It may be overridden for a
+ * single entry by its -*font option. */
+
+ XColor *defColor; /* Global text color for labels. This
+ * may be overridden by the -color
+ * option for an individual entry. */
+
+ Pixmap iconBitmap, iconMask;/* Default icon bitmaps */
+ XColor *iconColor;
+
+ char *takeFocus;
+ char *sortCmd; /* Tcl command to invoke to sort entries */
+
+ ClientData clientData;
+ Blt_BindTable bindTable; /* Binding information for entries. */
+ Blt_BindTable buttonBindTable; /* Binding information for buttons. */
+};
+
+static Tk_ConfigSpec buttonConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
+ DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Hierbox, button.activeBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
+ DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Hierbox, button.activeBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Hierbox, button.activeFgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Hierbox, button.activeFgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_NORMAL_BG_COLOR, Tk_Offset(Hierbox, button.border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_NORMAL_BG_MONO, Tk_Offset(Hierbox, button.border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Hierbox, button.borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_RELIEF, "-closerelief", "closeRelief", "Relief",
+ DEF_BUTTON_CLOSE_RELIEF, Tk_Offset(Hierbox, button.closeRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_NORMAL_FG_COLOR, Tk_Offset(Hierbox, button.fgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_NORMAL_FG_MONO, Tk_Offset(Hierbox, button.fgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-images", "images", "Images",
+ DEF_BUTTON_IMAGES, Tk_Offset(Hierbox, button.images),
+ TK_CONFIG_NULL_OK, &imagesOption},
+ {TK_CONFIG_RELIEF, "-openrelief", "openRelief", "Relief",
+ DEF_BUTTON_OPEN_RELIEF, Tk_Offset(Hierbox, button.openRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-size", "size", "Size", DEF_BUTTON_SIZE,
+ Tk_Offset(Hierbox, button.reqSize), 0, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground",
+ DEF_HIERBOX_ACTIVE_BG_COLOR, Tk_Offset(Hierbox, activeBorder),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground",
+ DEF_HIERBOX_ACTIVE_BG_MONO, Tk_Offset(Hierbox, activeBorder),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_HIERBOX_ACTIVE_FG_COLOR, Tk_Offset(Hierbox, activeFgColor), 0},
+ {TK_CONFIG_BOOLEAN, "-allowduplicates", "allowDuplicates",
+ "AllowDuplicates",
+ DEF_HIERBOX_ALLOW_DUPLICATES, Tk_Offset(Hierbox, allowDuplicates),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-autocreate", "autoCreate", "AutoCreate",
+ DEF_HIERBOX_MAKE_PATH, Tk_Offset(Hierbox, autoCreate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_HIERBOX_BACKGROUND, Tk_Offset(Hierbox, border), 0},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_HIERBOX_CURSOR, Tk_Offset(Hierbox, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_HIERBOX_BORDER_WIDTH, Tk_Offset(Hierbox, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-closecommand", "closeCommand", "CloseCommand",
+ DEF_HIERBOX_COMMAND, Tk_Offset(Hierbox, closeCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_HIERBOX_DASHES, Tk_Offset(Hierbox, dashes),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
+ "ExportSelection",
+ DEF_HIERBOX_EXPORT_SELECTION, Tk_Offset(Hierbox, exportSelection),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-focusdashes", "focusDashes", "FocusDashes",
+ DEF_HIERBOX_FOCUS_DASHES, Tk_Offset(Hierbox, focusDashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_BOOLEAN, "-focusedit", "focusEdit", "FocusEdit",
+ DEF_HIERBOX_FOCUS_EDIT, Tk_Offset(Hierbox, focusEdit), 0},
+ {TK_CONFIG_COLOR, "-focusforeground", "focusForeground",
+ "FocusForeground",
+ DEF_HIERBOX_FOCUS_FG_COLOR, Tk_Offset(Hierbox, focusColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-focusforeground", "focusForeground",
+ "FocusForeground",
+ DEF_HIERBOX_FOCUS_FG_MONO, Tk_Offset(Hierbox, focusColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_HIERBOX_FONT, Tk_Offset(Hierbox, defFont), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_HIERBOX_TEXT_COLOR, Tk_Offset(Hierbox, defColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_HIERBOX_TEXT_MONO, Tk_Offset(Hierbox, defColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_HIERBOX_HEIGHT, Tk_Offset(Hierbox, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-hideroot", "hideRoot", "HideRoot",
+ DEF_HIERBOX_HIDE_ROOT, Tk_Offset(Hierbox, hideRoot),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_HIERBOX_HIGHLIGHT_BG_COLOR, Tk_Offset(Hierbox, highlightBgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_HIERBOX_HIGHLIGHT_BG_MONO, Tk_Offset(Hierbox, highlightBgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_HIERBOX_HIGHLIGHT_COLOR, Tk_Offset(Hierbox, highlightColor), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_HIERBOX_HIGHLIGHT_WIDTH, Tk_Offset(Hierbox, highlightWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+ DEF_HIERBOX_LINE_COLOR, Tk_Offset(Hierbox, lineColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+ DEF_HIERBOX_LINE_MONO, Tk_Offset(Hierbox, lineColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-linespacing", "lineSpacing", "LineSpacing",
+ DEF_HIERBOX_LINE_SPACING, Tk_Offset(Hierbox, leader),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_HIERBOX_LINE_WIDTH, Tk_Offset(Hierbox, lineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-opencommand", "openCommand", "OpenCommand",
+ DEF_HIERBOX_COMMAND, Tk_Offset(Hierbox, openCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_HIERBOX_RELIEF, Tk_Offset(Hierbox, relief), 0},
+ {TK_CONFIG_CUSTOM, "-scrollmode", "scrollMode", "ScrollMode",
+ DEF_HIERBOX_SCROLL_MODE, Tk_Offset(Hierbox, scrollMode),
+ TK_CONFIG_DONT_SET_DEFAULT, &scrollModeOption},
+ {TK_CONFIG_BOOLEAN, "-scrolltile", "scrollTile", "ScrollTile",
+ DEF_HIERBOX_SCROLL_TILE, Tk_Offset(Hierbox, scrollTile),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_HIERBOX_SELECT_BG_MONO, Tk_Offset(Hierbox, selBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_HIERBOX_SELECT_BG_COLOR, Tk_Offset(Hierbox, selBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
+ DEF_HIERBOX_SELECT_BORDER_WIDTH, Tk_Offset(Hierbox, selBorderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ DEF_HIERBOX_SELECT_CMD, Tk_Offset(Hierbox, selectCmd),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_HIERBOX_SELECT_FG_MONO, Tk_Offset(Hierbox, selFgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_HIERBOX_SELECT_FG_COLOR, Tk_Offset(Hierbox, selFgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_STRING, "-selectmode", "selectMode", "SelectMode",
+ DEF_HIERBOX_SELECT_MODE, Tk_Offset(Hierbox, selectMode),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+ DEF_HIERBOX_SELECT_RELIEF, Tk_Offset(Hierbox, selRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-separator", "separator", "Separator",
+ DEF_HIERBOX_SEPARATOR, Tk_Offset(Hierbox, separator),
+ TK_CONFIG_NULL_OK, &separatorOption},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_HIERBOX_TAKE_FOCUS, Tk_Offset(Hierbox, takeFocus), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Hierbox, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_STRING, "-trimleft", "trimLeft", "Trim",
+ DEF_HIERBOX_TRIMLEFT, Tk_Offset(Hierbox, trimLeft), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_HIERBOX_WIDTH, Tk_Offset(Hierbox, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(Hierbox, xScrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-xscrollincrement", "xScrollIncrement",
+ "ScrollIncrement",
+ DEF_HIERBOX_SCROLL_INCREMENT, Tk_Offset(Hierbox, reqScrollX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(Hierbox, yScrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-yscrollincrement", "yScrollIncrement",
+ "ScrollIncrement",
+ DEF_HIERBOX_SCROLL_INCREMENT, Tk_Offset(Hierbox, reqScrollY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+typedef struct {
+ int x, y; /* Tracks the current world
+ * coordinates as we traverse through
+ * the tree. After a full-tree
+ * traversal, the y-coordinate will be
+ * the height of the virtual
+ * hierarchy. */
+ int maxWidth; /* Maximum entry width. This is the
+ * width of the virtual hierarchy. */
+ int labelOffset;
+ int minHeight; /* Minimum entry height. Used to
+ * to compute what the y-scroll unit
+ * should be. */
+ int maxIconWidth;
+ int level, depth;
+} LayoutInfo;
+
+#define DEF_ICON_WIDTH 16
+#define DEF_ICON_HEIGHT 16
+static unsigned char folderBits[] =
+{
+ 0x00, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x81, 0x3f, 0x41, 0x40, 0x3f, 0xc0,
+ 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
+ 0x01, 0xc0, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00};
+
+static unsigned char folderMaskBits[] =
+{
+ 0x00, 0x00, 0x7c, 0x00, 0xfe, 0x00, 0xff, 0x3f, 0xff, 0x7f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00};
+
+
+/* Forward Declarations */
+static void DestroyHierbox _ANSI_ARGS_((DestroyData dataPtr));
+static void HierboxEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void DrawButton _ANSI_ARGS_((Hierbox *hboxPtr, Tree * treePtr,
+ Drawable drawable));
+static void DisplayHierbox _ANSI_ARGS_((ClientData clientData));
+static void HierboxInstCmdDeleteProc _ANSI_ARGS_((ClientData clientdata));
+static int HierboxInstCmd _ANSI_ARGS_((ClientData clientdata,
+ Tcl_Interp *interp, int argc, char **argv));
+static void EventuallyRedraw _ANSI_ARGS_((Hierbox *hboxPtr));
+static void SelectCmdProc _ANSI_ARGS_((ClientData clientData));
+static void EventuallyInvokeSelectCmd _ANSI_ARGS_((Hierbox *hboxPtr));
+static int ComputeVisibleEntries _ANSI_ARGS_((Hierbox *hboxPtr));
+static int ConfigureEntry _ANSI_ARGS_((Hierbox *hboxPtr, Entry * entryPtr,
+ int argc, char **argv, int flags));
+static void ComputeLayout _ANSI_ARGS_((Hierbox *hboxPtr));
+
+#ifdef __STDC__
+static CompareProc ExactCompare, GlobCompare, RegexpCompare;
+static ApplyProc SelectNode, GetSelectedLabels, CloseNode, SizeOfNode, IsSelectedNode, MapAncestors,
+ FixUnmappedSelections, UnmapNode, SortNode, OpenNode;
+static IterProc NextNode, LastNode;
+static Tk_ImageChangedProc ImageChangedProc;
+static Blt_ChainCompareProc CompareNodesByTclCmd, CompareNodesByName;
+static Blt_BindPickProc PickButton, PickEntry;
+static Blt_BindTagProc GetTags;
+static Tk_SelectionProc SelectionProc;
+static Tk_LostSelProc LostSelection;
+static Tcl_CmdProc HierboxCmd;
+static Blt_TileChangedProc TileChangedProc;
+static Tcl_TimerProc LabelBlinkProc;
+static Tcl_FreeProc DestroyNode;
+#endif /* __STDC__ */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToScrollMode --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToScrollMode(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ int *modePtr = (int *)(widgRec + offset);
+
+ if ((string[0] == 'l') && (strcmp(string, "listbox") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_LISTBOX;
+ } else if ((string[0] == 'h') && (strcmp(string, "hierbox") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_HIERBOX;
+ } else if ((string[0] == 'c') && (strcmp(string, "canvas") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_CANVAS;
+ } else {
+ Tcl_AppendResult(interp, "bad scroll mode \"", string,
+ "\": should be \"hierbox\", \"listbox\", or \"canvas\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollModeToString --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ScrollModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of flags field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ int mode = *(int *)(widgRec + offset);
+
+ switch (mode) {
+ case BLT_SCROLL_MODE_LISTBOX:
+ return "listbox";
+ case BLT_SCROLL_MODE_HIERBOX:
+ return "hierbox";
+ case BLT_SCROLL_MODE_CANVAS:
+ return "canvas";
+ default:
+ return "unknown scroll mode";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToButton --
+ *
+ * Convert a string to one of three values.
+ * 0 - false, no, off
+ * 1 - true, yes, on
+ * 2 - auto
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToButton(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ int *flags = (int *)(widgRec + offset);
+
+ *flags &= ~BUTTON_MASK;
+ if ((string[0] == 'a') && (strcmp(string, "auto") == 0)) {
+ *flags |= BUTTON_AUTO;
+ } else {
+ int value;
+
+ if (Tcl_GetBoolean(interp, string, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value) {
+ *flags |= BUTTON_SHOW;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonToString --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ButtonToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of flags field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ unsigned int flags = *(int *)(widgRec + offset);
+
+ switch (flags & BUTTON_MASK) {
+ case 0:
+ return "0";
+ case BUTTON_SHOW:
+ return "1";
+ case BUTTON_AUTO:
+ return "auto";
+ default:
+ return "unknown button value";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+}
+
+static CachedImage
+GetCachedImage(hboxPtr, interp, tkwin, name)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *name;
+{
+ struct CachedImageStruct *imagePtr;
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&(hboxPtr->imageTable), (char *)name, &isNew);
+ if (isNew) {
+ Tk_Image tkImage;
+ int width, height;
+
+ tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, hboxPtr);
+ if (tkImage == NULL) {
+ Blt_DeleteHashEntry(&(hboxPtr->imageTable), hPtr);
+ return NULL;
+ }
+ Tk_SizeOfImage(tkImage, &width, &height);
+ imagePtr = Blt_Malloc(sizeof(struct CachedImageStruct));
+ imagePtr->tkImage = tkImage;
+ imagePtr->hashPtr = hPtr;
+ imagePtr->refCount = 1;
+ imagePtr->width = width, imagePtr->height = height;
+ Blt_SetHashValue(hPtr, imagePtr);
+ } else {
+ imagePtr = (struct CachedImageStruct *)Blt_GetHashValue(hPtr);
+ imagePtr->refCount++;
+ }
+ return imagePtr;
+}
+
+static void
+FreeCachedImage(hboxPtr, imagePtr)
+ Hierbox *hboxPtr;
+ struct CachedImageStruct *imagePtr;
+{
+ imagePtr->refCount--;
+ if (imagePtr->refCount == 0) {
+ Blt_DeleteHashEntry(&(hboxPtr->imageTable), imagePtr->hashPtr);
+ Tk_FreeImage(imagePtr->tkImage);
+ Blt_Free(imagePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToImages --
+ *
+ * Convert a list of image names into Tk images.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StringToImages(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to field in structure */
+{
+ Hierbox *hboxPtr = *(Hierbox **)clientData;
+ CachedImage **imagePtrPtr = (CachedImage **) (widgRec + offset);
+ CachedImage *imageArr;
+ int result;
+
+ result = TCL_OK;
+ imageArr = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ int nNames;
+ char **nameArr;
+
+ if (Tcl_SplitList(interp, string, &nNames, &nameArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nNames > 0) {
+ register int i;
+
+ imageArr = Blt_Malloc(sizeof(CachedImage *) * (nNames + 1));
+ assert(imageArr);
+ for (i = 0; i < nNames; i++) {
+ imageArr[i] = GetCachedImage(hboxPtr, interp, tkwin, nameArr[i]);
+ if (imageArr[i] == NULL) {
+ result = TCL_ERROR;
+ break;
+ }
+ }
+ Blt_Free(nameArr);
+ imageArr[nNames] = NULL;
+ }
+ }
+ if (*imagePtrPtr != NULL) {
+ register CachedImage *imagePtr;
+
+ for (imagePtr = *imagePtrPtr; *imagePtr != NULL; imagePtr++) {
+ FreeCachedImage(hboxPtr, *imagePtr);
+ }
+ Blt_Free(*imagePtrPtr);
+ }
+ *imagePtrPtr = imageArr;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImagesToString --
+ *
+ * Converts the image into its string representation (its name).
+ *
+ * Results:
+ * The name of the image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ImagesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of images array in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ CachedImage **imagePtrPtr = (CachedImage **) (widgRec + offset);
+ Tcl_DString dString;
+ char *result;
+
+ Tcl_DStringInit(&dString);
+ if (*imagePtrPtr != NULL) {
+ register CachedImage *imagePtr;
+
+ for (imagePtr = *imagePtrPtr; *imagePtr != NULL; imagePtr++) {
+ Tcl_DStringAppendElement(&dString,
+ Blt_NameOfImage((*imagePtr)->tkImage));
+ }
+ }
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToScrollmode --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSeparator(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing new value */
+ char *widgRec; /* Widget record */
+ int offset; /* offset in structure */
+{
+ char **sepPtr = (char **)(widgRec + offset);
+
+ if ((*sepPtr != SEPARATOR_LIST) && (*sepPtr != SEPARATOR_NONE)) {
+ Blt_Free(*sepPtr);
+ }
+ if ((string == NULL) || (*string == '\0')) {
+ *sepPtr = SEPARATOR_LIST;
+ } else if (strcmp(string, "none") == 0) {
+ *sepPtr = SEPARATOR_NONE;
+ } else {
+ *sepPtr = Blt_Strdup(string);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SeparatorToString --
+ *
+ * Results:
+ * The string representation of the separator is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SeparatorToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of flags field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ char *separator = *(char **)(widgRec + offset);
+
+ if (separator == SEPARATOR_NONE) {
+ return "";
+ } else if (separator == SEPARATOR_LIST) {
+ return "list";
+ }
+ return separator;
+}
+
+static int
+ApplyToTree(hboxPtr, rootPtr, proc, flags)
+ Hierbox *hboxPtr;
+ Tree *rootPtr;
+ ApplyProc *proc;
+ unsigned int flags;
+{
+ if (flags & APPLY_BEFORE) {
+ if ((*proc) (hboxPtr, rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (flags & APPLY_RECURSE) {
+ if (!(flags & APPLY_OPEN_ONLY) ||
+ (rootPtr->entryPtr->flags & ENTRY_OPEN)) {
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Tree *treePtr;
+
+ nextPtr = NULL;
+ for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ /* Get the next link in the chain before calling
+ * ApplyToTree. This is because ApplyToTree may
+ * delete the node and its link. */
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (ApplyToTree(hboxPtr, treePtr, proc, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+ if ((flags & APPLY_BEFORE) == 0) {
+ if ((*proc) (hboxPtr, rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+static void
+ConfigureButtons(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+
+ gcMask = GCForeground;
+ gcValues.foreground = buttonPtr->fgColor->pixel;
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->normalGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->normalGC);
+ }
+ buttonPtr->normalGC = newGC;
+
+ gcMask = (GCForeground | GCLineWidth);
+ gcValues.foreground = hboxPtr->lineColor->pixel;
+ gcValues.line_width = hboxPtr->lineWidth;
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->lineGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->lineGC);
+ }
+ buttonPtr->lineGC = newGC;
+
+ gcMask = GCForeground;
+ gcValues.foreground = buttonPtr->activeFgColor->pixel;
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->activeGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->activeGC);
+ }
+ buttonPtr->activeGC = newGC;
+
+ buttonPtr->width = buttonPtr->height = ODD(buttonPtr->reqSize);
+ if (buttonPtr->images != NULL) {
+ register int i;
+
+ for (i = 0; i < 2; i++) {
+ if (buttonPtr->images[i] == NULL) {
+ break;
+ }
+ if (buttonPtr->width < ImageWidth(buttonPtr->images[i])) {
+ buttonPtr->width = ImageWidth(buttonPtr->images[i]);
+ }
+ if (buttonPtr->height < ImageHeight(buttonPtr->images[i])) {
+ buttonPtr->height = ImageHeight(buttonPtr->images[i]);
+ }
+ }
+ }
+ buttonPtr->width += 2 * buttonPtr->borderWidth;
+ buttonPtr->height += 2 * buttonPtr->borderWidth;
+}
+
+static void
+DestroyEntry(entryPtr)
+ Entry *entryPtr;
+{
+ Hierbox *hboxPtr = entryPtr->hboxPtr;
+ register CachedImage *imagePtr;
+
+ Tk_FreeOptions(entryConfigSpecs, (char *)entryPtr, hboxPtr->display, 0);
+ if (entryPtr->labelGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->labelGC);
+ }
+ if (entryPtr->dataGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->dataGC);
+ }
+ if (entryPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(hboxPtr->nodeTable), entryPtr->hashPtr);
+ }
+ if (entryPtr->dataShadow.color != NULL) {
+ Tk_FreeColor(entryPtr->dataShadow.color);
+ }
+ if (entryPtr->labelShadow.color != NULL) {
+ Tk_FreeColor(entryPtr->labelShadow.color);
+ }
+ if (entryPtr->iconGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->iconGC);
+ }
+ if (entryPtr->openCmd != NULL) {
+ Blt_FreeUid(entryPtr->openCmd);
+ }
+ if (entryPtr->closeCmd != NULL) {
+ Blt_FreeUid(entryPtr->closeCmd);
+ }
+ if (entryPtr->dataUid != NULL) {
+ Blt_FreeUid(entryPtr->dataUid);
+ }
+ if (entryPtr->dataText != NULL) {
+ Blt_FreeUid(entryPtr->dataText);
+ }
+ if (entryPtr->tags != NULL) {
+ Blt_FreeUid(entryPtr->tags);
+ }
+ if (entryPtr->icons != NULL) {
+ for (imagePtr = entryPtr->icons; *imagePtr != NULL; imagePtr++) {
+ FreeCachedImage(hboxPtr, *imagePtr);
+ }
+ Blt_Free(entryPtr->icons);
+ }
+ if (entryPtr->activeIcons != NULL) {
+ for (imagePtr = entryPtr->activeIcons; *imagePtr != NULL; imagePtr++) {
+ FreeCachedImage(hboxPtr, *imagePtr);
+ }
+ Blt_Free(entryPtr->activeIcons);
+ }
+ if (entryPtr->images != NULL) {
+ for (imagePtr = entryPtr->images; *imagePtr != NULL; imagePtr++) {
+ FreeCachedImage(hboxPtr, *imagePtr);
+ }
+ Blt_Free(entryPtr->images);
+ }
+ Blt_Free(entryPtr);
+}
+
+/*ARGSUSED*/
+static Tree *
+LastNode(treePtr, mask)
+ register Tree *treePtr;
+ unsigned int mask;
+{
+ Blt_ChainLink *linkPtr;
+
+ if (treePtr->parentPtr == NULL) {
+ return NULL; /* The root is the first node. */
+ }
+ linkPtr = Blt_ChainPrevLink(treePtr->linkPtr);
+ if (linkPtr == NULL) {
+ /* There are no siblings previous to this one, so pick the parent. */
+ return treePtr->parentPtr;
+ }
+ /*
+ * Traverse down the right-most thread, in order to select the
+ * next entry. Stop if we find a "closed" entry or reach a leaf.
+ */
+ treePtr = Blt_ChainGetValue(linkPtr);
+ while ((treePtr->entryPtr->flags & mask) == mask) {
+ linkPtr = Blt_ChainLastLink(treePtr->chainPtr);
+ if (linkPtr == NULL) {
+ break; /* Found a leaf. */
+ }
+ treePtr = Blt_ChainGetValue(linkPtr);
+ }
+ return treePtr;
+}
+
+
+static Tree *
+NextNode(treePtr, mask)
+ Tree *treePtr;
+ unsigned int mask;
+{
+ Blt_ChainLink *linkPtr;
+
+ if ((treePtr->entryPtr->flags & mask) == mask) {
+ /* Pick the first sub-node. */
+ linkPtr = Blt_ChainFirstLink(treePtr->chainPtr);
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ }
+ /*
+ * Back up until we can find a level where we can pick a "next" entry.
+ * For the last entry we'll thread our way back to the root.
+ */
+ while (treePtr->parentPtr != NULL) {
+ linkPtr = Blt_ChainNextLink(treePtr->linkPtr);
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ treePtr = treePtr->parentPtr;
+ }
+ return NULL; /* At root, no next node. */
+}
+
+/*ARGSUSED*/
+static Tree *
+EndNode(treePtr, mask)
+ Tree *treePtr;
+ unsigned int mask;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainLastLink(treePtr->chainPtr);
+ while (linkPtr != NULL) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if ((treePtr->entryPtr->flags & mask) != mask) {
+ break;
+ }
+ linkPtr = Blt_ChainLastLink(treePtr->chainPtr);
+ }
+ return treePtr;
+}
+
+static void
+ExposeAncestors(treePtr)
+ register Tree *treePtr;
+{
+ treePtr = treePtr->parentPtr;
+ while (treePtr != NULL) {
+ treePtr->entryPtr->flags |= (ENTRY_OPEN | ENTRY_MAPPED);
+ treePtr = treePtr->parentPtr;
+ }
+}
+
+static int
+IsBefore(t1Ptr, t2Ptr)
+ register Tree *t1Ptr, *t2Ptr;
+{
+ int depth;
+ register int i;
+ Blt_ChainLink *linkPtr;
+ Tree *treePtr;
+
+ depth = MIN(t1Ptr->level, t2Ptr->level);
+
+ if (depth == 0) { /* One of the nodes is root. */
+ if (t1Ptr->parentPtr == NULL) {
+ return 1;
+ }
+ return 0;
+ }
+ /*
+ * Traverse back from the deepest node, until the both nodes are at the
+ * same depth. Check if the ancestor node found is the other node.
+ */
+ for (i = t1Ptr->level; i > depth; i--) {
+ t1Ptr = t1Ptr->parentPtr;
+ }
+ if (t1Ptr == t2Ptr) {
+ return 0;
+ }
+ for (i = t2Ptr->level; i > depth; i--) {
+ t2Ptr = t2Ptr->parentPtr;
+ }
+ if (t2Ptr == t1Ptr) {
+ return 1;
+ }
+ /*
+ * First find the mutual ancestor of both nodes. Look at each
+ * preceding ancestor level-by-level for both nodes. Eventually
+ * we'll find a node that's the parent of both ancestors. Then
+ * find the first ancestor in the parent's list of subnodes.
+ */
+ for (i = depth; i > 0; i--) {
+ if (t1Ptr->parentPtr == t2Ptr->parentPtr) {
+ break;
+ }
+ t1Ptr = t1Ptr->parentPtr;
+ t2Ptr = t2Ptr->parentPtr;
+ }
+ for (linkPtr = Blt_ChainFirstLink(t1Ptr->parentPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (treePtr == t1Ptr) {
+ return 1;
+ } else if (treePtr == t2Ptr) {
+ return 0;
+ }
+ }
+ assert(linkPtr != NULL);
+ return 0;
+}
+
+static int
+IsAncestor(rootPtr, treePtr)
+ Tree *rootPtr, *treePtr;
+{
+ if (treePtr != NULL) {
+ treePtr = treePtr->parentPtr;
+ while (treePtr != NULL) {
+ if (treePtr == rootPtr) {
+ return 1;
+ }
+ treePtr = treePtr->parentPtr;
+ }
+ }
+ return 0;
+}
+
+static int
+IsHidden(treePtr)
+ register Tree *treePtr;
+{
+ if (treePtr != NULL) {
+ unsigned int mask;
+
+ if (!(treePtr->entryPtr->flags & ENTRY_MAPPED)) {
+ return TRUE;
+ }
+ treePtr = treePtr->parentPtr;
+ mask = (ENTRY_OPEN | ENTRY_MAPPED);
+ while (treePtr != NULL) {
+ if ((treePtr->entryPtr->flags & mask) != mask) {
+ return TRUE;
+ }
+ treePtr = treePtr->parentPtr;
+ }
+ }
+ return FALSE;
+}
+
+static void
+SelectEntry(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&(hboxPtr->selectTable), (char *)treePtr,
+ &isNew);
+ if (isNew) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainAppend(&(hboxPtr->selectChain), treePtr);
+ Blt_SetHashValue(hPtr, linkPtr);
+ }
+}
+
+static void
+DeselectEntry(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(hboxPtr->selectTable), (char *)treePtr);
+ if (hPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = (Blt_ChainLink *)Blt_GetHashValue(hPtr);
+ Blt_ChainDeleteLink(&(hboxPtr->selectChain), linkPtr);
+ Blt_DeleteHashEntry(&(hboxPtr->selectTable), hPtr);
+ }
+}
+
+static void
+ClearSelection(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ Blt_DeleteHashTable(&(hboxPtr->selectTable));
+ Blt_InitHashTable(&(hboxPtr->selectTable), BLT_ONE_WORD_KEYS);
+ Blt_ChainReset(&(hboxPtr->selectChain));
+ EventuallyRedraw(hboxPtr);
+ if (hboxPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(hboxPtr);
+ }
+}
+
+static void
+PruneSelection(hboxPtr, rootPtr)
+ Hierbox *hboxPtr;
+ Tree *rootPtr;
+{
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Tree *treePtr;
+ int selectionChanged;
+
+ selectionChanged = FALSE;
+ for (linkPtr = Blt_ChainFirstLink(&(hboxPtr->selectChain));
+ linkPtr != NULL; linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (IsAncestor(rootPtr, treePtr)) {
+ DeselectEntry(hboxPtr, treePtr);
+ selectionChanged = TRUE;
+ }
+ }
+ if (selectionChanged) {
+ EventuallyRedraw(hboxPtr);
+ if (hboxPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(hboxPtr);
+ }
+ }
+}
+
+static int
+IsSelected(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(hboxPtr->selectTable), (char *)treePtr);
+ return (hPtr != NULL);
+}
+
+
+static void
+GetFullPath(treePtr, separator, resultPtr)
+ Tree *treePtr;
+ char *separator;
+ Tcl_DString *resultPtr;
+{
+ char **nameArr; /* Used the stack the component names. */
+ register int i;
+ int level;
+
+ level = treePtr->level;
+ nameArr = Blt_Malloc((level + 1) * sizeof(char *));
+ assert(nameArr);
+ for (i = level; i >= 0; i--) {
+ /* Save the name of each ancestor in the name array. */
+ nameArr[i] = treePtr->nameId;
+ treePtr = treePtr->parentPtr;
+ }
+ Tcl_DStringInit(resultPtr);
+ if ((separator == SEPARATOR_LIST) || (separator == SEPARATOR_NONE)) {
+ for (i = 0; i <= level; i++) {
+ Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+ }
+ } else {
+ Tcl_DStringAppend(resultPtr, nameArr[0], -1);
+ if (strcmp(nameArr[0], separator) != 0) {
+ Tcl_DStringAppend(resultPtr, separator, -1);
+ }
+ if (level > 0) {
+ for (i = 1; i < level; i++) {
+ Tcl_DStringAppend(resultPtr, nameArr[i], -1);
+ Tcl_DStringAppend(resultPtr, separator, -1);
+ }
+ Tcl_DStringAppend(resultPtr, nameArr[i], -1);
+ }
+ }
+ Blt_Free(nameArr);
+}
+
+static void
+InsertNode(parentPtr, position, nodePtr)
+ Tree *parentPtr; /* Parent node where the new node will
+ * be added. */
+ int position; /* Position in the parent of the new node. */
+ Tree *nodePtr; /* The node to be inserted. */
+{
+ Blt_ChainLink *linkPtr;
+
+ /*
+ * Create lists to contain subnodes as needed. We don't want to
+ * unnecessarily allocate storage for leaves (of which there may
+ * be many).
+ */
+ if (parentPtr->chainPtr == NULL) {
+ parentPtr->chainPtr = Blt_ChainCreate();
+ }
+ linkPtr = Blt_ChainNewLink();
+ if (position == APPEND) {
+ Blt_ChainAppendLink(parentPtr->chainPtr, linkPtr);
+ } else {
+ Blt_ChainLink *beforePtr;
+
+ beforePtr = Blt_ChainGetNthLink(parentPtr->chainPtr, position);
+ Blt_ChainLinkBefore(parentPtr->chainPtr, linkPtr, beforePtr);
+ }
+ nodePtr->level = parentPtr->level + 1;
+ nodePtr->parentPtr = parentPtr;
+ nodePtr->linkPtr = linkPtr;
+ Blt_ChainSetValue(linkPtr, nodePtr);
+}
+
+static void
+DestroyNode(data)
+ DestroyData data;
+{
+ Tree *treePtr = (Tree *)data;
+
+ if (treePtr->nameId != NULL) {
+ Blt_FreeUid(treePtr->nameId);
+ }
+ if (treePtr->chainPtr != NULL) {
+ Blt_ChainDestroy(treePtr->chainPtr);
+ }
+ if (treePtr->entryPtr != NULL) {
+ DestroyEntry(treePtr->entryPtr);
+ }
+ treePtr->entryPtr = NULL;
+ Blt_Free(treePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateNode --
+ *
+ * Creates and inserts a new node into the given tree at the
+ * specified position.
+ *
+ * Results:
+ * Returns a pointer to the newly created node. If an error
+ * occurred, such as the entry could not be configured, NULL
+ * is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tree *
+CreateNode(hboxPtr, parentPtr, position, name)
+ Hierbox *hboxPtr; /* Hierarchy widget record */
+ Tree *parentPtr; /* Pointer to parent node. */
+ int position; /* Position in node list to insert node. */
+ char *name; /* Name identifier for the new node. */
+{
+ Entry *entryPtr;
+ Tree *treePtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ int serial;
+
+ /* Create the entry structure */
+ entryPtr = Blt_Calloc(1, sizeof(Entry));
+ assert(entryPtr);
+ entryPtr->flags |= (BUTTON_AUTO | ENTRY_MAPPED);
+ entryPtr->hboxPtr = hboxPtr;
+
+ if (name == NULL) {
+ name = "";
+ }
+ entryPtr->labelText = Blt_Strdup(name);
+
+ if (ConfigureEntry(hboxPtr, entryPtr, 0, (char **)NULL, 0) != TCL_OK) {
+ DestroyEntry(entryPtr);
+ return NULL;
+ }
+ /* Create the container structure too */
+ treePtr = Blt_Calloc(1, sizeof(Tree));
+ assert(treePtr);
+ treePtr->nameId = Blt_GetUid(name);
+ treePtr->entryPtr = entryPtr;
+
+ /* Generate a unique node serial number. */
+ do {
+ serial = hboxPtr->nextSerial++;
+ hPtr = Blt_CreateHashEntry(&(hboxPtr->nodeTable), (char *)serial,
+ &isNew);
+ } while (!isNew);
+ Blt_SetHashValue(hPtr, treePtr);
+ entryPtr->hashPtr = hPtr;
+
+ if (parentPtr != NULL) {
+ InsertNode(parentPtr, position, treePtr);
+ }
+ return treePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindComponent --
+ *
+ * Searches for the given child node. The node is designated
+ * by its node identifier string.
+ *
+ * Results:
+ * If found, returns a node pointer. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tree *
+FindComponent(parentPtr, name)
+ Tree *parentPtr;
+ char *name;
+{
+ Tk_Uid nameId;
+
+ nameId = Blt_FindUid(name);
+ if (nameId != NULL) {
+ register Tree *treePtr;
+ Blt_ChainLink *linkPtr;
+
+ /* The component identifier must already exist if the node exists. */
+ for (linkPtr = Blt_ChainFirstLink(parentPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (nameId == treePtr->nameId) {
+ return treePtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SkipSeparators --
+ *
+ * Moves the character pointer past one of more separators.
+ *
+ * Results:
+ * Returns the updates character pointer.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+SkipSeparators(path, separator, length)
+ char *path, *separator;
+ int length;
+{
+ while ((*path == separator[0]) && (strncmp(path, separator, length) == 0)) {
+ path += length;
+ }
+ return path;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SplitPath --
+ *
+ * Returns the trailing component of the given path. Trailing
+ * separators are ignored.
+ *
+ * Results:
+ * Returns the string of the tail component.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SplitPath(hboxPtr, path, levelPtr, compPtrPtr)
+ Hierbox *hboxPtr;
+ char *path;
+ int *levelPtr;
+ char ***compPtrPtr;
+{
+ int skipLen, pathLen;
+ char *sep;
+ int level, listSize;
+ char **components;
+ register char *p;
+
+ skipLen = strlen(hboxPtr->separator);
+ path = SkipSeparators(path, hboxPtr->separator, skipLen);
+ pathLen = strlen(path);
+
+ level = pathLen / skipLen;
+ listSize = (level + 1) * sizeof(char *);
+ components = Blt_Malloc(listSize + (pathLen + 1));
+ assert(components);
+ p = (char *)components + listSize;
+ strcpy(p, path);
+
+ sep = strstr(p, hboxPtr->separator);
+ level = 0;
+ while ((*p != '\0') && (sep != NULL)) {
+ *sep = '\0';
+ components[level++] = p;
+ p = SkipSeparators(sep + skipLen, hboxPtr->separator, skipLen);
+ sep = strstr(p, hboxPtr->separator);
+ }
+ if (*p != '\0') {
+ components[level++] = p;
+ }
+ components[level] = NULL;
+ *levelPtr = level;
+ *compPtrPtr = components;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindPath --
+ *
+ * Finds the node designated by the given path. Each path
+ * component is searched for as the tree is traversed.
+ *
+ * A leading character string is trimmed off the path if it
+ * matches the one designated (see the -trimleft option).
+ *
+ * If no separator is designated (see the -separator
+ * configuration option), the path is considered a Tcl list.
+ * Otherwise the each component of the path is separated by a
+ * character string. Leading and trailing separators are
+ * ignored. Multiple separators are treated as one.
+ *
+ * Results:
+ * Returns the pointer to the designated node. If any component
+ * can't be found, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tree *
+FindPath(hboxPtr, rootPtr, path)
+ Hierbox *hboxPtr;
+ Tree *rootPtr;
+ char *path;
+{
+ register char *p;
+ int skip;
+ char *sep;
+ Tree *treePtr;
+ char save;
+
+ /* Trim off characters that we don't want */
+ if (hboxPtr->trimLeft != NULL) {
+ register char *s;
+
+ /* Trim off leading character string if one exists. */
+ for (p = path, s = hboxPtr->trimLeft; *s != '\0'; s++, p++) {
+ if (*p != *s) {
+ break;
+ }
+ }
+ if (*s == '\0') {
+ path = p;
+ }
+ }
+ if (*path == '\0') {
+ return rootPtr;
+ }
+ if (hboxPtr->separator == SEPARATOR_NONE) {
+ return FindComponent(rootPtr, path);
+ }
+ if (hboxPtr->separator == SEPARATOR_LIST) {
+ char **nameArr;
+ int nComp;
+ register int i;
+
+ /*
+ * No separator, so this must be a Tcl list of components.
+ */
+ if (Tcl_SplitList(hboxPtr->interp, path, &nComp, &nameArr)
+ != TCL_OK) {
+ return NULL;
+ }
+ for (i = 0; i < nComp; i++) {
+ treePtr = FindComponent(rootPtr, nameArr[i]);
+ if (treePtr == NULL) {
+ Blt_Free(nameArr);
+ return NULL;
+ }
+ rootPtr = treePtr;
+ }
+ Blt_Free(nameArr);
+ return rootPtr;
+ }
+ skip = strlen(hboxPtr->separator);
+ path = SkipSeparators(path, hboxPtr->separator, skip);
+ sep = strstr(path, hboxPtr->separator);
+ p = path;
+ treePtr = rootPtr;
+ while ((*p != '\0') && (sep != NULL)) {
+ save = *sep, *sep = '\0';
+ treePtr = FindComponent(treePtr, p);
+ *sep = save;
+ if (treePtr == NULL) {
+ return NULL; /* Bad component name. */
+ }
+ p = SkipSeparators(sep + skip, hboxPtr->separator, skip);
+ sep = strstr(p, hboxPtr->separator);
+ }
+ if (*p != '\0') {
+ treePtr = FindComponent(treePtr, p);
+ if (treePtr == NULL) {
+ return NULL;
+ }
+ }
+ return treePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NearestNode --
+ *
+ * Finds the entry closest to the given screen X-Y coordinates
+ * in the viewport.
+ *
+ * Results:
+ * Returns the pointer to the closest node. If no node is
+ * visible (nodes may be hidden), NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tree *
+NearestNode(hboxPtr, x, y, selectOne)
+ Hierbox *hboxPtr;
+ int x, y;
+ int selectOne;
+{
+ register Tree *lastPtr, **treePtrPtr;
+ register Entry *entryPtr;
+
+ /*
+ * We implicitly can pick only visible entries. So make sure that
+ * the tree exists.
+ */
+ if (hboxPtr->nVisible == 0) {
+ return NULL;
+ }
+ /*
+ * Since the entry positions were previously computed in world
+ * coordinates, convert Y-coordinate from screen to world
+ * coordinates too.
+ */
+ y = WORLDY(hboxPtr, y);
+ treePtrPtr = hboxPtr->visibleArr;
+ lastPtr = *treePtrPtr;
+ for ( /* empty */ ; *treePtrPtr != NULL; treePtrPtr++) {
+ entryPtr = (*treePtrPtr)->entryPtr;
+ /*
+ * If the start of the next entry starts beyond the point,
+ * use the last entry.
+ */
+ if (entryPtr->worldY > y) {
+ return (selectOne) ? lastPtr : NULL;
+ }
+ if (y < (entryPtr->worldY + entryPtr->height)) {
+ return *treePtrPtr; /* Found it. */
+ }
+ lastPtr = *treePtrPtr;
+ }
+ return (selectOne) ? lastPtr : NULL;
+}
+
+static Tree *
+GetNodeByIndex(hboxPtr, string)
+ Hierbox *hboxPtr;
+ char *string;
+{
+ if (isdigit(UCHAR(string[0]))) {
+ int serial;
+
+ if (Tcl_GetInt(NULL, string, &serial) == TCL_OK) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(hboxPtr->nodeTable), (char *)serial);
+ if (hPtr != NULL) {
+ return (Tree *) Blt_GetHashValue(hPtr);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NodeToString --
+ *
+ * Converts a node pointer in its string representation. The
+ * string is the node's identifier number.
+ *
+ * Results:
+ * The string representation of the node is returned. Note that
+ * the string is stored statically, so that callers must save the
+ * string before the next call to this routine overwrites the
+ * static array again.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NodeToString(hboxPtr, nodePtr)
+ Hierbox *hboxPtr;
+ Tree *nodePtr;
+{
+ static char string[200];
+ int serial;
+
+ /* Node table keys are integers. Convert them to strings. */
+ serial = (int)Blt_GetHashKey(&(hboxPtr->nodeTable),
+ nodePtr->entryPtr->hashPtr);
+ sprintf(string, "%d", serial);
+
+ return string;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNode --
+ *
+ * Converts a string into node pointer. The string may be in one
+ * of the following forms:
+ *
+ * @x,y - Closest node to the specified X-Y position.
+ * NNN - inode.
+ * "active" - Currently active node.
+ * "anchor" - anchor of selected region.
+ * "mark" - mark of selected region.
+ * "current" - Currently picked node in bindtable.
+ * "focus" - The node currently with focus.
+ * "root" - Root node.
+ * "end" - Last open node in the entire hierarchy.
+ * "next" - Next open node from the currently active
+ * node. Wraps around back to top.
+ * "last" - Previous open node from the currently active
+ * node. Wraps around back to bottom.
+ * "up" - Next open node from the currently active
+ * node. Does not wrap around.
+ * "down" - Previous open node from the currently active
+ * node. Does not wrap around.
+ * "nextsibling" - Next sibling of the current node.
+ * "prevsibling" - Previous sibling of the current node.
+ * "parent" - Parent of the current node.
+ * "view.top" - Top of viewport.
+ * "view.bottom" - Bottom of viewport.
+ * @path - Absolute path to a node.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via treePtrPtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetNode(hboxPtr, string, treePtrPtr)
+ Hierbox *hboxPtr;
+ char *string;
+ Tree **treePtrPtr;
+{
+ Tree *nodePtr;
+ char c;
+ Tree *fromPtr;
+
+ c = string[0];
+ fromPtr = *treePtrPtr;
+ nodePtr = NULL;
+ if (isdigit(UCHAR(string[0]))) {
+ nodePtr = GetNodeByIndex(hboxPtr, string);
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ nodePtr = EndNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ } else if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ nodePtr = hboxPtr->selAnchorPtr;
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ nodePtr = hboxPtr->focusPtr;
+ } else if ((c == 'r') && (strcmp(string, "root") == 0)) {
+ nodePtr = hboxPtr->rootPtr;
+ } else if ((c == 'p') && (strcmp(string, "parent") == 0)) {
+ nodePtr = fromPtr;
+ if (nodePtr->parentPtr != NULL) {
+ nodePtr = nodePtr->parentPtr;
+ }
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ /* Can't trust picked item, if entries have been added or deleted. */
+ if (!(hboxPtr->flags & HIERBOX_DIRTY)) {
+ nodePtr = (Tree *) Blt_GetCurrentItem(hboxPtr->bindTable);
+ if (nodePtr == NULL) {
+ nodePtr = (Tree *)Blt_GetCurrentItem(hboxPtr->buttonBindTable);
+ }
+ }
+ } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+ nodePtr = LastNode(fromPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ if (nodePtr == NULL) {
+ nodePtr = fromPtr;
+ }
+ if ((nodePtr == hboxPtr->rootPtr) && (hboxPtr->hideRoot)) {
+ nodePtr = NextNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ }
+ } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+ nodePtr = NextNode(fromPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ if (nodePtr == NULL) {
+ nodePtr = fromPtr;
+ }
+ if ((nodePtr == hboxPtr->rootPtr) && (hboxPtr->hideRoot)) {
+ nodePtr = NextNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ }
+ } else if (((c == 'l') && (strcmp(string, "last") == 0)) ||
+ ((c == 'p') && (strcmp(string, "prev") == 0))) {
+ nodePtr = LastNode(fromPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ if (nodePtr == NULL) {
+ nodePtr = EndNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ }
+ if ((nodePtr == hboxPtr->rootPtr) && (hboxPtr->hideRoot)) {
+ nodePtr = NextNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ }
+ } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
+ nodePtr = NextNode(fromPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ if (nodePtr == NULL) {
+ if (hboxPtr->hideRoot) {
+ nodePtr = NextNode(hboxPtr->rootPtr, ENTRY_OPEN | ENTRY_MAPPED);
+ } else {
+ nodePtr = hboxPtr->rootPtr;
+ }
+ }
+ } else if ((c == 'n') && (strcmp(string, "nextsibling") == 0)) {
+ if (fromPtr->linkPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNextLink(fromPtr->linkPtr);
+ if (linkPtr != NULL) {
+ nodePtr = Blt_ChainGetValue(linkPtr);
+ }
+ }
+ } else if ((c == 'p') && (strcmp(string, "prevsibling") == 0)) {
+ if (fromPtr->linkPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainPrevLink(fromPtr->linkPtr);
+ if (linkPtr != NULL) {
+ nodePtr = Blt_ChainGetValue(linkPtr);
+ }
+ }
+ } else if ((c == 'v') && (strcmp(string, "view.top") == 0)) {
+ if (hboxPtr->nVisible > 0) {
+ nodePtr = hboxPtr->visibleArr[0];
+ }
+ } else if ((c == 'v') && (strcmp(string, "view.bottom") == 0)) {
+ if (hboxPtr->nVisible > 0) {
+ nodePtr = hboxPtr->visibleArr[hboxPtr->nVisible - 1];
+ }
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(hboxPtr->interp, hboxPtr->tkwin, string, &x, &y)
+ == TCL_OK) {
+ nodePtr = NearestNode(hboxPtr, x, y, TRUE);
+ } else {
+ nodePtr = FindPath(hboxPtr, hboxPtr->rootPtr, string + 1);
+ }
+ if (nodePtr == NULL) {
+ Tcl_ResetResult(hboxPtr->interp);
+ Tcl_AppendResult(hboxPtr->interp, "can't find node entry \"", string,
+ "\" in \"", Tk_PathName(hboxPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ nodePtr = FindPath(hboxPtr, hboxPtr->rootPtr, string);
+ if (nodePtr == NULL) {
+ Tcl_ResetResult(hboxPtr->interp);
+ Tcl_AppendResult(hboxPtr->interp, "can't find node entry \"",
+ string, "\" in \"", Tk_PathName(hboxPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ *treePtrPtr = nodePtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToNode --
+ *
+ * Like GetNode but also finds nodes by serial number.
+ * If the string starts with a digit, it's converted into a
+ * number and then looked-up in a hash table. This means that
+ * serial identifiers take precedence over node names with
+ * the contain only numbers.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via treePtrPtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+StringToNode(hboxPtr, string, treePtrPtr)
+ Hierbox *hboxPtr;
+ char *string;
+ Tree **treePtrPtr;
+{
+ *treePtrPtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, string, treePtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (*treePtrPtr == NULL) {
+ Tcl_ResetResult(hboxPtr->interp);
+ Tcl_AppendResult(hboxPtr->interp, "can't find node entry \"", string,
+ "\" in \"", Tk_PathName(hboxPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+static void
+PercentSubst(hboxPtr, treePtr, command, resultPtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+ char *command;
+ Tcl_DString *resultPtr;
+{
+ Tcl_DString dString;
+ register char *last, *p;
+
+ /*
+ * Get the full path name of the node, in case we need to
+ * substitute for it.
+ */
+ GetFullPath(treePtr, hboxPtr->separator, &dString);
+ Tcl_DStringInit(resultPtr);
+ for (last = p = command; *p != '\0'; p++) {
+ if (*p == '%') {
+ char *string;
+ char buf[3];
+
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ *p = '%';
+ }
+ switch (*(p + 1)) {
+ case '%': /* Percent sign */
+ string = "%";
+ break;
+ case 'W': /* Widget name */
+ string = Tk_PathName(hboxPtr->tkwin);
+ break;
+ case 'P': /* Full pathname */
+ string = Tcl_DStringValue(&dString);
+ break;
+ case 'p': /* Name of the node */
+ string = treePtr->nameId;
+ break;
+ case 'n': /* Node identifier */
+ string = NodeToString(hboxPtr, treePtr);
+ break;
+ default:
+ if (*(p + 1) == '\0') {
+ p--;
+ }
+ buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+ string = buf;
+ break;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ p++;
+ last = p + 1;
+ }
+ }
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ }
+ Tcl_DStringFree(&dString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CloseNode --
+ *
+ * Closes the node. If a close Tcl command is specified (see the
+ * "-close" option), it's invoked after the node is closed.
+ *
+ * Results:
+ * If an error occurred when executing the Tcl proc, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CloseNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ char *command;
+ int result;
+
+ /*
+ * The tricky part here is that the "close" command can delete
+ * this node. So we'll preserve it until after we mark it closed.
+ */
+ Tcl_Preserve(treePtr);
+
+ /*
+ * Invoke the entry's "close" command, if there is one. Otherwise
+ * try the hierbox's global "close" command.
+ */
+ command = (entryPtr->closeCmd != NULL) ? entryPtr->closeCmd :
+ hboxPtr->closeCmd;
+ result = TCL_OK;
+ if ((entryPtr->flags & ENTRY_OPEN) && (command != NULL)) {
+ Tcl_DString cmdString;
+
+ PercentSubst(hboxPtr, treePtr, command, &cmdString);
+ result = Tcl_GlobalEval(hboxPtr->interp, Tcl_DStringValue(&cmdString));
+ Tcl_DStringFree(&cmdString);
+ }
+ /*
+ * Mark the entry closed, only after the Tcl proc callback.
+ */
+ entryPtr->flags &= ~ENTRY_OPEN;
+
+ Tcl_Release(treePtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OpenNode --
+ *
+ * Opens the node. If an open Tcl command is specified (see the
+ * "-open" option), it's invoked after the node is opened.
+ *
+ * Results:
+ * If an error occurred when executing the Tcl proc, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+OpenNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ char *string;
+ int result;
+
+ /*
+ * The tricky part here is that the "open" command can delete
+ * this node. So we'll preserve it until after we mark it closed.
+ */
+ Tcl_Preserve(treePtr);
+
+ /*
+ * Invoke the entry's "open" command, if there is one. Otherwise
+ * try the hierbox's global "open" command.
+ */
+ result = TCL_OK;
+ string = (entryPtr->openCmd != NULL) ? entryPtr->openCmd : hboxPtr->openCmd;
+ if (!(entryPtr->flags & ENTRY_OPEN) && (string != NULL)) {
+ Tcl_DString dString;
+
+ PercentSubst(hboxPtr, treePtr, string, &dString);
+ result = Tcl_GlobalEval(hboxPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ }
+ /*
+ * Mark the entry open, only after the Tcl proc callback has run.
+ */
+ entryPtr->flags |= ENTRY_OPEN;
+
+ Tcl_Release(treePtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectNode --
+ *
+ * Sets the selection flag for a node. The selection flag is
+ * set/cleared/toggled based upon the flag set in the hierarchy
+ * widget.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ switch (hboxPtr->flags & SELECTION_MASK) {
+ case SELECTION_CLEAR:
+ DeselectEntry(hboxPtr, treePtr);
+ break;
+
+ case SELECTION_SET:
+ SelectEntry(hboxPtr, treePtr);
+ break;
+
+ case SELECTION_TOGGLE:
+ if (IsSelected(hboxPtr, treePtr)) {
+ DeselectEntry(hboxPtr, treePtr);
+ } else {
+ SelectEntry(hboxPtr, treePtr);
+ }
+ break;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectRange --
+ *
+ * Sets the selection flag for a range of nodes. The range is
+ * determined by two pointers which designate the first/last
+ * nodes of the range.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectRange(hboxPtr, fromPtr, toPtr)
+ Hierbox *hboxPtr;
+ Tree *fromPtr, *toPtr;
+{
+ register Tree *treePtr;
+ IterProc *proc;
+
+ /* From the range determine the direction to select entries. */
+ proc = (IsBefore(toPtr, fromPtr)) ? LastNode : NextNode;
+ for (treePtr = fromPtr; treePtr != NULL;
+ treePtr = (*proc)(treePtr, ENTRY_OPEN | ENTRY_MAPPED)) {
+ SelectNode(hboxPtr, treePtr);
+ if (treePtr == toPtr) {
+ break;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsSelectedNode --
+ *
+ * Adds the name of the node the interpreter result if it is
+ * currently selected.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+IsSelectedNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ if (IsSelected(hboxPtr, treePtr)) {
+ Tcl_AppendElement(hboxPtr->interp, NodeToString(hboxPtr, treePtr));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetSelectedLabels --
+ *
+ * This routine is invoked when the selection is exported. Each
+ * selected entry is appended line-by-line to a dynamic string
+ * passed via the clientData field of the hierarchy widget.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetSelectedLabels(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ if (IsSelected(hboxPtr, treePtr)) {
+ Tcl_DString *resultPtr = (Tcl_DString *) hboxPtr->clientData;
+ Entry *entryPtr = treePtr->entryPtr;
+
+ Tcl_DStringAppend(resultPtr, entryPtr->labelText, -1);
+ Tcl_DStringAppend(resultPtr, "\n", -1);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SizeOfNode --
+ *
+ * Returns the number of children at the given node. The sum
+ * is passed via the clientData field of the hierarchy widget.
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SizeOfNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ int *sumPtr = (int *)&(hboxPtr->clientData);
+
+ *sumPtr += Blt_ChainGetLength(treePtr->chainPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareNodesByName --
+ *
+ * Comparison routine (used by qsort) to sort a chain of subnodes.
+ * A simple string comparison is performed on each node name.
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CompareNodesByName(link1PtrPtr, link2PtrPtr)
+ Blt_ChainLink **link1PtrPtr, **link2PtrPtr;
+{
+ Tree *t1Ptr, *t2Ptr;
+
+ t1Ptr = Blt_ChainGetValue(*link1PtrPtr);
+ t2Ptr = Blt_ChainGetValue(*link2PtrPtr);
+ return strcmp(t1Ptr->nameId, t2Ptr->nameId);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareNodesByTclCmd --
+ *
+ * Comparison routine (used by qsort) to sort a list of subnodes.
+ * A specified Tcl proc is invoked to compare the nodes.
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CompareNodesByTclCmd(link1PtrPtr, link2PtrPtr)
+ Blt_ChainLink **link1PtrPtr, **link2PtrPtr;
+{
+ int result;
+ Tree *t1Ptr, *t2Ptr;
+ Hierbox *hboxPtr = hierBox;
+ Tcl_Interp *interp = hboxPtr->interp;
+
+ t1Ptr = Blt_ChainGetValue(*link1PtrPtr);
+ t2Ptr = Blt_ChainGetValue(*link2PtrPtr);
+ result = 0; /* Hopefully this will be Ok even if the
+ * Tcl command fails to return the correct
+ * result. */
+ if ((Tcl_VarEval(interp, hboxPtr->sortCmd, " ",
+ Tk_PathName(hboxPtr->tkwin), " ", NodeToString(hboxPtr, t1Ptr), " ",
+ NodeToString(hboxPtr, t2Ptr)) != TCL_OK) ||
+ (Tcl_GetInt(interp, Tcl_GetStringResult(interp), &result) != TCL_OK)) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_ResetResult(interp);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortNode --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SortNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ if (treePtr->chainPtr != NULL) {
+ if (hboxPtr->sortCmd != NULL) {
+ hierBox = hboxPtr;
+ Blt_ChainSort(treePtr->chainPtr, CompareNodesByTclCmd);
+ } else {
+ Blt_ChainSort(treePtr->chainPtr, CompareNodesByName);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnmapNode --
+ *
+ * Unmaps the given node. The node will not be drawn. Ignore
+ * unmapping of the root node (it makes no sense).
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+UnmapNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ if (treePtr != hboxPtr->rootPtr) {
+ treePtr->entryPtr->flags &= ~ENTRY_MAPPED;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapAncestors --
+ *
+ * If a node in mapped, then all its ancestors must be mapped also.
+ * This routine traverses upwards and maps each unmapped ancestor.
+ * It's assumed that for any mapped ancestor, all it's ancestors
+ * will already be mapped too.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapAncestors(hboxPtr, treePtr)
+ Hierbox *hboxPtr; /* Not used. */
+ Tree *treePtr;
+{
+ /*
+ * Make sure that all the ancestors of this node are mapped too.
+ */
+ treePtr = treePtr->parentPtr;
+ while (treePtr != NULL) {
+ if (treePtr->entryPtr->flags & ENTRY_MAPPED) {
+ break; /* Assume ancestors are also mapped. */
+ }
+ treePtr->entryPtr->flags |= ENTRY_MAPPED;
+ treePtr = treePtr->parentPtr;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapNode --
+ *
+ * Maps the given node. Only mapped nodes are drawn.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+MapNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ treePtr->entryPtr->flags |= ENTRY_MAPPED;
+ MapAncestors(hboxPtr, treePtr);
+ return TCL_OK;
+}
+
+static int
+FixUnmappedSelections(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ if (!(treePtr->entryPtr->flags & ENTRY_MAPPED)) {
+ DeselectEntry(hboxPtr, treePtr);
+ PruneSelection(hboxPtr, treePtr);
+ if (IsAncestor(treePtr, hboxPtr->focusPtr)) {
+ hboxPtr->focusPtr = treePtr->parentPtr;
+ if (hboxPtr->focusPtr == NULL) {
+ hboxPtr->focusPtr = hboxPtr->rootPtr;
+ }
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+static int
+DeleteNode(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ /*
+ * Indicate that the screen layout of the hierarchy may have changed
+ * because the node was deleted. We don't want to access the
+ * hboxPtr->visibleArr array if one of the nodes is bogus.
+ */
+ hboxPtr->flags |= HIERBOX_DIRTY;
+ if (treePtr == hboxPtr->activePtr) {
+ hboxPtr->activePtr = treePtr->parentPtr;
+ }
+ if (treePtr == hboxPtr->activeButtonPtr) {
+ hboxPtr->activeButtonPtr = NULL;
+ }
+ if (treePtr == hboxPtr->focusPtr) {
+ hboxPtr->focusPtr = treePtr->parentPtr;
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+ }
+ if (treePtr == hboxPtr->selAnchorPtr) {
+ hboxPtr->selAnchorPtr = NULL;
+ }
+ DeselectEntry(hboxPtr, treePtr);
+ PruneSelection(hboxPtr, treePtr);
+ if (treePtr->linkPtr != NULL) { /* Remove from parent's list */
+ Blt_ChainDeleteLink(treePtr->parentPtr->chainPtr, treePtr->linkPtr);
+ treePtr->linkPtr = NULL;
+ }
+ /*
+ * Node may still be in use, so we can't free it right now. We'll
+ * mark the parent as NULL and remove it from the parent's list of
+ * children.
+ */
+ treePtr->parentPtr = NULL;
+ Blt_DeleteBindings(hboxPtr->bindTable, treePtr);
+ Blt_DeleteBindings(hboxPtr->buttonBindTable, treePtr);
+ Tcl_EventuallyFree(treePtr, DestroyNode);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyTree --
+ *
+ * Recursively deletes the given node and all its subnodes.
+ *
+ * Results:
+ * If successful, returns TCL_OK. Otherwise TCL_ERROR is
+ * returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+DestroyTree(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ return ApplyToTree(hboxPtr, treePtr, DeleteNode, APPLY_RECURSE);
+}
+
+/*ARGSUSED*/
+static void
+GetTags(table, object, list)
+ Blt_BindTable table; /* Not used. */
+ ClientData object;
+ Blt_List list;
+{
+ Tree *treePtr;
+
+ Blt_ListAppend(list, (char *)object, 0);
+ treePtr = (Tree *) object;
+ if (treePtr->entryPtr->tags != NULL) {
+ int nNames;
+ char **names;
+ register char **p;
+
+ if (Tcl_SplitList((Tcl_Interp *)NULL, treePtr->entryPtr->tags, &nNames,
+ &names) == TCL_OK) {
+ for (p = names; *p != NULL; p++) {
+ Blt_ListAppend(list, Tk_GetUid(*p), 0);
+ }
+ Blt_Free(names);
+ }
+ }
+}
+
+/*ARGSUSED*/
+static ClientData
+PickButton(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates of the test point. */
+{
+ Hierbox *hboxPtr = clientData;
+ Entry *entryPtr;
+ Tree *treePtr;
+
+ if (hboxPtr->flags & HIERBOX_DIRTY) {
+ /* Can't trust selected entry, if entries have been added or deleted. */
+ if (hboxPtr->flags & HIERBOX_LAYOUT) {
+ ComputeLayout(hboxPtr);
+ }
+ ComputeVisibleEntries(hboxPtr);
+ }
+ if (hboxPtr->nVisible == 0) {
+ return (ClientData) 0;
+ }
+ treePtr = NearestNode(hboxPtr, x, y, FALSE);
+ if (treePtr == NULL) {
+ return (ClientData) 0;
+ }
+ entryPtr = treePtr->entryPtr;
+ if (entryPtr->flags & ENTRY_BUTTON) {
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ int left, right, top, bottom;
+#define BUTTON_PAD 2
+ left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
+ right = left + buttonPtr->width + 2 * BUTTON_PAD;
+ top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
+ bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
+ x = WORLDX(hboxPtr, x);
+ y = WORLDY(hboxPtr, y);
+ if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
+ return treePtr;
+ }
+ }
+ return (ClientData) 0;
+}
+
+/*ARGSUSED*/
+static ClientData
+PickEntry(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates of the test point. */
+{
+ Hierbox *hboxPtr = clientData;
+ Entry *entryPtr;
+ Tree *treePtr;
+
+ if (hboxPtr->flags & HIERBOX_DIRTY) {
+ /* Can't trust selected entry, if entries have been added or deleted. */
+ if (hboxPtr->flags & HIERBOX_LAYOUT) {
+ ComputeLayout(hboxPtr);
+ }
+ ComputeVisibleEntries(hboxPtr);
+ }
+ if (hboxPtr->nVisible == 0) {
+ return (ClientData) 0;
+ }
+ treePtr = NearestNode(hboxPtr, x, y, FALSE);
+ if (treePtr == NULL) {
+ return (ClientData) 0;
+ }
+ entryPtr = treePtr->entryPtr;
+ if (entryPtr->flags & ENTRY_BUTTON) {
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ int left, right, top, bottom;
+#define BUTTON_PAD 2
+ left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
+ right = left + buttonPtr->width + 2 * BUTTON_PAD;
+ top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
+ bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
+ x = WORLDX(hboxPtr, x);
+ y = WORLDY(hboxPtr, y);
+ if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
+ return NULL;
+ }
+ }
+ return treePtr;
+}
+
+static int
+ConfigureEntry(hboxPtr, entryPtr, argc, argv, flags)
+ Hierbox *hboxPtr;
+ Entry *entryPtr;
+ int argc;
+ char **argv;
+ int flags;
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ int entryWidth, entryHeight;
+ int width, height;
+ Tk_Font font;
+ XColor *colorPtr;
+
+ hierBox = hboxPtr;
+ if (Tk_ConfigureWidget(hboxPtr->interp, hboxPtr->tkwin, entryConfigSpecs,
+ argc, argv, (char *)entryPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ entryPtr->iconWidth = entryPtr->iconHeight = 0;
+ if (entryPtr->icons != NULL) {
+ register int i;
+
+ for (i = 0; i < 2; i++) {
+ if (entryPtr->icons[i] == NULL) {
+ break;
+ }
+ if (entryPtr->iconWidth < ImageWidth(entryPtr->icons[i])) {
+ entryPtr->iconWidth = ImageWidth(entryPtr->icons[i]);
+ }
+ if (entryPtr->iconHeight < ImageHeight(entryPtr->icons[i])) {
+ entryPtr->iconHeight = ImageHeight(entryPtr->icons[i]);
+ }
+ }
+ }
+ newGC = NULL;
+ if ((entryPtr->icons == NULL) || (entryPtr->icons[0] == NULL)) {
+ gcMask = GCClipMask | GCBackground;
+ gcValues.clip_mask = hboxPtr->iconMask;
+ gcValues.background = hboxPtr->iconColor->pixel;
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ entryPtr->iconWidth = DEF_ICON_WIDTH;
+ entryPtr->iconHeight = DEF_ICON_HEIGHT;
+ }
+ entryPtr->iconWidth += 2 * ICON_PADX;
+ entryPtr->iconHeight += 2 * ICON_PADY;
+ if (entryPtr->iconGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->iconGC);
+ }
+ entryPtr->iconGC = newGC;
+
+ entryHeight = MAX(entryPtr->iconHeight, hboxPtr->button.height);
+ entryWidth = 0;
+
+ gcMask = GCForeground | GCFont;
+ colorPtr = GETCOLOR(hboxPtr, entryPtr->labelColor);
+ gcValues.foreground = colorPtr->pixel;
+ font = GETFONT(hboxPtr, entryPtr->labelFont);
+ gcValues.font = Tk_FontId(font);
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (entryPtr->labelGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->labelGC);
+ }
+ entryPtr->labelGC = newGC;
+
+ if (*entryPtr->labelText == '\0') {
+ Tk_FontMetrics fontMetrics;
+
+ Tk_GetFontMetrics(font, &fontMetrics);
+ width = height = fontMetrics.linespace;
+ } else {
+ TextStyle ts;
+
+ Blt_InitTextStyle(&ts);
+ ts.shadow.offset = entryPtr->labelShadow.offset;
+ ts.font = font;
+ Blt_GetTextExtents(&ts, entryPtr->labelText, &width, &height);
+ }
+ width += 2 * (FOCUS_WIDTH + LABEL_PADX + hboxPtr->selBorderWidth);
+ height += 2 * (FOCUS_WIDTH + LABEL_PADY + hboxPtr->selBorderWidth);
+ width = ODD(width);
+ height = ODD(height);
+ entryWidth += width;
+ if (entryHeight < height) {
+ entryHeight = height;
+ }
+ entryPtr->labelWidth = width;
+ entryPtr->labelHeight = height;
+ width = height = 0;
+ if (entryPtr->images != NULL) {
+ register CachedImage *imagePtr;
+
+ for (imagePtr = entryPtr->images; *imagePtr != NULL; imagePtr++) {
+ width += ImageWidth(*imagePtr);
+ if (height < ImageHeight(*imagePtr)) {
+ height = ImageHeight(*imagePtr);
+ }
+ }
+ } else if (entryPtr->dataText != NULL) {
+ TextStyle ts;
+
+ gcMask = GCForeground | GCFont;
+ colorPtr = GETCOLOR(hboxPtr, entryPtr->dataColor);
+ gcValues.foreground = colorPtr->pixel;
+ font = GETFONT(hboxPtr, entryPtr->dataFont);
+ gcValues.font = Tk_FontId(font);
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (entryPtr->dataGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, entryPtr->dataGC);
+ }
+ entryPtr->dataGC = newGC;
+
+ Blt_InitTextStyle(&ts);
+ ts.font = font;
+ ts.shadow.offset = entryPtr->dataShadow.offset;
+ Blt_GetTextExtents(&ts, entryPtr->dataText, &width, &height);
+ width += 2 * LABEL_PADX;
+ height += 2 * LABEL_PADY;
+ }
+ entryWidth += width;
+ if (entryHeight < height) {
+ entryHeight = height;
+ }
+ entryPtr->width = entryWidth + 4;
+ entryPtr->height = entryHeight + hboxPtr->leader;
+
+ /*
+ * Force the height of the entry to an even number. This is to
+ * make the dots or the vertical line segments coincide with the
+ * start of the horizontal lines.
+ */
+ if (entryPtr->height & 0x01) {
+ entryPtr->height++;
+ }
+ hboxPtr->flags |= HIERBOX_LAYOUT;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ * Hierbox Procedures
+ */
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ if ((hboxPtr->tkwin != NULL) && !(hboxPtr->flags & HIERBOX_REDRAW)) {
+ hboxPtr->flags |= HIERBOX_REDRAW;
+ Tcl_DoWhenIdle(DisplayHierbox, hboxPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LabelBlinkProc --
+ *
+ * This procedure is called as a timer handler to blink the
+ * insertion cursor off and on.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor gets turned on or off, redisplay gets invoked,
+ * and this procedure reschedules itself.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+LabelBlinkProc(clientData)
+ ClientData clientData; /* Pointer to record describing entry. */
+{
+ Hierbox *hboxPtr = clientData;
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int interval;
+
+ if (!(hboxPtr->flags & HIERBOX_FOCUS) || (editPtr->offTime == 0)) {
+ return;
+ }
+ if (editPtr->active) {
+ editPtr->cursorOn ^= 1;
+ interval = (editPtr->cursorOn) ? editPtr->onTime : editPtr->offTime;
+ editPtr->timerToken = Tcl_CreateTimerHandler(interval, LabelBlinkProc,
+ hboxPtr);
+ EventuallyRedraw(hboxPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyInvokeSelectCmd --
+ *
+ * Queues a request to execute the -selectcommand code associated
+ * with the widget at the next idle point. Invoked whenever the
+ * selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyInvokeSelectCmd(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ if (!(hboxPtr->flags & SELECTION_PENDING)) {
+ hboxPtr->flags |= SELECTION_PENDING;
+ Tcl_DoWhenIdle(SelectCmdProc, hboxPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateHierbox --
+ *
+ * ----------------------------------------------------------------------
+ */
+static Hierbox *
+CreateHierbox(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+{
+ Hierbox *hboxPtr;
+
+ hboxPtr = Blt_Calloc(1, sizeof(Hierbox));
+ assert(hboxPtr);
+
+ Tk_SetClass(tkwin, "Hierbox");
+ hboxPtr->tkwin = tkwin;
+ hboxPtr->display = Tk_Display(tkwin);
+ hboxPtr->interp = interp;
+
+ hboxPtr->leader = 0;
+ hboxPtr->dashes = 1;
+ hboxPtr->highlightWidth = 2;
+ hboxPtr->selBorderWidth = 1;
+ hboxPtr->borderWidth = 2;
+ hboxPtr->relief = TK_RELIEF_SUNKEN;
+ hboxPtr->selRelief = TK_RELIEF_FLAT;
+ hboxPtr->scrollMode = BLT_SCROLL_MODE_HIERBOX;
+ hboxPtr->button.closeRelief = hboxPtr->button.openRelief = TK_RELIEF_SOLID;
+ hboxPtr->reqWidth = 200;
+ hboxPtr->reqHeight = 400;
+ hboxPtr->lineWidth = 1;
+ hboxPtr->button.borderWidth = 1;
+ hboxPtr->labelEdit.selAnchor = -1;
+ hboxPtr->labelEdit.selFirst = hboxPtr->labelEdit.selLast = -1;
+ hboxPtr->labelEdit.onTime = 600, hboxPtr->labelEdit.offTime = 300;
+ Blt_ChainInit(&(hboxPtr->selectChain));
+ Blt_InitHashTable(&(hboxPtr->selectTable), BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&(hboxPtr->nodeTable), BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&(hboxPtr->imageTable), BLT_STRING_KEYS);
+ hboxPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, hboxPtr,
+ PickEntry, GetTags);
+ hboxPtr->buttonBindTable = Blt_CreateBindingTable(interp, tkwin, hboxPtr,
+ PickButton, GetTags);
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, hboxPtr);
+#endif
+ return hboxPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyHierbox --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a Hierbox at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyHierbox(dataPtr)
+ DestroyData dataPtr; /* Pointer to the widget record. */
+{
+ Hierbox *hboxPtr = (Hierbox *)dataPtr;
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+
+ Tk_FreeOptions(configSpecs, (char *)hboxPtr, hboxPtr->display, 0);
+ if (hboxPtr->tkwin != NULL) {
+ Tk_DeleteSelHandler(hboxPtr->tkwin, XA_PRIMARY, XA_STRING);
+ }
+ if (hboxPtr->lineGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, hboxPtr->lineGC);
+ }
+ if (hboxPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(hboxPtr->display, hboxPtr->focusGC);
+ }
+ if (hboxPtr->tile != NULL) {
+ Blt_FreeTile(hboxPtr->tile);
+ }
+ if (hboxPtr->visibleArr != NULL) {
+ Blt_Free(hboxPtr->visibleArr);
+ }
+ if (hboxPtr->levelInfo != NULL) {
+ Blt_Free(hboxPtr->levelInfo);
+ }
+ if (hboxPtr->iconBitmap != None) {
+ Tk_FreeBitmap(hboxPtr->display, hboxPtr->iconBitmap);
+ }
+ if (hboxPtr->iconMask != None) {
+ Tk_FreeBitmap(hboxPtr->display, hboxPtr->iconMask);
+ }
+ if (hboxPtr->iconColor != NULL) {
+ Tk_FreeColor(hboxPtr->iconColor);
+ }
+ if (buttonPtr->images != NULL) {
+ register CachedImage *imagePtr;
+
+ for (imagePtr = buttonPtr->images; *imagePtr != NULL; imagePtr++) {
+ FreeCachedImage(hboxPtr, *imagePtr);
+ }
+ Blt_Free(buttonPtr->images);
+ }
+ if (buttonPtr->activeGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->activeGC);
+ }
+ if (buttonPtr->normalGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->normalGC);
+ }
+ if (buttonPtr->lineGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, buttonPtr->lineGC);
+ }
+ DestroyTree(hboxPtr, hboxPtr->rootPtr);
+ Blt_DeleteHashTable(&(hboxPtr->nodeTable));
+ Blt_ChainReset(&(hboxPtr->selectChain));
+ Blt_DeleteHashTable(&(hboxPtr->selectTable));
+ Blt_DestroyBindingTable(hboxPtr->bindTable);
+ Blt_DestroyBindingTable(hboxPtr->buttonBindTable);
+ Blt_Free(hboxPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * HierboxEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on hierarchy widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+HierboxEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedraw(hboxPtr);
+ }
+ } else if (eventPtr->type == ConfigureNotify) {
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+
+ if (eventPtr->type == FocusIn) {
+ hboxPtr->flags |= HIERBOX_FOCUS;
+ } else {
+ hboxPtr->flags &= ~HIERBOX_FOCUS;
+ }
+ Tcl_DeleteTimerHandler(editPtr->timerToken);
+ if ((editPtr->active) && (hboxPtr->flags & HIERBOX_FOCUS)) {
+ editPtr->cursorOn = TRUE;
+ if (editPtr->offTime != 0) {
+ editPtr->timerToken =
+ Tcl_CreateTimerHandler(editPtr->onTime,
+ LabelBlinkProc, clientData);
+ }
+ } else {
+ editPtr->cursorOn = FALSE;
+ editPtr->timerToken = (Tcl_TimerToken) NULL;
+ }
+ EventuallyRedraw(hboxPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (hboxPtr->tkwin != NULL) {
+ hboxPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(hboxPtr->interp, hboxPtr->cmdToken);
+ }
+ if (hboxPtr->flags & HIERBOX_REDRAW) {
+ Tcl_CancelIdleCall(DisplayHierbox, hboxPtr);
+ }
+ if (hboxPtr->flags & SELECTION_PENDING) {
+ Tcl_CancelIdleCall(SelectCmdProc, hboxPtr);
+ }
+ Tcl_EventuallyFree(hboxPtr, DestroyHierbox);
+ }
+}
+
+/* Selection Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is
+ * requested by someone. It returns part or all of the selection
+ * in a buffer provided by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored at
+ * buffer. Buffer is filled (or partially filled) with a
+ * NUL-terminated string containing part or all of the
+ * selection, as given by offset and maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectionProc(clientData, offset, buffer, maxBytes)
+ ClientData clientData; /* Information about the widget. */
+ int offset; /* Offset within selection of first
+ * character to be returned. */
+ char *buffer; /* Location in which to place
+ * selection. */
+ int maxBytes; /* Maximum number of bytes to place
+ * at buffer, not including terminating
+ * NULL character. */
+{
+ Hierbox *hboxPtr = clientData;
+ int size;
+ Tcl_DString dString;
+
+ if (!hboxPtr->exportSelection) {
+ return -1;
+ }
+ /*
+ * Retrieve the names of the selected entries.
+ */
+ Tcl_DStringInit(&dString);
+ if (hboxPtr->sortSelection) {
+ hboxPtr->clientData = &dString;
+ ApplyToTree(hboxPtr, hboxPtr->rootPtr, GetSelectedLabels,
+ APPLY_RECURSE | APPLY_BEFORE | APPLY_OPEN_ONLY);
+ } else {
+ Blt_ChainLink *linkPtr;
+ Tree *treePtr;
+
+ for (linkPtr = Blt_ChainFirstLink(&(hboxPtr->selectChain));
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ Tcl_DStringAppend(&dString, treePtr->entryPtr->labelText, -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ size = Tcl_DStringLength(&dString) - offset;
+ strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
+ Tcl_DStringFree(&dString);
+ buffer[maxBytes] = '\0';
+ return (size > maxBytes) ? maxBytes : size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LostSelection --
+ *
+ * This procedure is called back by Tk when the selection is grabbed
+ * away.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is
+ * marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LostSelection(clientData)
+ ClientData clientData; /* Information about the widget. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ if ((hboxPtr->selAnchorPtr != NULL) && (hboxPtr->exportSelection)) {
+ ClearSelection(hboxPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HierboxInstCmdDeleteProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+HierboxInstCmdDeleteProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this
+ * procedure destroys the widget.
+ */
+ if (hboxPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = hboxPtr->tkwin;
+ hboxPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Stub for image change notifications. Since we immediately draw
+ * the image into a pixmap, we don't care about image changes.
+ *
+ * It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ if (hboxPtr->tkwin != NULL) {
+ EventuallyRedraw(hboxPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureHierbox --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for hboxPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureHierbox(interp, hboxPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ Hierbox *hboxPtr; /* Information about widget; may or may not
+ * already have values for some fields. */
+ int argc;
+ char **argv;
+ int flags;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ Tk_Uid nameId;
+ Pixmap bitmap;
+ hierBox = hboxPtr;
+ if (Tk_ConfigureWidget(interp, hboxPtr->tkwin, configSpecs, argc, argv,
+ (char *)hboxPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ConfigModified(configSpecs, "-font", "-linespacing", "-width",
+ "-height", "-hideroot", (char *)NULL)) {
+ /*
+ * These options change the layout of the box. Mark for
+ * update.
+ */
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ }
+ if ((hboxPtr->reqHeight != Tk_ReqHeight(hboxPtr->tkwin)) ||
+ (hboxPtr->reqWidth != Tk_ReqWidth(hboxPtr->tkwin))) {
+ Tk_GeometryRequest(hboxPtr->tkwin, hboxPtr->reqWidth,
+ hboxPtr->reqHeight);
+ }
+ gcMask = (GCForeground | GCLineWidth);
+ gcValues.foreground = hboxPtr->lineColor->pixel;
+ gcValues.line_width = hboxPtr->lineWidth;
+ if (hboxPtr->dashes > 0) {
+ gcMask |= (GCLineStyle | GCDashList);
+ gcValues.line_style = LineOnOffDash;
+ gcValues.dashes = hboxPtr->dashes;
+ }
+ newGC = Tk_GetGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (hboxPtr->lineGC != NULL) {
+ Tk_FreeGC(hboxPtr->display, hboxPtr->lineGC);
+ }
+ hboxPtr->lineGC = newGC;
+
+ /*
+ * GC for active label. Dashed outline.
+ */
+ gcMask = GCForeground | GCLineStyle;
+ gcValues.foreground = hboxPtr->focusColor->pixel;
+ gcValues.line_style = (LineIsDashed(hboxPtr->focusDashes))
+ ? LineOnOffDash : LineSolid;
+ newGC = Blt_GetPrivateGC(hboxPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(hboxPtr->focusDashes)) {
+ hboxPtr->focusDashes.offset = 2;
+ Blt_SetDashes(hboxPtr->display, newGC, &(hboxPtr->focusDashes));
+ }
+ if (hboxPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(hboxPtr->display, hboxPtr->focusGC);
+ }
+ hboxPtr->focusGC = newGC;
+
+ if (hboxPtr->iconBitmap == None) {
+ nameId = Tk_GetUid("HierboxFolder");
+ bitmap = Tk_GetBitmap(interp, hboxPtr->tkwin, nameId);
+ if (bitmap == None) {
+ if (Tk_DefineBitmap(interp, nameId, (char *)folderBits,
+ DEF_ICON_WIDTH, DEF_ICON_HEIGHT) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bitmap = Tk_GetBitmap(interp, hboxPtr->tkwin, nameId);
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->iconBitmap = bitmap;
+ Tcl_ResetResult(interp);
+ }
+ if (hboxPtr->iconMask == None) {
+ nameId = Tk_GetUid("HierboxFolderMask");
+ bitmap = Tk_GetBitmap(interp, hboxPtr->tkwin, nameId);
+ if (bitmap == None) {
+ if (Tk_DefineBitmap(interp, nameId, (char *)folderMaskBits,
+ DEF_ICON_WIDTH, DEF_ICON_HEIGHT) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bitmap = Tk_GetBitmap(interp, hboxPtr->tkwin, nameId);
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->iconMask = bitmap;
+ Tcl_ResetResult(interp);
+ }
+ if (hboxPtr->iconColor == NULL) {
+ hboxPtr->iconColor = Tk_GetColor(interp, hboxPtr->tkwin,
+ Tk_GetUid("yellow"));
+ if (hboxPtr->iconColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (hboxPtr->tile != NULL) {
+ Blt_SetTileChangedProc(hboxPtr->tile, TileChangedProc, hboxPtr);
+ }
+ ConfigureButtons(hboxPtr);
+
+ hboxPtr->inset = hboxPtr->highlightWidth + hboxPtr->borderWidth + INSET_PAD;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetCoordinates --
+ *
+ * Determines the maximum height of all visible entries.
+ *
+ * Results:
+ * Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ * The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetCoordinates(hboxPtr, treePtr, infoPtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+ LayoutInfo *infoPtr;
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ int width;
+
+ /*
+ * If the entry is hidden, then do nothing.
+ * Otherwise, include it in the layout.
+ */
+ entryPtr->worldY = infoPtr->y;
+ if (!(entryPtr->flags & ENTRY_MAPPED)) {
+ return;
+ }
+ treePtr->level = infoPtr->level;
+ if (infoPtr->depth < infoPtr->level) {
+ infoPtr->depth = infoPtr->level;
+ }
+ if ((entryPtr->flags & BUTTON_SHOW) || ((entryPtr->flags & BUTTON_AUTO) &&
+ (Blt_ChainGetLength(treePtr->chainPtr) > 0))) {
+ entryPtr->flags |= ENTRY_BUTTON;
+ } else {
+ entryPtr->flags &= ~ENTRY_BUTTON;
+ }
+ if (entryPtr->height < infoPtr->minHeight) {
+ infoPtr->minHeight = entryPtr->height;
+ }
+ /*
+ * Note: The maximum entry width below does not take into account
+ * the space for the icon (level offset). This has to be
+ * deferred because it's dependent upon the maximum icon
+ * size.
+ */
+ width = infoPtr->x + entryPtr->width;
+ if (width > infoPtr->maxWidth) {
+ infoPtr->maxWidth = width;
+ }
+ if (infoPtr->maxIconWidth < entryPtr->iconWidth) {
+ infoPtr->maxIconWidth = entryPtr->iconWidth;
+ }
+ entryPtr->lineHeight = -(infoPtr->y);
+ infoPtr->y += entryPtr->height;
+ if (entryPtr->flags & ENTRY_OPEN) {
+ Blt_ChainLink *linkPtr;
+ int labelOffset;
+ Tree *bottomPtr;
+
+ infoPtr->level++;
+ labelOffset = infoPtr->labelOffset;
+ infoPtr->labelOffset = 0;
+ bottomPtr = treePtr;
+ for (linkPtr = Blt_ChainFirstLink(treePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (treePtr->entryPtr->flags & ENTRY_MAPPED) {
+ ResetCoordinates(hboxPtr, treePtr, infoPtr);
+ bottomPtr = treePtr;
+ }
+ }
+ infoPtr->level--;
+ entryPtr->lineHeight += bottomPtr->entryPtr->worldY;
+ entryPtr->levelX = infoPtr->labelOffset;
+ infoPtr->labelOffset = labelOffset;
+ }
+ if (infoPtr->labelOffset < entryPtr->labelWidth) {
+ infoPtr->labelOffset = entryPtr->labelWidth;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeWidths --
+ *
+ * Determines the maximum height of all visible entries.
+ *
+ * Results:
+ * Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ * The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeWidths(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ Entry *entryPtr = treePtr->entryPtr;
+
+ if (!(entryPtr->flags & ENTRY_MAPPED)) {
+ return;
+ }
+ if (entryPtr->iconWidth > LEVELWIDTH(treePtr->level + 1)) {
+ LEVELWIDTH(treePtr->level + 1) = entryPtr->iconWidth;
+ }
+ if (entryPtr->flags & ENTRY_OPEN) {
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(treePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (treePtr->entryPtr->flags & ENTRY_MAPPED) {
+ ComputeWidths(hboxPtr, treePtr);
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeLayout --
+ *
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change (such as
+ * font, linespacing).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeLayout(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ LayoutInfo info;
+
+ info.level = info.depth = 0;
+ info.x = info.y = 0;
+ info.maxWidth = hboxPtr->button.width;
+ info.maxIconWidth = hboxPtr->button.width;
+ info.minHeight = INT_MAX;
+ info.labelOffset = 0;
+
+ if (hboxPtr->hideRoot) {
+ info.y = -(hboxPtr->rootPtr->entryPtr->height);
+ }
+ ResetCoordinates(hboxPtr, hboxPtr->rootPtr, &info);
+
+ hboxPtr->xScrollUnits = info.maxIconWidth;
+ hboxPtr->minHeight = hboxPtr->yScrollUnits = info.minHeight;
+ if (hboxPtr->reqScrollX > 0) {
+ hboxPtr->xScrollUnits = hboxPtr->reqScrollX;
+ }
+ if (hboxPtr->reqScrollY > 0) {
+ hboxPtr->yScrollUnits = hboxPtr->reqScrollY;
+ }
+ hboxPtr->depth = info.depth + 1;
+ hboxPtr->worldWidth = info.maxWidth + (hboxPtr->depth * info.maxIconWidth);
+ if (hboxPtr->worldWidth < 1) {
+ hboxPtr->worldWidth = 1;
+ }
+ hboxPtr->worldHeight = info.y;
+ if (hboxPtr->worldHeight < 1) {
+ hboxPtr->worldHeight = 1;
+ }
+ if (hboxPtr->yScrollUnits < 1) {
+ hboxPtr->yScrollUnits = 1;
+ }
+ if (hboxPtr->xScrollUnits < 1) {
+ hboxPtr->xScrollUnits = 1;
+ }
+ if (hboxPtr->levelInfo != NULL) {
+ Blt_Free(hboxPtr->levelInfo);
+ }
+ hboxPtr->levelInfo = Blt_Calloc(hboxPtr->depth + 2, sizeof(LevelInfo));
+ assert(hboxPtr->levelInfo);
+ ComputeWidths(hboxPtr, hboxPtr->rootPtr);
+ {
+ int sum, width;
+ register int i;
+
+ sum = 0;
+ for (i = 0; i <= hboxPtr->depth; i++) {
+ width = hboxPtr->levelInfo[i].width;
+ width |= 0x01;
+ hboxPtr->levelInfo[i].width = width;
+ sum += width;
+ hboxPtr->levelInfo[i + 1].x = sum;
+ }
+ }
+ hboxPtr->flags &= ~HIERBOX_LAYOUT;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeVisibleEntries --
+ *
+ * The entries visible in the viewport (the widget's window) are
+ * inserted into the array of visible nodes.
+ *
+ * Results:
+ * Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ * The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ComputeVisibleEntries(hboxPtr)
+ Hierbox *hboxPtr;
+{
+ Entry *entryPtr;
+ int height;
+ Blt_ChainLink *linkPtr;
+ register Tree *treePtr;
+ int x, maxX;
+ int nSlots;
+
+ hboxPtr->xOffset = Blt_AdjustViewport(hboxPtr->xOffset, hboxPtr->worldWidth,
+ VPORTWIDTH(hboxPtr), hboxPtr->xScrollUnits, hboxPtr->scrollMode);
+ hboxPtr->yOffset = Blt_AdjustViewport(hboxPtr->yOffset,
+ hboxPtr->worldHeight, VPORTHEIGHT(hboxPtr), hboxPtr->yScrollUnits,
+ hboxPtr->scrollMode);
+
+ height = VPORTHEIGHT(hboxPtr);
+
+ /* Allocate worst case number of slots for entry array. */
+ nSlots = (height / hboxPtr->minHeight) + 3;
+ if ((nSlots != hboxPtr->nVisible) && (hboxPtr->visibleArr != NULL)) {
+ Blt_Free(hboxPtr->visibleArr);
+ }
+ hboxPtr->visibleArr = Blt_Calloc(nSlots, sizeof(Tree *));
+ assert(hboxPtr->visibleArr);
+ hboxPtr->nVisible = 0;
+
+ /* Find the node where the view port starts. */
+ treePtr = hboxPtr->rootPtr;
+ entryPtr = treePtr->entryPtr;
+ while ((entryPtr->worldY + entryPtr->height) <= hboxPtr->yOffset) {
+ for (linkPtr = Blt_ChainLastLink(treePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainPrevLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ if (IsHidden(treePtr)) {
+ continue; /* Ignore hidden entries. */
+ }
+ entryPtr = treePtr->entryPtr;
+ if (entryPtr->worldY <= hboxPtr->yOffset) {
+ break;
+ }
+ }
+
+ /*
+ * If we can't find the starting node, then the view must be
+ * scrolled down, but some nodes were deleted. Reset the view
+ * back to the top and try again.
+ */
+ if (linkPtr == NULL) {
+ if (hboxPtr->yOffset == 0) {
+ return TCL_OK; /* All entries are hidden. */
+ }
+ hboxPtr->yOffset = 0;
+ continue;
+ }
+ }
+
+ height += hboxPtr->yOffset;
+ maxX = 0;
+ while (treePtr != NULL) {
+ if (!IsHidden(treePtr)) {
+ entryPtr = treePtr->entryPtr;
+ /*
+ * Compute and save the entry's X-coordinate now that we know
+ * what the maximum level offset for the entire Hierbox is.
+ */
+ entryPtr->worldX = LEVELX(treePtr->level);
+ x = entryPtr->worldX + LEVELWIDTH(treePtr->level) +
+ LEVELWIDTH(treePtr->level + 1) + entryPtr->width;
+ if (x > maxX) {
+ maxX = x;
+ }
+ if (entryPtr->worldY >= height) {
+ break;
+ }
+ hboxPtr->visibleArr[hboxPtr->nVisible] = treePtr;
+ hboxPtr->nVisible++;
+ }
+ treePtr = NextNode(treePtr, ENTRY_OPEN | ENTRY_MAPPED);
+ }
+ hboxPtr->worldWidth = maxX;
+
+ /*
+ * -------------------------------------------------------------------
+ *
+ * Note: It's assumed that the view port always starts at or
+ * over an entry. Check that a change in the hierarchy
+ * (e.g. closing a node) hasn't left the viewport beyond
+ * the last entry. If so, adjust the viewport to start
+ * on the last entry.
+ *
+ * -------------------------------------------------------------------
+ */
+ if (hboxPtr->xOffset > (hboxPtr->worldWidth - hboxPtr->xScrollUnits)) {
+ hboxPtr->xOffset = hboxPtr->worldWidth - hboxPtr->xScrollUnits;
+ }
+ if (hboxPtr->yOffset > (hboxPtr->worldHeight - hboxPtr->yScrollUnits)) {
+ hboxPtr->yOffset = hboxPtr->worldHeight - hboxPtr->yScrollUnits;
+ }
+ hboxPtr->xOffset = Blt_AdjustViewport(hboxPtr->xOffset, hboxPtr->worldWidth,
+ VPORTWIDTH(hboxPtr), hboxPtr->xScrollUnits, hboxPtr->scrollMode);
+ hboxPtr->yOffset = Blt_AdjustViewport(hboxPtr->yOffset,
+ hboxPtr->worldHeight, VPORTHEIGHT(hboxPtr), hboxPtr->yScrollUnits,
+ hboxPtr->scrollMode);
+ hboxPtr->flags &= ~HIERBOX_DIRTY;
+ return TCL_OK;
+}
+
+static int
+GetCursorLocation(hboxPtr, treePtr)
+ Hierbox *hboxPtr;
+ Tree *treePtr;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int x, y;
+ int maxLines;
+ Tk_Font font;
+ TextStyle ts;
+ TextLayout *textPtr;
+ Tk_FontMetrics fontMetrics;
+ int nBytes;
+ int sum;
+ Entry *entryPtr;
+ TextFragment *fragPtr;
+ register int i;
+
+ entryPtr = treePtr->entryPtr;
+ font = GETFONT(hboxPtr, entryPtr->labelFont);
+ memset(&ts, 0, sizeof(TextStyle));
+ ts.font = font;
+ ts.justify = TK_JUSTIFY_LEFT;
+ ts.shadow.offset = entryPtr->labelShadow.offset;
+ textPtr = Blt_GetTextLayout(entryPtr->labelText, &ts);
+
+ Tk_GetFontMetrics(font, &fontMetrics);
+ maxLines = (textPtr->height / fontMetrics.linespace) - 1;
+
+ nBytes = sum = 0;
+ x = y = 0;
+
+ fragPtr = textPtr->fragArr;
+ for (i = 0; i <= maxLines; i++) {
+ /* Total the number of bytes on each line. Include newlines. */
+ nBytes = fragPtr->count + 1;
+ if ((sum + nBytes) > editPtr->insertPos) {
+ x += Tk_TextWidth(font, fragPtr->text, editPtr->insertPos - sum);
+ break;
+ }
+ y += fontMetrics.linespace;
+ sum += nBytes;
+ fragPtr++;
+ }
+ editPtr->x = x;
+ editPtr->y = y;
+ editPtr->height = fontMetrics.linespace;
+ editPtr->width = 3;
+ Blt_Free(textPtr);
+ return TCL_OK;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawVerticals --
+ *
+ * Draws vertical lines for the ancestor nodes. While the entry
+ * of the ancestor may not be visible, its vertical line segment
+ * does extent into the viewport. So walk back up the hierarchy
+ * drawing lines until we get to the root.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Vertical lines are drawn for the ancestor nodes.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawVerticals(hboxPtr, treePtr, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ Entry *entryPtr; /* Entry to be drawn. */
+ int x1, y1, x2, y2;
+ int height;
+ int x, y;
+
+ while (treePtr->parentPtr != NULL) {
+ treePtr = treePtr->parentPtr;
+ entryPtr = treePtr->entryPtr;
+
+ /*
+ * World X-coordinates are computed only for entries that are in
+ * the current view port. So for each of the off-screen ancestor
+ * nodes we must compute it here too.
+ */
+ entryPtr->worldX = LEVELX(treePtr->level);
+ x = SCREENX(hboxPtr, entryPtr->worldX);
+ y = SCREENY(hboxPtr, entryPtr->worldY);
+ height = MAX(entryPtr->iconHeight, hboxPtr->button.height);
+ y += (height - hboxPtr->button.height) / 2;
+ x1 = x2 = x + LEVELWIDTH(treePtr->level) +
+ LEVELWIDTH(treePtr->level + 1) / 2;
+ y1 = y + hboxPtr->button.height / 2;
+ y2 = y1 + entryPtr->lineHeight;
+ if ((treePtr == hboxPtr->rootPtr) && (hboxPtr->hideRoot)) {
+ y1 += entryPtr->height;
+ }
+ /*
+ * Clip the line's Y-coordinates at the window border.
+ */
+ if (y1 < 0) {
+ y1 = 0;
+ }
+ if (y2 > Tk_Height(hboxPtr->tkwin)) {
+ y2 = Tk_Height(hboxPtr->tkwin);
+ }
+ if ((y1 < Tk_Height(hboxPtr->tkwin)) && (y2 > 0)) {
+ XDrawLine(hboxPtr->display, drawable, hboxPtr->lineGC, x1, y1,
+ x2, y2);
+ }
+ }
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawButton --
+ *
+ * Draws a button for the given entry. The button is drawn
+ * centered in the region immediately to the left of the origin
+ * of the entry (computed in the layout routines). The height
+ * and width of the button were previously calculated from the
+ * average row height.
+ *
+ * button height = entry height - (2 * some arbitrary padding).
+ * button width = button height.
+ *
+ * The button may have a border. The symbol (either a plus or
+ * minus) is slight smaller than the width or height minus the
+ * border.
+ *
+ * x,y origin of entry
+ *
+ * +---+
+ * | + | icon label
+ * +---+
+ * closed
+ *
+ * |----|----| horizontal offset
+ *
+ * +---+
+ * | - | icon label
+ * +---+
+ * open
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawButton(hboxPtr, treePtr, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Node of entry. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ Entry *entryPtr;
+ int relief;
+ Tk_3DBorder border;
+ GC gc, lineGC;
+ int x, y;
+ CachedImage image;
+ int width, height;
+
+ entryPtr = treePtr->entryPtr;
+
+ width = LEVELWIDTH(treePtr->level);
+ height = MAX(entryPtr->iconHeight, buttonPtr->height);
+ entryPtr->buttonX = (width - buttonPtr->width) / 2;
+ entryPtr->buttonY = (height - buttonPtr->height) / 2;
+
+ x = SCREENX(hboxPtr, entryPtr->worldX) + entryPtr->buttonX;
+ y = SCREENY(hboxPtr, entryPtr->worldY) + entryPtr->buttonY;
+
+ if (treePtr == hboxPtr->activeButtonPtr) {
+ border = buttonPtr->activeBorder;
+ lineGC = gc = buttonPtr->activeGC;
+ } else {
+ border = buttonPtr->border;
+ gc = buttonPtr->normalGC;
+ lineGC = buttonPtr->lineGC;
+ }
+ relief = (entryPtr->flags & ENTRY_OPEN)
+ ? buttonPtr->openRelief : buttonPtr->closeRelief;
+ /*
+ * FIXME: Reliefs "flat" and "solid" the same, since there's no
+ * "solid" in pre-8.0 releases. Should change this when we go to a
+ * pure 8.x release.
+ */
+ if (relief == TK_RELIEF_SOLID) {
+ relief = TK_RELIEF_FLAT;
+ }
+ Tk_Fill3DRectangle(hboxPtr->tkwin, drawable, border, x, y,
+ buttonPtr->width, buttonPtr->height, buttonPtr->borderWidth, relief);
+ if (relief == TK_RELIEF_FLAT) {
+ XDrawRectangle(hboxPtr->display, drawable, lineGC, x, y,
+ buttonPtr->width - 1, buttonPtr->height - 1);
+ }
+ x += buttonPtr->borderWidth;
+ y += buttonPtr->borderWidth;
+ width = buttonPtr->width - (2 * buttonPtr->borderWidth);
+ height = buttonPtr->height - (2 * buttonPtr->borderWidth);
+
+ image = NULL;
+ if (buttonPtr->images != NULL) {
+ /* Open or close button image? */
+ image = buttonPtr->images[0];
+ if ((entryPtr->flags & ENTRY_OPEN) && (buttonPtr->images[1] != NULL)) {
+ image = buttonPtr->images[1];
+ }
+ }
+ /* Image or rectangle? */
+ if (image != NULL) {
+ Tk_RedrawImage(ImageData(image), 0, 0, width, height, drawable, x, y);
+ } else {
+ XSegment segArr[2];
+ int count;
+
+ gc = (treePtr == hboxPtr->activeButtonPtr)
+ ? buttonPtr->activeGC : buttonPtr->normalGC;
+ count = 1;
+ segArr[0].y1 = segArr[0].y2 = y + height / 2;
+ segArr[0].x1 = x + BUTTON_IPAD;
+#ifdef WIN32
+ segArr[0].x2 = x + width - BUTTON_IPAD;
+#else
+ segArr[0].x2 = x + width - BUTTON_IPAD - 1;
+#endif
+ if (!(entryPtr->flags & ENTRY_OPEN)) {
+ segArr[1].x1 = segArr[1].x2 = x + width / 2;
+ segArr[1].y1 = y + BUTTON_IPAD;
+#ifdef WIN32
+ segArr[1].y2 = y + height - BUTTON_IPAD;
+#else
+ segArr[1].y2 = y + height - BUTTON_IPAD - 1;
+#endif
+ count++;
+ }
+ XDrawSegments(hboxPtr->display, drawable, gc, segArr, count);
+ }
+}
+
+
+static void
+DisplayIcon(hboxPtr, treePtr, x, y, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Node of entry. */
+ int x, y;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ int entryHeight;
+ CachedImage image;
+ int isActive;
+
+ entryHeight = MAX(entryPtr->iconHeight, hboxPtr->button.height);
+ isActive = (treePtr == hboxPtr->activePtr);
+ image = NULL;
+ if ((isActive) && (entryPtr->activeIcons != NULL)) {
+ image = entryPtr->activeIcons[0];
+ if ((treePtr == hboxPtr->focusPtr) &&
+ (entryPtr->activeIcons[1] != NULL)) {
+ image = entryPtr->activeIcons[1];
+ }
+ } else if (entryPtr->icons != NULL) { /* Selected or normal icon? */
+ image = entryPtr->icons[0];
+ if ((treePtr == hboxPtr->focusPtr) && (entryPtr->icons[1] != NULL)) {
+ image = entryPtr->icons[1];
+ }
+ }
+ if (image != NULL) { /* Image or default icon bitmap? */
+ int width, height;
+ int top, bottom;
+ int inset;
+ int maxY;
+
+ height = ImageHeight(image);
+ width = ImageWidth(image);
+ x += (LEVELWIDTH(treePtr->level + 1) - width) / 2;
+ y += (entryHeight - height) / 2;
+ inset = hboxPtr->inset - INSET_PAD;
+ maxY = Tk_Height(hboxPtr->tkwin) - inset;
+ top = 0;
+ bottom = y + height;
+ if (y < inset) {
+ height += y - inset;
+ top = -y + inset;
+ y = inset;
+ } else if (bottom >= maxY) {
+ height = maxY - y;
+ }
+ Tk_RedrawImage(ImageData(image), 0, top, width, height, drawable, x, y);
+ } else {
+ x += (LEVELWIDTH(treePtr->level + 1) - DEF_ICON_WIDTH) / 2;
+ y += (entryHeight - DEF_ICON_HEIGHT) / 2;
+ XSetClipOrigin(hboxPtr->display, entryPtr->iconGC, x, y);
+ XCopyPlane(hboxPtr->display, hboxPtr->iconBitmap, drawable,
+ entryPtr->iconGC, 0, 0, DEF_ICON_WIDTH, DEF_ICON_HEIGHT, x, y, 1);
+ }
+}
+
+static void
+DrawData(hboxPtr, treePtr, x, y, entryHeight, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Node of entry. */
+ int x, y;
+ int entryHeight;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ /*
+ * Auxillary data: text string or images.
+ */
+ if (entryPtr->images != NULL) {
+ register CachedImage *imagePtr;
+ int imageY;
+
+ for (imagePtr = entryPtr->images; *imagePtr != NULL; imagePtr++) {
+ imageY = y;
+ if (ImageHeight(*imagePtr) < entryHeight) {
+ imageY += (entryHeight - ImageHeight(*imagePtr)) / 2;
+ }
+ Tk_RedrawImage(ImageData(*imagePtr), 0, 0, ImageWidth(*imagePtr),
+ ImageHeight(*imagePtr), drawable, x, imageY);
+ x += ImageWidth(*imagePtr);
+ }
+ } else if (entryPtr->dataText != NULL) {
+ TextStyle ts;
+ Tk_Font font;
+ XColor *colorPtr;
+ int width, height;
+
+ font = GETFONT(hboxPtr, entryPtr->dataFont);
+ colorPtr = GETCOLOR(hboxPtr, entryPtr->dataColor);
+ y += hboxPtr->selBorderWidth + LABEL_PADY;
+
+ Blt_SetDrawTextStyle(&ts, font, entryPtr->dataGC, colorPtr,
+ hboxPtr->selFgColor, entryPtr->dataShadow.color, 0.0,
+ TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, entryPtr->dataShadow.offset);
+ Blt_GetTextExtents(&ts, entryPtr->dataText, &width, &height);
+ if (height < entryHeight) {
+ y += (entryHeight - height) / 2;
+ }
+ Blt_DrawText(hboxPtr->tkwin, drawable, entryPtr->dataText, &ts, x, y);
+ }
+}
+
+
+static int
+DrawLabel(hboxPtr, treePtr, x, y, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Node of entry. */
+ int x, y;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ Entry *entryPtr = treePtr->entryPtr;
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ TextStyle ts;
+ int width, height; /* Width and height of label. */
+ Tk_Font font;
+ int isSelected, isFocused;
+ int entryHeight;
+
+ entryHeight = MAX(entryPtr->iconHeight, hboxPtr->button.height);
+ font = GETFONT(hboxPtr, entryPtr->labelFont);
+ isFocused = ((treePtr == hboxPtr->focusPtr) &&
+ (hboxPtr->flags & HIERBOX_FOCUS));
+ isSelected = IsSelected(hboxPtr, treePtr);
+
+ /* Includes padding, selection 3-D border, and focus outline. */
+ width = entryPtr->labelWidth;
+ height = entryPtr->labelHeight;
+
+ /* Center the label, if necessary, vertically along the entry row. */
+ if (height < entryHeight) {
+ y += (entryHeight - height) / 2;
+ }
+
+#ifdef notdef
+ /* Normal background color */
+ Tk_Fill3DRectangle(hboxPtr->tkwin, drawable, hboxPtr->border, x, y, width,
+ height, 0, TK_RELIEF_FLAT);
+#endif
+ if (isFocused) { /* Focus outline */
+ XDrawRectangle(hboxPtr->display, drawable, hboxPtr->focusGC,
+ x, y, width - 1, height - 1);
+ }
+ x += FOCUS_WIDTH;
+ y += FOCUS_WIDTH;
+ if (isSelected) {
+ Tk_Fill3DRectangle(hboxPtr->tkwin, drawable, hboxPtr->selBorder,
+ x, y, width - 2 * FOCUS_WIDTH, height - 2 * FOCUS_WIDTH,
+ hboxPtr->selBorderWidth, hboxPtr->selRelief);
+ }
+ x += LABEL_PADX + hboxPtr->selBorderWidth;
+ y += LABEL_PADY + hboxPtr->selBorderWidth;
+
+ if (*entryPtr->labelText != '\0') {
+ XColor *normalColor;
+
+ normalColor = GETCOLOR(hboxPtr, entryPtr->labelColor);
+ Blt_SetDrawTextStyle(&ts, font, entryPtr->labelGC, normalColor,
+ hboxPtr->selFgColor, entryPtr->labelShadow.color, 0.0, TK_ANCHOR_NW,
+ TK_JUSTIFY_LEFT, 0, entryPtr->labelShadow.offset);
+ ts.state = (isSelected) ? STATE_ACTIVE : 0;
+ Blt_DrawText(hboxPtr->tkwin, drawable, entryPtr->labelText, &ts,
+ x, y);
+ }
+ if ((isFocused) && (hboxPtr->focusEdit) && (editPtr->cursorOn)) {
+ int x1, y1, x2, y2;
+
+ GetCursorLocation(hboxPtr, treePtr);
+ x1 = x + editPtr->x;
+ x2 = x1 + 3;
+ y1 = y + editPtr->y - 1;
+ y2 = y1 + editPtr->height - 1;
+ XDrawLine(hboxPtr->display, drawable, entryPtr->labelGC,
+ x1, y1, x1, y2);
+ XDrawLine(hboxPtr->display, drawable, entryPtr->labelGC,
+ x1 - 2, y1, x2, y1);
+ XDrawLine(hboxPtr->display, drawable, entryPtr->labelGC,
+ x1 - 2, y2, x2, y2);
+ }
+ return entryHeight;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawEntry --
+ *
+ * Draws a button for the given entry. Note that buttons should only
+ * be drawn if the entry has sub-entries to be opened or closed. It's
+ * the responsibility of the calling routine to ensure this.
+ *
+ * The button is drawn centered in the region immediately to the left
+ * of the origin of the entry (computed in the layout routines). The
+ * height and width of the button were previously calculated from the
+ * average row height.
+ *
+ * button height = entry height - (2 * some arbitrary padding).
+ * button width = button height.
+ *
+ * The button has a border. The symbol (either a plus or minus) is
+ * slight smaller than the width or height minus the border.
+ *
+ * x,y origin of entry
+ *
+ * +---+
+ * | + | icon label
+ * +---+
+ * closed
+ *
+ * |----|----| horizontal offset
+ *
+ * +---+
+ * | - | icon label
+ * +---+
+ * open
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawEntry(hboxPtr, treePtr, drawable)
+ Hierbox *hboxPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ Tree *treePtr; /* Node of entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ int x, y;
+ int width, height;
+ int entryHeight;
+ int buttonY;
+ int x1, y1, x2, y2;
+ Entry *entryPtr;
+
+ entryPtr = treePtr->entryPtr;
+ x = SCREENX(hboxPtr, entryPtr->worldX);
+ y = SCREENY(hboxPtr, entryPtr->worldY);
+
+ width = LEVELWIDTH(treePtr->level);
+ height = MAX(entryPtr->iconHeight, buttonPtr->height);
+
+ entryPtr->buttonX = (width - buttonPtr->width) / 2;
+ entryPtr->buttonY = (height - buttonPtr->height) / 2;
+
+ buttonY = y + entryPtr->buttonY;
+
+ x1 = x + (width / 2);
+ y1 = y2 = buttonY + (buttonPtr->height / 2);
+ x2 = x1 + (LEVELWIDTH(treePtr->level) + LEVELWIDTH(treePtr->level + 1)) / 2;
+
+ if ((treePtr->parentPtr != NULL) && (hboxPtr->lineWidth > 0)) {
+ /*
+ * For every node except root, draw a horizontal line from
+ * the vertical bar to the middle of the icon.
+ */
+ XDrawLine(hboxPtr->display, drawable, hboxPtr->lineGC, x1, y1, x2, y2);
+ }
+ if ((entryPtr->flags & ENTRY_OPEN) && (hboxPtr->lineWidth > 0)) {
+ /*
+ * Entry is open, draw vertical line.
+ */
+ y2 = y1 + entryPtr->lineHeight;
+ if (y2 > Tk_Height(hboxPtr->tkwin)) {
+ y2 = Tk_Height(hboxPtr->tkwin); /* Clip line at window border. */
+ }
+ XDrawLine(hboxPtr->display, drawable, hboxPtr->lineGC, x2, y1, x2, y2);
+ }
+ if ((entryPtr->flags & ENTRY_BUTTON) && (treePtr->parentPtr != NULL)) {
+ /*
+ * Except for root, draw a button for every entry that needs
+ * one. The displayed button can be either a Tk image or a
+ * rectangle with plus or minus sign.
+ */
+ DrawButton(hboxPtr, treePtr, drawable);
+ }
+ x += LEVELWIDTH(treePtr->level);
+ DisplayIcon(hboxPtr, treePtr, x, y, drawable);
+
+ x += LEVELWIDTH(treePtr->level + 1) + 4;
+
+ /* Entry label. */
+ entryHeight = DrawLabel(hboxPtr, treePtr, x, y, drawable);
+ if (treePtr->parentPtr != NULL) {
+ x += treePtr->parentPtr->entryPtr->levelX + LABEL_PADX;
+ } else {
+ x += width + entryPtr->labelWidth + LABEL_PADX;
+ }
+ /* Auxilary data */
+ DrawData(hboxPtr, treePtr, x, y, entryHeight, drawable);
+
+}
+
+static void
+DrawOuterBorders(hboxPtr, drawable)
+ Hierbox *hboxPtr;
+ Drawable drawable;
+{
+ /* Draw 3D border just inside of the focus highlight ring. */
+ if ((hboxPtr->borderWidth > 0) && (hboxPtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(hboxPtr->tkwin, drawable, hboxPtr->border,
+ hboxPtr->highlightWidth, hboxPtr->highlightWidth,
+ Tk_Width(hboxPtr->tkwin) - 2 * hboxPtr->highlightWidth,
+ Tk_Height(hboxPtr->tkwin) - 2 * hboxPtr->highlightWidth,
+ hboxPtr->borderWidth, hboxPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if (hboxPtr->highlightWidth > 0) {
+ XColor *color;
+ GC gc;
+
+ color = (hboxPtr->flags & HIERBOX_FOCUS)
+ ? hboxPtr->highlightColor : hboxPtr->highlightBgColor;
+ gc = Tk_GCForColor(color, drawable);
+ Tk_DrawFocusHighlight(hboxPtr->tkwin, gc, hboxPtr->highlightWidth,
+ drawable);
+ }
+ hboxPtr->flags &= ~HIERBOX_BORDERS;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayHierbox --
+ *
+ * This procedure is invoked to display the widget.
+ *
+ * Recompute the layout of the text if necessary. This is
+ * necessary if the world coordinate system has changed.
+ * Specifically, the following may have occurred:
+ *
+ * 1. a text attribute has changed (font, linespacing, etc.).
+ * 2. an entry's option changed, possibly resizing the entry.
+ *
+ * This is deferred to the display routine since potentially
+ * many of these may occur.
+ *
+ * Set the vertical and horizontal scrollbars. This is done
+ * here since the window width and height are needed for the
+ * scrollbar calculations.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayHierbox(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Hierbox *hboxPtr = clientData;
+ Pixmap drawable;
+
+ hboxPtr->flags &= ~HIERBOX_REDRAW;
+ if (hboxPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (hboxPtr->flags & HIERBOX_LAYOUT) {
+ /*
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change
+ * (such as font, linespacing).
+ */
+ ComputeLayout(hboxPtr);
+ }
+ if (hboxPtr->flags & HIERBOX_SCROLL) {
+ int width, height;
+
+ /* Scrolling means that the view port has changed and that the
+ * visible entries need to be recomputed. */
+ ComputeVisibleEntries(hboxPtr);
+ Blt_PickCurrentItem(hboxPtr->bindTable);
+ Blt_PickCurrentItem(hboxPtr->buttonBindTable);
+
+ width = VPORTWIDTH(hboxPtr);
+ height = VPORTHEIGHT(hboxPtr);
+ if (hboxPtr->flags & HIERBOX_XSCROLL) {
+ if (hboxPtr->xScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(hboxPtr->interp, hboxPtr->xScrollCmdPrefix,
+ (double)hboxPtr->xOffset / hboxPtr->worldWidth,
+ (double)(hboxPtr->xOffset + width) / hboxPtr->worldWidth);
+ }
+ }
+ if (hboxPtr->flags & HIERBOX_YSCROLL) {
+ if (hboxPtr->yScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(hboxPtr->interp, hboxPtr->yScrollCmdPrefix,
+ (double)hboxPtr->yOffset / hboxPtr->worldHeight,
+ (double)(hboxPtr->yOffset + height) / hboxPtr->worldHeight);
+ }
+ }
+ hboxPtr->flags &= ~HIERBOX_SCROLL;
+ }
+ if (!Tk_IsMapped(hboxPtr->tkwin)) {
+ return;
+ }
+ drawable = Tk_GetPixmap(hboxPtr->display, Tk_WindowId(hboxPtr->tkwin),
+ Tk_Width(hboxPtr->tkwin), Tk_Height(hboxPtr->tkwin),
+ Tk_Depth(hboxPtr->tkwin));
+
+ /*
+ * Clear the background either by tiling a pixmap or filling with
+ * a solid color. Tiling takes precedence.
+ */
+ if (hboxPtr->tile != NULL) {
+ if (hboxPtr->scrollTile) {
+ Blt_SetTSOrigin(hboxPtr->tkwin, hboxPtr->tile, -hboxPtr->xOffset,
+ -hboxPtr->yOffset);
+ } else {
+ Blt_SetTileOrigin(hboxPtr->tkwin, hboxPtr->tile, 0, 0);
+ }
+ Blt_TileRectangle(hboxPtr->tkwin, drawable, hboxPtr->tile, 0, 0,
+ Tk_Width(hboxPtr->tkwin), Tk_Height(hboxPtr->tkwin));
+ } else {
+ Tk_Fill3DRectangle(hboxPtr->tkwin, drawable, hboxPtr->border, 0, 0,
+ Tk_Width(hboxPtr->tkwin), Tk_Height(hboxPtr->tkwin), 0,
+ TK_RELIEF_FLAT);
+ }
+
+ if (hboxPtr->nVisible > 0) {
+ register Tree **treePtrPtr;
+
+ if (hboxPtr->activePtr != NULL) {
+ int y, height;
+
+ y = SCREENY(hboxPtr, hboxPtr->activePtr->entryPtr->worldY);
+ height = MAX(hboxPtr->activePtr->entryPtr->iconHeight,
+ hboxPtr->activePtr->entryPtr->labelHeight);
+ Tk_Fill3DRectangle(hboxPtr->tkwin, drawable, hboxPtr->activeBorder,
+ 0, y, Tk_Width(hboxPtr->tkwin), height, 0, TK_RELIEF_FLAT);
+ }
+ if (hboxPtr->lineWidth > 0) {
+ DrawVerticals(hboxPtr, hboxPtr->visibleArr[0], drawable);
+ }
+ for (treePtrPtr = hboxPtr->visibleArr; *treePtrPtr != NULL;
+ treePtrPtr++) {
+ DrawEntry(hboxPtr, *treePtrPtr, drawable);
+ }
+ }
+ DrawOuterBorders(hboxPtr, drawable);
+ /* Now copy the new view to the window. */
+ XCopyArea(hboxPtr->display, drawable, Tk_WindowId(hboxPtr->tkwin),
+ hboxPtr->lineGC, 0, 0, Tk_Width(hboxPtr->tkwin),
+ Tk_Height(hboxPtr->tkwin), 0, 0);
+ Tk_FreePixmap(hboxPtr->display, drawable);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectCmdProc --
+ *
+ * Invoked at the next idle point whenever the current
+ * selection changes. Executes some application-specific code
+ * in the -selectcommand option. This provides a way for
+ * applications to handle selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SelectCmdProc(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Hierbox *hboxPtr = clientData;
+
+ /*
+ * Preserve the widget structure in case the "select" callback
+ * destroys it.
+ */
+ Tcl_Preserve(hboxPtr);
+ if (hboxPtr->selectCmd != NULL) {
+ hboxPtr->flags &= ~SELECTION_PENDING;
+ if (Tcl_GlobalEval(hboxPtr->interp, hboxPtr->selectCmd) != TCL_OK) {
+ Tcl_BackgroundError(hboxPtr->interp);
+ }
+ }
+ Tcl_Release(hboxPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * HierboxCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+HierboxCmd(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. */
+{
+ Hierbox *hboxPtr;
+ Tk_Window tkwin;
+ Tree *treePtr;
+ Tcl_CmdInfo cmdInfo;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hboxPtr = CreateHierbox(interp, tkwin);
+
+ if (Blt_ConfigureWidgetComponent(interp, tkwin, "button", "Button",
+ buttonConfigSpecs, 0, (char **)NULL, (char *)hboxPtr, 0) != TCL_OK) {
+ goto error;
+ }
+ if (ConfigureHierbox(interp, hboxPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ goto error;
+ }
+ treePtr = CreateNode(hboxPtr, (Tree *) NULL, APPEND, hboxPtr->separator);
+ if (treePtr == NULL) {
+ goto error;
+ }
+ hboxPtr->rootPtr = hboxPtr->focusPtr = treePtr;
+ hboxPtr->selAnchorPtr = NULL;
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+
+ Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING, SelectionProc, hboxPtr,
+ XA_STRING);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask |
+ FocusChangeMask, HierboxEventProc, hboxPtr);
+
+ hboxPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], HierboxInstCmd,
+ hboxPtr, HierboxInstCmdDeleteProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(hboxPtr->tkwin, hboxPtr->cmdToken);
+#endif
+
+ /*
+ * Invoke a procedure to initialize various bindings on hierbox
+ * entries. If the procedure doesn't already exist, source it
+ * from "$blt_library/bltHierbox.tcl". We deferred sourcing the file
+ * until now so that the variable $blt_library could be set within a
+ * script.
+ */
+ if (!Tcl_GetCommandInfo(interp, "blt::Hierbox::Init", &cmdInfo)) {
+ static char initCmd[] =
+ {
+ "source [file join $blt_library hierbox.tcl]"
+ };
+ if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) {
+ char info[200];
+
+ sprintf(info, "\n (while loading bindings for %s)", argv[0]);
+ Tcl_AddErrorInfo(interp, info);
+ goto error;
+ }
+ }
+ if (Tcl_VarEval(interp, "blt::Hierbox::Init ", argv[1], (char *)NULL)
+ != TCL_OK) {
+ goto error;
+ }
+ treePtr->entryPtr->flags = (ENTRY_MAPPED);
+ if (OpenNode(hboxPtr, treePtr) != TCL_OK) {
+ goto error;
+ }
+ Tcl_SetResult(interp, Tk_PathName(hboxPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+ error:
+ Tk_DestroyWindow(tkwin);
+ return TCL_ERROR;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Hierbox operations
+ *
+ * --------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+FocusOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ Tree *treePtr;
+
+ treePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[2], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((treePtr != NULL) && (treePtr != hboxPtr->focusPtr)) {
+ if (IsHidden(treePtr)) {
+ /* Doesn't make sense to set focus to a node you can't see. */
+ ExposeAncestors(treePtr);
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ hboxPtr->focusPtr = treePtr;
+ hboxPtr->labelEdit.insertPos = strlen(treePtr->entryPtr->labelText);
+ }
+ EventuallyRedraw(hboxPtr);
+ }
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+ if (hboxPtr->focusPtr != NULL) {
+ Tcl_SetResult(interp, NodeToString(hboxPtr, hboxPtr->focusPtr),
+ TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BboxOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BboxOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+ register int i;
+ Entry *entryPtr;
+ char string[200];
+ int width, height, yBot;
+ int left, top, right, bottom;
+ int screen;
+
+ if (hboxPtr->flags & HIERBOX_LAYOUT) {
+ /*
+ * The layout is dirty. Recompute it now, before we use the
+ * world dimensions. But remember, the "bbox" operation isn't
+ * valid for hidden entries (since they're not visible, they
+ * don't have world coordinates).
+ */
+ ComputeLayout(hboxPtr);
+ }
+ left = hboxPtr->worldWidth;
+ top = hboxPtr->worldHeight;
+ right = bottom = 0;
+
+ screen = FALSE;
+ if ((argc > 2) && (argv[2][0] == '-') &&
+ (strcmp(argv[2], "-screen") == 0)) {
+ screen = TRUE;
+ argc--, argv++;
+ }
+ for (i = 2; i < argc; i++) {
+ if ((argv[i][0] == 'a') && (strcmp(argv[i], "all") == 0)) {
+ left = top = 0;
+ right = hboxPtr->worldWidth;
+ bottom = hboxPtr->worldHeight;
+ break;
+ }
+ treePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[i], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((treePtr == NULL) || (IsHidden(treePtr))) {
+ continue;
+ }
+ entryPtr = treePtr->entryPtr;
+ yBot = entryPtr->worldY + entryPtr->height;
+ height = VPORTHEIGHT(hboxPtr);
+ if ((yBot <= hboxPtr->yOffset) &&
+ (entryPtr->worldY >= (hboxPtr->yOffset + height))) {
+ continue;
+ }
+ if (bottom < yBot) {
+ bottom = yBot;
+ }
+ if (top > entryPtr->worldY) {
+ top = entryPtr->worldY;
+ }
+ if (right <
+ (entryPtr->worldX + entryPtr->width + LEVELWIDTH(treePtr->level))) {
+ right = (entryPtr->worldX + entryPtr->width +
+ LEVELWIDTH(treePtr->level));
+ }
+ if (left > entryPtr->worldX) {
+ left = entryPtr->worldX;
+ }
+ }
+
+ if (screen) {
+ width = VPORTWIDTH(hboxPtr);
+ height = VPORTHEIGHT(hboxPtr);
+
+ /*
+ * Do a min-max text for the intersection of the viewport and
+ * the computed bounding box. If there is no intersection, return
+ * the empty string.
+ */
+ if ((right < hboxPtr->xOffset) || (bottom < hboxPtr->yOffset) ||
+ (left >= (hboxPtr->xOffset + width)) ||
+ (top >= (hboxPtr->yOffset + height))) {
+ return TCL_OK;
+ }
+ /* Otherwise clip the coordinates at the view port boundaries. */
+ if (left < hboxPtr->xOffset) {
+ left = hboxPtr->xOffset;
+ } else if (right > (hboxPtr->xOffset + width)) {
+ right = hboxPtr->xOffset + width;
+ }
+ if (top < hboxPtr->yOffset) {
+ top = hboxPtr->yOffset;
+ } else if (bottom > (hboxPtr->yOffset + height)) {
+ bottom = hboxPtr->yOffset + height;
+ }
+ left = SCREENX(hboxPtr, left), top = SCREENY(hboxPtr, top);
+ right = SCREENX(hboxPtr, right), bottom = SCREENY(hboxPtr, bottom);
+ }
+ if ((left < right) && (top < bottom)) {
+ sprintf(string, "%d %d %d %d", left, top, right - left, bottom - top);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonActivateOp --
+ *
+ * Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonActivateOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr, *oldPtr;
+
+ treePtr = hboxPtr->focusPtr;
+ if (argv[3][0] == '\0') {
+ treePtr = NULL;
+ } else if (GetNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ oldPtr = hboxPtr->activeButtonPtr;
+ hboxPtr->activeButtonPtr = treePtr;
+ if (treePtr != oldPtr) {
+ /* FIXME: Entries changed, how do you draw old? */
+ Drawable drawable;
+
+ drawable = Tk_WindowId(hboxPtr->tkwin);
+ if (oldPtr != NULL) {
+ DrawButton(hboxPtr, oldPtr, drawable);
+ }
+ if (treePtr != NULL) {
+ DrawButton(hboxPtr, treePtr, drawable);
+ }
+ DrawOuterBorders(hboxPtr, drawable);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonBindOp --
+ *
+ * .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonBindOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ ClientData object;
+ /*
+ * Individual entries are selected by inode only. All other
+ * strings are interpreted as a binding tag. For example, if one
+ * binds to "focus", it is assumed that this refers to a bind tag,
+ * not the entry with focus.
+ */
+ object = GetNodeByIndex(hboxPtr, argv[3]);
+ if (object == 0) {
+ object = Tk_GetUid(argv[3]);
+ }
+ return Blt_ConfigureBindings(interp, hboxPtr->buttonBindTable, object,
+ argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButCgetOpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonCgetOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, hboxPtr->tkwin, buttonConfigSpecs,
+ (char *)hboxPtr, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h entryconfigure node node node node option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for hboxPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ButtonConfigureOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+
+ if (argc == 0) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, buttonConfigSpecs,
+ (char *)hboxPtr, (char *)NULL, 0);
+ } else if (argc == 1) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, buttonConfigSpecs,
+ (char *)hboxPtr, argv[0], 0);
+ }
+ if (Tk_ConfigureWidget(hboxPtr->interp, hboxPtr->tkwin, buttonConfigSpecs,
+ argc, argv, (char *)hboxPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureButtons(hboxPtr);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonOp --
+ *
+ * This procedure handles button operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec buttonOperSpecs[] =
+{
+ {"activate", 1, (Blt_Op)ButtonActivateOp, 4, 4, "node",},
+ {"bind", 1, (Blt_Op)ButtonBindOp, 4, 6,
+ "tagName ?sequence command?",},
+ {"cget", 2, (Blt_Op)ButtonCgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ButtonConfigureOp, 3, 0,
+ "?option value?...",},
+ {"highlight", 1, (Blt_Op)ButtonActivateOp, 4, 4, "node",},
+};
+
+static int nButtonSpecs = sizeof(buttonOperSpecs) / sizeof(Blt_OpSpec);
+
+static int
+ButtonOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nButtonSpecs, buttonOperSpecs, BLT_OP_ARG2, argc,
+ argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (hboxPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ return Tk_ConfigureValue(interp, hboxPtr->tkwin, configSpecs,
+ (char *)hboxPtr, argv[2], 0);
+}
+
+/*ARGSUSED*/
+static int
+CloseOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tree *rootPtr;
+ unsigned int flags;
+ register int i;
+
+ flags = 0;
+ if (argc > 2) {
+ int length;
+
+ length = strlen(argv[2]);
+ if ((argv[2][0] == '-') && (length > 1) &&
+ (strncmp(argv[2], "-recurse", length) == 0)) {
+ argv++, argc--;
+ flags |= APPLY_RECURSE;
+ }
+ }
+ for (i = 2; i < argc; i++) {
+ rootPtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[i], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (rootPtr == NULL) {
+ continue;
+ }
+ /*
+ * Clear any selected entries that may become hidden by
+ * closing the node.
+ */
+ PruneSelection(hboxPtr, rootPtr);
+
+ /*
+ * -----------------------------------------------------------
+ *
+ * Check if either the "focus" entry or selection anchor
+ * is in this hierarchy. Must move it or disable it before
+ * we close the node. Otherwise it may be deleted by a Tcl
+ * "close" script, and we'll be left pointing to a bogus
+ * memory location.
+ *
+ * -----------------------------------------------------------
+ */
+ if (IsAncestor(rootPtr, hboxPtr->focusPtr)) {
+ hboxPtr->focusPtr = rootPtr;
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+ }
+ if (IsAncestor(rootPtr, hboxPtr->selAnchorPtr)) {
+ hboxPtr->selAnchorPtr = NULL;
+ }
+ if (IsAncestor(rootPtr, hboxPtr->activePtr)) {
+ hboxPtr->activePtr = rootPtr;
+ }
+ if (ApplyToTree(hboxPtr, rootPtr, CloseNode, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for hboxPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, configSpecs,
+ (char *)hboxPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, configSpecs,
+ (char *)hboxPtr, argv[2], 0);
+ }
+ if (ConfigureHierbox(interp, hboxPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CurselectionOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ if (hboxPtr->sortSelection) {
+ ApplyToTree(hboxPtr, hboxPtr->rootPtr, IsSelectedNode,
+ APPLY_RECURSE | APPLY_OPEN_ONLY | APPLY_BEFORE);
+ } else {
+ Blt_ChainLink *linkPtr;
+ Tree *treePtr;
+
+ for (linkPtr = Blt_ChainFirstLink(&(hboxPtr->selectChain));
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ treePtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, treePtr));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOpOp --
+ *
+ * Selects the tab to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ActivateOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr, *oldPtr;
+
+ treePtr = hboxPtr->focusPtr;
+ if (argv[3][0] == '\0') {
+ treePtr = NULL;
+ } else if (GetNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ oldPtr = hboxPtr->activePtr;
+ hboxPtr->activePtr = treePtr;
+ if (treePtr != oldPtr) {
+ EventuallyRedraw(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ ClientData item;
+
+ /*
+ * Individual entries are selected by inode only. All other strings
+ * are interpreted as a binding tag.
+ */
+ item = GetNodeByIndex(hboxPtr, argv[2]);
+ if (item == 0) {
+ item = Tk_GetUid(argv[2]);
+ }
+ return Blt_ConfigureBindings(interp, hboxPtr->bindTable, item, argc - 3,
+ argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+
+ if (StringToNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)treePtr->entryPtr, argv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOpOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h entryconfigure node node node node option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for hboxPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int nIds, nOpts;
+ char **options;
+ register int i;
+ Tree *treePtr;
+
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (StringToNode(hboxPtr, argv[i], &treePtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find node. */
+ }
+ }
+ nIds = i; /* Number of element names specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + i; /* Start of options in argv */
+
+ for (i = 0; i < nIds; i++) {
+ StringToNode(hboxPtr, argv[i], &treePtr);
+ if (argc == 1) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)treePtr->entryPtr, (char *)NULL, 0);
+ } else if (argc == 2) {
+ return Tk_ConfigureInfo(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)treePtr->entryPtr, argv[2], 0);
+ }
+ if (ConfigureEntry(hboxPtr, treePtr->entryPtr, nOpts, options,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsHiddenOpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsHiddenOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+
+ if (StringToNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_SetBooleanResult(interp, IsHidden(treePtr));
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+IsBeforeOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *n1Ptr, *n2Ptr;
+
+ if ((StringToNode(hboxPtr, argv[3], &n1Ptr) != TCL_OK) ||
+ (StringToNode(hboxPtr, argv[4], &n2Ptr) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ Blt_SetBooleanResult(interp, IsBefore(n1Ptr, n2Ptr));
+ return TCL_OK;
+}
+
+
+static int
+ScreenToIndex(hboxPtr, x, y)
+ Hierbox *hboxPtr;
+ int x, y;
+{
+ Tk_Font font;
+ TextStyle ts;
+ TextLayout *textPtr;
+ Tk_FontMetrics fontMetrics;
+ TextFragment *fragPtr;
+ int nBytes;
+ Entry *entryPtr;
+ Tree *treePtr;
+ int lineNum;
+ register int i;
+
+ treePtr = hboxPtr->focusPtr;
+ entryPtr = treePtr->entryPtr;
+
+ if (*entryPtr->labelText == '\0') {
+ return 0;
+ }
+ /*
+ * Compute the X-Y offsets of the screen point from the start of
+ * label. Force the offsets to point within the label.
+ */
+ x -= SCREENX(hboxPtr, entryPtr->worldX) + LABEL_PADX +
+ hboxPtr->selBorderWidth;
+ y -= SCREENY(hboxPtr, entryPtr->worldY) + LABEL_PADY +
+ hboxPtr->selBorderWidth;
+ x -= LEVELWIDTH(treePtr->level) + LEVELWIDTH(treePtr->level + 1) + 4;
+
+ font = GETFONT(hboxPtr, entryPtr->labelFont);
+ memset(&ts, 0, sizeof(TextStyle));
+ ts.font = font;
+ ts.justify = TK_JUSTIFY_LEFT;
+ ts.shadow.offset = entryPtr->labelShadow.offset;
+ textPtr = Blt_GetTextLayout(entryPtr->labelText, &ts);
+
+ if (y < 0) {
+ y = 0;
+ } else if (y >= textPtr->height) {
+ y = textPtr->height - 1;
+ }
+ Tk_GetFontMetrics(font, &fontMetrics);
+ lineNum = y / fontMetrics.linespace;
+ fragPtr = textPtr->fragArr + lineNum;
+
+ if (x < 0) {
+ nBytes = 0;
+ } else if (x >= textPtr->width) {
+ nBytes = fragPtr->count;
+ } else {
+ int newX;
+ /* Find the character underneath the pointer. */
+ nBytes = Tk_MeasureChars(font, fragPtr->text, fragPtr->count, x, 0,
+ &newX);
+ if ((newX < x) && (nBytes < fragPtr->count)) {
+ double fract;
+ int length, charSize;
+ char *next;
+
+ next = fragPtr->text + nBytes;
+#if HAVE_UTF
+ {
+ Tcl_UniChar dummy;
+
+ length = Tcl_UtfToUniChar(next, &dummy);
+ }
+#else
+ length = 1;
+#endif
+ charSize = Tk_TextWidth(font, next, length);
+ fract = ((double)(x - newX) / (double)charSize);
+ if (ROUND(fract)) {
+ nBytes += length;
+ }
+ }
+ }
+ for (i = lineNum - 1; i >= 0; i--) {
+ fragPtr--;
+ nBytes += fragPtr->count + 1;
+ }
+ Blt_Free(textPtr);
+ return nBytes;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetLabelIndex --
+ *
+ * Parse an index into an entry and return either its value
+ * or an error.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the character index (into entryPtr) corresponding to
+ * string. The index value is guaranteed to lie between 0 and
+ * the number of characters in the string, inclusive. If an
+ * error occurs then an error message is left in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetLabelIndex(hboxPtr, entryPtr, string, indexPtr)
+ Hierbox *hboxPtr;
+ Entry *entryPtr;
+ char *string;
+ int *indexPtr;
+{
+ Tcl_Interp *interp = hboxPtr->interp;
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ char c;
+
+ c = string[0];
+ if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ *indexPtr = editPtr->selAnchor;
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ *indexPtr = strlen(entryPtr->labelText);
+ } else if ((c == 'i') && (strcmp(string, "insert") == 0)) {
+ *indexPtr = editPtr->insertPos;
+ } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
+ if (editPtr->selFirst < 0) {
+ Tcl_AppendResult(interp, "nothing is selected", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = editPtr->selFirst;
+ } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
+ if (editPtr->selLast < 0) {
+ Tcl_AppendResult(interp, "nothing is selected", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = editPtr->selLast;
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(interp, hboxPtr->tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *indexPtr = ScreenToIndex(hboxPtr, x, y);
+ } else if (isdigit((int)c)) {
+ int number;
+ int maxChars;
+
+ if (Tcl_GetInt(interp, string, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Don't allow the index to point outside the label. */
+ maxChars = Tcl_NumUtfChars(entryPtr->labelText, -1);
+ if (number < 0) {
+ *indexPtr = 0;
+ } else if (number > maxChars) {
+ *indexPtr = strlen(entryPtr->labelText);
+ } else {
+ *indexPtr = Tcl_UtfAtIndex(entryPtr->labelText, number) -
+ entryPtr->labelText;
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad label index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOpOp --
+ *
+ * Returns the numeric index of the given string. Indices can be
+ * one of the following:
+ *
+ * "anchor" Selection anchor.
+ * "end" End of the label.
+ * "insert" Insertion cursor.
+ * "sel.first" First character selected.
+ * "sel.last" Last character selected.
+ * @x,y Index at X-Y screen coordinate.
+ * number Returns the same number.
+ *
+ * Results:
+ * A standard Tcl result. If the argument does not represent a
+ * valid label index, then TCL_ERROR is returned and the interpreter
+ * result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Entry *entryPtr;
+ int nBytes, nChars;
+
+ entryPtr = hboxPtr->focusPtr->entryPtr;
+ if (GetLabelIndex(hboxPtr, entryPtr, argv[3], &nBytes) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nChars = Tcl_NumUtfChars(entryPtr->labelText, nBytes);
+ Tcl_SetResult(interp, Blt_Itoa(nChars), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOpOp --
+ *
+ * Add new characters to the label of an entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * New information gets added to editPtr; it will be redisplayed
+ * soon, but not necessarily immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tree *treePtr;
+ Entry *entryPtr;
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int oldSize, newSize;
+ char *string;
+ int extra;
+ int insertPos;
+
+ if (!hboxPtr->focusEdit) {
+ return TCL_OK; /* Not in edit mode. */
+ }
+ if (StringToNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (treePtr == NULL) {
+ return TCL_OK; /* Not in edit mode. */
+ }
+ entryPtr = treePtr->entryPtr;
+ if (hboxPtr->focusPtr != treePtr) {
+ hboxPtr->focusPtr = treePtr;
+ editPtr->insertPos = strlen(entryPtr->labelText);
+ editPtr->selAnchor = editPtr->selFirst = editPtr->selLast = -1;
+ }
+ if (GetLabelIndex(hboxPtr, entryPtr, argv[4], &insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ extra = strlen(argv[5]);
+ if (extra == 0) {
+ /* Nothing to insert. Move the cursor anyways. */
+ editPtr->insertPos = insertPos;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+ }
+ oldSize = strlen(entryPtr->labelText);
+ newSize = oldSize + extra;
+ string = Blt_Malloc(sizeof(char) * (newSize + 1));
+
+ if (insertPos == oldSize) { /* Append */
+ strcpy(string, entryPtr->labelText);
+ strcat(string, argv[5]);
+ } else if (insertPos == 0) {/* Prepend */
+ strcpy(string, argv[5]);
+ strcat(string, entryPtr->labelText);
+ } else { /* Insert into existing. */
+ char *left, *right;
+ char *p;
+
+ left = entryPtr->labelText;
+ right = left + insertPos;
+ p = string;
+ strncpy(p, left, insertPos);
+ p += insertPos;
+ strcpy(p, argv[5]);
+ p += extra;
+ strcpy(p, right);
+ }
+ /*
+ * All indices from the start of the insertion to the end of the
+ * string need to be updated. Simply move the indices down by the
+ * number of characters added.
+ */
+ if (editPtr->selFirst >= insertPos) {
+ editPtr->selFirst += extra;
+ }
+ if (editPtr->selLast > insertPos) {
+ editPtr->selLast += extra;
+ }
+ if ((editPtr->selAnchor > insertPos) || (editPtr->selFirst >= insertPos)) {
+ editPtr->selAnchor += extra;
+ }
+ Blt_Free(entryPtr->labelText);
+ entryPtr->labelText = string;
+
+ editPtr->insertPos = insertPos + extra;
+ GetCursorLocation(hboxPtr, treePtr);
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOpOp --
+ *
+ * Remove one or more characters from the label of an entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets freed, the entry gets modified and (eventually)
+ * redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ Tree *treePtr;
+ Entry *entryPtr;
+ int oldSize, newSize;
+ int nDeleted;
+ int first, last;
+ char *string;
+ char *p;
+
+ if (!hboxPtr->focusEdit) {
+ return TCL_OK; /* Not in edit mode. */
+ }
+ if (StringToNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (treePtr == NULL) {
+ return TCL_OK; /* Not in edit mode. */
+ }
+ entryPtr = treePtr->entryPtr;
+ if (hboxPtr->focusPtr != treePtr) {
+ hboxPtr->focusPtr = treePtr;
+ editPtr->insertPos = strlen(entryPtr->labelText);
+ editPtr->selAnchor = editPtr->selFirst = editPtr->selLast = -1;
+ }
+ if ((GetLabelIndex(hboxPtr, entryPtr, argv[4], &first) != TCL_OK) ||
+ (GetLabelIndex(hboxPtr, entryPtr, argv[5], &last) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (first >= last) {
+ return TCL_OK;
+ }
+ if ((!hboxPtr->focusEdit) || (entryPtr == NULL)) {
+ return TCL_OK; /* Not in edit mode. */
+ }
+ oldSize = strlen(entryPtr->labelText);
+ newSize = oldSize - (last - first);
+ p = string = Blt_Malloc(sizeof(char) * (newSize + 1));
+ strncpy(p, entryPtr->labelText, first);
+ p += first;
+ strcpy(p, entryPtr->labelText + last);
+
+ Blt_Free(entryPtr->labelText);
+ entryPtr->labelText = string;
+ nDeleted = last - first + 1;
+
+ /*
+ * Since deleting characters compacts the character array, we need to
+ * update the various character indices according. It depends where
+ * the index occurs in relation to range of deleted characters:
+ *
+ * before Ignore.
+ * within Move the index back to the start of the deletion.
+ * after Subtract off the deleted number of characters,
+ * since the array has been compressed by that
+ * many characters.
+ *
+ */
+ if (editPtr->selFirst >= first) {
+ if (editPtr->selFirst >= last) {
+ editPtr->selFirst -= nDeleted;
+ } else {
+ editPtr->selFirst = first;
+ }
+ }
+ if (editPtr->selLast >= first) {
+ if (editPtr->selLast >= last) {
+ editPtr->selLast -= nDeleted;
+ } else {
+ editPtr->selLast = first;
+ }
+ }
+ if (editPtr->selLast <= editPtr->selFirst) {
+ editPtr->selFirst = editPtr->selLast = -1; /* Cut away the entire
+ * selection. */
+ }
+ if (editPtr->selAnchor >= first) {
+ if (editPtr->selAnchor >= last) {
+ editPtr->selAnchor -= nDeleted;
+ } else {
+ editPtr->selAnchor = first;
+ }
+ }
+ if (editPtr->insertPos >= first) {
+ if (editPtr->insertPos >= last) {
+ editPtr->insertPos -= nDeleted;
+ } else {
+ editPtr->insertPos = first;
+ }
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsOpenOpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsOpenOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+
+ if (StringToNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_SetBooleanResult(interp, (treePtr->entryPtr->flags & ENTRY_OPEN));
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ChildrenOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Tree *parentPtr, *nodePtr;
+
+ if (StringToNode(hboxPtr, argv[3], &parentPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(parentPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ nodePtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, nodePtr));
+ }
+ } else if (argc == 6) {
+ Blt_ChainLink *firstPtr, *lastPtr;
+ int first, last;
+ int nNodes;
+
+ if ((Blt_GetPosition(interp, argv[4], &first) != TCL_OK) ||
+ (Blt_GetPosition(interp, argv[5], &last) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nNodes = Blt_ChainGetLength(parentPtr->chainPtr);
+ if (nNodes == 0) {
+ return TCL_OK;
+ }
+ if ((last == -1) || (last >= nNodes)) {
+ last = nNodes - 1;
+ }
+ if ((first == -1) || (first >= nNodes)) {
+ first = nNodes - 1;
+ }
+ firstPtr = Blt_ChainGetNthLink(parentPtr->chainPtr, first);
+ lastPtr = Blt_ChainGetNthLink(parentPtr->chainPtr, last);
+ if (first > last) {
+ for ( /*empty*/ ; lastPtr != NULL;
+ lastPtr = Blt_ChainPrevLink(lastPtr)) {
+ nodePtr = Blt_ChainGetValue(lastPtr);
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, nodePtr));
+ if (lastPtr == firstPtr) {
+ break;
+ }
+ }
+ } else {
+ for ( /*empty*/ ; firstPtr != NULL;
+ firstPtr = Blt_ChainNextLink(firstPtr)) {
+ nodePtr = Blt_ChainGetValue(firstPtr);
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, nodePtr));
+ if (firstPtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ",
+ argv[1], " ", argv[2], " index ?first last?", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SizeOpOp --
+ *
+ * Counts the number of entries at this node.
+ *
+ * Results:
+ * A standard Tcl result. If an error occurred TCL_ERROR is
+ * returned and interp->result will contain an error message.
+ * Otherwise, TCL_OK is returned and interp->result contains
+ * the number of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SizeOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int length;
+ Tree *rootPtr;
+ int *sumPtr = (int *)&(hboxPtr->clientData);
+ unsigned int flags;
+
+ flags = 0;
+ length = strlen(argv[3]);
+ if ((argv[3][0] == '-') && (length > 1) &&
+ (strncmp(argv[3], "-recurse", length) == 0)) {
+ argv++, argc--;
+ flags |= APPLY_RECURSE;
+ }
+ if (argc == 3) {
+ Tcl_AppendResult(interp, "missing node argument: should be \"",
+ argv[0], " entry open node\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (StringToNode(hboxPtr, argv[3], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *sumPtr = 0;
+ if (ApplyToTree(hboxPtr, rootPtr, SizeOfNode, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Blt_Itoa(*sumPtr), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryOp --
+ *
+ * This procedure handles entry operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec entryOperSpecs[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOpOp, 4, 4, "node",},
+ {"cget", 2, (Blt_Op)CgetOpOp, 5, 5, "node option",},
+ {"children", 2, (Blt_Op)ChildrenOpOp, 4, 6, "node first last",},
+ {"configure", 2, (Blt_Op)ConfigureOpOp, 4, 0,
+ "node ?node...? ?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteOpOp, 6, 6, "node first last"},
+ {"highlight", 1, (Blt_Op)ActivateOpOp, 4, 4, "node",},
+ {"index", 3, (Blt_Op)IndexOpOp, 6, 6, "index"},
+ {"insert", 3, (Blt_Op)InsertOpOp, 6, 6, "node index string"},
+ {"isbefore", 3, (Blt_Op)IsBeforeOpOp, 5, 5, "node node",},
+ {"ishidden", 3, (Blt_Op)IsHiddenOpOp, 4, 4, "node",},
+ {"isopen", 3, (Blt_Op)IsOpenOpOp, 4, 4, "node",},
+ {"size", 1, (Blt_Op)SizeOpOp, 4, 5, "?-recurse? node",},
+};
+static int nEntrySpecs = sizeof(entryOperSpecs) / sizeof(Blt_OpSpec);
+
+static int
+EntryOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nEntrySpecs, entryOperSpecs, BLT_OP_ARG2, argc,
+ argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (hboxPtr, interp, argc, argv);
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+ExactCompare(interp, name, pattern)
+ Tcl_Interp *interp; /* Not used. */
+ char *name;
+ char *pattern;
+{
+ return (strcmp(name, pattern) == 0);
+}
+
+/*ARGSUSED*/
+static int
+GlobCompare(interp, name, pattern)
+ Tcl_Interp *interp; /* Not used. */
+ char *name;
+ char *pattern;
+{
+ return Tcl_StringMatch(name, pattern);
+}
+
+static int
+RegexpCompare(interp, name, pattern)
+ Tcl_Interp *interp;
+ char *name;
+ char *pattern;
+{
+ return Tcl_RegExpMatch(interp, name, pattern);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ * Find one or more nodes based upon the pattern provided.
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain a
+ * list of the node serial identifiers.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+FindOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Tree *treePtr;
+ Tree *firstPtr, *lastPtr;
+ int nMatches, maxMatches;
+ char c;
+ int length;
+ CompareProc *compareProc;
+ IterProc *nextProc;
+ int invertMatch; /* normal search mode (matching entries) */
+ char *namePattern, *fullPattern;
+ char *execCmd;
+ register int i;
+ int result;
+ char *pattern, *option, *value;
+ Tcl_DString dString, pathString;
+ Blt_List optionList;
+ register Blt_ListNode node;
+
+ invertMatch = FALSE;
+ maxMatches = 0;
+ execCmd = namePattern = fullPattern = NULL;
+ compareProc = ExactCompare;
+ nextProc = NextNode;
+ optionList = Blt_ListCreate(TCL_STRING_KEYS);
+
+ /*
+ * Step 1: Process flags for find operation.
+ */
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ option = argv[i] + 1;
+ length = strlen(option);
+ c = option[0];
+ if ((c == 'e') && (length > 2) &&
+ (strncmp(option, "exact", length) == 0)) {
+ compareProc = ExactCompare;
+ } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+ compareProc = GlobCompare;
+ } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+ compareProc = RegexpCompare;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "nonmatching", length) == 0)) {
+ invertMatch = TRUE;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "name", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ namePattern = argv[i];
+ } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ fullPattern = argv[i];
+ } else if ((c == 'e') && (length > 2) &&
+ (strncmp(option, "exec", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ execCmd = argv[i];
+ } else if ((c == 'c') && (strncmp(option, "count", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ if (Tcl_GetInt(interp, argv[i], &maxMatches) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (maxMatches < 0) {
+ Tcl_AppendResult(interp, "bad match count \"", argv[i],
+ "\": should be a positive number", (char *)NULL);
+ Blt_ListDestroy(optionList);
+ return TCL_ERROR;
+ }
+ } else if ((option[0] == '-') && (option[1] == '\0')) {
+ break;
+ } else {
+ /*
+ * Verify that the switch is actually an node configuration
+ * option.
+ */
+ if (Tk_ConfigureValue(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)hboxPtr->rootPtr->entryPtr, argv[i], 0) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad find switch \"", argv[i], "\"",
+ (char *)NULL);
+ Blt_ListDestroy(optionList);
+ return TCL_ERROR;
+ }
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ /* Save the option in the list of configuration options */
+ node = Blt_ListGetNode(optionList, argv[i]);
+ if (node == NULL) {
+ node = Blt_ListCreateNode(optionList, argv[i]);
+ Blt_ListAppendNode(optionList, node);
+ }
+ Blt_ListSetValue(node, argv[i + 1]);
+ i++;
+ }
+ }
+
+ if ((argc - i) > 2) {
+ Blt_ListDestroy(optionList);
+ Tcl_AppendResult(interp, "too many args", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Step 2: Find the range of the search. Check the order of two
+ * nodes and arrange the search accordingly.
+ *
+ * Note: Be careful to treat "end" as the end of all nodes, instead
+ * of the end of visible nodes. That way, we can search the
+ * entire tree, even if the last folder is closed.
+ */
+ firstPtr = hboxPtr->rootPtr;/* Default to root node */
+ lastPtr = EndNode(firstPtr, 0);
+
+ if (i < argc) {
+ if ((argv[i][0] == 'e') && (strcmp(argv[i], "end") == 0)) {
+ firstPtr = EndNode(hboxPtr->rootPtr, 0);
+ } else if (StringToNode(hboxPtr, argv[i], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ i++;
+ }
+ if (i < argc) {
+ if ((argv[i][0] == 'e') && (strcmp(argv[i], "end") == 0)) {
+ lastPtr = EndNode(hboxPtr->rootPtr, 0);
+ } else if (StringToNode(hboxPtr, argv[i], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (IsBefore(lastPtr, firstPtr)) {
+ nextProc = LastNode;
+ }
+ nMatches = 0;
+
+ /*
+ * Step 3: Search through the tree and look for nodes that match the
+ * current pattern specifications. Save the name of each of
+ * the matching nodes.
+ */
+ Tcl_DStringInit(&dString);
+ for (treePtr = firstPtr; treePtr != NULL;
+ treePtr = (*nextProc) (treePtr, 0)) {
+ if (namePattern != NULL) {
+ result = (*compareProc) (interp, treePtr->nameId, namePattern);
+ if (result == invertMatch) {
+ goto nextNode; /* Failed to match */
+ }
+ }
+ if (fullPattern != NULL) {
+ GetFullPath(treePtr, hboxPtr->separator, &pathString);
+ result = (*compareProc) (interp, Tcl_DStringValue(&pathString),
+ fullPattern);
+ Tcl_DStringFree(&pathString);
+ if (result == invertMatch) {
+ goto nextNode; /* Failed to match */
+ }
+ }
+ for (node = Blt_ListFirstNode(optionList); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ option = Blt_ListGetKey(node);
+ Tcl_ResetResult(interp);
+ if (Tk_ConfigureValue(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)treePtr->entryPtr, option, 0) != TCL_OK) {
+ goto error; /* This shouldn't happen. */
+ }
+ pattern = (char *)Blt_ListGetValue(node);
+ value = Tcl_GetStringResult(interp);
+ result = (*compareProc) (interp, value, pattern);
+ if (result == invertMatch) {
+ goto nextNode; /* Failed to match */
+ }
+ }
+ /*
+ * As unlikely as it sounds, the "exec" callback may delete this
+ * node. We need to preverse it until we're not using it anymore
+ * (i.e. no longer accessing its fields).
+ */
+ Tcl_Preserve(treePtr);
+ if (execCmd != NULL) {
+ Tcl_DString cmdString;
+
+ PercentSubst(hboxPtr, treePtr, execCmd, &cmdString);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
+ Tcl_DStringFree(&cmdString);
+ if (result != TCL_OK) {
+ Tcl_Release(treePtr);
+ goto error;
+ }
+ }
+ /* Finally, save the matching node name. */
+ Tcl_DStringAppendElement(&dString, NodeToString(hboxPtr, treePtr));
+ Tcl_Release(treePtr);
+ nMatches++;
+ if ((nMatches == maxMatches) && (maxMatches > 0)) {
+ break;
+ }
+ nextNode:
+ if (treePtr == lastPtr) {
+ break;
+ }
+ }
+ Tcl_ResetResult(interp);
+ Blt_ListDestroy(optionList);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+
+ missingArg:
+ Tcl_AppendResult(interp, "missing argument for find option \"", argv[i],
+ "\"", (char *)NULL);
+ error:
+ Tcl_DStringFree(&dString);
+ Blt_ListDestroy(optionList);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Converts one or more node identifiers to its path component.
+ * The path may be either the single entry name or the full path
+ * of the entry.
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain a
+ * list of the convert names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int fullName;
+ Tree *treePtr;
+ Tcl_DString dString;
+ Tcl_DString pathString;
+ register int i;
+
+ fullName = FALSE;
+ if ((argc > 2) && (argv[2][0] == '-') && (strcmp(argv[2], "-full") == 0)) {
+ fullName = TRUE;
+ argv++, argc--;
+ }
+ Tcl_DStringInit(&dString);
+ Tcl_DStringInit(&pathString);
+ for (i = 2; i < argc; i++) {
+ treePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[i], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (treePtr == NULL) {
+ Tcl_DStringAppendElement(&dString, "");
+ continue;
+ }
+ if (fullName) {
+ GetFullPath(treePtr, hboxPtr->separator, &pathString);
+ Tcl_DStringAppendElement(&dString, Tcl_DStringValue(&pathString));
+ } else {
+ Tcl_DStringAppendElement(&dString, treePtr->nameId);
+ }
+ }
+ Tcl_DStringFree(&pathString);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SearchAndApplyToTree --
+ *
+ * Searches through the current tree and applies a procedure
+ * to matching nodes. The search specification is taken from
+ * the following command-line arguments:
+ *
+ * ?-exact? ?-glob? ?-regexp? ?-nonmatching?
+ * ?-data string?
+ * ?-name string?
+ * ?-full string?
+ * ?--?
+ * ?inode...?
+ *
+ * Results:
+ * A standard Tcl result. If the result is valid, and if the
+ * nonmatchPtr is specified, it returns a boolean value
+ * indicating whether or not the search was inverted. This
+ * is needed to fix things properly for the "hide nonmatching"
+ * case.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SearchAndApplyToTree(hboxPtr, interp, argc, argv, proc, nonMatchPtr)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ ApplyProc *proc;
+ int *nonMatchPtr; /* returns: inverted search indicator */
+{
+ CompareProc *compareProc;
+ int invertMatch; /* normal search mode (matching entries) */
+ char *namePattern, *fullPattern;
+ register int i;
+ int length;
+ int result;
+ char *option, *pattern, *value;
+ Tree *treePtr;
+ char c;
+ Blt_List optionList;
+ register Blt_ListNode node;
+
+ optionList = Blt_ListCreate(TCL_STRING_KEYS);
+ invertMatch = FALSE;
+ namePattern = fullPattern = NULL;
+ compareProc = ExactCompare;
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ option = argv[i] + 1;
+ length = strlen(option);
+ c = option[0];
+ if ((c == 'e') && (strncmp(option, "exact", length) == 0)) {
+ compareProc = ExactCompare;
+ } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+ compareProc = GlobCompare;
+ } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+ compareProc = RegexpCompare;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "nonmatching", length) == 0)) {
+ invertMatch = TRUE;
+ } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ fullPattern = argv[i];
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "name", length) == 0)) {
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ i++;
+ namePattern = argv[i];
+ } else if ((option[0] == '-') && (option[1] == '\0')) {
+ break;
+ } else {
+ /*
+ * Verify that the switch is actually an entry configuration option.
+ */
+ if (Tk_ConfigureValue(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)hboxPtr->rootPtr->entryPtr, argv[i], 0) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad switch \"", argv[i],
+ "\": must be -exact, -glob, -regexp, -name, -full, or -nonmatching",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((i + 1) == argc) {
+ goto missingArg;
+ }
+ /* Save the option in the list of configuration options */
+ node = Blt_ListGetNode(optionList, argv[i]);
+ if (node == NULL) {
+ node = Blt_ListCreateNode(optionList, argv[i]);
+ Blt_ListAppendNode(optionList, node);
+ }
+ Blt_ListSetValue(node, argv[i + 1]);
+ }
+ }
+
+ if ((namePattern != NULL) || (fullPattern != NULL) ||
+ (Blt_ListGetLength(optionList) > 0)) {
+ /*
+ * Search through the tree and look for nodes that match the
+ * current spec. Apply the input procedure to each of the
+ * matching nodes.
+ */
+ for (treePtr = hboxPtr->rootPtr; treePtr != NULL;
+ treePtr = NextNode(treePtr, 0)) {
+
+ if (namePattern != NULL) {
+ result = (*compareProc) (interp, treePtr->nameId, namePattern);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ if (fullPattern != NULL) {
+ Tcl_DString dString;
+
+ GetFullPath(treePtr, hboxPtr->separator, &dString);
+ result = (*compareProc) (interp, Tcl_DStringValue(&dString),
+ fullPattern);
+ Tcl_DStringFree(&dString);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ for (node = Blt_ListFirstNode(optionList); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ option = Blt_ListGetKey(node);
+ Tcl_ResetResult(interp);
+ if (Tk_ConfigureValue(interp, hboxPtr->tkwin, entryConfigSpecs,
+ (char *)treePtr->entryPtr, option, 0) != TCL_OK) {
+ return TCL_ERROR; /* This shouldn't happen. */
+ }
+ pattern = (char *)Blt_ListGetValue(node);
+ value = Tcl_GetStringResult(interp);
+ result = (*compareProc) (interp, value, pattern);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ /* Finally, apply the procedure to the node */
+ (*proc) (hboxPtr, treePtr);
+ }
+ Tcl_ResetResult(interp);
+ Blt_ListDestroy(optionList);
+ }
+ /*
+ * Apply the procedure to nodes that have been specified
+ * individually.
+ */
+ for ( /*empty*/ ; i < argc; i++) {
+ if ((argv[i][0] == 'a') && (strcmp(argv[i], "all") == 0)) {
+ return ApplyToTree(hboxPtr, hboxPtr->rootPtr, proc, APPLY_RECURSE);
+ }
+ if (StringToNode(hboxPtr, argv[i], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((*proc) (hboxPtr, treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ if (nonMatchPtr != NULL) {
+ *nonMatchPtr = invertMatch; /* return "inverted search" status */
+ }
+ return TCL_OK;
+
+ missingArg:
+ Blt_ListDestroy(optionList);
+ Tcl_AppendResult(interp, "missing pattern for search option \"", argv[i],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideOp --
+ *
+ * Hides one or more nodes. Nodes can be specified by their
+ * inode, or by matching a name or data value pattern. By
+ * default, the patterns are matched exactly. They can also
+ * be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+HideOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int status, nonmatching;
+
+ status = SearchAndApplyToTree(hboxPtr, interp, argc, argv, UnmapNode,
+ &nonmatching);
+
+ if (status != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * If this was an inverted search, scan back through the
+ * tree and make sure that the parents for all visible
+ * nodes are also visible. After all, if a node is supposed
+ * to be visible, its parent can't be hidden.
+ */
+ if (nonmatching) {
+ ApplyToTree(hboxPtr, hboxPtr->rootPtr, MapAncestors, APPLY_RECURSE);
+ }
+ /*
+ * Make sure that selections arme cleared from any hidden
+ * nodes. This wasn't done earlier--we had to delay it until
+ * we fixed the visibility status for the parents.
+ */
+ ApplyToTree(hboxPtr, hboxPtr->rootPtr, FixUnmappedSelections,
+ APPLY_RECURSE);
+
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ * Mark one or more nodes to be exposed. Nodes can be specified
+ * by their inode, or by matching a name or data value pattern. By
+ * default, the patterns are matched exactly. They can also
+ * be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShowOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (SearchAndApplyToTree(hboxPtr, interp, argc, argv, MapNode,
+ (int *)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Converts one of more words representing indices of the entries
+ * in the hierarchy widget to their respective serial identifiers.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each inode found. If an inode could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *nodePtr, *rootPtr;
+
+ rootPtr = hboxPtr->focusPtr;
+ if ((argv[2][0] == '-') && (strcmp(argv[2], "-at") == 0)) {
+ if (StringToNode(hboxPtr, argv[3], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ argv += 2, argc -= 2;
+ }
+ if (argc > 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " index ?-at index? index\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ nodePtr = rootPtr;
+ if ((GetNode(hboxPtr, argv[2], &nodePtr) == TCL_OK) && (nodePtr != NULL)) {
+ Tcl_SetResult(interp, NodeToString(hboxPtr, nodePtr), TCL_VOLATILE);
+ } else {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Add new entries into a hierarchy. If no node is specified,
+ * new entries will be added to the root of the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *rootPtr, *nodePtr, *parentPtr;
+ int position;
+ int level, count;
+ char *path;
+ Tcl_DString dString;
+ register int i, l;
+ int nOpts;
+ char **options;
+ char **nameArr;
+
+ rootPtr = hboxPtr->rootPtr;
+ if ((argv[2][0] == '-') && (strcmp(argv[2], "-at") == 0)) {
+ if (StringToNode(hboxPtr, argv[3], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ argv += 2, argc -= 2;
+ }
+ if (Blt_GetPosition(hboxPtr->interp, argv[2], &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ argc -= 3, argv += 3;
+
+ /*
+ * Count the pathnames that follow. Count the arguments until we
+ * spot one that looks like a configuration option (i.e. starts
+ * with a minus ("-")).
+ */
+ for (count = 0; count < argc; count++) {
+ if (argv[count][0] == '-') {
+ break;
+ }
+ }
+ nOpts = argc - count;
+ options = argv + count;
+
+ Tcl_DStringInit(&dString);
+ for (i = 0; i < count; i++) {
+ path = argv[i];
+ if (hboxPtr->trimLeft != NULL) {
+ register char *p, *s;
+
+ /* Trim off leading character string if one exists. */
+ for (p = path, s = hboxPtr->trimLeft; *s != '\0'; s++, p++) {
+ if (*p != *s) {
+ break;
+ }
+ }
+ if (*s == '\0') {
+ path = p;
+ }
+ }
+ /*
+ * Split the path and find the parent node of the path.
+ */
+ nameArr = &path;
+ level = 1;
+ if (hboxPtr->separator == SEPARATOR_LIST) {
+ if (Tcl_SplitList(interp, path, &level, &nameArr) != TCL_OK) {
+ goto error;
+ }
+ } else if (hboxPtr->separator != SEPARATOR_NONE) {
+ if (SplitPath(hboxPtr, path, &level, &nameArr) != TCL_OK) {
+ goto error;
+ }
+ }
+ if (level == 0) {
+ continue; /* Root already exists. */
+ }
+ parentPtr = rootPtr;
+ level--;
+ for (l = 0; l < level; l++) {
+ nodePtr = FindComponent(parentPtr, nameArr[l]);
+ if (nodePtr == NULL) {
+ if (!hboxPtr->autoCreate) {
+ Tcl_AppendResult(interp, "can't find path component \"",
+ nameArr[l], "\" in \"", path, "\"", (char *)NULL);
+ goto error;
+ }
+ nodePtr = CreateNode(hboxPtr, parentPtr, APPEND, nameArr[l]);
+ }
+ parentPtr = nodePtr;
+ }
+ nodePtr = NULL;
+ if (!hboxPtr->allowDuplicates) {
+ nodePtr = FindComponent(parentPtr, nameArr[level]);
+ }
+ if (nodePtr == NULL) {
+ nodePtr = CreateNode(hboxPtr, parentPtr, position, nameArr[level]);
+ if (nodePtr == NULL) {
+ goto error;
+ }
+ if (ConfigureEntry(hboxPtr, nodePtr->entryPtr, nOpts, options,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ DeleteNode(hboxPtr, nodePtr);
+ goto error;
+ }
+ Tcl_DStringAppendElement(&dString, NodeToString(hboxPtr, nodePtr));
+ } else {
+ if (ConfigureEntry(hboxPtr, nodePtr->entryPtr, nOpts, options,
+ 0) != TCL_OK) {
+ goto error;
+ }
+ }
+ if (nameArr != &path) {
+ Blt_Free(nameArr);
+ }
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL | HIERBOX_DIRTY);
+ EventuallyRedraw(hboxPtr);
+ Tcl_DStringResult(hboxPtr->interp, &dString);
+ return TCL_OK;
+ error:
+ if (nameArr != &path) {
+ Blt_Free(nameArr);
+ }
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes nodes from the hierarchy. Deletes either a range of
+ * entries from a hierarchy or a single node (except root).
+ * In all cases, nodes are removed recursively.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+ Blt_ChainLink *linkPtr;
+ Blt_ChainLink *firstPtr, *lastPtr, *nextPtr;
+
+ if (argc == 2) {
+ return TCL_OK;
+ }
+ if (StringToNode(hboxPtr, argv[2], &treePtr) != TCL_OK) {
+ return TCL_ERROR; /* Node or path doesn't already exist */
+ }
+ firstPtr = lastPtr = NULL;
+ switch (argc) {
+ case 3:
+ /*
+ * Delete a single hierarchy. If the node specified is root,
+ * delete only the children.
+ */
+ if (treePtr != hboxPtr->rootPtr) {
+ DestroyTree(hboxPtr, treePtr); /* Don't delete root */
+ goto done;
+ }
+ firstPtr = Blt_ChainFirstLink(treePtr->chainPtr);
+ lastPtr = Blt_ChainLastLink(treePtr->chainPtr);
+ break;
+
+ case 4:
+ /*
+ * Delete a single node from hierarchy specified by its
+ * numeric position.
+ */
+ {
+ int position;
+
+ if (Blt_GetPosition(interp, argv[3], &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position >= Blt_ChainGetLength(treePtr->chainPtr)) {
+ return TCL_OK; /* Bad first index */
+ }
+ if (position == APPEND) {
+ linkPtr = Blt_ChainLastLink(treePtr->chainPtr);
+ } else {
+ linkPtr = Blt_ChainGetNthLink(treePtr->chainPtr, position);
+ }
+ firstPtr = lastPtr = linkPtr;
+ }
+ break;
+
+ case 5:
+ /*
+ * Delete range of nodes in hierarchy specified by first/last
+ * positions.
+ */
+ {
+ int first, last;
+ int nEntries;
+
+ if ((Blt_GetPosition(interp, argv[3], &first) != TCL_OK) ||
+ (Blt_GetPosition(interp, argv[4], &last) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nEntries = Blt_ChainGetLength(treePtr->chainPtr);
+ if (nEntries == 0) {
+ return TCL_OK;
+ }
+ if (first == APPEND) {
+ first = nEntries - 1;
+ }
+ if (first >= nEntries) {
+ Tcl_AppendResult(interp, "first position \"", argv[3],
+ " is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((last == APPEND) || (last >= nEntries)) {
+ last = nEntries - 1;
+ }
+ if (first > last) {
+ Tcl_AppendResult(interp, "bad range: \"", argv[3],
+ " > ", argv[4], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ firstPtr = Blt_ChainGetNthLink(treePtr->chainPtr, first);
+ lastPtr = Blt_ChainGetNthLink(treePtr->chainPtr, last);
+ }
+ break;
+ }
+ for (linkPtr = firstPtr; linkPtr != NULL; linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ DestroyTree(hboxPtr, Blt_ChainGetValue(linkPtr));
+ if (linkPtr == lastPtr) {
+ break;
+ }
+ }
+ done:
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ * Move an entry into a new location in the hierarchy.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *srcPtr, *destPtr, *parentPtr;
+ char c;
+ int action;
+
+#define MOVE_INTO (1<<0)
+#define MOVE_BEFORE (1<<1)
+#define MOVE_AFTER (1<<2)
+ if (StringToNode(hboxPtr, argv[2], &srcPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = argv[3][0];
+ action = MOVE_INTO;
+ if ((c == 'i') && (strcmp(argv[3], "into") == 0)) {
+ action = MOVE_INTO;
+ } else if ((c == 'b') && (strcmp(argv[3], "before") == 0)) {
+ action = MOVE_BEFORE;
+ } else if ((c == 'a') && (strcmp(argv[3], "after") == 0)) {
+ action = MOVE_AFTER;
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", argv[3],
+ "\": should be into, before, or after", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (StringToNode(hboxPtr, argv[4], &destPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Verify they aren't ancestors. */
+ if (IsAncestor(srcPtr, destPtr)) {
+ Tcl_AppendResult(interp, "can't move node: \"", argv[2],
+ "\" is an ancestor of \"", argv[4], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ parentPtr = destPtr->parentPtr;
+ if (parentPtr == NULL) {
+ action = MOVE_INTO;
+ }
+ Blt_ChainUnlinkLink(srcPtr->parentPtr->chainPtr, srcPtr->linkPtr);
+ switch (action) {
+ case MOVE_INTO:
+ Blt_ChainLinkBefore(destPtr->chainPtr, srcPtr->linkPtr,
+ (Blt_ChainLink *) NULL);
+ parentPtr = destPtr;
+ break;
+
+ case MOVE_BEFORE:
+ Blt_ChainLinkBefore(parentPtr->chainPtr, srcPtr->linkPtr,
+ destPtr->linkPtr);
+ break;
+
+ case MOVE_AFTER:
+ Blt_ChainLinkAfter(parentPtr->chainPtr, srcPtr->linkPtr,
+ destPtr->linkPtr);
+ break;
+ }
+ srcPtr->parentPtr = parentPtr;
+ srcPtr->level = parentPtr->level + 1;
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL | HIERBOX_DIRTY);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ ButtonAttributes *buttonPtr = &(hboxPtr->button);
+ int x, y; /* Screen coordinates of the test point. */
+ register Entry *entryPtr;
+ register Tree *treePtr;
+
+ if ((Tk_GetPixels(interp, hboxPtr->tkwin, argv[2], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, hboxPtr->tkwin, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (hboxPtr->nVisible == 0) {
+ return TCL_OK;
+ }
+ treePtr = NearestNode(hboxPtr, x, y, TRUE);
+ if (treePtr == NULL) {
+ return TCL_OK;
+ }
+ x = WORLDX(hboxPtr, x);
+ y = WORLDY(hboxPtr, y);
+ entryPtr = treePtr->entryPtr;
+ if (argc > 4) {
+ char *where;
+ int labelX;
+
+ where = "";
+ if (entryPtr->flags & ENTRY_BUTTON) {
+ int buttonX, buttonY;
+
+ buttonX = entryPtr->worldX + entryPtr->buttonX;
+ buttonY = entryPtr->worldY + entryPtr->buttonY;
+ if ((x >= buttonX) && (x < (buttonX + buttonPtr->width)) &&
+ (y >= buttonY) && (y < (buttonY + buttonPtr->height))) {
+ where = "gadget";
+ }
+ }
+ labelX = entryPtr->worldX + LEVELWIDTH(treePtr->level);
+ if ((x >= labelX) &&
+ (x < (labelX + LEVELWIDTH(treePtr->level + 1) + entryPtr->width))) {
+ where = "select";
+ }
+ if (Tcl_SetVar(interp, argv[4], where, TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_SetResult(interp, NodeToString(hboxPtr, treePtr), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+OpenOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tree *rootPtr;
+ unsigned int flags;
+ register int i;
+
+ flags = 0;
+ if (argc > 2) {
+ int length;
+
+ length = strlen(argv[2]);
+ if ((argv[2][0] == '-') && (length > 1) &&
+ (strncmp(argv[2], "-recurse", length) == 0)) {
+ argv++, argc--;
+ flags |= APPLY_RECURSE;
+ }
+ }
+ for (i = 2; i < argc; i++) {
+ rootPtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[i], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (rootPtr == NULL) {
+ continue;
+ }
+ ExposeAncestors(rootPtr); /* Also make sure that all the ancestors
+ * of this node are also not hidden. */
+ if (ApplyToTree(hboxPtr, rootPtr, OpenNode, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns the node identifiers in a given range.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RangeOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Tree *firstPtr, *lastPtr;
+ register Tree *treePtr;
+ unsigned int flags;
+ int length;
+
+ flags = 0;
+ length = strlen(argv[2]);
+ if ((argv[2][0] == '-') && (length > 1) &&
+ (strncmp(argv[2], "-open", length) == 0)) {
+ argv++, argc--;
+ flags |= ENTRY_OPEN;
+ }
+ if (StringToNode(hboxPtr, argv[2], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ lastPtr = EndNode(firstPtr, flags);
+ if (argc > 3) {
+ if (StringToNode(hboxPtr, argv[3], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (flags & ENTRY_OPEN) {
+ if (IsHidden(firstPtr)) {
+ Tcl_AppendResult(interp, "first node \"", argv[2], "\" is hidden.",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (IsHidden(lastPtr)) {
+ Tcl_AppendResult(interp, "last node \"", argv[3], "\" is hidden.",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ /*
+ * The relative order of the first/last markers determines the
+ * direction.
+ */
+ if (IsBefore(lastPtr, firstPtr)) {
+ for (treePtr = lastPtr; treePtr != NULL;
+ treePtr = LastNode(treePtr, flags)) {
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, treePtr));
+ if (treePtr == firstPtr) {
+ break;
+ }
+ }
+ } else {
+ for (treePtr = firstPtr; treePtr != NULL;
+ treePtr = NextNode(treePtr, flags)) {
+ Tcl_AppendElement(interp, NodeToString(hboxPtr, treePtr));
+ if (treePtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ * Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y;
+ char c;
+ unsigned int length;
+ int oper;
+
+#define SCAN_MARK 1
+#define SCAN_DRAGTO 2
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
+ oper = SCAN_MARK;
+ } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
+ oper = SCAN_DRAGTO;
+ } else {
+ Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
+ "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tk_GetPixels(interp, hboxPtr->tkwin, argv[3], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, hboxPtr->tkwin, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (oper == SCAN_MARK) {
+ hboxPtr->scanAnchorX = x;
+ hboxPtr->scanAnchorY = y;
+ hboxPtr->scanX = hboxPtr->xOffset;
+ hboxPtr->scanY = hboxPtr->yOffset;
+ } else {
+ int worldX, worldY;
+ int dx, dy;
+
+ dx = hboxPtr->scanAnchorX - x;
+ dy = hboxPtr->scanAnchorY - y;
+ worldX = hboxPtr->scanX + (10 * dx);
+ worldY = hboxPtr->scanY + (10 * dy);
+
+ if (worldX < 0) {
+ worldX = 0;
+ } else if (worldX >= hboxPtr->worldWidth) {
+ worldX = hboxPtr->worldWidth - hboxPtr->xScrollUnits;
+ }
+ if (worldY < 0) {
+ worldY = 0;
+ } else if (worldY >= hboxPtr->worldHeight) {
+ worldY = hboxPtr->worldHeight - hboxPtr->yScrollUnits;
+ }
+ hboxPtr->xOffset = worldX;
+ hboxPtr->yOffset = worldY;
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Entry *entryPtr;
+ int width, height;
+ int x, y;
+ Tree *treePtr;
+ Tk_Anchor anchor;
+ int left, right, top, bottom;
+
+ anchor = TK_ANCHOR_W; /* Default anchor is West */
+ if ((argv[2][0] == '-') && (strcmp(argv[2], "-anchor") == 0)) {
+ if (argc == 3) {
+ Tcl_AppendResult(interp, "missing \"-anchor\" argument",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_GetAnchor(interp, argv[3], &anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ argc -= 2, argv += 2;
+ }
+ if (argc == 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ "see ?-anchor anchor? index\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ treePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[2], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (treePtr == NULL) {
+ return TCL_OK;
+ }
+ if (IsHidden(treePtr)) {
+ ExposeAncestors(treePtr);
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ /*
+ * If the entry wasn't previously exposed, its world coordinates
+ * aren't likely to be valid. So re-compute the layout before
+ * we try to see the viewport to the entry's location.
+ */
+ ComputeLayout(hboxPtr);
+ }
+ entryPtr = treePtr->entryPtr;
+ width = VPORTWIDTH(hboxPtr);
+ height = VPORTHEIGHT(hboxPtr);
+
+ /*
+ * XVIEW: If the entry is left or right of the current view, adjust
+ * the offset. If the entry is nearby, adjust the view just
+ * a bit. Otherwise, center the entry.
+ */
+ left = hboxPtr->xOffset;
+ right = hboxPtr->xOffset + width;
+
+ switch (anchor) {
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_SW:
+ x = 0;
+ break;
+ case TK_ANCHOR_E:
+ case TK_ANCHOR_NE:
+ case TK_ANCHOR_SE:
+ x = entryPtr->worldX + entryPtr->width + LEVELWIDTH(treePtr->level) -
+ width;
+ break;
+ default:
+ if (entryPtr->worldX < left) {
+ x = entryPtr->worldX;
+ } else if ((entryPtr->worldX + entryPtr->width) > right) {
+ x = entryPtr->worldX + entryPtr->width - width;
+ } else {
+ x = hboxPtr->xOffset;
+ }
+ break;
+ }
+ /*
+ * YVIEW: If the entry is above or below the current view, adjust
+ * the offset. If the entry is nearby, adjust the view just
+ * a bit. Otherwise, center the entry.
+ */
+ top = hboxPtr->yOffset;
+ bottom = hboxPtr->yOffset + height;
+
+ switch (anchor) {
+ case TK_ANCHOR_N:
+ y = hboxPtr->yOffset;
+ break;
+ case TK_ANCHOR_NE:
+ case TK_ANCHOR_NW:
+ y = entryPtr->worldY - (height / 2);
+ break;
+ case TK_ANCHOR_S:
+ case TK_ANCHOR_SE:
+ case TK_ANCHOR_SW:
+ y = entryPtr->worldY + entryPtr->height - height;
+ break;
+ default:
+ if (entryPtr->worldY < top) {
+ y = entryPtr->worldY;
+ } else if ((entryPtr->worldY + entryPtr->height) > bottom) {
+ y = entryPtr->worldY + entryPtr->height - height;
+ } else {
+ y = hboxPtr->yOffset;
+ }
+ break;
+ }
+ if ((y != hboxPtr->yOffset) || (x != hboxPtr->xOffset)) {
+ hboxPtr->xOffset = x;
+ hboxPtr->yOffset = y;
+ hboxPtr->flags |= (HIERBOX_SCROLL | HIERBOX_LAYOUT);
+ }
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AnchorOpOp --
+ *
+ * Sets the selection anchor to the element given by a index.
+ * The selection anchor is the end of the selection that is fixed
+ * while dragging out a selection with the mouse. The index
+ * "anchor" may be used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AnchorOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *nodePtr;
+
+ nodePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[3], &nodePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hboxPtr->selAnchorPtr = nodePtr;
+ if (nodePtr != NULL) {
+ Tcl_SetResult(interp, NodeToString(hboxPtr, nodePtr), TCL_VOLATILE);
+ }
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClearallOpOp
+ *
+ * Clears the entire selection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearallOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ ClearSelection(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IncludesOpOp
+ *
+ * Returns 1 if the element indicated by index is currently
+ * selected, 0 if it isn't.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IncludesOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *treePtr;
+
+ treePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[3], &treePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (treePtr != NULL) {
+ int bool;
+
+ bool = IsSelected(hboxPtr, treePtr);
+ Blt_SetBooleanResult(interp, bool);
+ }
+ return TCL_OK;
+}
+
+/*----------------------------------------------------------------------
+ *
+ * MarkOpOp --
+ *
+ * Sets the selection mark to the element given by a index. The
+ * selection mark is the end of the selection that is not fixed
+ * while dragging out a selection with the mouse. The index
+ * "mark" may be used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MarkOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *nodePtr;
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Tree *selectPtr;
+
+ nodePtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[3], &nodePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (hboxPtr->selAnchorPtr == NULL) {
+ Tcl_AppendResult(interp, "selection anchor must be set first",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /* Deselect entry from the list all the way back to the anchor. */
+ for (linkPtr = Blt_ChainLastLink(&(hboxPtr->selectChain));
+ linkPtr != NULL; linkPtr = nextPtr) {
+ nextPtr = Blt_ChainPrevLink(linkPtr);
+ selectPtr = Blt_ChainGetValue(linkPtr);
+ if (selectPtr == hboxPtr->selAnchorPtr) {
+ break;
+ }
+ DeselectEntry(hboxPtr, selectPtr);
+ }
+ if (nodePtr != NULL) {
+ hboxPtr->flags &= ~SELECTION_MASK;
+ hboxPtr->flags |= SELECTION_SET;
+ SelectRange(hboxPtr, hboxPtr->selAnchorPtr, nodePtr);
+ hboxPtr->flags &= ~SELECTION_MASK;
+ Tcl_SetResult(interp, NodeToString(hboxPtr, nodePtr), TCL_VOLATILE);
+ }
+ EventuallyRedraw(hboxPtr);
+ if (hboxPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PresentOpOp
+ *
+ * Indicates if there is a selection present.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PresentOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int bool;
+
+ bool = (Blt_ChainGetLength(&(hboxPtr->selectChain)) > 0);
+ Blt_SetBooleanResult(interp, bool);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectOpOp
+ *
+ * Selects, deselects, or toggles all of the elements in the
+ * range between first and last, inclusive, without affecting the
+ * selection state of elements outside that range.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tree *firstPtr, *lastPtr;
+
+ hboxPtr->flags &= ~SELECTION_MASK;
+ switch (argv[2][0]) {
+ case 's':
+ hboxPtr->flags |= SELECTION_SET;
+ break;
+ case 'c':
+ hboxPtr->flags |= SELECTION_CLEAR;
+ break;
+ case 't':
+ hboxPtr->flags |= SELECTION_TOGGLE;
+ break;
+ }
+
+ if (StringToNode(hboxPtr, argv[3], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((IsHidden(firstPtr)) && !(hboxPtr->flags & SELECTION_CLEAR)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"", argv[3], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ lastPtr = firstPtr;
+ if (argc > 4) {
+ if (StringToNode(hboxPtr, argv[4], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((IsHidden(lastPtr)) && !(hboxPtr->flags & SELECTION_CLEAR)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"", argv[4],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (firstPtr == lastPtr) {
+ SelectNode(hboxPtr, firstPtr);
+ } else {
+ SelectRange(hboxPtr, firstPtr, lastPtr);
+ }
+ hboxPtr->flags &= ~SELECTION_MASK;
+ if (hboxPtr->flags & SELECTION_EXPORT) {
+ Tk_OwnSelection(hboxPtr->tkwin, XA_PRIMARY, LostSelection, hboxPtr);
+ }
+ EventuallyRedraw(hboxPtr);
+ if (hboxPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ * This procedure handles the individual options for text
+ * selections. The selected text is designated by start and end
+ * indices into the text pool. The selected segment has both a
+ * anchored and unanchored ends.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec selectionOperSpecs[] =
+{
+ {"anchor", 1, (Blt_Op)AnchorOpOp, 4, 4, "index",},
+ {"clear", 5, (Blt_Op)SelectOpOp, 4, 5, "firstIndex ?lastIndex?",},
+ {"clearall", 6, (Blt_Op)ClearallOpOp, 3, 3, "",},
+ {"includes", 2, (Blt_Op)IncludesOpOp, 4, 4, "index",},
+ {"mark", 1, (Blt_Op)MarkOpOp, 4, 4, "index",},
+ {"present", 1, (Blt_Op)PresentOpOp, 3, 3, "",},
+ {"set", 1, (Blt_Op)SelectOpOp, 4, 5, "firstIndex ?lastIndex?",},
+ {"toggle", 1, (Blt_Op)SelectOpOp, 4, 5, "firstIndex ?lastIndex?",},
+};
+static int nSelectionSpecs = sizeof(selectionOperSpecs) / sizeof(Blt_OpSpec);
+
+static int
+SelectionOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nSelectionSpecs, selectionOperSpecs, BLT_OP_ARG2,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (hboxPtr, interp, argc, argv);
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+SortOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int length;
+ Tree *rootPtr;
+ register int i;
+ unsigned int flags;
+
+ flags = 0;
+ hboxPtr->sortCmd = NULL;
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ break; /* Found start of indices */
+ }
+ length = strlen(argv[i]);
+ if ((length > 1) && (strncmp(argv[i], "-recurse", length) == 0)) {
+ flags |= APPLY_RECURSE;
+ } else if ((length > 1) && (strncmp(argv[i], "-command", length) ==0)) {
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp, "\"-command\" must be",
+ " followed by comparison command", (char *)NULL);
+ return TCL_ERROR;
+ }
+ i++;
+ hboxPtr->sortCmd = argv[i];
+ } else if ((argv[i][1] == '-') && (argv[i][2] == '\0')) {
+ break; /* Allow first index to start with a '-' */
+ } else {
+ Tcl_AppendResult(interp, "bad switch \"", argv[i],
+ "\": must be -command or -recurse", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ for ( /*empty*/ ; i < argc; i++) {
+ if (StringToNode(hboxPtr, argv[i], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ApplyToTree(hboxPtr, rootPtr, SortNode, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ hboxPtr->flags |= HIERBOX_LAYOUT;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ToggleOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tree *rootPtr;
+ int result;
+
+ rootPtr = hboxPtr->focusPtr;
+ if (GetNode(hboxPtr, argv[2], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (rootPtr == NULL) {
+ return TCL_OK;
+ }
+ if (rootPtr->entryPtr->flags & ENTRY_OPEN) {
+ PruneSelection(hboxPtr, rootPtr);
+ if (IsAncestor(rootPtr, hboxPtr->focusPtr)) {
+ hboxPtr->focusPtr = rootPtr;
+ Blt_SetFocusItem(hboxPtr->bindTable, hboxPtr->focusPtr);
+ }
+ if (IsAncestor(rootPtr, hboxPtr->selAnchorPtr)) {
+ hboxPtr->selAnchorPtr = NULL;
+ }
+ result = CloseNode(hboxPtr, rootPtr);
+ } else {
+ result = OpenNode(hboxPtr, rootPtr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hboxPtr->flags |= (HIERBOX_LAYOUT | HIERBOX_SCROLL);
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+static int
+XViewOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int width, worldWidth;
+
+ width = VPORTWIDTH(hboxPtr);
+ worldWidth = hboxPtr->worldWidth;
+ if (argc == 2) {
+ double fract;
+
+ /*
+ * Note that we are bounding the fractions between 0.0 and 1.0
+ * to support the "canvas"-style of scrolling.
+ */
+ fract = (double)hboxPtr->xOffset / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(hboxPtr->xOffset + width) / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(hboxPtr->xOffset),
+ worldWidth, width, hboxPtr->xScrollUnits, hboxPtr->scrollMode)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hboxPtr->flags |= HIERBOX_XSCROLL;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+static int
+YViewOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int height, worldHeight;
+
+ height = VPORTHEIGHT(hboxPtr);
+ worldHeight = hboxPtr->worldHeight;
+ if (argc == 2) {
+ double fract;
+
+ /* Report first and last fractions */
+ fract = (double)hboxPtr->yOffset / worldHeight;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(hboxPtr->yOffset + height) / worldHeight;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(hboxPtr->yOffset),
+ worldHeight, height, hboxPtr->yScrollUnits, hboxPtr->scrollMode)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hboxPtr->flags |= HIERBOX_SCROLL;
+ EventuallyRedraw(hboxPtr);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * HierboxInstCmd --
+ *
+ * This procedure is invoked to process the "hierbox" command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec operSpecs[] =
+{
+ {"bbox", 2, (Blt_Op)BboxOp, 2, 0, "index...",},
+ {"bind", 2, (Blt_Op)BindOp, 3, 5, "tagName ?sequence command?",},
+ {"button", 2, (Blt_Op)ButtonOp, 2, 0, "args",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"close", 2, (Blt_Op)CloseOp, 2, 0, "?-recurse? index...",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+ {"curselection", 2, (Blt_Op)CurselectionOp, 2, 2, "",},
+ {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "?-recurse? index ?index...?",},
+ {"entry", 1, (Blt_Op)EntryOp, 2, 0, "oper args",},
+ {"find", 2, (Blt_Op)FindOp, 2, 0, "?flags...? ?firstIndex lastIndex?",},
+ {"focus", 2, (Blt_Op)FocusOp, 3, 3, "index",},
+ {"get", 1, (Blt_Op)GetOp, 2, 0, "?-full? index ?index...?",},
+ {"hide", 1, (Blt_Op)HideOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?index...?",},
+ {"index", 3, (Blt_Op)IndexOp, 3, 5, "?-at index? string",},
+ {"insert", 3, (Blt_Op)InsertOp, 3, 0, "?-at index? position label ?label...? ?option value?",},
+ {"move", 1, (Blt_Op)MoveOp, 5, 5, "index into|before|after index",},
+ {"nearest", 1, (Blt_Op)NearestOp, 4, 5, "x y ?varName?",},
+ {"open", 1, (Blt_Op)OpenOp, 2, 0, "?-recurse? index...",},
+ {"range", 1, (Blt_Op)RangeOp, 4, 5, "?-open? firstIndex lastIndex",},
+ {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+ {"see", 3, (Blt_Op)SeeOp, 3, 0, "?-anchor anchor? index",},
+ {"selection", 3, (Blt_Op)SelectionOp, 2, 0, "oper args",},
+ {"show", 2, (Blt_Op)ShowOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?index...?",},
+ {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-recurse? ?-command string? index...",},
+ {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "index",},
+ {"xview", 1, (Blt_Op)XViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+ {"yview", 1, (Blt_Op)YViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+};
+
+static int nSpecs = sizeof(operSpecs) / sizeof(Blt_OpSpec);
+
+static int
+HierboxInstCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ Hierbox *hboxPtr = clientData;
+ int result;
+
+ proc = Blt_GetOp(interp, nSpecs, operSpecs, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(hboxPtr);
+ result = (*proc) (hboxPtr, interp, argc, argv);
+ Tcl_Release(hboxPtr);
+ return result;
+}
+
+int
+Blt_HierboxInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "hierbox", HierboxCmd,
+ };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#ifdef notdef
+
+/* Selection Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectTextBlock --
+ *
+ * Modify the selection by moving its un-anchored end. This
+ * could make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectTextBlock(hboxPtr, to)
+ Hierbox *hboxPtr; /* Information about widget. */
+ int to; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int first, last;
+
+ /* If the anchor hasn't been set yet, assume the beginning of the label. */
+ if (editPtr->selAnchor < 0) {
+ editPtr->selAnchor = 0;
+ }
+ if (editPtr->selAnchor <= to) {
+ first = editPtr->selAnchor;
+ last = to;
+ } else {
+ first = to;
+ last = editPtr->selAnchor;
+ }
+ if ((editPtr->selFirst != first) || (editPtr->selLast != last)) {
+ editPtr->selFirst = first, editPtr->selLast = last;
+ EventuallyRedraw(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClearOpOp --
+ *
+ * Clears the selection from the label.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClearOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+
+ if (editPtr->selFirst != -1) {
+ editPtr->selFirst = editPtr->selLast = -1;
+ EventuallyRedraw(hboxPtr);
+ }
+ return TCL_OK;
+}
+
+static int
+FromOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int from;
+
+ if (GetLabelIndex(hboxPtr, argv[3], &from) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ editPtr->selAnchor = from;
+ return TCL_OK;
+}
+
+static int
+PresentOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ Tcl_AppendResult(interp, (editPtr->selFirst != -1) ? "0" : "1", (char *)NULL);
+ return TCL_OK;
+}
+
+static int
+AdjustOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int adjust;
+
+ if (GetLabelIndex(hboxPtr, argv[3], &adjust) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ half1 = (editPtr->selFirst + editPtr->selLast) / 2;
+ half2 = (editPtr->selFirst + editPtr->selLast + 1) / 2;
+ if (adjust < half1) {
+ editPtr->selAnchor = editPtr->selLast;
+ } else if (adjust > half2) {
+ editPtr->selAnchor = editPtr->selFirst;
+ }
+ result = SelectTextBlock(hboxPtr, adjust);
+ return TCL_OK;
+}
+
+static int
+ToOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int to;
+
+ if (GetLabelIndex(hboxPtr, argv[3], &to) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ SelectTextBlock(hboxPtr, to);
+ return TCL_OK;
+}
+
+static int
+RangeOpOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TextEdit *editPtr = &(hboxPtr->labelEdit);
+ int first, last;
+
+ if (GetLabelIndex(hboxPtr, argv[4], &first) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetLabelIndex(hboxPtr, argv[5], &last) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ editPtr->selAnchor = first;
+ return SelectTextBlock(hboxPtr, last);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ * This procedure handles the individual options for text
+ * selections. The selected text is designated by a starting
+ * and terminating index that points into the text. The selected
+ * segment has both a anchored and unanchored ends. The following
+ * selection operations are implemented:
+ *
+ * adjust index Resets either the first or last index
+ * of the selection.
+ * clear Clears the selection. Sets first/last
+ * indices to -1.
+ * from index Sets the index of the selection anchor.
+ * present Return "1" if a selection is available,
+ * "0" otherwise.
+ * range first last Sets the selection.
+ * to index Sets the index of the un-anchored end.
+
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec entrySelectionOperSpecs[] =
+{
+ {"adjust", 2, (Blt_Op)AdjustOpOp, 5, 5, "index",},
+ {"clear", 2, (Blt_Op)ClearOpOp, 4, 4, "",},
+ {"from", 1, (Blt_Op)FromOpOp, 5, 5, "index",},
+ {"present", 1, (Blt_Op)PresentOpOp, 4, 4, "",},
+ {"range", 1, (Blt_Op)RangeOpOp, 4, 5, "firstIndex lastIndex",},
+ {"to", 1, (Blt_Op)ToOpOp, 5, 5, "index",},
+};
+static int nEntrySelectionSpecs =
+sizeof(entrySelectionOperSpecs) / sizeof(Blt_OpSpec);
+
+static int
+EntrySelectionOp(hboxPtr, interp, argc, argv)
+ Hierbox *hboxPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nSelectionSpecs, selectionOperSpecs, BLT_OP_ARG2,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (hboxPtr, interp, argc, argv);
+ return result;
+}
+
+#endif
+
+#endif /* NO_HIERBOX */
diff --git a/blt/src/bltHtext.c b/blt/src/bltHtext.c
new file mode 100644
index 00000000000..d0c3f36501e
--- /dev/null
+++ b/blt/src/bltHtext.c
@@ -0,0 +1,4496 @@
+/*
+ * bltHtext.c --
+ *
+ * This module implements a hypertext widget for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "htext" widget was created by George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 1) Fix scroll unit round off errors.
+ *
+ * 2) Better error checking.
+ *
+ * 3) Use html format.
+ *
+ * 4) The dimension of cavities using -relwidth and -relheight
+ * should be 0 when computing initial estimates for the size
+ * of the virtual text.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_HTEXT
+#include <bltChain.h>
+#include <bltHash.h>
+#include "bltTile.h"
+
+#include <sys/stat.h>
+#include <X11/Xatom.h>
+
+#define DEF_LINES_ALLOC 512 /* Default block of lines allocated */
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+/*
+ * Justify option values
+ */
+typedef enum {
+ JUSTIFY_CENTER, JUSTIFY_TOP, JUSTIFY_BOTTOM
+} Justify;
+
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltTileOption;
+
+static int StringToWidth _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static int StringToHeight _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+static char *WidthHeightToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProc));
+
+static Tk_CustomOption widthOption =
+{
+ StringToWidth, WidthHeightToString, (ClientData)0
+};
+
+static Tk_CustomOption heightOption =
+{
+ StringToHeight, WidthHeightToString, (ClientData)0
+};
+
+static int StringToJustify _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *JustifyToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption justifyOption =
+{
+ StringToJustify, JustifyToString, (ClientData)0
+};
+
+
+static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window));
+static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window));
+
+static Tk_GeomMgr htextMgrInfo =
+{
+ "htext", /* Name of geometry manager used by winfo */
+ EmbeddedWidgetGeometryProc, /* Procedure to for new geometry requests */
+ EmbeddedWidgetCustodyProc, /* Procedure when window is taken away */
+};
+
+
+/*
+ * Line --
+ *
+ * Structure to contain the contents of a single line of text and
+ * the widgets on that line.
+ *
+ * Individual lines are not configurable, although changes to the
+ * size of widgets do effect its values.
+ */
+typedef struct {
+ int offset; /* Offset of line from y-origin (0) in
+ * world coordinates */
+ int baseline; /* Baseline y-coordinate of the text */
+ short int width, height; /* Dimensions of the line */
+ int textStart, textEnd; /* Start and end indices of characters
+ * forming the line in the text array */
+ Blt_Chain *chainPtr; /* Chain of embedded widgets on the line of
+ * text */
+} Line;
+
+typedef struct {
+ int textStart;
+ int textEnd;
+} Segment;
+
+typedef struct {
+ int x, y;
+} Position;
+
+/*
+ * Hypertext widget.
+ */
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+
+ Tcl_Command cmdToken; /* Token for htext's widget command. */
+ int flags;
+
+ /* User-configurable fields */
+
+ XColor *normalFg, *normalBg;
+ Tk_Font font; /* Font for normal text. May affect the size
+ * of the viewport if the width/height is
+ * specified in columns/rows */
+ GC drawGC; /* Graphics context for normal text */
+ Blt_Tile tile;
+ int tileOffsetPage; /* Set tile offset to top of page instead
+ * of toplevel window */
+ GC fillGC; /* GC for clearing the window in the
+ * designated background color. The
+ * background color is the foreground
+ * attribute in GC. */
+
+ int nRows, nColumns; /* # of characters of the current font
+ * for a row or column of the viewport.
+ * Used to determine the width and height
+ * of the text window (i.e. viewport) */
+ int reqWidth, reqHeight; /* Requested dimensions of the viewport */
+ int maxWidth, maxHeight; /* Maximum dimensions allowed for the viewport,
+ * regardless of the size of the text */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ char *fileName; /* If non-NULL, indicates the name of a
+ * hypertext file to be read into the widget.
+ * If NULL, the *text* field is considered
+ * instead */
+ char *text; /* Hypertext to be loaded into the widget. This
+ * value is ignored if *fileName* is non-NULL */
+ int specChar; /* Special character designating a TCL
+ * command block in a hypertext file. */
+ int leader; /* # of pixels between lines */
+
+ char *yScrollCmdPrefix; /* Name of vertical scrollbar to invoke */
+ int yScrollUnits; /* # of pixels per vertical scroll */
+ char *xScrollCmdPrefix; /* Name of horizontal scroll bar to invoke */
+ int xScrollUnits; /* # of pixels per horizontal scroll */
+
+ int reqLineNum; /* Line requested by "goto" command */
+
+ /*
+ * The view port is the width and height of the window and the
+ * origin of the viewport (upper left corner) in world coordinates.
+ */
+ int worldWidth, worldHeight;/* Size of view text in world coordinates */
+ int xOffset, yOffset; /* Position of viewport in world coordinates */
+
+ int pendingX, pendingY; /* New upper-left corner (origin) of
+ * the viewport (not yet posted) */
+
+ int first, last; /* Range of lines displayed */
+
+ int lastWidth, lastHeight;
+ /* Last known size of the window: saved to
+ * recognize when the viewport is resized. */
+
+ Blt_HashTable widgetTable; /* Table of embedded widgets. */
+
+ /*
+ * Selection display information:
+ */
+ Tk_3DBorder selBorder; /* Border and background color */
+ int selBorderWidth; /* Border width */
+ XColor *selFgColor; /* Text foreground color */
+ GC selectGC; /* GC for drawing selected text */
+ int selAnchor; /* Fixed end of selection
+ * (i.e. "selection to" operation will
+ * use this as one end of the selection).*/
+ int selFirst; /* The index of first character in the
+ * text array selected */
+ int selLast; /* The index of the last character selected */
+ int exportSelection; /* Non-zero means to export the internal text
+ * selection to the X server. */
+ char *takeFocus;
+
+ /*
+ * Scanning information:
+ */
+ XPoint scanMark; /* Anchor position of scan */
+ XPoint scanPt; /* x,y position where the scan started. */
+
+ char *charArr; /* Pool of characters representing the text
+ * to be displayed */
+ int nChars; /* Length of the text pool */
+
+ Line *lineArr; /* Array of pointers to text lines */
+ int nLines; /* # of line entered into array. */
+ int arraySize; /* Size of array allocated. */
+
+} HText;
+
+/*
+ * Bit flags for the hypertext widget:
+ */
+#define REDRAW_PENDING (1<<0) /* A DoWhenIdle handler has already
+ * been queued to redraw the window */
+#define IGNORE_EXPOSURES (1<<1) /* Ignore exposure events in the text
+ * window. Potentially many expose
+ * events can occur while rearranging
+ * embedded widgets during a single call to
+ * the DisplayText. */
+
+#define REQUEST_LAYOUT (1<<4) /* Something has happened which
+ * requires the layout of text and
+ * embedded widget positions to be
+ * recalculated. The following
+ * actions may cause this:
+ *
+ * 1) the contents of the hypertext
+ * has changed by either the -file or
+ * -text options.
+ *
+ * 2) a text attribute has changed
+ * (line spacing, font, etc)
+ *
+ * 3) a embedded widget has been resized or
+ * moved.
+ *
+ * 4) a widget configuration option has
+ * changed.
+ */
+#define TEXT_DIRTY (1<<5) /* The layout was recalculated and the
+ * size of the world (text layout) has
+ * changed. */
+#define GOTO_PENDING (1<<6) /* Indicates the starting text line
+ * number has changed. To be reflected
+ * the next time the widget is redrawn. */
+#define WIDGET_APPENDED (1<<7) /* Indicates a embedded widget has just
+ * been appended to the text. This is
+ * used to determine when to add a
+ * space to the text array */
+
+#define DEF_HTEXT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_HTEXT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_HTEXT_CURSOR "arrow"
+#define DEF_HTEXT_EXPORT_SELECTION "1"
+
+#define DEF_HTEXT_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_HTEXT_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_HTEXT_FILE_NAME (char *)NULL
+#define DEF_HTEXT_FONT STD_FONT
+#define DEF_HTEXT_HEIGHT "0"
+#define DEF_HTEXT_LINE_SPACING "1"
+#define DEF_HTEXT_MAX_HEIGHT (char *)NULL
+#define DEF_HTEXT_MAX_WIDTH (char *)NULL
+#define DEF_HTEXT_SCROLL_UNITS "10"
+#define DEF_HTEXT_SPEC_CHAR "0x25"
+#define DEF_HTEXT_SELECT_BORDER_WIDTH STD_SELECT_BORDERWIDTH
+#define DEF_HTEXT_SELECT_BG_COLOR STD_COLOR_SELECT_BG
+#define DEF_HTEXT_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_HTEXT_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_HTEXT_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_HTEXT_TAKE_FOCUS "1"
+#define DEF_HTEXT_TEXT (char *)NULL
+#define DEF_HTEXT_TILE_OFFSET "1"
+#define DEF_HTEXT_WIDTH "0"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_HTEXT_BG_COLOR, Tk_Offset(HText, normalBg), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_HTEXT_BG_MONO, Tk_Offset(HText, normalBg), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_HTEXT_CURSOR, Tk_Offset(HText, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection", "ExportSelection",
+ DEF_HTEXT_EXPORT_SELECTION, Tk_Offset(HText, exportSelection), 0},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_STRING, "-file", "file", "File",
+ DEF_HTEXT_FILE_NAME, Tk_Offset(HText, fileName), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_HTEXT_FONT, Tk_Offset(HText, font), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_HTEXT_FG_COLOR, Tk_Offset(HText, normalFg), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_HTEXT_FG_MONO, Tk_Offset(HText, normalFg), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_HTEXT_HEIGHT, Tk_Offset(HText, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &heightOption},
+ {TK_CONFIG_CUSTOM, "-linespacing", "lineSpacing", "LineSpacing",
+ DEF_HTEXT_LINE_SPACING, Tk_Offset(HText, leader),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-maxheight", "maxHeight", "MaxHeight",
+ DEF_HTEXT_MAX_HEIGHT, Tk_Offset(HText, maxHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-maxwidth", "maxWidth", "MaxWidth",
+ DEF_HTEXT_MAX_WIDTH, Tk_Offset(HText, maxWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_HTEXT_SELECT_BG_MONO, Tk_Offset(HText, selBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_HTEXT_SELECT_BG_COLOR, Tk_Offset(HText, selBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
+ DEF_HTEXT_SELECT_BORDER_WIDTH, Tk_Offset(HText, selBorderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_HTEXT_SELECT_FG_MONO, Tk_Offset(HText, selFgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_HTEXT_SELECT_FG_COLOR, Tk_Offset(HText, selFgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_INT, "-specialchar", "specialChar", "SpecialChar",
+ DEF_HTEXT_SPEC_CHAR, Tk_Offset(HText, specChar), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_HTEXT_TAKE_FOCUS, Tk_Offset(HText, takeFocus),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(HText, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_BOOLEAN, "-tileoffset", "tileOffset", "TileOffset",
+ DEF_HTEXT_TILE_OFFSET, Tk_Offset(HText, tileOffsetPage), 0},
+ {TK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_HTEXT_TEXT, Tk_Offset(HText, text), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_HTEXT_WIDTH, Tk_Offset(HText, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &widthOption},
+ {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(HText, xScrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-xscrollunits", "xScrollUnits", "ScrollUnits",
+ DEF_HTEXT_SCROLL_UNITS, Tk_Offset(HText, xScrollUnits),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(HText, yScrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-yscrollunits", "yScrollUnits", "yScrollUnits",
+ DEF_HTEXT_SCROLL_UNITS, Tk_Offset(HText, yScrollUnits),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+typedef struct {
+ HText *htPtr; /* Pointer to parent's Htext structure */
+ Tk_Window tkwin; /* Widget window */
+ int flags;
+
+ int x, y; /* Origin of embedded widget in text */
+
+ int cavityWidth, cavityHeight; /* Dimensions of the cavity
+ * surrounding the embedded widget */
+ /*
+ * Dimensions of the embedded widget. Compared against actual
+ * embedded widget sizes when checking for resizing.
+ */
+ int winWidth, winHeight;
+
+ int precedingTextEnd; /* Index (in charArr) of the the last
+ * character immediatedly preceding
+ * the embedded widget */
+ int precedingTextWidth; /* Width of normal text preceding widget. */
+
+ Tk_Anchor anchor;
+ Justify justify; /* Justification of region wrt to line */
+
+ /*
+ * Requested dimensions of the cavity (includes padding). If non-zero,
+ * it overrides the calculated dimension of the cavity.
+ */
+ int reqCavityWidth, reqCavityHeight;
+
+ /*
+ * Relative dimensions of cavity wrt the size of the viewport. If
+ * greater than 0.0.
+ */
+ double relCavityWidth, relCavityHeight;
+
+ int reqWidth, reqHeight; /* If non-zero, overrides the requested
+ * dimension of the embedded widget */
+
+ double relWidth, relHeight; /* Relative dimensions of embedded
+ * widget wrt the size of the viewport */
+
+ Blt_Pad padX, padY; /* Extra padding to frame around */
+
+ int ipadX, ipadY; /* internal padding for window */
+
+ int fill; /* Fill style flag */
+
+} EmbeddedWidget;
+
+/*
+ * Flag bits embedded widgets:
+ */
+#define WIDGET_VISIBLE (1<<2) /* Widget is currently visible in the
+ * viewport. */
+#define WIDGET_NOT_CHILD (1<<3) /* Widget is not a child of hypertext. */
+/*
+ * Defaults for embedded widgets:
+ */
+#define DEF_WIDGET_ANCHOR "center"
+#define DEF_WIDGET_FILL "none"
+#define DEF_WIDGET_HEIGHT "0"
+#define DEF_WIDGET_JUSTIFY "center"
+#define DEF_WIDGET_PAD_X "0"
+#define DEF_WIDGET_PAD_Y "0"
+#define DEF_WIDGET_REL_HEIGHT "0.0"
+#define DEF_WIDGET_REL_WIDTH "0.0"
+#define DEF_WIDGET_WIDTH "0"
+
+static Tk_ConfigSpec widgetConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_ANCHOR, Tk_Offset(EmbeddedWidget, anchor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_FILL, Tk_Offset(EmbeddedWidget, fill),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-cavityheight", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_HEIGHT, Tk_Offset(EmbeddedWidget, reqCavityHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-cavitywidth", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_WIDTH, Tk_Offset(EmbeddedWidget, reqCavityWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_HEIGHT, Tk_Offset(EmbeddedWidget, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-justify", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_JUSTIFY, Tk_Offset(EmbeddedWidget, justify),
+ TK_CONFIG_DONT_SET_DEFAULT, &justifyOption},
+ {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_PAD_X, Tk_Offset(EmbeddedWidget, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_PAD_Y, Tk_Offset(EmbeddedWidget, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_DOUBLE, "-relcavityheight", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_REL_HEIGHT, Tk_Offset(EmbeddedWidget, relCavityHeight),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-relcavitywidth", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_REL_WIDTH, Tk_Offset(EmbeddedWidget, relCavityWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-relheight", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_REL_HEIGHT, Tk_Offset(EmbeddedWidget, relHeight),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-relwidth", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_REL_WIDTH, Tk_Offset(EmbeddedWidget, relWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
+ DEF_WIDGET_WIDTH, Tk_Offset(EmbeddedWidget, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+
+/* Forward Declarations */
+static void DestroyText _ANSI_ARGS_((DestroyData dataPtr));
+static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void DisplayText _ANSI_ARGS_((ClientData clientData));
+static void TextDeleteCmdProc _ANSI_ARGS_((ClientData clientdata));
+
+#ifdef __STDC__
+static Tcl_VarTraceProc TextVarProc;
+static Blt_TileChangedProc TileChangedProc;
+static Tk_LostSelProc TextLostSelection;
+static Tk_SelectionProc TextSelectionProc;
+static Tk_EventProc TextEventProc;
+static Tcl_CmdProc TextWidgetCmd;
+static Tcl_CmdProc TextCmd;
+#endif /* __STDC__ */
+/* end of Forward Declarations */
+
+
+ /* Custom options */
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToJustify --
+ *
+ * Converts the justification string into its numeric
+ * representation. This configuration option affects how the
+ * embedded widget is positioned with respect to the line on which
+ * it sits.
+ *
+ * Valid style strings are:
+ *
+ * "top" Uppermost point of region is top of the line's
+ * text
+ * "center" Center point of region is line's baseline.
+ * "bottom" Lowermost point of region is bottom of the
+ * line's text
+ *
+ * Returns:
+ * A standard Tcl result. If the value was not valid
+ *
+ *---------------------------------------------------------------------- */
+/*ARGSUSED*/
+static int
+StringToJustify(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Justification string */
+ char *widgRec; /* Structure record */
+ int offset; /* Offset of justify in record */
+{
+ Justify *justPtr = (Justify *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'c') && (strncmp(string, "center", length) == 0)) {
+ *justPtr = JUSTIFY_CENTER;
+ } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+ *justPtr = JUSTIFY_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+ *justPtr = JUSTIFY_BOTTOM;
+ } else {
+ Tcl_AppendResult(interp, "bad justification argument \"", string,
+ "\": should be \"center\", \"top\", or \"bottom\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfJustify --
+ *
+ * Returns the justification style string based upon the value.
+ *
+ * Results:
+ * The static justification style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfJustify(justify)
+ Justify justify;
+{
+ switch (justify) {
+ case JUSTIFY_CENTER:
+ return "center";
+ case JUSTIFY_TOP:
+ return "top";
+ case JUSTIFY_BOTTOM:
+ return "bottom";
+ default:
+ return "unknown justification value";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * JustifyToString --
+ *
+ * Returns the justification style string based upon the value.
+ *
+ * Results:
+ * The justification style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+JustifyToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Structure record */
+ int offset; /* Offset of justify record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Justify justify = *(Justify *)(widgRec + offset);
+
+ return NameOfJustify(justify);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetScreenDistance --
+ *
+ * Converts the given string into the screen distance or number
+ * of characters. The valid formats are
+ *
+ * N - pixels Nm - millimeters
+ * Ni - inches Np - pica
+ * Nc - centimeters N# - number of characters
+ *
+ * where N is a non-negative decimal number.
+ *
+ * Results:
+ * A standard Tcl result. The screen distance and the number of
+ * characters are returned. If the string can't be converted,
+ * TCL_ERROR is returned and interp->result will contain an error
+ * message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetScreenDistance(interp, tkwin, string, sizePtr, countPtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *string;
+ int *sizePtr;
+ int *countPtr;
+{
+ int nPixels, nChars;
+ char *endPtr; /* Pointer to last character scanned */
+ double value;
+ int rounded;
+
+ value = strtod(string, &endPtr);
+ if (endPtr == string) {
+ Tcl_AppendResult(interp, "bad screen distance \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (value < 0.0) {
+ Tcl_AppendResult(interp, "screen distance \"", string,
+ "\" must be non-negative value", (char *)NULL);
+ return TCL_ERROR;
+ }
+ while (isspace(UCHAR(*endPtr))) {
+ if (*endPtr == '\0') {
+ break;
+ }
+ endPtr++;
+ }
+ nPixels = nChars = 0;
+ rounded = ROUND(value);
+ switch (*endPtr) {
+ case '\0': /* Distance in pixels */
+ nPixels = rounded;
+ break;
+ case '#': /* Number of characters */
+ nChars = rounded;
+ break;
+ default: /* cm, mm, pica, inches */
+ if (Tk_GetPixels(interp, tkwin, string, &rounded) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nPixels = rounded;
+ break;
+ }
+ *sizePtr = nPixels;
+ *countPtr = nChars;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToHeight --
+ *
+ * Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ * values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToHeight(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Not used. */
+{
+ HText *htPtr = (HText *)widgRec;
+ int height, nRows;
+
+ if (GetScreenDistance(interp, tkwin, string, &height, &nRows) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ htPtr->nRows = nRows;
+ htPtr->reqHeight = height;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToWidth --
+ *
+ * Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ * values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToWidth(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ char *string; /* Pixel value string */
+ char *widgRec; /* Widget record */
+ int offset; /* Not used. */
+{
+ HText *htPtr = (HText *)widgRec;
+ int width, nColumns;
+
+ if (GetScreenDistance(interp, tkwin, string, &width,
+ &nColumns) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ htPtr->nColumns = nColumns;
+ htPtr->reqWidth = width;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WidthHeightToString --
+ *
+ * Returns the string representing the positive pixel size.
+ *
+ * Results:
+ * The pixel size string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+WidthHeightToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Row/column structure record */
+ int offset; /* Offset of fill in Partition record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int pixels = *(int *)(widgRec + offset);
+ char *result;
+ char string[200];
+
+ sprintf(string, "%d", pixels);
+ result = Blt_Strdup(string);
+ if (result == NULL) {
+ return "out of memory";
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+/* General routines */
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the text window at the next idle
+ * point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn. This doesn't
+ * seem to hurt performance noticeably, but if it does then this
+ * could be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(htPtr)
+ HText *htPtr; /* Information about widget. */
+{
+ if ((htPtr->tkwin != NULL) && !(htPtr->flags & REDRAW_PENDING)) {
+ htPtr->flags |= REDRAW_PENDING;
+ Tcl_DoWhenIdle(DisplayText, htPtr);
+ }
+}
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * ResizeArray --
+ *
+ * Reallocates memory to the new size given. New memory
+ * is also cleared (zeros).
+ *
+ * Results:
+ * Returns a pointer to the new object or NULL if an error occurred.
+ *
+ * Side Effects:
+ * Memory is re/allocated.
+ *
+ * --------------------------------------------------------------------
+ */
+static int
+ResizeArray(arrayPtr, elemSize, newSize, prevSize)
+ char **arrayPtr;
+ int elemSize;
+ int newSize;
+ int prevSize;
+{
+ char *newPtr;
+
+ if (newSize == prevSize) {
+ return TCL_OK;
+ }
+ if (newSize == 0) { /* Free entire array */
+ Blt_Free(*arrayPtr);
+ *arrayPtr = NULL;
+ return TCL_OK;
+ }
+ newPtr = Blt_Calloc(elemSize, newSize);
+ if (newPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if ((prevSize > 0) && (*arrayPtr != NULL)) {
+ int size;
+
+ size = MIN(prevSize, newSize) * elemSize;
+ if (size > 0) {
+ memcpy(newPtr, *arrayPtr, size);
+ }
+ Blt_Free(*arrayPtr);
+ }
+ *arrayPtr = newPtr;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LineSearch --
+ *
+ * Performs a binary search for the line of text located at some
+ * world y-coordinate (not screen y-coordinate). The search is
+ * inclusive of those lines from low to high.
+ *
+ * Results:
+ * Returns the array index of the line found at the given
+ * y-coordinate. If the y-coordinate is outside of the given range
+ * of lines, -1 is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+LineSearch(htPtr, yCoord, low, high)
+ HText *htPtr; /* HText widget */
+ int yCoord; /* Search y-coordinate */
+ int low, high; /* Range of lines to search */
+{
+ int median;
+ Line *linePtr;
+
+ while (low <= high) {
+ median = (low + high) >> 1;
+ linePtr = htPtr->lineArr + median;
+ if (yCoord < linePtr->offset) {
+ high = median - 1;
+ } else if (yCoord >= (linePtr->offset + linePtr->height)) {
+ low = median + 1;
+ } else {
+ return median;
+ }
+ }
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * IndexSearch --
+ *
+ * Try to find what line contains a given text index. Performs
+ * a binary search for the text line which contains the given index.
+ * The search is inclusive of those lines from low and high.
+ *
+ * Results:
+ * Returns the line number containing the given index. If the index
+ * is outside the range of lines, -1 is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+IndexSearch(htPtr, key, low, high)
+ HText *htPtr; /* HText widget */
+ int key; /* Search index */
+ int low, high; /* Range of lines to search */
+{
+ int median;
+ Line *linePtr;
+
+ while (low <= high) {
+ median = (low + high) >> 1;
+ linePtr = htPtr->lineArr + median;
+ if (key < linePtr->textStart) {
+ high = median - 1;
+ } else if (key > linePtr->textEnd) {
+ low = median + 1;
+ } else {
+ return median;
+ }
+ }
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetXYPosIndex --
+ *
+ * Converts a string in the form "@x,y", where x and y are
+ * window coordinates, to a text index.
+ *
+ * Window coordinates are first translated into world coordinates.
+ * Any coordinate outside of the bounds of the virtual text is
+ * silently set the nearest boundary.
+ *
+ * Results:
+ * A standard Tcl result. If "string" is a valid index, then
+ * *indexPtr is filled with the numeric index corresponding.
+ * Otherwise an error message is left in interp->result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetXYPosIndex(htPtr, string, indexPtr)
+ HText *htPtr;
+ char *string;
+ int *indexPtr;
+{
+ int x, y, curX, dummy;
+ int textLength, textStart;
+ int cindex, lindex;
+ Line *linePtr;
+
+ if (Blt_GetXY(htPtr->interp, htPtr->tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Locate the line corresponding to the window y-coordinate position */
+
+ y += htPtr->yOffset;
+ if (y < 0) {
+ lindex = htPtr->first;
+ } else if (y >= htPtr->worldHeight) {
+ lindex = htPtr->last;
+ } else {
+ lindex = LineSearch(htPtr, y, 0, htPtr->nLines - 1);
+ }
+ if (lindex < 0) {
+ Tcl_AppendResult(htPtr->interp, "can't find line at \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ x += htPtr->xOffset;
+ if (x < 0) {
+ x = 0;
+ } else if (x > htPtr->worldWidth) {
+ x = htPtr->worldWidth;
+ }
+ linePtr = htPtr->lineArr + lindex;
+ curX = 0;
+ textStart = linePtr->textStart;
+ textLength = linePtr->textEnd - linePtr->textStart;
+ if (Blt_ChainGetLength(linePtr->chainPtr) > 0) {
+ Blt_ChainLink *linkPtr;
+ int deltaX;
+ EmbeddedWidget *winPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+ deltaX = winPtr->precedingTextWidth + winPtr->cavityWidth;
+ if ((curX + deltaX) > x) {
+ textLength = (winPtr->precedingTextEnd - textStart);
+ break;
+ }
+ curX += deltaX;
+ /*
+ * Skip over the trailing space. It designates the position of
+ * a embedded widget in the text
+ */
+ textStart = winPtr->precedingTextEnd + 1;
+ }
+ }
+ cindex = Tk_MeasureChars(htPtr->font, htPtr->charArr + textStart,
+ textLength, 10000, DEF_TEXT_FLAGS, &dummy);
+ *indexPtr = textStart + cindex;
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseIndex --
+ *
+ * Parse a string representing a text index into numeric
+ * value. A text index can be in one of the following forms.
+ *
+ * "anchor" - anchor position of the selection.
+ * "sel.first" - index of the first character in the selection.
+ * "sel.last" - index of the last character in the selection.
+ * "page.top" - index of the first character on the page.
+ * "page.bottom" - index of the last character on the page.
+ * "@x,y" - x and y are window coordinates.
+ * "number - raw index of text
+ * "line.char" - line number and character position
+ *
+ * Results:
+ * A standard Tcl result. If "string" is a valid index, then
+ * *indexPtr is filled with the corresponding numeric index.
+ * Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+ParseIndex(htPtr, string, indexPtr)
+ HText *htPtr; /* Text for which the index is being
+ * specified. */
+ char *string; /* Numerical index into htPtr's element
+ * list, or "end" to refer to last element. */
+ int *indexPtr; /* Where to store converted relief. */
+{
+ unsigned int length;
+ char c;
+ Tcl_Interp *interp = htPtr->interp;
+
+ length = strlen(string);
+ c = string[0];
+
+ if ((c == 'a') && (strncmp(string, "anchor", length) == 0)) {
+ *indexPtr = htPtr->selAnchor;
+ } else if ((c == 's') && (length > 4)) {
+ if (strncmp(string, "sel.first", length) == 0) {
+ *indexPtr = htPtr->selFirst;
+ } else if (strncmp(string, "sel.last", length) == 0) {
+ *indexPtr = htPtr->selLast;
+ } else {
+ goto badIndex; /* Not a valid index */
+ }
+ if (*indexPtr < 0) {
+ Tcl_AppendResult(interp, "bad index \"", string,
+ "\": nothing selected in \"",
+ Tk_PathName(htPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'p') && (length > 5) &&
+ (strncmp(string, "page.top", length) == 0)) {
+ int first;
+
+ first = htPtr->first;
+ if (first < 0) {
+ first = 0;
+ }
+ *indexPtr = htPtr->lineArr[first].textStart;
+ } else if ((c == 'p') && (length > 5) &&
+ (strncmp(string, "page.bottom", length) == 0)) {
+ *indexPtr = htPtr->lineArr[htPtr->last].textEnd;
+ } else if (c == '@') { /* Screen position */
+ if (GetXYPosIndex(htPtr, string, indexPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ char *period;
+
+ period = strchr(string, '.');
+ if (period == NULL) { /* Raw index */
+ int tindex;
+
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ tindex = htPtr->nChars - 1;
+ } else if (Tcl_GetInt(interp, string, &tindex) != TCL_OK) {
+ goto badIndex;
+ }
+ if (tindex < 0) {
+ tindex = 0;
+ } else if (tindex > (htPtr->nChars - 1)) {
+ tindex = htPtr->nChars - 1;
+ }
+ *indexPtr = tindex;
+ } else {
+ int lindex, cindex, offset;
+ Line *linePtr;
+ int result;
+
+ *period = '\0';
+ result = TCL_OK;
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ lindex = htPtr->nLines - 1;
+ } else {
+ result = Tcl_GetInt(interp, string, &lindex);
+ }
+ *period = '.'; /* Repair index string before returning */
+ if (result != TCL_OK) {
+ goto badIndex; /* Bad line number */
+ }
+ if (lindex < 0) {
+ lindex = 0; /* Silently repair bad line numbers */
+ }
+ if (htPtr->nChars == 0) {
+ *indexPtr = 0;
+ return TCL_OK;
+ }
+ if (lindex >= htPtr->nLines) {
+ lindex = htPtr->nLines - 1;
+ }
+ linePtr = htPtr->lineArr + lindex;
+ cindex = 0;
+ if ((*(period + 1) != '\0')) {
+ string = period + 1;
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ cindex = linePtr->textEnd - linePtr->textStart;
+ } else if (Tcl_GetInt(interp, string, &cindex) != TCL_OK) {
+ goto badIndex;
+ }
+ }
+ if (cindex < 0) {
+ cindex = 0; /* Silently fix bogus indices */
+ }
+ offset = 0;
+ if (htPtr->nChars > 0) {
+ offset = linePtr->textStart + cindex;
+ if (offset > linePtr->textEnd) {
+ offset = linePtr->textEnd;
+ }
+ }
+ *indexPtr = offset;
+ }
+ }
+ if (htPtr->nChars == 0) {
+ *indexPtr = 0;
+ }
+ return TCL_OK;
+
+ badIndex:
+
+ /*
+ * Some of the paths here leave messages in interp->result, so we
+ * have to clear it out before storing our own message.
+ */
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad index \"", string, "\": \
+should be one of the following: anchor, sel.first, sel.last, page.bottom, \
+page.top, @x,y, index, line.char", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GetIndex --
+ *
+ * Get the index from a string representing a text index.
+ *
+ *
+ * Results:
+ * A standard Tcl result. If "string" is a valid index, then
+ * *indexPtr is filled with the numeric index corresponding.
+ * Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+GetIndex(htPtr, string, indexPtr)
+ HText *htPtr; /* Text for which the index is being
+ * specified. */
+ char *string; /* Numerical index into htPtr's element
+ * list, or "end" to refer to last element. */
+ int *indexPtr; /* Where to store converted relief. */
+{
+ int tindex;
+
+ if (ParseIndex(htPtr, string, &tindex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *indexPtr = tindex;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetTextPosition --
+ *
+ * Performs a binary search for the index located on line in
+ * the text. The search is limited to those lines between
+ * low and high inclusive.
+ *
+ * Results:
+ * Returns the line number at the given Y coordinate. If position
+ * does not correspond to any of the lines in the given the set,
+ * -1 is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetTextPosition(htPtr, tindex, lindexPtr, cindexPtr)
+ HText *htPtr;
+ int tindex;
+ int *lindexPtr;
+ int *cindexPtr;
+{
+ int lindex, cindex;
+
+ lindex = cindex = 0;
+ if (htPtr->nChars > 0) {
+ Line *linePtr;
+
+ lindex = IndexSearch(htPtr, tindex, 0, htPtr->nLines - 1);
+ if (lindex < 0) {
+ char string[200];
+
+ sprintf(string, "can't determine line number from index \"%d\"",
+ tindex);
+ Tcl_AppendResult(htPtr->interp, string, (char *)NULL);
+ return TCL_ERROR;
+ }
+ linePtr = htPtr->lineArr + lindex;
+ if (tindex > linePtr->textEnd) {
+ tindex = linePtr->textEnd;
+ }
+ cindex = tindex - linePtr->textStart;
+ }
+ *lindexPtr = lindex;
+ *cindexPtr = cindex;
+ return TCL_OK;
+}
+
+/* EmbeddedWidget Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEmbeddedWidgetWidth --
+ *
+ * Returns the width requested by the embedded widget. The requested
+ * space also includes any internal padding which has been designated
+ * for this window.
+ *
+ * Results:
+ * Returns the requested width of the embedded widget.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetEmbeddedWidgetWidth(winPtr)
+ EmbeddedWidget *winPtr;
+{
+ int width;
+
+ if (winPtr->reqWidth > 0) {
+ width = winPtr->reqWidth;
+ } else if (winPtr->relWidth > 0.0) {
+ width = (int)
+ ((double)Tk_Width(winPtr->htPtr->tkwin) * winPtr->relWidth + 0.5);
+ } else {
+ width = Tk_ReqWidth(winPtr->tkwin);
+ }
+ width += (2 * winPtr->ipadX);
+ return width;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEmbeddedWidgetHeight --
+ *
+ * Returns the height requested by the embedded widget. The requested
+ * space also includes any internal padding which has been designated
+ * for this window.
+ *
+ * Results:
+ * Returns the requested height of the embedded widget.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetEmbeddedWidgetHeight(winPtr)
+ EmbeddedWidget *winPtr;
+{
+ int height;
+
+ if (winPtr->reqHeight > 0) {
+ height = winPtr->reqHeight;
+ } else if (winPtr->relHeight > 0.0) {
+ height = (int)((double)Tk_Height(winPtr->htPtr->tkwin) *
+ winPtr->relHeight + 0.5);
+ } else {
+ height = Tk_ReqHeight(winPtr->tkwin);
+ }
+ height += (2 * winPtr->ipadY);
+ return height;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * EmbeddedWidgetEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on hypertext widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+EmbeddedWidgetEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the embedded widget. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ EmbeddedWidget *winPtr = clientData;
+ HText *htPtr;
+
+ if ((winPtr == NULL) || (winPtr->tkwin == NULL)) {
+ return;
+ }
+ htPtr = winPtr->htPtr;
+
+ if (eventPtr->type == DestroyNotify) {
+ Blt_HashEntry *hPtr;
+ /*
+ * Mark the widget as deleted by dereferencing the Tk window
+ * pointer. Zero out the height and width to collapse the area
+ * used by the widget. Redraw the window only if the widget is
+ * currently visible.
+ */
+ winPtr->htPtr->flags |= REQUEST_LAYOUT;
+ if (Tk_IsMapped(winPtr->tkwin) && (winPtr->flags & WIDGET_VISIBLE)) {
+ EventuallyRedraw(htPtr);
+ }
+ Tk_DeleteEventHandler(winPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, winPtr);
+ hPtr = Blt_FindHashEntry(&(htPtr->widgetTable), (char *)winPtr->tkwin);
+ Blt_DeleteHashEntry(&(htPtr->widgetTable), hPtr);
+ winPtr->cavityWidth = winPtr->cavityHeight = 0;
+ winPtr->tkwin = NULL;
+
+ } else if (eventPtr->type == ConfigureNotify) {
+ /*
+ * EmbeddedWidgets can't request new positions. Worry only about resizing.
+ */
+ if (winPtr->winWidth != Tk_Width(winPtr->tkwin) ||
+ winPtr->winHeight != Tk_Height(winPtr->tkwin)) {
+ EventuallyRedraw(htPtr);
+ htPtr->flags |= REQUEST_LAYOUT;
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbeddedWidgetCustodyProc --
+ *
+ * This procedure is invoked when a embedded widget has been
+ * stolen by another geometry manager. The information and
+ * memory associated with the embedded widget is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the widget formerly associated with the widget
+ * to have its layout re-computed and arranged at the
+ * next idle point.
+ *
+ *--------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Information about the former embedded widget. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Blt_HashEntry *hPtr;
+ EmbeddedWidget *winPtr = clientData;
+ /*
+ * Mark the widget as deleted by dereferencing the Tk window
+ * pointer. Zero out the height and width to collapse the area
+ * used by the widget. Redraw the window only if the widget is
+ * currently visible.
+ */
+ winPtr->htPtr->flags |= REQUEST_LAYOUT;
+ if (Tk_IsMapped(winPtr->tkwin) && (winPtr->flags & WIDGET_VISIBLE)) {
+ EventuallyRedraw(winPtr->htPtr);
+ }
+ Tk_DeleteEventHandler(winPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, winPtr);
+ hPtr = Blt_FindHashEntry(&(winPtr->htPtr->widgetTable),
+ (char *)winPtr->tkwin);
+ Blt_DeleteHashEntry(&(winPtr->htPtr->widgetTable), hPtr);
+ winPtr->cavityWidth = winPtr->cavityHeight = 0;
+ winPtr->tkwin = NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbeddedWidgetGeometryProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for
+ * embedded widgets managed by the hypertext widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for tkwin, and all its managed siblings, to
+ * be repacked and drawn at the next idle point.
+ *
+ *--------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Information about window that got new
+ * preferred geometry. */
+ Tk_Window tkwin; /* Other Tk-related information about the
+ * window. */
+{
+ EmbeddedWidget *winPtr = clientData;
+
+ winPtr->htPtr->flags |= REQUEST_LAYOUT;
+ EventuallyRedraw(winPtr->htPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FindEmbeddedWidget --
+ *
+ * Searches for a widget matching the path name given
+ * If found, the pointer to the widget structure is returned,
+ * otherwise NULL.
+ *
+ * Results:
+ * The pointer to the widget structure. If not found, NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static EmbeddedWidget *
+FindEmbeddedWidget(htPtr, tkwin)
+ HText *htPtr; /* Hypertext widget structure */
+ Tk_Window tkwin; /* Path name of embedded widget */
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(htPtr->widgetTable), (char *)tkwin);
+ if (hPtr != NULL) {
+ return (EmbeddedWidget *) Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateEmbeddedWidget --
+ *
+ * This procedure creates and initializes a new embedded widget
+ * in the hyper text widget.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing the
+ * new embedded widget. If an error occurred, then the return
+ * value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated. EmbeddedWidget window is mapped.
+ * Callbacks are set up for embedded widget resizes and geometry
+ * requests.
+ *
+ * ----------------------------------------------------------------------
+ */
+static EmbeddedWidget *
+CreateEmbeddedWidget(htPtr, name)
+ HText *htPtr; /* Hypertext widget */
+ char *name; /* Name of embedded widget */
+{
+ EmbeddedWidget *winPtr;
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ tkwin = Tk_NameToWindow(htPtr->interp, name, htPtr->tkwin);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ if (Tk_Parent(tkwin) != htPtr->tkwin) {
+ Tcl_AppendResult(htPtr->interp, "parent window of \"", name,
+ "\" must be \"", Tk_PathName(htPtr->tkwin), "\"", (char *)NULL);
+ return NULL;
+ }
+ hPtr = Blt_CreateHashEntry(&(htPtr->widgetTable), (char *)tkwin, &isNew);
+ /* Check is the widget is already embedded into this widget */
+ if (!isNew) {
+ Tcl_AppendResult(htPtr->interp, "\"", name,
+ "\" is already appended to ", Tk_PathName(htPtr->tkwin),
+ (char *)NULL);
+ return NULL;
+ }
+ winPtr = Blt_Calloc(1, sizeof(EmbeddedWidget));
+ assert(winPtr);
+ winPtr->flags = 0;
+ winPtr->tkwin = tkwin;
+ winPtr->htPtr = htPtr;
+ winPtr->x = winPtr->y = 0;
+ winPtr->fill = FILL_NONE;
+ winPtr->justify = JUSTIFY_CENTER;
+ winPtr->anchor = TK_ANCHOR_CENTER;
+ Blt_SetHashValue(hPtr, winPtr);
+
+ Tk_ManageGeometry(tkwin, &htextMgrInfo, winPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbeddedWidgetEventProc,
+ winPtr);
+ return winPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyEmbeddedWidget --
+ *
+ * This procedure is invoked by DestroyLine to clean up the
+ * internal structure of a widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyEmbeddedWidget(winPtr)
+ EmbeddedWidget *winPtr;
+{
+ /* Destroy the embedded widget if it still exists */
+ if (winPtr->tkwin != NULL) {
+ Blt_HashEntry *hPtr;
+
+ Tk_DeleteEventHandler(winPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, winPtr);
+ hPtr = Blt_FindHashEntry(&(winPtr->htPtr->widgetTable),
+ (char *)winPtr->tkwin);
+ Blt_DeleteHashEntry(&(winPtr->htPtr->widgetTable), hPtr);
+ Tk_DestroyWindow(winPtr->tkwin);
+ }
+ Blt_Free(winPtr);
+}
+
+/* Line Procedures */
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateLine --
+ *
+ * This procedure creates and initializes a new line of text.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing the new
+ * line of text. If an error occurred, then the return value is NULL
+ * and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Line *
+CreateLine(htPtr)
+ HText *htPtr;
+{
+ Line *linePtr;
+
+ if (htPtr->nLines >= htPtr->arraySize) {
+ if (htPtr->arraySize == 0) {
+ htPtr->arraySize = DEF_LINES_ALLOC;
+ } else {
+ htPtr->arraySize += htPtr->arraySize;
+ }
+ if (ResizeArray((char **)&(htPtr->lineArr), sizeof(Line),
+ htPtr->arraySize, htPtr->nLines) != TCL_OK) {
+ return NULL;
+ }
+ }
+ /* Initialize values in the new entry */
+
+ linePtr = htPtr->lineArr + htPtr->nLines;
+ linePtr->offset = 0;
+ linePtr->height = linePtr->width = 0;
+ linePtr->textStart = 0;
+ linePtr->textEnd = -1;
+ linePtr->baseline = 0;
+ linePtr->chainPtr = Blt_ChainCreate();
+
+ htPtr->nLines++;
+ return linePtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyLine --
+ *
+ * This procedure is invoked to clean up the internal structure
+ * of a line.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the line (text and widgets) is
+ * freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyLine(linePtr)
+ Line *linePtr;
+{
+ Blt_ChainLink *linkPtr;
+ EmbeddedWidget *winPtr;
+
+ /* Free the list of embedded widget structures */
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+ DestroyEmbeddedWidget(winPtr);
+ }
+ Blt_ChainDestroy(linePtr->chainPtr);
+}
+
+static void
+FreeText(htPtr)
+ HText *htPtr;
+{
+ int i;
+
+ for (i = 0; i < htPtr->nLines; i++) {
+ DestroyLine(htPtr->lineArr + i);
+ }
+ htPtr->nLines = 0;
+ htPtr->nChars = 0;
+ if (htPtr->charArr != NULL) {
+ Blt_Free(htPtr->charArr);
+ htPtr->charArr = NULL;
+ }
+}
+
+/* Text Procedures */
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyText --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a HText at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyText(dataPtr)
+ DestroyData dataPtr; /* Info about hypertext widget. */
+{
+ HText *htPtr = (HText *)dataPtr;
+
+ Tk_FreeOptions(configSpecs, (char *)htPtr, htPtr->display, 0);
+ if (htPtr->drawGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->drawGC);
+ }
+ if (htPtr->fillGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->fillGC);
+ }
+ if (htPtr->tile != NULL) {
+ Blt_FreeTile(htPtr->tile);
+ }
+ if (htPtr->selectGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->selectGC);
+ }
+ FreeText(htPtr);
+ if (htPtr->lineArr != NULL) {
+ Blt_Free(htPtr->lineArr);
+ }
+ Blt_DeleteHashTable(&(htPtr->widgetTable));
+ Blt_Free(htPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TextEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on hypertext widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TextEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ HText *htPtr = clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if ((htPtr->lastWidth != Tk_Width(htPtr->tkwin)) ||
+ (htPtr->lastHeight != Tk_Height(htPtr->tkwin))) {
+ htPtr->flags |= (REQUEST_LAYOUT | TEXT_DIRTY);
+ EventuallyRedraw(htPtr);
+ }
+ } else if (eventPtr->type == Expose) {
+
+ /*
+ * If the Expose event was synthetic (i.e. we manufactured it
+ * ourselves during a redraw operation), toggle the bit flag
+ * which controls redraws.
+ */
+
+ if (eventPtr->xexpose.send_event) {
+ htPtr->flags ^= IGNORE_EXPOSURES;
+ return;
+ }
+ if ((eventPtr->xexpose.count == 0) &&
+ !(htPtr->flags & IGNORE_EXPOSURES)) {
+ htPtr->flags |= TEXT_DIRTY;
+ EventuallyRedraw(htPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (htPtr->tkwin != NULL) {
+ htPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(htPtr->interp, htPtr->cmdToken);
+ }
+ if (htPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayText, htPtr);
+ }
+ Tcl_EventuallyFree(htPtr, DestroyText);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextDeleteCmdProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TextDeleteCmdProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ HText *htPtr = clientData;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (htPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = htPtr->tkwin;
+ htPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Stub for image change notifications. Since we immediately draw
+ * the image into a pixmap, we don't care about image changes.
+ *
+ * It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ HText *htPtr = clientData;
+
+ if (htPtr->tkwin != NULL) {
+ EventuallyRedraw(htPtr);
+ }
+}
+
+/* Configuration Procedures */
+static void
+ResetTextInfo(htPtr)
+ HText *htPtr;
+{
+ htPtr->first = 0;
+ htPtr->last = htPtr->nLines - 1;
+ htPtr->selFirst = htPtr->selLast = -1;
+ htPtr->selAnchor = 0;
+ htPtr->pendingX = htPtr->pendingY = 0;
+ htPtr->worldWidth = htPtr->worldHeight = 0;
+ htPtr->xOffset = htPtr->yOffset = 0;
+}
+
+static Line *
+GetLastLine(htPtr)
+ HText *htPtr;
+{
+ if (htPtr->nLines == 0) {
+ return CreateLine(htPtr);
+ }
+ return (htPtr->lineArr + (htPtr->nLines - 1));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ReadNamedFile --
+ *
+ * Read the named file into a newly allocated buffer.
+ *
+ * Results:
+ * Returns the size of the allocated buffer if the file was
+ * read correctly. Otherwise -1 is returned and "interp->result"
+ * will contain an error message.
+ *
+ * Side Effects:
+ * If successful, the contents of "bufferPtr" will point
+ * to the allocated buffer.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ReadNamedFile(interp, fileName, bufferPtr)
+ Tcl_Interp *interp;
+ char *fileName;
+ char **bufferPtr;
+{
+ FILE *f;
+ int nRead, fileSize;
+ int count, bytesLeft;
+ char *buffer;
+ int result = -1;
+#ifdef _MSC_VER
+#define fstat _fstat
+#define stat _stat
+#define fileno _fileno
+#endif
+ struct stat fileInfo;
+
+ f = fopen(fileName, "r");
+ if (f == NULL) {
+ Tcl_AppendResult(interp, "can't open \"", fileName,
+ "\" for reading: ", Tcl_PosixError(interp), (char *)NULL);
+ return -1;
+ }
+ if (fstat(fileno(f), &fileInfo) < 0) {
+ Tcl_AppendResult(interp, "can't stat \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ fclose(f);
+ return -1;
+ }
+ fileSize = fileInfo.st_size + 1;
+ buffer = Blt_Malloc(sizeof(char) * fileSize);
+ if (buffer == NULL) {
+ fclose(f);
+ return -1; /* Can't allocate memory for file buffer */
+ }
+ nRead = count = 0;
+ for (bytesLeft = fileInfo.st_size; bytesLeft > 0; bytesLeft -= nRead) {
+ nRead = fread(buffer + count, sizeof(char), bytesLeft, f);
+ if (nRead < 0) {
+ Tcl_AppendResult(interp, "error reading \"", fileName, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ fclose(f);
+ Blt_Free(buffer);
+ return -1;
+ } else if (nRead == 0) {
+ break;
+ }
+ count += nRead;
+ }
+ fclose(f);
+ buffer[count] = '\0';
+ result = count;
+ *bufferPtr = buffer;
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CollectCommand --
+ *
+ * Collect the characters representing a Tcl command into a
+ * given buffer.
+ *
+ * Results:
+ * Returns the number of bytes examined. If an error occurred,
+ * -1 is returned and "interp->result" will contain an error
+ * message.
+ *
+ * Side Effects:
+ * If successful, the "cmdArr" will be filled with the string
+ * representing the Tcl command.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static int
+CollectCommand(htPtr, inputArr, maxBytes, cmdArr)
+ HText *htPtr; /* Widget record */
+ char inputArr[]; /* Array of bytes representing the htext input */
+ int maxBytes; /* Maximum number of bytes left in input */
+ char cmdArr[]; /* Output buffer to be filled with the Tcl
+ * command */
+{
+ int c;
+ int i;
+ int state, count;
+
+ /* Simply collect the all the characters until %% into a buffer */
+
+ state = count = 0;
+ for (i = 0; i < maxBytes; i++) {
+ c = inputArr[i];
+ if (c == htPtr->specChar) {
+ state++;
+ } else if ((state == 0) && (c == '\\')) {
+ state = 3;
+ } else {
+ state = 0;
+ }
+ switch (state) {
+ case 2: /* End of command block found */
+ cmdArr[count - 1] = '\0';
+ return i;
+
+ case 4: /* Escaped block designator */
+ cmdArr[count] = c;
+ state = 0;
+ break;
+
+ default: /* Add to command buffer */
+ cmdArr[count++] = c;
+ break;
+ }
+ }
+ Tcl_AppendResult(htPtr->interp, "premature end of TCL command block",
+ (char *)NULL);
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ParseInput --
+ *
+ * Parse the input to the HText structure into an array of lines.
+ * Each entry contains the beginning index and end index of the
+ * characters in the text array which comprise the line.
+ *
+ * |*|*|*|\n|T|h|i|s| |a| |l|i|n|e| |o|f| |t|e|x|t|.|\n|*|*|*|
+ * ^ ^
+ * textStart textEnd
+ *
+ * Note that the end index contains the '\n'.
+ *
+ * Results:
+ * Returns TCL_OK or error depending if the file was read correctly.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ParseInput(interp, htPtr, input, nBytes)
+ Tcl_Interp *interp;
+ HText *htPtr;
+ char input[];
+ int nBytes;
+{
+ int c;
+ int i;
+ char *textArr;
+ char *cmdArr;
+ int count, nLines;
+ int length;
+ int state;
+ Line *linePtr;
+
+ linePtr = CreateLine(htPtr);
+ if (linePtr == NULL) {
+ return TCL_ERROR; /* Error allocating the line structure */
+ }
+ /* Right now, we replace the text array instead of appending to it */
+
+ linePtr->textStart = 0;
+
+ /* In the worst case, assume the entire input could be Tcl commands */
+ cmdArr = Blt_Malloc(sizeof(char) * (nBytes + 1));
+
+ textArr = Blt_Malloc(sizeof(char) * (nBytes + 1));
+ if (htPtr->charArr != NULL) {
+ Blt_Free(htPtr->charArr);
+ }
+ htPtr->charArr = textArr;
+ htPtr->nChars = 0;
+
+ nLines = count = state = 0;
+ htPtr->flags &= ~WIDGET_APPENDED;
+
+ for (i = 0; i < nBytes; i++) {
+ c = input[i];
+ if (c == htPtr->specChar) {
+ state++;
+ } else if (c == '\n') {
+ state = -1;
+ } else if ((state == 0) && (c == '\\')) {
+ state = 3;
+ } else {
+ state = 0;
+ }
+ switch (state) {
+ case 2: /* Block of Tcl commands found */
+ count--, i++;
+ length = CollectCommand(htPtr, input + i, nBytes - i, cmdArr);
+ if (length < 0) {
+ goto error;
+ }
+ i += length;
+ linePtr->textEnd = count;
+ htPtr->nChars = count + 1;
+ if (Tcl_Eval(interp, cmdArr) != TCL_OK) {
+ goto error;
+ }
+ if (htPtr->flags & WIDGET_APPENDED) {
+ /* Indicates the location a embedded widget in the text array */
+ textArr[count++] = ' ';
+ htPtr->flags &= ~WIDGET_APPENDED;
+ }
+ state = 0;
+ break;
+
+ case 4: /* Escaped block designator */
+ textArr[count - 1] = c;
+ state = 0;
+ break;
+
+ case -1: /* End of line or input */
+ linePtr->textEnd = count;
+ textArr[count++] = '\n';
+ nLines++;
+ linePtr = CreateLine(htPtr);
+ if (linePtr == NULL) {
+ goto error;
+ }
+ linePtr->textStart = count;
+ state = 0;
+ break;
+
+ default: /* Default action, add to text buffer */
+ textArr[count++] = c;
+ break;
+ }
+ }
+ if (count > linePtr->textStart) {
+ linePtr->textEnd = count;
+ textArr[count++] = '\n';/* Every line must end with a '\n' */
+ nLines++;
+ }
+ Blt_Free(cmdArr);
+ /* Reset number of lines allocated */
+ if (ResizeArray((char **)&(htPtr->lineArr), sizeof(Line), nLines,
+ htPtr->arraySize) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't reallocate array of lines", (char *)NULL);
+ return TCL_ERROR;
+ }
+ htPtr->nLines = htPtr->arraySize = nLines;
+ /* and the size of the character array */
+ if (ResizeArray(&(htPtr->charArr), sizeof(char), count,
+ nBytes) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't reallocate text character buffer",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ htPtr->nChars = count;
+ return TCL_OK;
+ error:
+ Blt_Free(cmdArr);
+ return TCL_ERROR;
+}
+
+static int
+IncludeText(interp, htPtr, fileName)
+ Tcl_Interp *interp;
+ HText *htPtr;
+ char *fileName;
+{
+ char *buffer;
+ int result;
+ int nBytes;
+
+ if ((htPtr->text == NULL) && (fileName == NULL)) {
+ return TCL_OK; /* Empty text string */
+ }
+ if (fileName != NULL) {
+ nBytes = ReadNamedFile(interp, fileName, &buffer);
+ if (nBytes < 0) {
+ return TCL_ERROR;
+ }
+ } else {
+ buffer = htPtr->text;
+ nBytes = strlen(htPtr->text);
+ }
+ result = ParseInput(interp, htPtr, buffer, nBytes);
+ if (fileName != NULL) {
+ Blt_Free(buffer);
+ }
+ return result;
+}
+
+/* ARGSUSED */
+static char *
+TextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about widget. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ HText *htPtr = clientData;
+ HText *lasthtPtr;
+
+ /* Check to see of this is the most recent trace */
+ lasthtPtr = (HText *)Tcl_VarTraceInfo2(interp, name1, name2, flags,
+ TextVarProc, NULL);
+ if (lasthtPtr != htPtr) {
+ return NULL; /* Ignore all but most current trace */
+ }
+ if (flags & TCL_TRACE_READS) {
+ char c;
+
+ c = name2[0];
+ if ((c == 'w') && (strcmp(name2, "widget") == 0)) {
+ Tcl_SetVar2(interp, name1, name2, Tk_PathName(htPtr->tkwin),
+ flags);
+ } else if ((c == 'l') && (strcmp(name2, "line") == 0)) {
+ char buf[80];
+ int lineNum;
+
+ lineNum = htPtr->nLines - 1;
+ if (lineNum < 0) {
+ lineNum = 0;
+ }
+ sprintf(buf, "%d", lineNum);
+ Tcl_SetVar2(interp, name1, name2, buf, flags);
+ } else if ((c == 'i') && (strcmp(name2, "index") == 0)) {
+ char buf[80];
+
+ sprintf(buf, "%d", htPtr->nChars - 1);
+ Tcl_SetVar2(interp, name1, name2, buf, flags);
+ } else if ((c == 'f') && (strcmp(name2, "file") == 0)) {
+ char *fileName;
+
+ fileName = htPtr->fileName;
+ if (fileName == NULL) {
+ fileName = "";
+ }
+ Tcl_SetVar2(interp, name1, name2, fileName, flags);
+ } else {
+ return "?unknown?";
+ }
+ }
+ return NULL;
+}
+
+static char *varNames[] =
+{
+ "widget", "line", "file", "index", (char *)NULL
+};
+
+static void
+CreateTraces(htPtr)
+ HText *htPtr;
+{
+ char **ptr;
+ static char globalCmd[] = "global htext";
+
+ /*
+ * Make the traced variables global to the widget
+ */
+ Tcl_Eval(htPtr->interp, globalCmd);
+ for (ptr = varNames; *ptr != NULL; ptr++) {
+ Tcl_TraceVar2(htPtr->interp, "htext", *ptr,
+ (TCL_GLOBAL_ONLY | TCL_TRACE_READS), TextVarProc, htPtr);
+ }
+}
+
+static void
+DeleteTraces(htPtr)
+ HText *htPtr;
+{
+ char **ptr;
+
+ for (ptr = varNames; *ptr != NULL; ptr++) {
+ Tcl_UntraceVar2(htPtr->interp, "htext", *ptr,
+ (TCL_GLOBAL_ONLY | TCL_TRACE_READS), TextVarProc, htPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureText --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a hypertext widget.
+ *
+ * The layout of the text must be calculated (by ComputeLayout)
+ * whenever particular options change; -font, -file, -linespacing
+ * and -text options. If the user has changes one of these options,
+ * it must be detected so that the layout can be recomputed. Since the
+ * coordinates of the layout are virtual, there is no need to adjust
+ * them if physical window attributes (window size, etc.)
+ * change.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for htPtr; old resources get freed, if there were any.
+ * The hypertext is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureText(interp, htPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ HText *htPtr; /* Information about widget; may or may not
+ * already have values for some fields. */
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+
+ if (Blt_ConfigModified(configSpecs, "-font", "-linespacing", "-file",
+ "-text", "-width", "-height", (char *)NULL)) {
+ /*
+ * These options change the layout of the text. Width/height
+ * and rows/columns may change a relatively sized window or cavity.
+ */
+ htPtr->flags |= (REQUEST_LAYOUT | TEXT_DIRTY); /* Mark for update */
+ }
+ gcMask = GCForeground | GCFont;
+ gcValues.font = Tk_FontId(htPtr->font);
+ gcValues.foreground = htPtr->normalFg->pixel;
+ newGC = Tk_GetGC(htPtr->tkwin, gcMask, &gcValues);
+ if (htPtr->drawGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->drawGC);
+ }
+ htPtr->drawGC = newGC;
+
+ gcValues.foreground = htPtr->selFgColor->pixel;
+ newGC = Tk_GetGC(htPtr->tkwin, gcMask, &gcValues);
+ if (htPtr->selectGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->selectGC);
+ }
+ htPtr->selectGC = newGC;
+
+ if (htPtr->xScrollUnits < 1) {
+ htPtr->xScrollUnits = 1;
+ }
+ if (htPtr->yScrollUnits < 1) {
+ htPtr->yScrollUnits = 1;
+ }
+ if (htPtr->tile != NULL) {
+ Blt_SetTileChangedProc(htPtr->tile, TileChangedProc, htPtr);
+ }
+ gcValues.foreground = htPtr->normalBg->pixel;
+ newGC = Tk_GetGC(htPtr->tkwin, gcMask, &gcValues);
+ if (htPtr->fillGC != NULL) {
+ Tk_FreeGC(htPtr->display, htPtr->fillGC);
+ }
+ htPtr->fillGC = newGC;
+
+ if (htPtr->nColumns > 0) {
+ htPtr->reqWidth =
+ htPtr->nColumns * Tk_TextWidth(htPtr->font, "0", 1);
+ }
+ if (htPtr->nRows > 0) {
+ Tk_FontMetrics fontMetrics;
+
+ Tk_GetFontMetrics(htPtr->font, &fontMetrics);
+ htPtr->reqHeight = htPtr->nRows * fontMetrics.linespace;
+ }
+ /*
+ * If the either the -text or -file option changed, read in the
+ * new text. The -text option supersedes any -file option.
+ */
+ if (Blt_ConfigModified(configSpecs, "-file", "-text", (char *)NULL)) {
+ int result;
+
+ FreeText(htPtr);
+ CreateTraces(htPtr); /* Create variable traces */
+
+ result = IncludeText(interp, htPtr, htPtr->fileName);
+
+ DeleteTraces(htPtr);
+ if (result == TCL_ERROR) {
+ FreeText(htPtr);
+ return TCL_ERROR;
+ }
+ ResetTextInfo(htPtr);
+ }
+ EventuallyRedraw(htPtr);
+ return TCL_OK;
+}
+
+/* Layout Procedures */
+/*
+ * -----------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based
+ * upon the anchor specified. The anchor indicates where
+ * the given xy position is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | | x,y ---+
+ * w center e | |
+ * | | +-----+
+ * sw --- s --- se
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+static XPoint
+TranslateAnchor(deltaX, deltaY, anchor)
+ int deltaX, deltaY; /* Difference between outer and inner regions
+ */
+ Tk_Anchor anchor; /* Direction of the anchor */
+{
+ XPoint point;
+
+ point.x = point.y = 0;
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ point.y = (deltaY / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ point.y = deltaY;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ point.x = (deltaX / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Centered */
+ point.x = (deltaX / 2);
+ point.y = (deltaY / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ point.x = (deltaX / 2);
+ point.y = deltaY;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ point.x = deltaX;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ point.x = deltaX;
+ point.y = (deltaY / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ point.x = deltaX;
+ point.y = deltaY;
+ break;
+ }
+ return point;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeCavitySize --
+ *
+ * Sets the width and height of the cavity based upon the
+ * requested size of the embedded widget. The requested space also
+ * includes any external padding which has been designated for
+ * this window.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The size of the cavity is set in the embedded widget information
+ * structure. These values can effect how the embedded widget is
+ * packed into the master window.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ComputeCavitySize(winPtr)
+ EmbeddedWidget *winPtr;
+{
+ int width, height;
+ int twiceBW;
+
+ twiceBW = 2 * Tk_Changes(winPtr->tkwin)->border_width;
+ if (winPtr->reqCavityWidth > 0) {
+ width = winPtr->reqCavityWidth;
+ } else if (winPtr->relCavityWidth > 0.0) {
+ width = (int)((double)Tk_Width(winPtr->htPtr->tkwin) *
+ winPtr->relCavityWidth + 0.5);
+ } else {
+ width = GetEmbeddedWidgetWidth(winPtr) + PADDING(winPtr->padX) +
+ twiceBW;
+ }
+ winPtr->cavityWidth = width;
+
+ if (winPtr->reqCavityHeight > 0) {
+ height = winPtr->reqCavityHeight;
+ } else if (winPtr->relCavityHeight > 0.0) {
+ height = (int)((double)Tk_Height(winPtr->htPtr->tkwin) *
+ winPtr->relCavityHeight + 0.5);
+ } else {
+ height = GetEmbeddedWidgetHeight(winPtr) + PADDING(winPtr->padY) +
+ twiceBW;
+ }
+ winPtr->cavityHeight = height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LayoutLine --
+ *
+ * This procedure computes the total width and height needed
+ * to contain the text and widgets for a particular line.
+ * It also calculates the baseline of the text on the line with
+ * respect to the other widgets on the line.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LayoutLine(htPtr, linePtr)
+ HText *htPtr;
+ Line *linePtr;
+{
+ EmbeddedWidget *winPtr;
+ int textStart, textLength;
+ int maxAscent, maxDescent, maxHeight;
+ int ascent, descent;
+ int median; /* Difference of font ascent/descent values */
+ Blt_ChainLink *linkPtr;
+ int x, y;
+ int newX;
+ Tk_FontMetrics fontMetrics;
+
+ /* Initialize line defaults */
+ Tk_GetFontMetrics(htPtr->font, &fontMetrics);
+ maxAscent = fontMetrics.ascent;
+ maxDescent = fontMetrics.descent;
+ median = fontMetrics.ascent - fontMetrics.descent;
+ ascent = descent = 0; /* Suppress compiler warnings */
+
+ /*
+ * Pass 1: Determine the maximum ascent (baseline) and descent
+ * needed for the line. We'll need this for figuring the top,
+ * bottom, and center anchors.
+ */
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+ if (winPtr->tkwin == NULL) {
+ continue;
+ }
+ ComputeCavitySize(winPtr);
+
+ switch (winPtr->justify) {
+ case JUSTIFY_TOP:
+ ascent = fontMetrics.ascent + winPtr->padTop;
+ descent = winPtr->cavityHeight - fontMetrics.ascent;
+ break;
+ case JUSTIFY_CENTER:
+ ascent = (winPtr->cavityHeight + median) / 2;
+ descent = (winPtr->cavityHeight - median) / 2;
+ break;
+ case JUSTIFY_BOTTOM:
+ ascent = winPtr->cavityHeight - fontMetrics.descent;
+ descent = fontMetrics.descent;
+ break;
+ }
+ if (descent > maxDescent) {
+ maxDescent = descent;
+ }
+ if (ascent > maxAscent) {
+ maxAscent = ascent;
+ }
+ }
+
+ maxHeight = maxAscent + maxDescent + htPtr->leader;
+ x = 0; /* Always starts from x=0 */
+ y = 0; /* Suppress compiler warning */
+ textStart = linePtr->textStart;
+
+ /*
+ * Pass 2: Find the placements of the text and widgets along each
+ * line.
+ */
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+ if (winPtr->tkwin == NULL) {
+ continue;
+ }
+ /* Get the width of the text leading to the widget. */
+ textLength = (winPtr->precedingTextEnd - textStart);
+ if (textLength > 0) {
+ Tk_MeasureChars(htPtr->font, htPtr->charArr + textStart,
+ textLength, 10000, TK_AT_LEAST_ONE, &newX);
+ winPtr->precedingTextWidth = newX;
+ x += newX;
+ }
+ switch (winPtr->justify) {
+ case JUSTIFY_TOP:
+ y = maxAscent - fontMetrics.ascent;
+ break;
+ case JUSTIFY_CENTER:
+ y = maxAscent - (winPtr->cavityHeight + median) / 2;
+ break;
+ case JUSTIFY_BOTTOM:
+ y = maxAscent + fontMetrics.descent - winPtr->cavityHeight;
+ break;
+ }
+ winPtr->x = x, winPtr->y = y;
+
+ /* Skip over trailing space */
+ textStart = winPtr->precedingTextEnd + 1;
+
+ x += winPtr->cavityWidth;
+ }
+
+ /*
+ * This can be either the trailing piece of a line after the last widget
+ * or the entire line if no widgets are embedded in it.
+ */
+ textLength = (linePtr->textEnd - textStart) + 1;
+ if (textLength > 0) {
+ Tk_MeasureChars(htPtr->font, htPtr->charArr + textStart,
+ textLength, 10000, DEF_TEXT_FLAGS, &newX);
+ x += newX;
+ }
+ /* Update line parameters */
+ if ((linePtr->width != x) || (linePtr->height != maxHeight) ||
+ (linePtr->baseline != maxAscent)) {
+ htPtr->flags |= TEXT_DIRTY;
+ }
+ linePtr->width = x;
+ linePtr->height = maxHeight;
+ linePtr->baseline = maxAscent;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeLayout --
+ *
+ * This procedure computes the total width and height needed
+ * to contain the text and widgets from all the lines of text.
+ * It merely sums the heights and finds the maximum width of
+ * all the lines. The width and height are needed for scrolling.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ComputeLayout(htPtr)
+ HText *htPtr;
+{
+ int count;
+ Line *linePtr;
+ int height, width;
+
+ width = height = 0;
+ for (count = 0; count < htPtr->nLines; count++) {
+ linePtr = htPtr->lineArr + count;
+
+ linePtr->offset = height;
+ LayoutLine(htPtr, linePtr);
+ height += linePtr->height;
+ if (linePtr->width > width) {
+ width = linePtr->width;
+ }
+ }
+ /*
+ * Set changed flag if new layout changed size of virtual text.
+ */
+ if ((height != htPtr->worldHeight) || (width != htPtr->worldWidth)) {
+ htPtr->worldHeight = height, htPtr->worldWidth = width;
+ htPtr->flags |= TEXT_DIRTY;
+ }
+}
+
+/* Display Procedures */
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetVisibleLines --
+ *
+ * Calculates which lines are visible using the height
+ * of the viewport and y offset from the top of the text.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Only those line between first and last inclusive are
+ * redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetVisibleLines(htPtr)
+ HText *htPtr;
+{
+ int topLine, bottomLine;
+ int firstY, lastY;
+ int lastLine;
+
+ if (htPtr->nLines == 0) {
+ htPtr->first = 0;
+ htPtr->last = -1;
+ return TCL_OK;
+ }
+ firstY = htPtr->pendingY;
+ lastLine = htPtr->nLines - 1;
+
+ /* First line */
+ topLine = LineSearch(htPtr, firstY, 0, lastLine);
+ if (topLine < 0) {
+ /*
+ * This can't be. The y-coordinate offset must be corrupted.
+ */
+ fprintf(stderr, "internal error: First position not found `%d'\n",
+ firstY);
+ return TCL_ERROR;
+ }
+ htPtr->first = topLine;
+
+ /*
+ * If there is less text than window space, the bottom line is the
+ * last line of text. Otherwise search for the line at the bottom
+ * of the window.
+ */
+ lastY = firstY + Tk_Height(htPtr->tkwin) - 1;
+ if (lastY < htPtr->worldHeight) {
+ bottomLine = LineSearch(htPtr, lastY, topLine, lastLine);
+ } else {
+ bottomLine = lastLine;
+ }
+ if (bottomLine < 0) {
+ /*
+ * This can't be. The newY offset must be corrupted.
+ */
+ fprintf(stderr, "internal error: Last position not found `%d'\n",
+ lastY);
+#ifdef notdef
+ fprintf(stderr, "worldHeight=%d,height=%d,top=%d,first=%d,last=%d\n",
+ htPtr->worldHeight, Tk_Height(htPtr->tkwin), firstY,
+ htPtr->lineArr[topLine].offset, htPtr->lineArr[lastLine].offset);
+#endif
+ return TCL_ERROR;
+ }
+ htPtr->last = bottomLine;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawSegment --
+ *
+ * Draws a line segment, designated by the segment structure.
+ * This routine handles the display of selected text by drawing
+ * a raised 3D border underneath the selected text.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The line segment is drawn on *draw*.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawSegment(htPtr, draw, linePtr, x, y, segPtr)
+ HText *htPtr;
+ Drawable draw;
+ Line *linePtr;
+ int x, y;
+ Segment *segPtr;
+{
+ int lastX, curPos, nChars;
+ int textLength;
+ int selStart, selEnd, selLength;
+ Tk_FontMetrics fontMetrics;
+
+#ifdef notdef
+ fprintf(stderr, "DS select: first=%d,last=%d text: first=%d,last=%d\n",
+ htPtr->selFirst, htPtr->selLast, segPtr->textStart, segPtr->textEnd);
+#endif
+ textLength = (segPtr->textEnd - segPtr->textStart) + 1;
+ if (textLength < 1) {
+ return;
+ }
+ Tk_GetFontMetrics(htPtr->font, &fontMetrics);
+ if ((segPtr->textEnd < htPtr->selFirst) ||
+ (segPtr->textStart > htPtr->selLast)) { /* No selected text */
+ Tk_DrawChars(htPtr->display, draw, htPtr->drawGC, htPtr->font,
+ htPtr->charArr + segPtr->textStart, textLength - 1,
+ x, y + linePtr->baseline);
+ return;
+ }
+ /*
+ * Text in a segment (with selected text) may have
+ * up to three regions:
+ *
+ * 1) the text before the start the selection
+ * 2) the selected text itself (drawn in a raised border)
+ * 3) the text following the selection.
+ */
+
+ selStart = segPtr->textStart;
+ selEnd = segPtr->textEnd;
+ if (htPtr->selFirst > segPtr->textStart) {
+ selStart = htPtr->selFirst;
+ }
+ if (htPtr->selLast < segPtr->textEnd) {
+ selEnd = htPtr->selLast;
+ }
+ selLength = (selEnd - selStart) + 1;
+ lastX = x;
+ curPos = segPtr->textStart;
+
+ if (selStart > segPtr->textStart) { /* Text preceding selection */
+ nChars = (selStart - segPtr->textStart);
+ Tk_MeasureChars(htPtr->font, htPtr->charArr + segPtr->textStart,
+ nChars, 10000, DEF_TEXT_FLAGS, &lastX);
+ lastX += x;
+ Tk_DrawChars(htPtr->display, draw, htPtr->drawGC, htPtr->font,
+ htPtr->charArr + segPtr->textStart, nChars, x,
+ y + linePtr->baseline);
+ curPos = selStart;
+ }
+ if (selLength > 0) { /* The selection itself */
+ int width, nextX;
+
+ Tk_MeasureChars(htPtr->font, htPtr->charArr + selStart,
+ selLength, 10000, DEF_TEXT_FLAGS, &nextX);
+ nextX += x;
+ width = (selEnd == linePtr->textEnd)
+ ? htPtr->worldWidth - htPtr->xOffset - lastX :
+ nextX - lastX;
+ Tk_Fill3DRectangle(htPtr->tkwin, draw, htPtr->selBorder,
+ lastX, y + linePtr->baseline - fontMetrics.ascent,
+ width, fontMetrics.linespace, htPtr->selBorderWidth,
+ TK_RELIEF_RAISED);
+ Tk_DrawChars(htPtr->display, draw, htPtr->selectGC,
+ htPtr->font, htPtr->charArr + selStart, selLength,
+ lastX, y + linePtr->baseline);
+ lastX = nextX;
+ curPos = selStart + selLength;
+ }
+ nChars = segPtr->textEnd - curPos;
+ if (nChars > 0) { /* Text following the selection */
+ Tk_DrawChars(htPtr->display, draw, htPtr->drawGC, htPtr->font,
+ htPtr->charArr + curPos, nChars - 1, lastX, y + linePtr->baseline);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MoveEmbeddedWidget --
+ *
+ * Move a embedded widget to a new location in the hypertext
+ * parent window. If the window has no geometry (i.e. width,
+ * or height is 0), simply unmap to window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Each embedded widget is moved to its new location, generating
+ * Expose events in the parent for each embedded widget moved.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MoveEmbeddedWidget(winPtr, offset)
+ EmbeddedWidget *winPtr;
+ int offset;
+{
+ int winWidth, winHeight;
+ int width, height;
+ int deltaX, deltaY;
+ int x, y;
+ int intBW;
+
+ winWidth = GetEmbeddedWidgetWidth(winPtr);
+ winHeight = GetEmbeddedWidgetHeight(winPtr);
+ if ((winWidth < 1) || (winHeight < 1)) {
+ if (Tk_IsMapped(winPtr->tkwin)) {
+ Tk_UnmapWindow(winPtr->tkwin);
+ }
+ return;
+ }
+ intBW = Tk_Changes(winPtr->tkwin)->border_width;
+ x = (winPtr->x + intBW + winPtr->padLeft) -
+ winPtr->htPtr->xOffset;
+ y = offset + (winPtr->y + intBW + winPtr->padTop) -
+ winPtr->htPtr->yOffset;
+
+ width = winPtr->cavityWidth - (2 * intBW + PADDING(winPtr->padX));
+ if (width < 0) {
+ width = 0;
+ }
+ if ((width < winWidth) || (winPtr->fill & FILL_X)) {
+ winWidth = width;
+ }
+ deltaX = width - winWidth;
+
+ height = winPtr->cavityHeight - (2 * intBW + PADDING(winPtr->padY));
+ if (height < 0) {
+ height = 0;
+ }
+ if ((height < winHeight) || (winPtr->fill & FILL_Y)) {
+ winHeight = height;
+ }
+ deltaY = height - winHeight;
+
+ if ((deltaX > 0) || (deltaY > 0)) {
+ XPoint point;
+
+ point = TranslateAnchor(deltaX, deltaY, winPtr->anchor);
+ x += point.x, y += point.y;
+ }
+ winPtr->winWidth = winWidth;
+ winPtr->winHeight = winHeight;
+
+ if ((x != Tk_X(winPtr->tkwin)) || (y != Tk_Y(winPtr->tkwin)) ||
+ (winWidth != Tk_Width(winPtr->tkwin)) ||
+ (winHeight != Tk_Height(winPtr->tkwin))) {
+ Tk_MoveResizeWindow(winPtr->tkwin, x, y, winWidth, winHeight);
+ }
+ if (!Tk_IsMapped(winPtr->tkwin)) {
+ Tk_MapWindow(winPtr->tkwin);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawPage --
+ *
+ * This procedure displays the lines of text and moves the widgets
+ * to their new positions. It draws lines with regard to
+ * the direction of the scrolling. The idea here is to make the
+ * text and buttons appear to move together. Otherwise you will
+ * get a "jiggling" effect where the windows appear to bump into
+ * the next line before that line is moved. In the worst case, where
+ * every line has at least one widget, you can get an aquarium effect
+ * (lines appear to ripple up).
+ *
+ * The text area may start between line boundaries (to accommodate
+ * both variable height lines and constant scrolling). Subtract the
+ * difference of the page offset and the line offset from the starting
+ * coordinates. For horizontal scrolling, simply subtract the offset
+ * of the viewport. The window will clip the top of the first line,
+ * the bottom of the last line, whatever text extends to the left
+ * or right of the viewport on any line.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the line in its current
+ * mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawPage(htPtr, deltaY)
+ HText *htPtr;
+ int deltaY; /* Change from previous Y coordinate */
+{
+ Line *linePtr;
+ EmbeddedWidget *winPtr;
+ Tk_Window tkwin = htPtr->tkwin;
+ Segment sgmt;
+ Pixmap pixmap;
+ int forceCopy = 0;
+ int i;
+ int lineNum;
+ int x, y, lastY;
+ Blt_ChainLink *linkPtr;
+ int width, height;
+ Display *display;
+
+ display = htPtr->display;
+ width = Tk_Width(tkwin);
+ height = Tk_Height(tkwin);
+
+ /* Create an off-screen pixmap for semi-smooth scrolling. */
+ pixmap = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height,
+ Tk_Depth(tkwin));
+
+ x = -(htPtr->xOffset);
+ y = -(htPtr->yOffset);
+
+ if (htPtr->tile != NULL) {
+ if (htPtr->tileOffsetPage) {
+ Blt_SetTSOrigin(htPtr->tkwin, htPtr->tile, x, y);
+ } else {
+ Blt_SetTileOrigin(htPtr->tkwin, htPtr->tile, 0, 0);
+ }
+ Blt_TileRectangle(htPtr->tkwin, pixmap, htPtr->tile, 0, 0, width,
+ height);
+ } else {
+ XFillRectangle(display, pixmap, htPtr->fillGC, 0, 0, width, height);
+ }
+
+
+ if (deltaY >= 0) {
+ y += htPtr->lineArr[htPtr->first].offset;
+ lineNum = htPtr->first;
+ lastY = 0;
+ } else {
+ y += htPtr->lineArr[htPtr->last].offset;
+ lineNum = htPtr->last;
+ lastY = height;
+ }
+ forceCopy = 0;
+
+ /* Draw each line */
+ for (i = htPtr->first; i <= htPtr->last; i++) {
+
+ /* Initialize character position in text buffer to start */
+ linePtr = htPtr->lineArr + lineNum;
+ sgmt.textStart = linePtr->textStart;
+ sgmt.textEnd = linePtr->textEnd;
+
+ /* Initialize X position */
+ x = -(htPtr->xOffset);
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+
+ if (winPtr->tkwin != NULL) {
+ winPtr->flags |= WIDGET_VISIBLE;
+ MoveEmbeddedWidget(winPtr, linePtr->offset);
+ }
+ sgmt.textEnd = winPtr->precedingTextEnd - 1;
+ if (sgmt.textEnd >= sgmt.textStart) {
+ DrawSegment(htPtr, pixmap, linePtr, x, y, &sgmt);
+ x += winPtr->precedingTextWidth;
+ }
+ /* Skip over the extra trailing space which designates the widget */
+ sgmt.textStart = winPtr->precedingTextEnd + 1;
+ x += winPtr->cavityWidth;
+ forceCopy++;
+ }
+
+ /*
+ * This may be the text trailing the last widget or the entire
+ * line if no widgets occur on it.
+ */
+ sgmt.textEnd = linePtr->textEnd;
+ if (sgmt.textEnd >= sgmt.textStart) {
+ DrawSegment(htPtr, pixmap, linePtr, x, y, &sgmt);
+ }
+ /* Go to the top of the next line */
+ if (deltaY >= 0) {
+ y += htPtr->lineArr[lineNum].height;
+ lineNum++;
+ }
+ if ((forceCopy > 0) && !(htPtr->flags & TEXT_DIRTY)) {
+ if (deltaY >= 0) {
+ XCopyArea(display, pixmap, Tk_WindowId(tkwin), htPtr->drawGC,
+ 0, lastY, width, y - lastY, 0, lastY);
+ } else {
+ XCopyArea(display, pixmap, Tk_WindowId(tkwin), htPtr->drawGC,
+ 0, y, width, lastY - y, 0, y);
+ }
+ forceCopy = 0; /* Reset drawing flag */
+ lastY = y; /* Record last Y position */
+ }
+ if ((deltaY < 0) && (lineNum > 0)) {
+ --lineNum;
+ y -= htPtr->lineArr[lineNum].height;
+ }
+ }
+ /*
+ * If the viewport was resized, draw the page in one operation.
+ * Otherwise draw any left-over block of text (either at the top
+ * or bottom of the page)
+ */
+ if (htPtr->flags & TEXT_DIRTY) {
+ XCopyArea(display, pixmap, Tk_WindowId(tkwin),
+ htPtr->drawGC, 0, 0, width, height, 0, 0);
+ } else if (lastY != y) {
+ if (deltaY >= 0) {
+ height -= lastY;
+ XCopyArea(display, pixmap, Tk_WindowId(tkwin),
+ htPtr->drawGC, 0, lastY, width, height, 0, lastY);
+ } else {
+ height = lastY;
+ XCopyArea(display, pixmap, Tk_WindowId(tkwin),
+ htPtr->drawGC, 0, 0, width, height, 0, 0);
+ }
+ }
+ Tk_FreePixmap(display, pixmap);
+}
+
+
+static void
+SendBogusEvent(tkwin)
+ Tk_Window tkwin;
+{
+#define DONTPROPAGATE 0
+ XEvent event;
+
+ event.type = event.xexpose.type = Expose;
+ event.xexpose.window = Tk_WindowId(tkwin);
+ event.xexpose.display = Tk_Display(tkwin);
+ event.xexpose.count = 0;
+ event.xexpose.x = event.xexpose.y = 0;
+ event.xexpose.width = Tk_Width(tkwin);
+ event.xexpose.height = Tk_Height(tkwin);
+
+ XSendEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), DONTPROPAGATE,
+ ExposureMask, &event);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayText --
+ *
+ * This procedure is invoked to display a hypertext widget.
+ * Many of the operations which might ordinarily be performed
+ * elsewhere (e.g. in a configuration routine) are done here
+ * because of the somewhat unusual interactions occurring between
+ * the parent and embedded widgets.
+ *
+ * Recompute the layout of the text if necessary. This is
+ * necessary if the world coordinate system has changed.
+ * Specifically, the following may have occurred:
+ *
+ * 1. a text attribute has changed (font, linespacing, etc.).
+ * 2. widget option changed (anchor, width, height).
+ * 3. actual embedded widget was resized.
+ * 4. new text string or file.
+ *
+ * This is deferred to the display routine since potentially
+ * many of these may occur (especially embedded widget changes).
+ *
+ * Set the vertical and horizontal scrollbars (if they are
+ * designated) by issuing a Tcl command. Done here since
+ * the text window width and height are needed.
+ *
+ * If the viewport position or contents have changed in the
+ * vertical direction, the now out-of-view embedded widgets
+ * must be moved off the viewport. Since embedded widgets will
+ * obscure the text window, it is imperative that the widgets
+ * are moved off before we try to redraw text in the same area.
+ * This is necessary only for vertical movements. Horizontal
+ * embedded widget movements are handled automatically in the
+ * page drawing routine.
+ *
+ * Get the new first and last line numbers for the viewport.
+ * These line numbers may have changed because either a)
+ * the viewport changed size or position, or b) the text
+ * (embedded widget sizes or text attributes) have changed.
+ *
+ * If the viewport has changed vertically (i.e. the first or
+ * last line numbers have changed), move the now out-of-view
+ * embedded widgets off the viewport.
+ *
+ * Potentially many expose events may be generated when the
+ * the individual embedded widgets are moved and/or resized.
+ * These events need to be ignored. Since (I think) expose
+ * events are guaranteed to happen in order, we can bracket
+ * them by sending phony events (via XSendEvent). The phony
+ * events turn on and off flags indicating which events
+* should be ignored.
+ *
+ * Finally, the page drawing routine is called.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the hypertext in its
+ * current mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayText(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ HText *htPtr = clientData;
+ Tk_Window tkwin = htPtr->tkwin;
+ int oldFirst; /* First line of old viewport */
+ int oldLast; /* Last line of old viewport */
+ int deltaY; /* Change in viewport in Y direction */
+ int reqWidth, reqHeight;
+
+#ifdef notdef
+ fprintf(stderr, "calling DisplayText(%s)\n", Tk_PathName(htPtr->tkwin));
+#endif
+ htPtr->flags &= ~REDRAW_PENDING;
+ if (tkwin == NULL) {
+ return; /* Window has been destroyed */
+ }
+ if (htPtr->flags & REQUEST_LAYOUT) {
+ /*
+ * Recompute the layout when widgets are created, deleted,
+ * moved, or resized. Also when text attributes (such as
+ * font, linespacing) have changed.
+ */
+ ComputeLayout(htPtr);
+ }
+ htPtr->lastWidth = Tk_Width(tkwin);
+ htPtr->lastHeight = Tk_Height(tkwin);
+
+ /*
+ * Check the requested width and height. We allow two modes:
+ * 1) If the user requested value is greater than zero, use it.
+ * 2) Otherwise, let the window be as big as the virtual text.
+ * This could be too large to display, so constrain it by
+ * the maxWidth and maxHeight values.
+ *
+ * In any event, we need to calculate the size of the virtual
+ * text and then make a geometry request. This is so that widgets
+ * whose size is relative to the master, will be set once.
+ */
+ if (htPtr->reqWidth > 0) {
+ reqWidth = htPtr->reqWidth;
+ } else {
+ reqWidth = MIN(htPtr->worldWidth, htPtr->maxWidth);
+ if (reqWidth < 1) {
+ reqWidth = 1;
+ }
+ }
+ if (htPtr->reqHeight > 0) {
+ reqHeight = htPtr->reqHeight;
+ } else {
+ reqHeight = MIN(htPtr->worldHeight, htPtr->maxHeight);
+ if (reqHeight < 1) {
+ reqHeight = 1;
+ }
+ }
+ if ((reqWidth != Tk_ReqWidth(tkwin)) || (reqHeight != Tk_ReqHeight(tkwin))) {
+ Tk_GeometryRequest(tkwin, reqWidth, reqHeight);
+
+ EventuallyRedraw(htPtr);
+ return; /* Try again with new geometry */
+ }
+ if (!Tk_IsMapped(tkwin)) {
+ return;
+ }
+ /*
+ * Turn off layout requests here, after the text window has been
+ * mapped. Otherwise, relative embedded widget size requests wrt
+ * to the size of parent text window will be wrong.
+ */
+ htPtr->flags &= ~REQUEST_LAYOUT;
+
+ /* Is there a pending goto request? */
+ if (htPtr->flags & GOTO_PENDING) {
+ htPtr->pendingY = htPtr->lineArr[htPtr->reqLineNum].offset;
+ htPtr->flags &= ~GOTO_PENDING;
+ }
+ deltaY = htPtr->pendingY - htPtr->yOffset;
+ oldFirst = htPtr->first, oldLast = htPtr->last;
+
+ /*
+ * If the viewport has changed size or position, or the text
+ * and/or embedded widgets have changed, adjust the scrollbars to
+ * new positions.
+ */
+ if (htPtr->flags & TEXT_DIRTY) {
+ int width, height;
+
+ width = Tk_Width(htPtr->tkwin);
+ height = Tk_Height(htPtr->tkwin);
+
+ /* Reset viewport origin and world extents */
+ htPtr->xOffset = Blt_AdjustViewport(htPtr->pendingX,
+ htPtr->worldWidth, width,
+ htPtr->xScrollUnits, BLT_SCROLL_MODE_LISTBOX);
+ htPtr->yOffset = Blt_AdjustViewport(htPtr->pendingY,
+ htPtr->worldHeight, height,
+ htPtr->yScrollUnits, BLT_SCROLL_MODE_LISTBOX);
+ if (htPtr->xScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(htPtr->interp, htPtr->xScrollCmdPrefix,
+ (double)htPtr->xOffset / htPtr->worldWidth,
+ (double)(htPtr->xOffset + width) / htPtr->worldWidth);
+ }
+ if (htPtr->yScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(htPtr->interp, htPtr->yScrollCmdPrefix,
+ (double)htPtr->yOffset / htPtr->worldHeight,
+ (double)(htPtr->yOffset + height) / htPtr->worldHeight);
+ }
+ /*
+ * Given a new viewport or text height, find the first and
+ * last line numbers of the new viewport.
+ */
+ if (GetVisibleLines(htPtr) != TCL_OK) {
+ return;
+ }
+ }
+ /*
+ * This is a kludge: Send an expose event before and after
+ * drawing the page of text. Since moving and resizing of the
+ * embedded widgets will cause redundant expose events in the parent
+ * window, the phony events will bracket them indicating no
+ * action should be taken.
+ */
+ SendBogusEvent(tkwin);
+
+ /*
+ * If either the position of the viewport has changed or the size
+ * of width or height of the entire text have changed, move the
+ * widgets from the previous viewport out of the current
+ * viewport. Worry only about the vertical embedded widget movements.
+ * The page is always draw at full width and the viewport will clip
+ * the text.
+ */
+ if ((htPtr->first != oldFirst) || (htPtr->last != oldLast)) {
+ int offset;
+ int i;
+ int first, last;
+ Blt_ChainLink *linkPtr;
+ EmbeddedWidget *winPtr;
+
+ /* Figure out which lines are now out of the viewport */
+
+ if ((htPtr->first > oldFirst) && (htPtr->first <= oldLast)) {
+ first = oldFirst, last = htPtr->first;
+ } else if ((htPtr->last < oldLast) && (htPtr->last >= oldFirst)) {
+ first = htPtr->last, last = oldLast;
+ } else {
+ first = oldFirst, last = oldLast;
+ }
+
+ for (i = first; i <= last; i++) {
+ offset = htPtr->lineArr[i].offset;
+ for (linkPtr = Blt_ChainFirstLink(htPtr->lineArr[i].chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ winPtr = Blt_ChainGetValue(linkPtr);
+ if (winPtr->tkwin != NULL) {
+ MoveEmbeddedWidget(winPtr, offset);
+ winPtr->flags &= ~WIDGET_VISIBLE;
+ }
+ }
+ }
+ }
+ DrawPage(htPtr, deltaY);
+ SendBogusEvent(tkwin);
+
+ /* Reset flags */
+ htPtr->flags &= ~TEXT_DIRTY;
+}
+
+/* Selection Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextSelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is
+ * requested by someone. It returns part or all of the selection
+ * in a buffer provided by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored
+ * at buffer. Buffer is filled (or partially filled) with a
+ * NULL-terminated string containing part or all of the selection,
+ * as given by offset and maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TextSelectionProc(clientData, offset, buffer, maxBytes)
+ ClientData clientData; /* Information about Text widget. */
+ int offset; /* Offset within selection of first
+ * character to be returned. */
+ char *buffer; /* Location in which to place
+ * selection. */
+ int maxBytes; /* Maximum number of bytes to place
+ * at buffer, not including terminating
+ * NULL character. */
+{
+ HText *htPtr = clientData;
+ int size;
+
+ if ((htPtr->selFirst < 0) || (!htPtr->exportSelection)) {
+ return -1;
+ }
+ size = (htPtr->selLast - htPtr->selFirst) + 1 - offset;
+ if (size > maxBytes) {
+ size = maxBytes;
+ }
+ if (size <= 0) {
+ return 0; /* huh? */
+ }
+ strncpy(buffer, htPtr->charArr + htPtr->selFirst + offset, size);
+ buffer[size] = '\0';
+ return size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextLostSelection --
+ *
+ * This procedure is called back by Tk when the selection is
+ * grabbed away from a Text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is
+ * marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TextLostSelection(clientData)
+ ClientData clientData; /* Information about Text widget. */
+{
+ HText *htPtr = clientData;
+
+ if ((htPtr->selFirst >= 0) && (htPtr->exportSelection)) {
+ htPtr->selFirst = htPtr->selLast = -1;
+ EventuallyRedraw(htPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectLine --
+ *
+ * Modify the selection by moving both its anchored and un-anchored
+ * ends. This could make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectLine(htPtr, tindex)
+ HText *htPtr; /* Information about widget. */
+ int tindex; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ int selFirst, selLast;
+ int lineNum;
+ Line *linePtr;
+
+ lineNum = IndexSearch(htPtr, tindex, 0, htPtr->nLines - 1);
+ if (lineNum < 0) {
+ char string[200];
+
+ sprintf(string, "can't determine line number from index \"%d\"",
+ tindex);
+ Tcl_AppendResult(htPtr->interp, string, (char *)NULL);
+ return TCL_ERROR;
+ }
+ linePtr = htPtr->lineArr + lineNum;
+ /*
+ * Grab the selection if we don't own it already.
+ */
+ if ((htPtr->exportSelection) && (htPtr->selFirst == -1)) {
+ Tk_OwnSelection(htPtr->tkwin, XA_PRIMARY, TextLostSelection, htPtr);
+ }
+ selFirst = linePtr->textStart;
+ selLast = linePtr->textEnd;
+ htPtr->selAnchor = tindex;
+ if ((htPtr->selFirst != selFirst) ||
+ (htPtr->selLast != selLast)) {
+ htPtr->selFirst = selFirst;
+ htPtr->selLast = selLast;
+ EventuallyRedraw(htPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectWord --
+ *
+ * Modify the selection by moving both its anchored and un-anchored
+ * ends. This could make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectWord(htPtr, tindex)
+ HText *htPtr; /* Information about widget. */
+ int tindex; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ int selFirst, selLast;
+ int i;
+
+ for (i = tindex; i < htPtr->nChars; i++) {
+ if (isspace(UCHAR(htPtr->charArr[i]))) {
+ break;
+ }
+ }
+ selLast = i - 1;
+ for (i = tindex; i >= 0; i--) {
+ if (isspace(UCHAR(htPtr->charArr[i]))) {
+ break;
+ }
+ }
+ selFirst = i + 1;
+ if (selFirst > selLast) {
+ selFirst = selLast = tindex;
+ }
+ /*
+ * Grab the selection if we don't own it already.
+ */
+ if ((htPtr->exportSelection) && (htPtr->selFirst == -1)) {
+ Tk_OwnSelection(htPtr->tkwin, XA_PRIMARY, TextLostSelection, htPtr);
+ }
+ htPtr->selAnchor = tindex;
+ if ((htPtr->selFirst != selFirst) || (htPtr->selLast != selLast)) {
+ htPtr->selFirst = selFirst, htPtr->selLast = selLast;
+ EventuallyRedraw(htPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectTextBlock --
+ *
+ * Modify the selection by moving its un-anchored end. This
+ * could make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectTextBlock(htPtr, tindex)
+ HText *htPtr; /* Information about widget. */
+ int tindex; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ int selFirst, selLast;
+
+ /*
+ * Grab the selection if we don't own it already.
+ */
+
+ if ((htPtr->exportSelection) && (htPtr->selFirst == -1)) {
+ Tk_OwnSelection(htPtr->tkwin, XA_PRIMARY, TextLostSelection, htPtr);
+ }
+ /* If the anchor hasn't been set yet, assume the beginning of the text*/
+ if (htPtr->selAnchor < 0) {
+ htPtr->selAnchor = 0;
+ }
+ if (htPtr->selAnchor <= tindex) {
+ selFirst = htPtr->selAnchor;
+ selLast = tindex;
+ } else {
+ selFirst = tindex;
+ selLast = htPtr->selAnchor;
+ }
+ if ((htPtr->selFirst != selFirst) || (htPtr->selLast != selLast)) {
+ htPtr->selFirst = selFirst, htPtr->selLast = selLast;
+ EventuallyRedraw(htPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * This procedure handles the individual options for text
+ * selections. The selected text is designated by start and end
+ * indices into the text pool. The selected segment has both a
+ * anchored and unanchored ends. The following selection
+ * operations are implemented:
+ *
+ * "adjust" - resets either the first or last index
+ * of the selection.
+ * "clear" - clears the selection. Sets first/last
+ * indices to -1.
+ * "from" - sets the index of the selection anchor.
+ * "line" - sets the first of last indices to the
+ * start and end of the line at the
+ * designated point.
+ * "present" - return "1" if a selection is available,
+ * "0" otherwise.
+ * "range" - sets the first and last indices.
+ * "to" - sets the index of the un-anchored end.
+ * "word" - sets the first of last indices to the
+ * start and end of the word at the
+ * designated point.
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int iselection;
+ unsigned int length;
+ int result = TCL_OK;
+ char c;
+
+ length = strlen(argv[2]);
+ c = argv[2][0];
+ if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " selection clear\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (htPtr->selFirst != -1) {
+ htPtr->selFirst = htPtr->selLast = -1;
+ EventuallyRedraw(htPtr);
+ }
+ return TCL_OK;
+ } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " selection present\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_AppendResult(interp, (htPtr->selFirst != -1) ? "0" : "1",
+ (char *)NULL);
+ return TCL_OK;
+ } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
+ int selFirst, selLast;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " selection range first last\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetIndex(htPtr, argv[3], &selFirst) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetIndex(htPtr, argv[4], &selLast) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ htPtr->selAnchor = selFirst;
+ result = SelectTextBlock(htPtr, selLast);
+ return TCL_OK;
+ }
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " selection ", argv[2], " index\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetIndex(htPtr, argv[3], &iselection) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
+ htPtr->selAnchor = iselection;
+ } else if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
+ int half1, half2;
+
+ half1 = (htPtr->selFirst + htPtr->selLast) / 2;
+ half2 = (htPtr->selFirst + htPtr->selLast + 1) / 2;
+ if (iselection < half1) {
+ htPtr->selAnchor = htPtr->selLast;
+ } else if (iselection > half2) {
+ htPtr->selAnchor = htPtr->selFirst;
+ }
+ result = SelectTextBlock(htPtr, iselection);
+ } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
+ result = SelectTextBlock(htPtr, iselection);
+ } else if ((c == 'w') && (strncmp(argv[2], "word", length) == 0)) {
+ result = SelectWord(htPtr, iselection);
+ } else if ((c == 'l') && (strncmp(argv[2], "line", length) == 0)) {
+ result = SelectLine(htPtr, iselection);
+ } else {
+ Tcl_AppendResult(interp, "bad selection operation \"", argv[2],
+ "\": should be \"adjust\", \"clear\", \"from\", \"line\", \
+\"present\", \"range\", \"to\", or \"word\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GotoOp --
+ *
+ * Move the top line of the viewport to the new location based
+ * upon the given line number. Force out-of-range requests to the
+ * top or bottom of text.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_OK, interp->result contains the
+ * current line number.
+ *
+ * Side effects:
+ * At the next idle point, the text viewport will be move to the
+ * new line.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GotoOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int line;
+
+ line = htPtr->first;
+ if (argc == 3) {
+ int tindex;
+
+ if (GetIndex(htPtr, argv[2], &tindex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ line = IndexSearch(htPtr, tindex, 0, htPtr->nLines - 1);
+ if (line < 0) {
+ char string[200];
+
+ sprintf(string, "can't determine line number from index \"%d\"",
+ tindex);
+ Tcl_AppendResult(htPtr->interp, string, (char *)NULL);
+ return TCL_ERROR;
+ }
+ htPtr->reqLineNum = line;
+ htPtr->flags |= TEXT_DIRTY;
+
+ /*
+ * Make only a request for a change in the viewport. Defer
+ * the actual scrolling until the text layout is adjusted at
+ * the next idle point.
+ */
+ if (line != htPtr->first) {
+ htPtr->flags |= GOTO_PENDING;
+ EventuallyRedraw(htPtr);
+ }
+ }
+ Tcl_SetResult(htPtr->interp, Blt_Itoa(line), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+
+static int
+XViewOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int width, worldWidth;
+
+ width = Tk_Width(htPtr->tkwin);
+ worldWidth = htPtr->worldWidth;
+ if (argc == 2) {
+ double fract;
+
+ /* Report first and last fractions */
+ fract = (double)htPtr->xOffset / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(htPtr->xOffset + width) / worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ htPtr->pendingX = htPtr->xOffset;
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(htPtr->pendingX),
+ worldWidth, width, htPtr->xScrollUnits, BLT_SCROLL_MODE_LISTBOX)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ htPtr->flags |= TEXT_DIRTY;
+ EventuallyRedraw(htPtr);
+ return TCL_OK;
+}
+
+static int
+YViewOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int height, worldHeight;
+
+ height = Tk_Height(htPtr->tkwin);
+ worldHeight = htPtr->worldHeight;
+ if (argc == 2) {
+ double fract;
+
+ /* Report first and last fractions */
+ fract = (double)htPtr->yOffset / worldHeight;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(htPtr->yOffset + height) / worldHeight;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ htPtr->pendingY = htPtr->yOffset;
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(htPtr->pendingY),
+ worldHeight, height, htPtr->yScrollUnits, BLT_SCROLL_MODE_LISTBOX)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ htPtr->flags |= TEXT_DIRTY;
+ EventuallyRedraw(htPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ * This procedure embeds a Tk widget into the hypertext.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Memory is allocated. EmbeddedWidget gets configured.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+AppendOp(htPtr, interp, argc, argv)
+ HText *htPtr; /* Hypertext widget */
+ Tcl_Interp *interp; /* Interpreter associated with widget */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Line *linePtr;
+ EmbeddedWidget *winPtr;
+
+ winPtr = CreateEmbeddedWidget(htPtr, argv[2]);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_ConfigureWidget(interp, htPtr->tkwin, widgetConfigSpecs,
+ argc - 3, argv + 3, (char *)winPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Append widget to list of embedded widgets of the last line.
+ */
+ linePtr = GetLastLine(htPtr);
+ if (linePtr == NULL) {
+ Tcl_AppendResult(htPtr->interp, "can't allocate line structure",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_ChainAppend(linePtr->chainPtr, winPtr);
+ linePtr->width += winPtr->cavityWidth;
+ winPtr->precedingTextEnd = linePtr->textEnd;
+
+ htPtr->flags |= (WIDGET_APPENDED | REQUEST_LAYOUT);
+ EventuallyRedraw(htPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowsOp --
+ *
+ * Returns a list of all the pathNames of embedded widgets of the
+ * HText widget. If a pattern argument is given, only the names
+ * of windows matching it will be placed into the list.
+ *
+ * Results:
+ * Standard Tcl result. If TCL_OK, interp->result will contain
+ * the list of the embedded widget pathnames. Otherwise it will
+ * contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+WindowsOp(htPtr, interp, argc, argv)
+ HText *htPtr; /* Hypertext widget record */
+ Tcl_Interp *interp; /* Interpreter associated with widget */
+ int argc;
+ char **argv;
+{
+ EmbeddedWidget *winPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *name;
+
+ for (hPtr = Blt_FirstHashEntry(&(htPtr->widgetTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ winPtr = (EmbeddedWidget *)Blt_GetHashValue(hPtr);
+ if (winPtr->tkwin == NULL) {
+ fprintf(stderr, "window `%s' is null\n",
+ Tk_PathName(Blt_GetHashKey(&(htPtr->widgetTable), hPtr)));
+ continue;
+ }
+ name = Tk_PathName(winPtr->tkwin);
+ if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
+ Tcl_AppendElement(interp, name);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ char *itemPtr;
+ Tk_ConfigSpec *specsPtr;
+
+ if ((argc > 2) && (argv[2][0] == '.')) {
+ Tk_Window tkwin;
+ EmbeddedWidget *winPtr;
+
+ /* EmbeddedWidget window to be configured */
+ tkwin = Tk_NameToWindow(interp, argv[2], htPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ winPtr = FindEmbeddedWidget(htPtr, tkwin);
+ if (winPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" is not managed by \"", argv[0], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ specsPtr = widgetConfigSpecs;
+ itemPtr = (char *)winPtr;
+ argv++;
+ argc--;
+ } else {
+ specsPtr = configSpecs;
+ itemPtr = (char *)htPtr;
+ }
+ return Tk_ConfigureValue(interp, htPtr->tkwin, specsPtr, itemPtr,
+ argv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * a hypertext widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for htPtr; old resources get freed, if there were any.
+ * The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ char *itemPtr;
+ Tk_ConfigSpec *specsPtr;
+
+ if ((argc > 2) && (argv[2][0] == '.')) {
+ Tk_Window tkwin;
+ EmbeddedWidget *winPtr;
+
+ /* EmbeddedWidget window to be configured */
+ tkwin = Tk_NameToWindow(interp, argv[2], htPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ winPtr = FindEmbeddedWidget(htPtr, tkwin);
+ if (winPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" is not managed by \"", argv[0], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ specsPtr = widgetConfigSpecs;
+ itemPtr = (char *)winPtr;
+ argv++;
+ argc--;
+ } else {
+ specsPtr = configSpecs;
+ itemPtr = (char *)htPtr;
+ }
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, htPtr->tkwin, specsPtr, itemPtr,
+ (char *)NULL, 0);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, htPtr->tkwin, specsPtr, itemPtr,
+ argv[2], 0);
+ }
+ if (Tk_ConfigureWidget(interp, htPtr->tkwin, specsPtr, argc - 2,
+ argv + 2, itemPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (itemPtr == (char *)htPtr) {
+ /* Reconfigure the master */
+ if (ConfigureText(interp, htPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ htPtr->flags |= REQUEST_LAYOUT;
+ }
+ EventuallyRedraw(htPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ * Implements the quick scan for hypertext widgets.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y;
+ char c;
+ unsigned int length;
+
+
+ if (Blt_GetXY(interp, htPtr->tkwin, argv[3], &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
+ htPtr->scanMark.x = x, htPtr->scanMark.y = y;
+ htPtr->scanPt.x = htPtr->xOffset;
+ htPtr->scanPt.y = htPtr->yOffset;
+
+ } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
+ int px, py;
+
+ px = htPtr->scanPt.x - (10 * (x - htPtr->scanMark.x));
+ py = htPtr->scanPt.y - (10 * (y - htPtr->scanMark.y));
+
+ if (px < 0) {
+ px = htPtr->scanPt.x = 0;
+ htPtr->scanMark.x = x;
+ } else if (px >= htPtr->worldWidth) {
+ px = htPtr->scanPt.x = htPtr->worldWidth - htPtr->xScrollUnits;
+ htPtr->scanMark.x = x;
+ }
+ if (py < 0) {
+ py = htPtr->scanPt.y = 0;
+ htPtr->scanMark.y = y;
+ } else if (py >= htPtr->worldHeight) {
+ py = htPtr->scanPt.y = htPtr->worldHeight - htPtr->yScrollUnits;
+ htPtr->scanMark.y = y;
+ }
+ if ((py != htPtr->pendingY) || (px != htPtr->pendingX)) {
+ htPtr->pendingX = px, htPtr->pendingY = py;
+ htPtr->flags |= TEXT_DIRTY;
+ EventuallyRedraw(htPtr);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
+ "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ * Returns the linenumber of the next line matching the given
+ * pattern within the range of lines provided. If the first
+ * line number is greater than the last, the search is done in
+ * reverse.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SearchOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ char *startPtr, *endPtr;
+ char saved;
+ Tcl_RegExp regExpToken;
+ int iFirst, iLast;
+ int matchStart, matchEnd;
+ int match;
+
+ regExpToken = Tcl_RegExpCompile(interp, argv[2]);
+ if (regExpToken == NULL) {
+ return TCL_ERROR;
+ }
+ iFirst = 0;
+ iLast = htPtr->nChars;
+ if (argc > 3) {
+ if (GetIndex(htPtr, argv[3], &iFirst) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (argc == 4) {
+ if (GetIndex(htPtr, argv[4], &iLast) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (iLast < iFirst) {
+ return TCL_ERROR;
+ }
+ matchStart = matchEnd = -1;
+ startPtr = htPtr->charArr + iFirst;
+ endPtr = htPtr->charArr + (iLast + 1);
+ saved = *endPtr;
+ *endPtr = '\0'; /* Make the line a string by changing the
+ * '\n' into a NUL byte before searching */
+ match = Tcl_RegExpExec(interp, regExpToken, startPtr, startPtr);
+ *endPtr = saved;
+ if (match < 0) {
+ return TCL_ERROR;
+ } else if (match > 0) {
+ Tcl_RegExpRange(regExpToken, 0, &startPtr, &endPtr);
+ if ((startPtr != NULL) || (endPtr != NULL)) {
+ matchStart = startPtr - htPtr->charArr;
+ matchEnd = endPtr - htPtr->charArr - 1;
+ }
+ }
+ if (match > 0) {
+ Tcl_AppendElement(interp, Blt_Itoa(matchStart));
+ Tcl_AppendElement(interp, Blt_Itoa(matchEnd));
+ } else {
+ Tcl_ResetResult(interp);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns the characters designated by the range of elements.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RangeOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ char *startPtr, *endPtr;
+ char saved;
+ int textFirst, textLast;
+
+ textFirst = htPtr->selFirst;
+ textLast = htPtr->selLast;
+ if (textFirst < 0) {
+ textFirst = 0;
+ textLast = htPtr->nChars - 1;
+ }
+ if (argc > 2) {
+ if (GetIndex(htPtr, argv[2], &textFirst) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (argc == 4) {
+ if (GetIndex(htPtr, argv[3], &textLast) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (textLast < textFirst) {
+ Tcl_AppendResult(interp, "first index is greater than last", (char *)NULL);
+ return TCL_ERROR;
+ }
+ startPtr = htPtr->charArr + textFirst;
+ endPtr = htPtr->charArr + (textLast + 1);
+ saved = *endPtr;
+ *endPtr = '\0'; /* Make the line into a string by
+ * changing the * '\n' into a '\0'
+ * before copying */
+ Tcl_SetResult(interp, startPtr, TCL_VOLATILE);
+ *endPtr = saved;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int tindex;
+
+ if (GetIndex(htPtr, argv[2], &tindex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Blt_Itoa(tindex), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinePosOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LinePosOp(htPtr, interp, argc, argv)
+ HText *htPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int line, cpos, tindex;
+ char string[200];
+
+ if (GetIndex(htPtr, argv[2], &tindex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetTextPosition(htPtr, tindex, &line, &cpos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ sprintf(string, "%d.%d", line, cpos);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TextWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+
+static Blt_OpSpec textOps[] =
+{
+ {"append", 1, (Blt_Op)AppendOp, 3, 0, "window ?option value?...",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "?window? option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0,
+ "?window? ?option value?...",},
+ {"gotoline", 2, (Blt_Op)GotoOp, 2, 3, "?line?",},
+ {"index", 1, (Blt_Op)IndexOp, 3, 3, "string",},
+ {"linepos", 1, (Blt_Op)LinePosOp, 3, 3, "string",},
+ {"range", 2, (Blt_Op)RangeOp, 2, 4, "?from? ?to?",},
+ {"scan", 2, (Blt_Op)ScanOp, 4, 4, "oper @x,y",},
+ {"search", 3, (Blt_Op)SearchOp, 3, 5, "pattern ?from? ?to?",},
+ {"selection", 3, (Blt_Op)SelectOp, 3, 5, "oper ?index?",},
+ {"windows", 6, (Blt_Op)WindowsOp, 2, 3, "?pattern?",},
+ {"xview", 1, (Blt_Op)XViewOp, 2, 5,
+ "?moveto fract? ?scroll number what?",},
+ {"yview", 1, (Blt_Op)YViewOp, 2, 5,
+ "?moveto fract? ?scroll number what?",},
+};
+static int nTextOps = sizeof(textOps) / sizeof(Blt_OpSpec);
+
+static int
+TextWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about hypertext widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Blt_Op proc;
+ int result;
+ HText *htPtr = clientData;
+
+ proc = Blt_GetOp(interp, nTextOps, textOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(htPtr);
+ result = (*proc) (htPtr, interp, argc, argv);
+ Tcl_Release(htPtr);
+ return result;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TextCmd --
+ *
+ * This procedure is invoked to process the "htext" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+TextCmd(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. */
+{
+ HText *htPtr;
+ Screen *screenPtr;
+ Tk_Window tkwin;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ htPtr = Blt_Calloc(1, sizeof(HText));
+ assert(htPtr);
+ tkwin = Tk_MainWindow(interp);
+ tkwin = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *)NULL);
+ if (tkwin == NULL) {
+ Blt_Free(htPtr);
+ return TCL_ERROR;
+ }
+ /* Initialize the new hypertext widget */
+
+ Tk_SetClass(tkwin, "Htext");
+ htPtr->tkwin = tkwin;
+ htPtr->display = Tk_Display(tkwin);
+ htPtr->interp = interp;
+ htPtr->nLines = htPtr->arraySize = 0;
+ htPtr->leader = 1;
+ htPtr->xScrollUnits = htPtr->yScrollUnits = 10;
+ htPtr->nRows = htPtr->nColumns = 0;
+ htPtr->selFirst = htPtr->selLast = -1;
+ htPtr->selAnchor = 0;
+ htPtr->exportSelection = TRUE;
+ htPtr->selBorderWidth = 2;
+ screenPtr = Tk_Screen(htPtr->tkwin);
+ htPtr->maxWidth = WidthOfScreen(screenPtr);
+ htPtr->maxHeight = HeightOfScreen(screenPtr);
+ Blt_InitHashTable(&(htPtr->widgetTable), BLT_ONE_WORD_KEYS);
+
+ Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING, TextSelectionProc,
+ htPtr, XA_STRING);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ TextEventProc, htPtr);
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, htPtr);
+#endif
+ /*
+ * -----------------------------------------------------------------
+ *
+ * Create the widget command before configuring the widget. This
+ * is because the "-file" and "-text" options may have embedded
+ * commands that self-reference the widget through the
+ * "$blt_htext(widget)" variable.
+ *
+ * ------------------------------------------------------------------
+ */
+ htPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], TextWidgetCmd, htPtr,
+ TextDeleteCmdProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(htPtr->tkwin, htPtr->cmdToken);
+#endif
+ if ((Tk_ConfigureWidget(interp, htPtr->tkwin, configSpecs, argc - 2,
+ argv + 2, (char *)htPtr, 0) != TCL_OK) ||
+ (ConfigureText(interp, htPtr) != TCL_OK)) {
+ Tk_DestroyWindow(htPtr->tkwin);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(htPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+int
+Blt_HtextInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"htext", TextCmd,};
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_HTEXT */
diff --git a/blt/src/bltImage.c b/blt/src/bltImage.c
new file mode 100644
index 00000000000..16eb0f5b3d8
--- /dev/null
+++ b/blt/src/bltImage.c
@@ -0,0 +1,2700 @@
+
+/*
+ * bltImage.c --
+ *
+ * This module implements image processing procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include <X11/Xutil.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+#define CLAMP(c) ((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+enum RightAngles { ROTATE_0, ROTATE_90, ROTATE_180, ROTATE_270 };
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateColorimage --
+ *
+ * Allocates a color image of a designated height and width.
+ *
+ * This routine will be augmented with other types of information
+ * such as a color table, etc.
+ *
+ * Results:
+ * Returns the new color image.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_CreateColorimage(width, height)
+ int width, height; /* Dimensions of new image */
+{
+ struct Colorimage *imagePtr;
+ size_t size;
+
+ size = width * height;
+ imagePtr = Blt_Malloc(sizeof(struct Colorimage));
+ assert(imagePtr);
+ imagePtr->bits = Blt_Malloc(sizeof(Pix32) * size);
+ assert(imagePtr->bits);
+
+ imagePtr->width = width;
+ imagePtr->height = height;
+ return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeColorimage --
+ *
+ * Deallocates the given color image.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeColorimage(imagePtr)
+ struct Colorimage *imagePtr;
+{
+ Blt_Free(imagePtr->bits);
+ Blt_Free(imagePtr);
+}
+
+void
+Blt_GammaCorrectColorimage(src, newGamma)
+ Blt_Colorimage src;
+ double newGamma;
+{
+ unsigned int nPixels;
+ register Pix32 *srcPtr, *endPtr;
+ register unsigned int i;
+ double value;
+ unsigned char lut[256];
+ double invGamma;
+
+ invGamma = 1.0 / newGamma;
+ for (i = 0; i < 256; i++) {
+ value = 255.0 * pow((double)i / 255.0, invGamma);
+ lut[i] = (unsigned char)CLAMP(value);
+ }
+ nPixels = Blt_ColorimageWidth(src) * Blt_ColorimageHeight(src);
+ srcPtr = Blt_ColorimageBits(src);
+ for (endPtr = srcPtr + nPixels; srcPtr < endPtr; srcPtr++) {
+ srcPtr->Red = lut[srcPtr->Red];
+ srcPtr->Green = lut[srcPtr->Green];
+ srcPtr->Blue = lut[srcPtr->Blue];
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToGreyscale --
+ *
+ * Converts a color image to PostScript grey scale (1 component)
+ * output. Luminosity isn't computed using the old NTSC formula,
+ *
+ * Y = 0.299 * Red + 0.587 * Green + 0.114 * Blue
+ *
+ * but the following
+ *
+ * Y = 0.212671 * Red + 0.715160 * Green + 0.072169 * Blue
+ *
+ * which better represents contemporary monitors.
+ *
+ * Results:
+ * The color image is converted to greyscale.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorimageToGreyscale(image)
+ Blt_Colorimage image;
+{
+ register Pix32 *srcPtr, *endPtr;
+ double Y;
+ int nPixels;
+ int width, height;
+
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+ nPixels = width * height;
+ srcPtr = Blt_ColorimageBits(image);
+ for (endPtr = srcPtr + nPixels; srcPtr < endPtr; srcPtr++) {
+ Y = ((0.212671 * (double)srcPtr->Red) +
+ (0.715160 * (double)srcPtr->Green) +
+ (0.072169 * (double)srcPtr->Blue));
+ srcPtr->Red = srcPtr->Green = srcPtr->Blue = (unsigned char)CLAMP(Y);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPhoto --
+ *
+ * Translates a color image into a Tk photo.
+ *
+ * Results:
+ * The photo is re-written with the new color image.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorimageToPhoto(src, photo)
+ Blt_Colorimage src; /* Image to use as source */
+ Tk_PhotoHandle photo; /* Photo to write color image into */
+{
+ Tk_PhotoImageBlock dest;
+ int width, height;
+
+ width = Blt_ColorimageWidth(src);
+ height = Blt_ColorimageHeight(src);
+
+ Tk_PhotoGetImage(photo, &dest);
+ dest.pixelSize = sizeof(Pix32);
+ dest.pitch = sizeof(Pix32) * width;
+ dest.width = width;
+ dest.height = height;
+ dest.offset[0] = Tk_Offset(Pix32, Red);
+ dest.offset[1] = Tk_Offset(Pix32, Green);
+ dest.offset[2] = Tk_Offset(Pix32, Blue);
+ dest.offset[3] = Tk_Offset(Pix32, Alpha);
+ dest.pixelPtr = (unsigned char *)Blt_ColorimageBits(src);
+ Tk_PhotoSetSize(photo, width, height);
+ Tk_PhotoPutBlock(photo, &dest, 0, 0, width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PhotoRegionToColorimage --
+ *
+ * Create a photo to a color image.
+ *
+ * Results:
+ * The new color image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_PhotoRegionToColorimage(photo, x, y, width, height)
+ Tk_PhotoHandle photo; /* Source photo image to scale */
+ int x, y;
+ int width, height;
+{
+ Tk_PhotoImageBlock src;
+ Blt_Colorimage image;
+ register Pix32 *destPtr;
+ register unsigned char *srcData;
+ register int offset;
+ unsigned int offR, offG, offB, offA;
+
+ Tk_PhotoGetImage(photo, &src);
+ if (x < 0) {
+ x = 0;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ if (width < 0) {
+ width = src.width;
+ }
+ if (height < 0) {
+ height = src.height;
+ }
+ if ((x + width) > src.width) {
+ width = src.width - x;
+ }
+ if ((height + y) > src.height) {
+ height = src.width - y;
+ }
+ image = Blt_CreateColorimage(width, height);
+ destPtr = Blt_ColorimageBits(image);
+
+ offset = (x * src.pixelSize) + (y * src.pitch);
+
+ offR = src.offset[0];
+ offG = src.offset[1];
+ offB = src.offset[2];
+ offA = src.offset[3];
+
+ if (src.pixelSize == 4) {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = srcData[offR];
+ destPtr->Green = srcData[offG];
+ destPtr->Blue = srcData[offB];
+ destPtr->Alpha = srcData[offA];
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ } else if (src.pixelSize == 3) {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = srcData[offR];
+ destPtr->Green = srcData[offG];
+ destPtr->Blue = srcData[offB];
+ /* No transparency information */
+ destPtr->Alpha = (unsigned char)-1;
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ } else {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = destPtr->Green = destPtr->Blue = srcData[offA];
+ /* No transparency information */
+ destPtr->Alpha = (unsigned char)-1;
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ }
+ return image;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PhotoToColorimage --
+ *
+ * Create a photo to a color image.
+ *
+ * Results:
+ * The new color image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_PhotoToColorimage(photo)
+ Tk_PhotoHandle photo; /* Source photo image to scale */
+
+{
+ Blt_Colorimage image;
+ Tk_PhotoImageBlock src;
+ int width, height;
+ register Pix32 *destPtr;
+ register int offset;
+ register int x, y;
+ register unsigned char *srcData;
+
+ Tk_PhotoGetImage(photo, &src);
+ width = src.width;
+ height = src.height;
+ image = Blt_CreateColorimage(width, height);
+ destPtr = Blt_ColorimageBits(image);
+ offset = 0;
+ if (src.pixelSize == 4) {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = srcData[src.offset[0]];
+ destPtr->Green = srcData[src.offset[1]];
+ destPtr->Blue = srcData[src.offset[2]];
+ destPtr->Alpha = srcData[src.offset[3]];
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ } else if (src.pixelSize == 3) {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = srcData[src.offset[0]];
+ destPtr->Green = srcData[src.offset[1]];
+ destPtr->Blue = srcData[src.offset[2]];
+ /* No transparency information */
+ destPtr->Alpha = (unsigned char)-1;
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ } else {
+ for (y = 0; y < height; y++) {
+ srcData = src.pixelPtr + offset;
+ for (x = 0; x < width; x++) {
+ destPtr->Red = destPtr->Green = destPtr->Blue =
+ srcData[src.offset[0]];
+ /* No transparency information */
+ destPtr->Alpha = (unsigned char)-1;
+ srcData += src.pixelSize;
+ destPtr++;
+ }
+ offset += src.pitch;
+ }
+ }
+ return image;
+}
+
+/*
+ * filter function definitions
+ */
+
+#ifdef __STDC__
+static ResampleFilterProc DefaultFilter;
+static ResampleFilterProc BellFilter;
+static ResampleFilterProc BesselFilter;
+static ResampleFilterProc BoxFilter;
+static ResampleFilterProc BSplineFilter;
+static ResampleFilterProc CatRomFilter;
+static ResampleFilterProc DummyFilter;
+static ResampleFilterProc GaussianFilter;
+static ResampleFilterProc GiFilter;
+static ResampleFilterProc Lanczos3Filter;
+static ResampleFilterProc MitchellFilter;
+static ResampleFilterProc SincFilter;
+static ResampleFilterProc TriangleFilter;
+static Tk_ImageChangedProc TempImageChangedProc;
+#endif
+
+static double
+DefaultFilter(x)
+ double x;
+{
+ if (x < 0.0) {
+ x = -x;
+ }
+ if (x < 1.0) {
+ /* f(x) = 2x^3 - 3x^2 + 1, -1 <= x <= 1 */
+ return (2.0 * x - 3.0) * x * x + 1.0;
+ }
+ return 0.0;
+}
+
+/* Just for testing */
+static double
+DummyFilter(x)
+ double x;
+{
+ return FABS(x);
+}
+
+/*
+ *
+ * Finite filters in increasing order:
+ * Box (constant)
+ * Triangle (linear)
+ * Bell
+ * BSpline (cubic)
+ *
+ */
+static double
+BoxFilter(x)
+ double x;
+{
+ if ((x < -0.5) || (x > 0.5)) {
+ return 0.0;
+ }
+ return 1.0;
+}
+
+static double
+TriangleFilter(x)
+ double x;
+{
+ if (x < 0.0) {
+ x = -x;
+ }
+ if (x < 1.0) {
+ return (1.0 - x);
+ }
+ return 0.0;
+}
+
+static double
+BellFilter(x)
+ double x;
+{
+ if (x < 0.0) {
+ x = -x;
+ }
+ if (x < 0.5) {
+ return (0.75 - (x * x));
+ }
+ if (x < 1.5) {
+ x = (x - 1.5);
+ return (0.5 * (x * x));
+ }
+ return 0.0;
+}
+
+static double
+BSplineFilter(x)
+ double x;
+{
+ double x2;
+
+ if (x < 0.0) {
+ x = -x;
+ }
+ if (x < 1) {
+ x2 = x * x;
+ return ((.5 * x2 * x) - x2 + (2.0 / 3.0));
+ } else if (x < 2) {
+ x = 2 - x;
+ return ((x * x * x) / 6.0);
+ }
+ return 0.0;
+}
+
+/*
+ *
+ * Infinite Filters:
+ * Sinc perfect lowpass filter
+ * Bessel circularly symmetric 2-D filter
+ * Gaussian
+ * Lanczos3
+ * Mitchell
+ */
+
+static double
+SincFilter(x)
+ double x;
+{
+ x *= M_PI;
+ if (x == 0.0) {
+ return 1.0;
+ }
+ return (sin(x) / x);
+}
+
+static double
+BesselFilter(x)
+ double x;
+{
+#ifdef NEED_DECL_J1
+ extern double j1 _ANSI_ARGS_((double value));
+#endif
+ /*
+ * See Pratt "Digital Image Processing" p. 97 for Bessel functions
+ * zeros are at approx x=1.2197, 2.2331, 3.2383, 4.2411, 5.2428, 6.2439,
+ * 7.2448, 8.2454
+ */
+ return (x == 0.0) ? M_PI / 4.0 : j1(M_PI * x) / (x + x);
+}
+
+#define SQRT_2PI 0.79788456080286541 /* sqrt(2.0 / M_PI) */
+
+static double
+GaussianFilter(x)
+ double x;
+{
+ return exp(-2.0 * x * x) * SQRT_2PI;
+}
+
+static double
+Lanczos3Filter(x)
+ double x;
+{
+ if (x < 0) {
+ x = -x;
+ }
+ if (x < 3.0) {
+ return (SincFilter(x) * SincFilter(x / 3.0));
+ }
+ return 0.0;
+}
+
+#define B 0.3333333333333333 /* (1.0 / 3.0) */
+#define C 0.3333333333333333 /* (1.0 / 3.0) */
+
+static double
+MitchellFilter(x)
+ double x;
+{
+ double x2;
+
+ x2 = x * x;
+ if (x < 0) {
+ x = -x;
+ }
+ if (x < 1.0) {
+ x = (((12.0 - 9.0 * B - 6.0 * C) * (x * x2)) +
+ ((-18.0 + 12.0 * B + 6.0 * C) * x2) + (6.0 - 2 * B));
+ return (x / 6.0);
+ } else if (x < 2.0) {
+ x = (((-1.0 * B - 6.0 * C) * (x * x2)) + ((6.0 * B + 30.0 * C) * x2) +
+ ((-12.0 * B - 48.0 * C) * x) + (8.0 * B + 24 * C));
+ return (x / 6.0);
+ }
+ return 0.0;
+}
+
+/*
+ * Catmull-Rom spline
+ */
+static double
+CatRomFilter(x)
+ double x;
+{
+ if (x < -2.) {
+ return 0.0;
+ }
+ if (x < -1.0) {
+ return 0.5 * (4.0 + x * (8.0 + x * (5.0 + x)));
+ }
+ if (x < 0.0) {
+ return 0.5 * (2.0 + x * x * (-5.0 + x * -3.0));
+ }
+ if (x < 1.0) {
+ return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
+ }
+ if (x < 2.0) {
+ return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
+ }
+ return 0.0;
+}
+
+/* approximation to the gaussian integral [x, inf) */
+static double
+GiFilter(x)
+ double x;
+{
+ if (x > 1.5) {
+ return 0.0;
+ } else if (x < -1.5) {
+ return 1.0;
+ } else {
+#define I6 0.166666666666667
+#define I4 0.25
+#define I3 0.333333333333333
+ double x2 = x * x;
+ double x3 = x2 * x;
+
+ if (x > 0.5) {
+ return .5625 - ( x3 * I6 - 3 * x2 * I4 + 1.125 * x);
+ } else if (x > -0.5) {
+ return 0.5 - (0.75 * x - x3 * I3);
+ } else {
+ return 0.4375 + (-x3 * I6 - 3 * x2 * I4 - 1.125 * x);
+ }
+ }
+}
+
+
+
+static ResampleFilter filterTable[] =
+{
+ /* name, function, support */
+ {"bell", BellFilter, 1.5 },
+ {"bessel", BesselFilter, 3.2383 },
+ {"box", BoxFilter, 0.5 },
+ {"bspline", BSplineFilter, 2.0 },
+ {"catrom", CatRomFilter, 2.0 },
+ {"default", DefaultFilter, 1.0 },
+ {"dummy", DummyFilter, 0.5 },
+ {"gauss8", GaussianFilter, 8.0 },
+ {"gaussian", GaussianFilter, 1.25 },
+ {"gi", GiFilter, 1.25 },
+ {"lanczos3", Lanczos3Filter, 3.0 },
+ {"mitchell", MitchellFilter, 2.0 },
+ {"none", (ResampleFilterProc *)NULL, 0.0 },
+ {"sinc", SincFilter, 4.0 },
+ {"triangle", TriangleFilter, 1.0 },
+};
+
+static int nFilters = sizeof(filterTable) / sizeof(ResampleFilter);
+
+ResampleFilter *bltBoxFilterPtr = &(filterTable[1]);
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetResampleFilter --
+ *
+ * Finds a 1-D filter associated by the given filter name.
+ *
+ * Results:
+ * A standard Tcl result. Returns TCL_OK is the filter was
+ * found. The filter information (proc and support) is returned
+ * via filterPtrPtr. Otherwise TCL_ERROR is returned and an error
+ * message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetResampleFilter(interp, name, filterPtrPtr)
+ Tcl_Interp *interp;
+ char *name;
+ ResampleFilter **filterPtrPtr;
+{
+ ResampleFilter *filterPtr, *endPtr;
+
+ endPtr = filterTable + nFilters;
+ for (filterPtr = filterTable; filterPtr < endPtr; filterPtr++) {
+ if (strcmp(name, filterPtr->name) == 0) {
+ *filterPtrPtr = (filterPtr->proc == NULL) ? NULL : filterPtr;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "can't find filter \"", name, "\"", (char *)NULL);
+ return TCL_ERROR;
+}
+
+
+/*
+ * Scaled integers are fixed point values. The upper 18 bits is the integer
+ * portion, the lower 14 bits the fractional remainder. Must be careful
+ * not to overflow the values (especially during multiplication).
+ *
+ * The following operations are defined:
+ *
+ * S * n Scaled integer times an integer.
+ * S1 + S2 Scaled integer plus another scaled integer.
+ *
+ */
+
+#define float2si(f) (int)((f) * 16384.0 + 0.5)
+#define uchar2si(b) (((int)(b)) << 14)
+#define si2int(s) (((s) + 8192) >> 14)
+
+#ifdef notdef
+typedef struct {
+ int pixel;
+ union Weight {
+ int i; /* Fixed point, scaled integer. */
+ float f;
+ } weight;
+} Sample;
+
+typedef struct {
+ int count; /* Number of contributors */
+ Sample *samples; /* Array of contributors */
+} Contribution;
+
+typedef struct {
+ int pixel;
+ union Weight {
+ int i; /* Fixed point, scaled integer. */
+ float f;
+ } weight;
+} Sample;
+#endif
+
+
+typedef union {
+ int i; /* Fixed point, scaled integer. */
+ float f;
+} Weight;
+
+typedef struct {
+ int count; /* Number of samples. */
+ int start;
+ Weight weights[1]; /* Array of weights. */
+} Sample;
+
+static size_t
+ComputeWeights(srcWidth, destWidth, filterPtr, samplePtrPtr)
+ int srcWidth, destWidth;
+ ResampleFilter *filterPtr;
+ Sample **samplePtrPtr;
+{
+ Sample *samples;
+ double scale;
+ int filterSize;
+ double center;
+ register Sample *s;
+ register Weight *weight;
+ register int x, i;
+ register int left, right; /* filter bounds */
+ double factor, sum;
+ size_t size;
+
+ /* Pre-calculate filter contributions for a row */
+ scale = (double)destWidth / (double)srcWidth;
+
+ if (scale < 1.0) {
+ double radius, fscale;
+
+ /* Downsample */
+
+ radius = filterPtr->support / scale;
+ fscale = 1.0 / scale;
+ filterSize = (int)(radius * 2 + 2);
+
+ size = sizeof(Sample) + (filterSize - 1) * sizeof(Weight);
+ samples = Blt_Calloc(destWidth, size);
+ assert(samples);
+
+ s = samples;
+ for (x = 0; x < destWidth; x++) {
+ center = (double)x * fscale;
+
+ /* Determine bounds of filter and its density */
+ left = (int)(center - radius + 0.5);
+ if (left < 0) {
+ left = 0;
+ }
+ right = (int)(center + radius + 0.5);
+ if (right >= srcWidth) {
+ right = srcWidth - 1;
+ }
+ sum = 0.0;
+ s->start = left;
+ for (weight = s->weights, i = left; i <= right; i++, weight++) {
+ weight->f = (float)
+ (*filterPtr->proc) (((double)i + 0.5 - center) * scale);
+ sum += weight->f;
+ }
+ s->count = right - left + 1;
+
+ factor = (sum == 0.0) ? 1.0 : (1.0 / sum);
+ for (weight = s->weights, i = left; i <= right; i++, weight++) {
+ weight->f = (float)(weight->f * factor);
+ weight->i = float2si(weight->f);
+ }
+ s = (Sample *)((char *)s + size);
+ }
+ } else {
+ double fscale;
+ /* Upsample */
+
+ filterSize = (int)(filterPtr->support * 2 + 2);
+ size = sizeof(Sample) + (filterSize - 1) * sizeof(Weight);
+ samples = Blt_Calloc(destWidth, size);
+ assert(samples);
+
+ fscale = 1.0 / scale;
+
+ s = samples;
+ for (x = 0; x < destWidth; x++) {
+ center = (double)x * fscale;
+ left = (int)(center - filterPtr->support + 0.5);
+ if (left < 0) {
+ left = 0;
+ }
+ right = (int)(center + filterPtr->support + 0.5);
+ if (right >= srcWidth) {
+ right = srcWidth - 1;
+ }
+ sum = 0.0;
+ s->start = left;
+ for (weight = s->weights, i = left; i <= right; i++, weight++) {
+ weight->f = (float)
+ (*filterPtr->proc) ((double)i - center + 0.5);
+ sum += weight->f;
+ }
+ s->count = right - left + 1;
+ factor = (sum == 0.0) ? 1.0 : (1.0 / sum);
+ for (weight = s->weights, i = left; i <= right; i++, weight++) {
+ weight->f = (float)(weight->f * factor);
+ weight->i = float2si(weight->f);
+ }
+ s = (Sample *)((char *)s + size);
+ }
+ }
+ *samplePtrPtr = samples;
+ return size;
+}
+
+/*
+ * The following macro converts a fixed-point scaled integer to a
+ * byte, clamping the value between 0 and 255.
+ */
+#define SICLAMP(s) \
+ (unsigned char)(((s) < 0) ? 0 : ((s) > 4177920) ? 255 : (si2int(s)))
+
+static void
+ZoomImageVertically(src, dest, filterPtr)
+ Blt_Colorimage src, dest;
+ ResampleFilter *filterPtr;
+{
+ Sample *samples, *s, *endPtr;
+ int destWidth, destHeight;
+ int red, green, blue, alpha;
+ int srcWidth, srcHeight;
+ register Pix32 *srcColumnPtr;
+ register Pix32 *srcPtr, *destPtr;
+ register Weight *weight;
+ int x, i;
+ size_t size; /* Size of sample. */
+
+ srcWidth = Blt_ColorimageWidth(src);
+ srcHeight = Blt_ColorimageHeight(src);
+ destWidth = Blt_ColorimageWidth(dest);
+ destHeight = Blt_ColorimageHeight(dest);
+
+ /* Pre-calculate filter contributions for a row */
+ size = ComputeWeights(srcHeight, destHeight, filterPtr, &samples);
+ endPtr = (Sample *)((char *)samples + (destHeight * size));
+
+ /* Apply filter to zoom vertically from tmp to destination */
+ for (x = 0; x < srcWidth; x++) {
+ srcColumnPtr = Blt_ColorimageBits(src) + x;
+ destPtr = Blt_ColorimageBits(dest) + x;
+ for (s = samples; s < endPtr; s = (Sample *)((char *)s + size)) {
+ red = green = blue = alpha = 0;
+ srcPtr = srcColumnPtr + (s->start * srcWidth);
+ for (weight = s->weights, i = 0; i < s->count; i++, weight++) {
+ red += srcPtr->Red * weight->i;
+ green += srcPtr->Green * weight->i;
+ blue += srcPtr->Blue * weight->i;
+ alpha += srcPtr->Alpha * weight->i;
+ srcPtr += srcWidth;
+ }
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ destPtr += destWidth;
+
+ }
+ }
+ /* Free the memory allocated for filter weights */
+ Blt_Free(samples);
+}
+
+static void
+ZoomImageHorizontally(src, dest, filterPtr)
+ Blt_Colorimage src, dest;
+ ResampleFilter *filterPtr;
+{
+ Sample *samples, *s, *endPtr;
+ Weight *weight;
+ int destWidth, destHeight;
+ int red, green, blue, alpha;
+ int srcWidth, srcHeight;
+ int y, i;
+ register Pix32 *srcPtr, *destPtr;
+ register Pix32 *srcRowPtr;
+ size_t size; /* Size of sample. */
+
+ srcWidth = Blt_ColorimageWidth(src);
+ srcHeight = Blt_ColorimageHeight(src);
+ destWidth = Blt_ColorimageWidth(dest);
+ destHeight = Blt_ColorimageHeight(dest);
+
+ /* Pre-calculate filter contributions for a row */
+ size = ComputeWeights(srcWidth, destWidth, filterPtr, &samples);
+ endPtr = (Sample *)((char *)samples + (destWidth * size));
+
+ /* Apply filter to zoom horizontally from srcPtr to tmpPixels */
+ srcRowPtr = Blt_ColorimageBits(src);
+ destPtr = Blt_ColorimageBits(dest);
+ for (y = 0; y < srcHeight; y++) {
+ for (s = samples; s < endPtr; s = (Sample *)((char *)s + size)) {
+ red = green = blue = alpha = 0;
+ srcPtr = srcRowPtr + s->start;
+ for (weight = s->weights, i = 0; i < s->count; i++, weight++) {
+ red += srcPtr->Red * weight->i;
+ green += srcPtr->Green * weight->i;
+ blue += srcPtr->Blue * weight->i;
+ alpha += srcPtr->Alpha * weight->i;
+ srcPtr++;
+ }
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ destPtr++;
+ }
+ srcRowPtr += srcWidth;
+ }
+ /* free the memory allocated for horizontal filter weights */
+ Blt_Free(samples);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResampleColorimage --
+ *
+ * Resamples a given color image using 1-D filters and returns
+ * a new color image of the designated size.
+ *
+ * Results:
+ * Returns the resampled color image. The original color image
+ * is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_ResampleColorimage(src, width, height, horzFilterPtr, vertFilterPtr)
+ Blt_Colorimage src;
+ int width, height;
+ ResampleFilter *horzFilterPtr, *vertFilterPtr;
+{
+ Blt_Colorimage tmp, dest;
+
+ /*
+ * It's usually faster to zoom vertically last. This has to do
+ * with the fact that images are stored in contiguous rows.
+ */
+
+ tmp = Blt_CreateColorimage(width, Blt_ColorimageHeight(src));
+ ZoomImageHorizontally(src, tmp, horzFilterPtr);
+ dest = Blt_CreateColorimage(width, height);
+ ZoomImageVertically(tmp, dest, vertFilterPtr);
+ Blt_FreeColorimage(tmp);
+ return dest;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResamplePhoto --
+ *
+ * Resamples a Tk photo image using 1-D filters and writes the
+ * image into another Tk photo. It is possible for the
+ * source and destination to be the same photo.
+ *
+ * Results:
+ * The designated destination photo will contain the resampled
+ * color image. The original photo is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ResamplePhoto(srcPhoto, x, y, width, height, destPhoto, horzFilterPtr,
+ vertFilterPtr)
+ Tk_PhotoHandle srcPhoto; /* Source photo image to scale */
+ int x, y;
+ int width, height;
+ Tk_PhotoHandle destPhoto; /* Resulting scaled photo image */
+ ResampleFilter *horzFilterPtr, *vertFilterPtr;
+{
+ Blt_Colorimage srcImage, destImage;
+ Tk_PhotoImageBlock dest;
+
+ Tk_PhotoGetImage(destPhoto, &dest);
+ srcImage = Blt_PhotoRegionToColorimage(srcPhoto, x, y, width, height);
+ destImage = Blt_ResampleColorimage(srcImage, dest.width, dest.height,
+ horzFilterPtr, vertFilterPtr);
+ Blt_FreeColorimage(srcImage);
+ Blt_ColorimageToPhoto(destImage, destPhoto);
+ Blt_FreeColorimage(destImage);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizePhoto --
+ *
+ * Scales the region of the source image to the size of the
+ * destination image. This routine performs raw scaling of
+ * the image and unlike Blt_ResamplePhoto does not handle
+ * aliasing effects from subpixel sampling. It is possible
+ * for the source and destination to be the same photo.
+ *
+ * Results:
+ * The designated destination photo will contain the resampled
+ * color image. The original photo is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ResizePhoto(srcPhoto, x, y, width, height, destPhoto)
+ Tk_PhotoHandle srcPhoto; /* Source photo image to scaled. */
+ register int x, y; /* Region of source photo to be
+ * scaled. */
+ int width, height;
+ Tk_PhotoHandle destPhoto; /* (out) Resulting scaled photo image.
+ * Scaling factors are derived from
+ * the destination photo's
+ * dimensions. */
+{
+ register int sx, sy;
+ double xScale, yScale;
+ Blt_Colorimage destImage;
+ Pix32 *destPtr;
+ Tk_PhotoImageBlock src, dest;
+ unsigned char *srcPtr, *srcRowPtr;
+ int *mapX, *mapY;
+ int left, right, top, bottom;
+
+ Tk_PhotoGetImage(srcPhoto, &src);
+ Tk_PhotoGetImage(destPhoto, &dest);
+
+ left = x, top = y; right = x + width - 1, bottom = y + height - 1;
+ destImage = Blt_CreateColorimage(dest.width, dest.height);
+ xScale = (double)width / (double)dest.width;
+ yScale = (double)height / (double)dest.height;
+ mapX = (int *)Blt_Malloc(sizeof(int) * dest.width);
+ mapY = (int *)Blt_Malloc(sizeof(int) * dest.height);
+ for(x = 0; x < dest.width; x++) {
+ mapX[x] = (int)(xScale * (double)x);
+ }
+ for(y = 0; y < dest.height; y++) {
+ mapY[y] = (int)(yScale * (double)y);
+ }
+ destPtr = Blt_ColorimageBits(destImage);
+ if (src.pixelSize == 4) {
+ for (y = 0; y < dest.height; y++) {
+ sy = mapY[y] + top;
+ if (sy > bottom) {
+ sy = bottom;
+ }
+ srcRowPtr = src.pixelPtr + (sy * src.pitch);
+ for (x = 0; x < dest.width; x++) {
+ sx = mapX[x] + left;
+ if (sx > right) {
+ sx = right;
+ }
+ srcPtr = srcRowPtr + (sx * src.pixelSize);
+ destPtr->Red = srcPtr[src.offset[0]];
+ destPtr->Green = srcPtr[src.offset[1]];
+ destPtr->Blue = srcPtr[src.offset[2]];
+ destPtr->Alpha = srcPtr[src.offset[3]];
+ destPtr++;
+ }
+ }
+ } else if (src.pixelSize == 3) {
+ for (y = 0; y < dest.height; y++) {
+ sy = mapY[y] + top;
+ if (sy > bottom) {
+ sy = bottom;
+ }
+ srcRowPtr = src.pixelPtr + (sy * src.pitch);
+ for (x = 0; x < dest.width; x++) {
+ sx = mapX[x] + left;
+ if (sx > right) {
+ sx = right;
+ }
+ srcPtr = srcRowPtr + (sx * src.pixelSize);
+ destPtr->Red = srcPtr[src.offset[0]];
+ destPtr->Green = srcPtr[src.offset[1]];
+ destPtr->Blue = srcPtr[src.offset[2]];
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else {
+ for (y = 0; y < dest.height; y++) {
+ sy = mapY[y] + top;
+ if (sy > bottom) {
+ sy = bottom;
+ }
+ srcRowPtr = src.pixelPtr + (sy * src.pitch);
+ for (x = 0; x < dest.width; x++) {
+ sx = mapX[x] + left;
+ if (sx > right) {
+ sx = right;
+ }
+ srcPtr = srcRowPtr + (sx * src.pixelSize);
+ destPtr->Red = destPtr->Green = destPtr->Blue =
+ srcPtr[src.offset[0]];
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ }
+ Blt_Free(mapX);
+ Blt_Free(mapY);
+ Blt_ColorimageToPhoto(destImage, destPhoto);
+ Blt_FreeColorimage(destImage);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizeColorimage --
+ *
+ * Scales the region of the source image to the size of the
+ * destination image. This routine performs raw scaling of
+ * the image and unlike Blt_ResamplePhoto does not perform
+ * any antialiasing.
+ *
+ * Results:
+ * Returns the new resized color image. The original image
+ * is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_ResizeColorimage(src, x, y, width, height, destWidth, destHeight)
+ Blt_Colorimage src; /* Source color image to be scaled. */
+ register int x, y; /* Region of source image to scaled. */
+ int width, height;
+ int destWidth, destHeight; /* Requested dimensions of the scaled
+ * image. */
+{
+ register int sx, sy;
+ double xScale, yScale;
+ Blt_Colorimage dest;
+ Pix32 *srcPtr, *srcRowPtr, *destPtr;
+ int *mapX, *mapY;
+ int left, right, top, bottom;
+
+ left = x, top = y; right = x + width - 1, bottom = y + height - 1;
+
+ dest = Blt_CreateColorimage(destWidth, destHeight);
+ xScale = (double)width / (double)destWidth;
+ yScale = (double)height / (double)destHeight;
+ mapX = (int *)Blt_Malloc(sizeof(int) * destWidth);
+ mapY = (int *)Blt_Malloc(sizeof(int) * destHeight);
+ for(x = 0; x < destWidth; x++) {
+ mapX[x] = (int)(xScale * (double)x);
+ }
+ for(y = 0; y < destHeight; y++) {
+ mapY[y] = (int)(yScale * (double)y);
+ }
+ destPtr = Blt_ColorimageBits(dest);
+ for (y = 0; y < destHeight; y++) {
+ sy = mapY[y] + top;
+ if (sy > bottom) {
+ sy = bottom;
+ }
+ srcRowPtr = Blt_ColorimageBits(src) + (Blt_ColorimageWidth(src) * sy);
+ for (x = 0; x < destWidth; x++) {
+ sx = mapX[x] + left;
+ if (sx > right) {
+ sx = right;
+ }
+ srcPtr = srcRowPtr + sx;
+ destPtr->value = srcPtr->value; /* Copy the pixel. */
+ destPtr++;
+ }
+ }
+ Blt_Free(mapX);
+ Blt_Free(mapY);
+ return dest;
+}
+
+/*
+ * FIXME: Boundary handling could be better (pixels are replicated).
+ * It's slow. Take boundary tests out of inner loop.
+ */
+Blt_Colorimage
+Blt_ConvolveColorimage(src, filterPtr)
+ Blt_Colorimage src;
+ Filter2D *filterPtr;
+{
+ Blt_Colorimage dest;
+ register Pix32 *srcPtr, *destPtr;
+#define MAXROWS 24
+ register int sx, sy, dx, dy;
+ register int x, y;
+ double red, green, blue;
+ int width, height;
+ int radius;
+ register double *valuePtr;
+
+ width = Blt_ColorimageWidth(src);
+ height = Blt_ColorimageHeight(src);
+
+ dest = Blt_CreateColorimage(width, height);
+ radius = (int)filterPtr->support;
+ if (radius < 1) {
+ radius = 1;
+ }
+ destPtr = Blt_ColorimageBits(dest);
+ for (dy = 0; dy < height; dy++) {
+ for (dx = 0; dx < width; dx++) {
+ red = green = blue = 0.0;
+ valuePtr = filterPtr->kernel;
+ for (sy = (dy - radius); sy <= (dy + radius); sy++) {
+ y = sy;
+ if (y < 0) {
+ y = 0;
+ } else if (y >= height) {
+ y = height - 1;
+ }
+ for (sx = (dx - radius); sx <= (dx + radius); sx++) {
+ x = sx;
+ if (x < 0) {
+ x = 0;
+ } else if (sx >= width) {
+ x = width - 1;
+ }
+ srcPtr = Blt_ColorimagePixel(src, x, y);
+ red += *valuePtr * (double)srcPtr->Red;
+ green += *valuePtr * (double)srcPtr->Green;
+ blue += *valuePtr * (double)srcPtr->Blue;
+#ifdef notdef
+ fprintf(stderr, "%d,%d = r=%f,g=%f,b=%f\n", x, y,
+ red, green, blue);
+#endif
+ valuePtr++;
+ }
+ }
+ red /= filterPtr->sum;
+ green /= filterPtr->sum;
+ blue /= filterPtr->sum;
+ destPtr->Red = (unsigned char)CLAMP(red);
+ destPtr->Green = (unsigned char)CLAMP(green);
+ destPtr->Blue = (unsigned char)CLAMP(blue);
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ return dest;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SnapPhoto --
+ *
+ * Takes a snapshot of an X drawable (pixmap or window) and
+ * writes it to an existing Tk photo image.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * The named Tk photo is updated with the snapshot.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_SnapPhoto(interp, tkwin, drawable, x, y, width, height, destWidth,
+ destHeight, photoName, inputGamma)
+ Tcl_Interp *interp; /* Interpreter to report errors back to */
+ Tk_Window tkwin;
+ Drawable drawable; /* Window or pixmap to be snapped */
+ int x, y; /* Offset of image from drawable origin. */
+ int width, height; /* Dimension of the drawable */
+ int destWidth, destHeight; /* Desired size of the Tk photo */
+ char *photoName; /* Name of an existing Tk photo image. */
+ double inputGamma;
+{
+ Tk_PhotoHandle photo; /* The photo image to write into. */
+ Blt_Colorimage image;
+
+ photo = Blt_FindPhoto(interp, photoName);
+ if (photo == NULL) {
+ Tcl_AppendResult(interp, "can't find photo \"", photoName, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ image = Blt_DrawableToColorimage(tkwin, drawable, x, y, width, height,
+ inputGamma);
+ if (image == NULL) {
+ Tcl_AppendResult(interp,
+ "can't grab window or pixmap (possibly obscured?)", (char *)NULL);
+ return TCL_ERROR; /* Can't grab window image */
+ }
+ if ((destWidth != width) || (destHeight != height)) {
+ Blt_Colorimage destImage;
+
+ /*
+ * The requested size for the destination image is different than
+ * that of the source snapshot. Resample the image as necessary.
+ * We'll use a cheap box filter. I'm assuming that the destination
+ * image will typically be smaller than the original.
+ */
+ destImage = Blt_ResampleColorimage(image, destWidth, destHeight,
+ bltBoxFilterPtr, bltBoxFilterPtr);
+ Blt_FreeColorimage(image);
+ image = destImage;
+ }
+ Blt_ColorimageToPhoto(image, photo);
+ Blt_FreeColorimage(image);
+ return TCL_OK;
+}
+
+#if HAVE_JPEG
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToPhoto --
+ *
+ * Reads a JPEG file and converts it into a Tk photo.
+ *
+ * Results:
+ * A standard Tcl result. If successful, TCL_OK is returned
+ * and the designated photo is re-written with the image.
+ * Otherwise, TCL_ERROR is returned and interp->result will
+ * contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_JPEGToPhoto(interp, fileName, photo)
+ Tcl_Interp *interp;
+ char *fileName;
+ Tk_PhotoHandle photo; /* The photo image to write into. */
+{
+ Blt_Colorimage image;
+
+ image = Blt_JPEGToColorimage(interp, fileName);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_ColorimageToPhoto(image, photo);
+ Blt_FreeColorimage(image);
+ return TCL_OK;
+}
+#endif /* HAVE_JPEG */
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * ShearY --
+ *
+ * Shears a row horizontally. Antialiasing limited to filtering
+ * two adjacent pixels. So the shear angle must be between +-45
+ * degrees.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The sheared image is drawn into the destination color image.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void
+ShearY(src, dest, y, offset, frac, bgColor)
+ Blt_Colorimage src, dest;
+ int y; /* Designates the row to be sheared */
+ int offset; /* Difference between of */
+ double frac;
+ Pix32 bgColor;
+{
+ Pix32 *srcPtr, *destPtr;
+ Pix32 *srcRowPtr, *destRowPtr;
+ register int x, dx;
+ int destWidth;
+ int srcWidth;
+ int red, blue, green, alpha;
+ int leftRed, leftGreen, leftBlue, leftAlpha;
+ int oldLeftRed, oldLeftGreen, oldLeftBlue, oldLeftAlpha;
+ int ifrac;
+
+ srcWidth = Blt_ColorimageWidth(src);
+ destWidth = Blt_ColorimageWidth(dest);
+
+ destRowPtr = Blt_ColorimageBits(dest) + (y * destWidth);
+ srcRowPtr = Blt_ColorimageBits(src) + (y * srcWidth);
+
+ destPtr = destRowPtr;
+ for (x = 0; x < offset; x++) {
+ *destPtr++ = bgColor;
+ }
+ destPtr = destRowPtr + offset;
+ srcPtr = srcRowPtr;
+ dx = offset;
+
+ oldLeftRed = uchar2si(bgColor.Red);
+ oldLeftGreen = uchar2si(bgColor.Green);
+ oldLeftBlue = uchar2si(bgColor.Blue);
+ oldLeftAlpha = uchar2si(bgColor.Alpha);
+
+ ifrac = float2si(frac);
+ for (x = 0; x < srcWidth; x++, dx++) { /* Loop through row pixels */
+ leftRed = srcPtr->Red * ifrac;
+ leftGreen = srcPtr->Green * ifrac;
+ leftBlue = srcPtr->Blue * ifrac;
+ leftAlpha = srcPtr->Alpha * ifrac;
+ if ((dx >= 0) && (dx < destWidth)) {
+ red = uchar2si(srcPtr->Red) - (leftRed - oldLeftRed);
+ green = uchar2si(srcPtr->Green) - (leftGreen - oldLeftGreen);
+ blue = uchar2si(srcPtr->Blue) - (leftBlue - oldLeftBlue);
+ alpha = uchar2si(srcPtr->Alpha) - (leftAlpha - oldLeftAlpha);
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ }
+ oldLeftRed = leftRed;
+ oldLeftGreen = leftGreen;
+ oldLeftBlue = leftBlue;
+ oldLeftAlpha = leftAlpha;
+ srcPtr++, destPtr++;
+ }
+ x = srcWidth + offset;
+ destPtr = Blt_ColorimageBits(dest) + (y * destWidth) + x;
+ if (x < destWidth) {
+ leftRed = uchar2si(bgColor.Red);
+ leftGreen = uchar2si(bgColor.Green);
+ leftBlue = uchar2si(bgColor.Blue);
+ leftAlpha = uchar2si(bgColor.Alpha);
+
+ red = leftRed + oldLeftRed - (bgColor.Red * ifrac);
+ green = leftGreen + oldLeftGreen - (bgColor.Green * ifrac);
+ blue = leftBlue + oldLeftBlue - (bgColor.Blue * ifrac);
+ alpha = leftAlpha + oldLeftAlpha - (bgColor.Alpha * ifrac);
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ destPtr++;
+ }
+ for (x++; x < destWidth; x++) {
+ *destPtr++ = bgColor;
+ }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * ShearX --
+ *
+ * Shears a column. Antialiasing is limited to filtering two
+ * adjacent pixels. So the shear angle must be between +-45
+ * degrees.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The sheared image is drawn into the destination color image.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void
+ShearX(src, dest, x, offset, frac, bgColor)
+ Blt_Colorimage src, dest;
+ int x; /* Column in source image to be sheared. */
+ int offset; /* Offset of */
+ double frac; /* Fraction of subpixel. */
+ Pix32 bgColor;
+{
+ Pix32 *srcPtr, *destPtr;
+ register int y, dy;
+ int destWidth, destHeight;
+ int srcWidth, srcHeight;
+ int red, blue, green, alpha;
+ int leftRed, leftGreen, leftBlue, leftAlpha;
+ int oldLeftRed, oldLeftGreen, oldLeftBlue, oldLeftAlpha;
+ int ifrac;
+
+ srcWidth = Blt_ColorimageWidth(src);
+ srcHeight = Blt_ColorimageHeight(src);
+ destWidth = Blt_ColorimageWidth(dest);
+ destHeight = Blt_ColorimageHeight(dest);
+
+ destPtr = Blt_ColorimageBits(dest) + x;
+ for (y = 0; y < offset; y++) {
+ destPtr = Blt_ColorimagePixel(dest, x, y);
+ *destPtr = bgColor;
+ destPtr += destWidth;
+ }
+
+ oldLeftRed = uchar2si(bgColor.Red);
+ oldLeftGreen = uchar2si(bgColor.Green);
+ oldLeftBlue = uchar2si(bgColor.Blue);
+ oldLeftAlpha = uchar2si(bgColor.Alpha);
+ destPtr = Blt_ColorimageBits(dest) + x + offset;
+ srcPtr = Blt_ColorimageBits(src) + x;
+ dy = offset;
+ ifrac = float2si(frac);
+ for (y = 0; y < srcHeight; y++, dy++) {
+ srcPtr = Blt_ColorimagePixel(src, x, y);
+ leftRed = srcPtr->Red * ifrac;
+ leftGreen = srcPtr->Green * ifrac;
+ leftBlue = srcPtr->Blue * ifrac;
+ leftAlpha = srcPtr->Alpha * ifrac;
+ if ((dy >= 0) && (dy < destHeight)) {
+ destPtr = Blt_ColorimagePixel(dest, x, dy);
+ red = uchar2si(srcPtr->Red) - (leftRed - oldLeftRed);
+ green = uchar2si(srcPtr->Green) - (leftGreen - oldLeftGreen);
+ blue = uchar2si(srcPtr->Blue) - (leftBlue - oldLeftBlue);
+ alpha = uchar2si(srcPtr->Alpha) - (leftAlpha - oldLeftAlpha);
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ }
+ oldLeftRed = leftRed;
+ oldLeftGreen = leftGreen;
+ oldLeftBlue = leftBlue;
+ oldLeftAlpha = leftAlpha;
+ srcPtr += srcWidth;
+ destPtr += destWidth;
+ }
+ y = srcHeight + offset;
+ destPtr = Blt_ColorimageBits(dest) + (y * destWidth) + x + offset;
+ if (y < destHeight) {
+ leftRed = uchar2si(bgColor.Red);
+ leftGreen = uchar2si(bgColor.Green);
+ leftBlue = uchar2si(bgColor.Blue);
+ leftAlpha = uchar2si(bgColor.Alpha);
+
+ destPtr = Blt_ColorimagePixel(dest, x, y);
+ red = leftRed + oldLeftRed - (bgColor.Red * ifrac);
+ green = leftGreen + oldLeftGreen - (bgColor.Green * ifrac);
+ blue = leftBlue + oldLeftBlue - (bgColor.Blue * ifrac);
+ alpha = leftAlpha + oldLeftAlpha - (bgColor.Alpha * ifrac);
+ destPtr->Red = SICLAMP(red);
+ destPtr->Green = SICLAMP(green);
+ destPtr->Blue = SICLAMP(blue);
+ destPtr->Alpha = SICLAMP(alpha);
+ destPtr += destWidth;
+ }
+
+ for (y++; y < destHeight; y++) {
+ destPtr = Blt_ColorimagePixel(dest, x, y);
+ *destPtr = bgColor;
+ destPtr += destWidth;
+ }
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate45 --
+ *
+ * Rotates an image by a given angle. The angle must be in the
+ * range -45.0 to 45.0 inclusive. Anti-aliasing filtering is
+ * performed on two adjacent pixels, so the angle can't be so
+ * great as to force a sheared pixel to occupy 3 destination
+ * pixels. Performs a three shear rotation described below.
+ *
+ * Reference: Alan W. Paeth, "A Fast Algorithm for General Raster
+ * Rotation", Graphics Gems, pp 179-195.
+ *
+ *
+ * Results:
+ * Returns a newly allocated rotated image.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static Blt_Colorimage
+Rotate45(src, theta, bgColor)
+ Blt_Colorimage src;
+ double theta;
+ Pix32 bgColor;
+{
+ int tmpWidth, tmpHeight;
+ int srcWidth, srcHeight;
+ double sinTheta, cosTheta, tanTheta;
+ double skewf;
+ int skewi;
+ Blt_Colorimage tmp1, tmp2, dest;
+ register int x, y;
+
+ sinTheta = sin(theta);
+ cosTheta = cos(theta);
+ tanTheta = tan(theta * 0.5);
+
+ srcWidth = Blt_ColorimageWidth(src);
+ srcHeight = Blt_ColorimageHeight(src);
+
+ tmpWidth = srcWidth + (int)(srcHeight * FABS(tanTheta));
+ tmpHeight = srcHeight;
+
+ /* 1st shear */
+
+ tmp1 = Blt_CreateColorimage(tmpWidth, tmpHeight);
+ assert(tmp1);
+
+ if (tanTheta >= 0.0) { /* Positive angle */
+ for (y = 0; y < tmpHeight; y++) {
+ skewf = (y + 0.5) * tanTheta;
+ skewi = (int)floor(skewf);
+ ShearY(src, tmp1, y, skewi, skewf - skewi, bgColor);
+ }
+ } else { /* Negative angle */
+ for (y = 0; y < tmpHeight; y++) {
+ skewf = ((y - srcHeight) + 0.5) * tanTheta;
+ skewi = (int)floor(skewf);
+ ShearY(src, tmp1, y, skewi, skewf - skewi, bgColor);
+ }
+ }
+ tmpHeight = (int)(srcWidth * FABS(sinTheta) + srcHeight * cosTheta) + 1;
+
+ tmp2 = Blt_CreateColorimage(tmpWidth, tmpHeight);
+ assert(tmp2);
+
+ /* 2nd shear */
+
+ if (sinTheta > 0.0) { /* Positive angle */
+ skewf = (srcWidth - 1) * sinTheta;
+ } else { /* Negative angle */
+ skewf = (srcWidth - tmpWidth) * -sinTheta;
+ }
+ for (x = 0; x < tmpWidth; x++) {
+ skewi = (int)floor(skewf);
+ ShearX(tmp1, tmp2, x, skewi, skewf - skewi, bgColor);
+ skewf -= sinTheta;
+ }
+
+ Blt_FreeColorimage(tmp1);
+
+ /* 3rd shear */
+
+ tmpWidth = (int)(srcHeight * FABS(sinTheta) + srcWidth * cosTheta) + 1;
+
+ dest = Blt_CreateColorimage(tmpWidth, tmpHeight);
+ assert(dest);
+
+ if (sinTheta >= 0.0) { /* Positive angle */
+ skewf = (srcWidth - 1) * sinTheta * -tanTheta;
+ } else { /* Negative angle */
+ skewf = tanTheta * ((srcWidth - 1) * -sinTheta - (tmpHeight - 1));
+ }
+ for (y = 0; y < tmpHeight; y++) {
+ skewi = (int)floor(skewf);
+ ShearY(tmp2, dest, y, skewi, skewf - skewi, bgColor);
+ skewf += tanTheta;
+ }
+ Blt_FreeColorimage(tmp2);
+ return dest;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * CopyColorimage --
+ *
+ * Creates a copy of the given color image.
+ *
+ * Results:
+ * Returns the new copy.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static Blt_Colorimage
+CopyColorimage(src)
+ Blt_Colorimage src;
+{
+ unsigned int width, height;
+ Pix32 *srcPtr, *destPtr;
+ Blt_Colorimage dest;
+
+ width = Blt_ColorimageWidth(src);
+ height = Blt_ColorimageHeight(src);
+ dest = Blt_CreateColorimage(width, height);
+ srcPtr = Blt_ColorimageBits(src);
+ destPtr = Blt_ColorimageBits(dest);
+ memcpy(destPtr, srcPtr, width * height * sizeof(Pix32));
+ return dest;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate90 --
+ *
+ * Rotates the given color image by 90 degrees. This is part
+ * of the special case right-angle rotations that do not create
+ * subpixel aliasing.
+ *
+ * Results:
+ * Returns a newly allocated, rotated color image.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static Blt_Colorimage
+Rotate90(src)
+ Blt_Colorimage src;
+{
+ int width, height, offset;
+ Pix32 *srcPtr, *destPtr;
+ Blt_Colorimage dest;
+ register int x, y;
+
+ height = Blt_ColorimageWidth(src);
+ width = Blt_ColorimageHeight(src);
+
+ dest = Blt_CreateColorimage(width, height);
+ offset = (height - 1) * width;
+
+ srcPtr = Blt_ColorimageBits(src);
+ offset = (height - 1) * width;
+ for (x = 0; x < width; x++) {
+ destPtr = Blt_ColorimageBits(dest) + offset + x;
+ for (y = 0; y < height; y++) {
+ *destPtr = *srcPtr++;
+ destPtr -= width;
+ }
+ }
+ return dest;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate180 --
+ *
+ * Rotates the given color image by 180 degrees. This is one of
+ * the special case orthogonal rotations that do not create
+ * subpixel aliasing.
+ *
+ * Results:
+ * Returns a newly allocated, rotated color image.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static Blt_Colorimage
+Rotate180(src)
+ Blt_Colorimage src;
+{
+ int width, height, offset;
+ Pix32 *srcPtr, *destPtr;
+ Blt_Colorimage dest;
+ register int x, y;
+
+ width = Blt_ColorimageWidth(src);
+ height = Blt_ColorimageHeight(src);
+ dest = Blt_CreateColorimage(width, height);
+
+ srcPtr = Blt_ColorimageBits(src);
+ offset = (height - 1) * width;
+ for (y = 0; y < height; y++) {
+ destPtr = Blt_ColorimageBits(dest) + offset + width - 1;
+ for (x = 0; x < width; x++) {
+ *destPtr-- = *srcPtr++;
+ }
+ offset -= width;
+ }
+ return dest;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate270 --
+ *
+ * Rotates the given color image by 270 degrees. This is part
+ * of the special case right-angle rotations that do not create
+ * subpixel aliasing.
+ *
+ * Results:
+ * Returns a newly allocated, rotated color image.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static Blt_Colorimage
+Rotate270(src)
+ Blt_Colorimage src;
+{
+ int width, height;
+ Pix32 *srcPtr, *destPtr;
+ Blt_Colorimage dest;
+ register int x, y;
+
+ height = Blt_ColorimageWidth(src);
+ width = Blt_ColorimageHeight(src);
+ dest = Blt_CreateColorimage(width, height);
+
+ srcPtr = Blt_ColorimageBits(src);
+ for (x = width - 1; x >= 0; x--) {
+ destPtr = Blt_ColorimageBits(dest) + x;
+ for (y = 0; y < height; y++) {
+ *destPtr = *srcPtr++;
+ destPtr += width;
+ }
+ }
+ return dest;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RotateColorimage --
+ *
+ * Rotates a color image by a given # of degrees.
+ *
+ * Results:
+ * Returns a newly allocated, rotated color image.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_RotateColorimage(src, angle)
+ Blt_Colorimage src;
+ double angle;
+{
+ Blt_Colorimage dest, tmp;
+ int quadrant;
+
+ tmp = src; /* Suppress compiler warning. */
+
+ /* Make the angle positive between 0 and 360 degrees. */
+ angle = FMOD(angle, 360.0);
+ if (angle < 0.0) {
+ angle += 360.0;
+ }
+ quadrant = ROTATE_0;
+ if ((angle > 45.0) && (angle <= 135.0)) {
+ quadrant = ROTATE_90;
+ angle -= 90.0;
+ } else if ((angle > 135.0) && (angle <= 225.0)) {
+ quadrant = ROTATE_180;
+ angle -= 180.0;
+ } else if ((angle > 225.0) && (angle <= 315.0)) {
+ quadrant = ROTATE_270;
+ angle -= 270.0;
+ } else if (angle > 315.0) {
+ angle -= 360.0;
+ }
+ /*
+ * If necessary, create a temporary image that's been rotated
+ * by a right-angle. We'll then rotate this color image between
+ * -45 to 45 degrees to arrive at its final angle.
+ */
+ switch (quadrant) {
+ case ROTATE_270: /* 270 degrees */
+ tmp = Rotate270(src);
+ break;
+
+ case ROTATE_90: /* 90 degrees */
+ tmp = Rotate90(src);
+ break;
+
+ case ROTATE_180: /* 180 degrees */
+ tmp = Rotate180(src);
+ break;
+
+ case ROTATE_0: /* 0 degrees */
+ if (angle == 0.0) {
+ tmp = CopyColorimage(src); /* Make a copy of the source. */
+ }
+ break;
+ }
+
+ assert((angle >= -45.0) && (angle <= 45.0));
+
+ dest = tmp;
+ if (angle != 0.0) {
+ double theta;
+ Pix32 *srcPtr;
+ Pix32 bgColor;
+
+ /* FIXME: pick up background blending color from somewhere */
+ srcPtr = Blt_ColorimageBits(src);
+ bgColor = *srcPtr;
+ bgColor.Red = bgColor.Green = bgColor.Blue = 0xFF;
+ bgColor.Alpha = 0x00; /* Transparent background */
+ theta = (angle / 180.0) * M_PI;
+ dest = Rotate45(tmp, theta, bgColor);
+ if (tmp != src) {
+ Blt_FreeColorimage(tmp);
+ }
+ }
+ return dest;
+}
+
+#define NC 256
+enum ColorIndices { RED, GREEN, BLUE };
+
+#define R0 (cubePtr->r0)
+#define R1 (cubePtr->r1)
+#define G0 (cubePtr->g0)
+#define G1 (cubePtr->g1)
+#define B0 (cubePtr->b0)
+#define B1 (cubePtr->b1)
+
+typedef struct {
+ int r0, r1; /* min, max values:
+ * min exclusive max inclusive */
+ int g0, g1;
+ int b0, b1;
+ int vol;
+} Cube;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Histogram is in elements 1..HISTSIZE along each axis,
+ * element 0 is for base or marginal value
+ * NB: these must start out 0!
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+ long int wt[33][33][33]; /* # pixels in voxel */
+ long int mR[33][33][33]; /* Sum over voxel of red pixel values */
+ long int mG[33][33][33]; /* Sum over voxel of green pixel values */
+ long int mB[33][33][33]; /* Sum over voxel of blue pixel values */
+ long int gm2[33][33][33]; /* Variance */
+} ColorimageStatistics;
+
+/*
+ * Build 3-D color histogram of counts, R/G/B, c^2
+ */
+static ColorimageStatistics *
+GetColorimageStatistics(image)
+ Blt_Colorimage image;
+{
+ register int r, g, b;
+#define MAX_INTENSITIES 256
+ unsigned int sqr[MAX_INTENSITIES];
+ int numPixels;
+ Pix32 *srcPtr, *endPtr;
+ register int i;
+ ColorimageStatistics *s;
+
+ s = Blt_Calloc(1, sizeof(ColorimageStatistics));
+ assert(s);
+
+ /* Precompute table of squares. */
+ for (i = 0; i < MAX_INTENSITIES; i++) {
+ sqr[i] = i * i;
+ }
+ numPixels = Blt_ColorimageWidth(image) * Blt_ColorimageHeight(image);
+
+ for (srcPtr = Blt_ColorimageBits(image), endPtr = srcPtr + numPixels;
+ srcPtr < endPtr; srcPtr++) {
+ /*
+ * Reduce the number of bits (5) per color component. This
+ * will keep the table size (2^15) reasonable without perceptually
+ * affecting the final image.
+ */
+ r = (srcPtr->Red >> 3) + 1;
+ g = (srcPtr->Green >> 3) + 1;
+ b = (srcPtr->Blue >> 3) + 1;
+ s->wt[r][g][b] += 1;
+ s->mR[r][g][b] += srcPtr->Red;
+ s->mG[r][g][b] += srcPtr->Green;
+ s->mB[r][g][b] += srcPtr->Blue;
+ s->gm2[r][g][b] += sqr[srcPtr->Red] + sqr[srcPtr->Green] +
+ sqr[srcPtr->Blue];
+ }
+ return s;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * At conclusion of the histogram step, we can interpret
+ * wt[r][g][b] = sum over voxel of P(c)
+ * mR[r][g][b] = sum over voxel of r*P(c) , similarly for mG, mB
+ * m2[r][g][b] = sum over voxel of c^2*P(c)
+ * Actually each of these should be divided by 'size' to give the usual
+ * interpretation of P() as ranging from 0 to 1, but we needn't do that here.
+ *----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ We now convert histogram into moments so that we can rapidly calculate
+ * the sums of the above quantities over any desired box.
+ *----------------------------------------------------------------------
+ */
+
+static void
+M3d(s) /* compute cumulative moments. */
+ ColorimageStatistics *s;
+{
+ register unsigned char i, r, g, b, r0;
+ long int line, rLine, gLine, bLine;
+ long int area[33], rArea[33], gArea[33], bArea[33];
+ unsigned int line2, area2[33];
+
+ for (r = 1, r0 = 0; r <= 32; r++, r0++) {
+ for (i = 0; i <= 32; ++i) {
+ area2[i] = area[i] = rArea[i] = gArea[i] = bArea[i] = 0;
+ }
+ for (g = 1; g <= 32; g++) {
+ line2 = line = rLine = gLine = bLine = 0;
+ for (b = 1; b <= 32; b++) {
+ /* ind1 = RGBIndex(r, g, b); */
+
+ line += s->wt[r][g][b];
+ rLine += s->mR[r][g][b];
+ gLine += s->mG[r][g][b];
+ bLine += s->mB[r][g][b];
+ line2 += s->gm2[r][g][b];
+
+ area[b] += line;
+ rArea[b] += rLine;
+ gArea[b] += gLine;
+ bArea[b] += bLine;
+ area2[b] += line2;
+
+ /* ind2 = ind1 - 1089; [r0][g][b] */
+ s->wt[r][g][b] = s->wt[r0][g][b] + area[b];
+ s->mR[r][g][b] = s->mR[r0][g][b] + rArea[b];
+ s->mG[r][g][b] = s->mG[r0][g][b] + gArea[b];
+ s->mB[r][g][b] = s->mB[r0][g][b] + bArea[b];
+ s->gm2[r][g][b] = s->gm2[r0][g][b] + area2[b];
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Compute sum over a box of any given statistic
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE
+long int
+Volume(cubePtr, m)
+ Cube *cubePtr;
+ long int m[33][33][33];
+{
+ return (m[R1][G1][B1] - m[R1][G1][B0] - m[R1][G0][B1] + m[R1][G0][B0] -
+ m[R0][G1][B1] + m[R0][G1][B0] + m[R0][G0][B1] - m[R0][G0][B0]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * The next two routines allow a slightly more efficient
+ * calculation of Volume() for a proposed subbox of a given box.
+ * The sum of Top() and Bottom() is the Volume() of a subbox split
+ * in the given direction and with the specified new upper
+ * bound.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* Compute part of Volume(cubePtr, mmt) that doesn't depend on r1, g1, or b1 */
+/* (depending on dir) */
+static long int
+Bottom(cubePtr, dir, m)
+ Cube *cubePtr;
+ unsigned char dir;
+ long int m[33][33][33]; /* Moment */
+{
+ switch (dir) {
+ case RED:
+ return -m[R0][G1][B1] + m[R0][G1][B0] + m[R0][G0][B1] - m[R0][G0][B0];
+ case GREEN:
+ return -m[R1][G0][B1] + m[R1][G0][B0] + m[R0][G0][B1] - m[R0][G0][B0];
+ case BLUE:
+ return -m[R1][G1][B0] + m[R1][G0][B0] + m[R0][G1][B0] - m[R0][G0][B0];
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Compute remainder of Volume(cubePtr, mmt), substituting pos for
+ * r1, g1, or b1 (depending on dir)
+ *
+ *----------------------------------------------------------------------
+ */
+static long int
+Top(cubePtr, dir, pos, m)
+ Cube *cubePtr;
+ unsigned char dir;
+ int pos;
+ long int m[33][33][33];
+{
+ switch (dir) {
+ case RED:
+ return (m[pos][G1][B1] - m[pos][G1][B0] -
+ m[pos][G0][B1] + m[pos][G0][B0]);
+
+ case GREEN:
+ return (m[R1][pos][B1] - m[R1][pos][B0] -
+ m[R0][pos][B1] + m[R0][pos][B0]);
+
+ case BLUE:
+ return (m[R1][G1][pos] - m[R1][G0][pos] -
+ m[R0][G1][pos] + m[R0][G0][pos]);
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Compute the weighted variance of a box NB: as with the raw
+ * statistics, this is really the (variance * size)
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+Variance(cubePtr, s)
+ Cube *cubePtr;
+ ColorimageStatistics *s;
+{
+ double dR, dG, dB, xx;
+
+ dR = Volume(cubePtr, s->mR);
+ dG = Volume(cubePtr, s->mG);
+ dB = Volume(cubePtr, s->mB);
+ xx = (s->gm2[R1][G1][B1] - s->gm2[R1][G1][B0] -
+ s->gm2[R1][G0][B1] + s->gm2[R1][G0][B0] -
+ s->gm2[R0][G1][B1] + s->gm2[R0][G1][B0] +
+ s->gm2[R0][G0][B1] - s->gm2[R0][G0][B0]);
+ return (xx - (dR * dR + dG * dG + dB * dB) / Volume(cubePtr, s->wt));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * We want to minimize the sum of the variances of two subboxes.
+ * The sum(c^2) terms can be ignored since their sum over both
+ * subboxes is the same (the sum for the whole box) no matter
+ * where we split. The remaining terms have a minus sign in
+ * the variance formula, so we drop the minus sign and MAXIMIZE
+ * the sum of the two terms.
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+Maximize(cubePtr, dir, first, last, cut, rWhole, gWhole, bWhole, wWhole, s)
+ Cube *cubePtr;
+ unsigned char dir;
+ int first, last, *cut;
+ long int rWhole, gWhole, bWhole, wWhole;
+ ColorimageStatistics *s;
+{
+ register long int rHalf, gHalf, bHalf, wHalf;
+ long int rBase, gBase, bBase, wBase;
+ register int i;
+ register double temp, max;
+
+ rBase = Bottom(cubePtr, dir, s->mR);
+ gBase = Bottom(cubePtr, dir, s->mG);
+ bBase = Bottom(cubePtr, dir, s->mB);
+ wBase = Bottom(cubePtr, dir, s->wt);
+ max = 0.0;
+ *cut = -1;
+ for (i = first; i < last; i++) {
+ rHalf = rBase + Top(cubePtr, dir, i, s->mR);
+ gHalf = gBase + Top(cubePtr, dir, i, s->mG);
+ bHalf = bBase + Top(cubePtr, dir, i, s->mB);
+ wHalf = wBase + Top(cubePtr, dir, i, s->wt);
+
+ /* Now half_x is sum over lower half of box, if split at i */
+ if (wHalf == 0) { /* subbox could be empty of pixels! */
+ continue; /* never split into an empty box */
+ } else {
+ temp = ((double)rHalf * rHalf + (float)gHalf * gHalf +
+ (double)bHalf * bHalf) / wHalf;
+ }
+ rHalf = rWhole - rHalf;
+ gHalf = gWhole - gHalf;
+ bHalf = bWhole - bHalf;
+ wHalf = wWhole - wHalf;
+ if (wHalf == 0) { /* Subbox could be empty of pixels! */
+ continue; /* never split into an empty box */
+ } else {
+ temp += ((double)rHalf * rHalf + (float)gHalf * gHalf +
+ (double)bHalf * bHalf) / wHalf;
+ }
+ if (temp > max) {
+ max = temp;
+ *cut = i;
+ }
+ }
+ return max;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *----------------------------------------------------------------------
+ */
+static int
+Cut(set1, set2, s)
+ Cube *set1, *set2;
+ ColorimageStatistics *s;
+{
+ unsigned char dir;
+ int rCut, gCut, bCut;
+ double rMax, gMax, bMax;
+ long int rWhole, gWhole, bWhole, wWhole;
+
+ rWhole = Volume(set1, s->mR);
+ gWhole = Volume(set1, s->mG);
+ bWhole = Volume(set1, s->mB);
+ wWhole = Volume(set1, s->wt);
+
+ rMax = Maximize(set1, RED, set1->r0 + 1, set1->r1, &rCut,
+ rWhole, gWhole, bWhole, wWhole, s);
+ gMax = Maximize(set1, GREEN, set1->g0 + 1, set1->g1, &gCut,
+ rWhole, gWhole, bWhole, wWhole, s);
+ bMax = Maximize(set1, BLUE, set1->b0 + 1, set1->b1, &bCut,
+ rWhole, gWhole, bWhole, wWhole, s);
+
+ if ((rMax >= gMax) && (rMax >= bMax)) {
+ dir = RED;
+ if (rCut < 0) {
+ return 0; /* can't split the box */
+ }
+ } else {
+ dir = ((gMax >= rMax) && (gMax >= bMax)) ? GREEN : BLUE;
+ }
+ set2->r1 = set1->r1;
+ set2->g1 = set1->g1;
+ set2->b1 = set1->b1;
+
+ switch (dir) {
+ case RED:
+ set2->r0 = set1->r1 = rCut;
+ set2->g0 = set1->g0;
+ set2->b0 = set1->b0;
+ break;
+
+ case GREEN:
+ set2->g0 = set1->g1 = gCut;
+ set2->r0 = set1->r0;
+ set2->b0 = set1->b0;
+ break;
+
+ case BLUE:
+ set2->b0 = set1->b1 = bCut;
+ set2->r0 = set1->r0;
+ set2->g0 = set1->g0;
+ break;
+ }
+ set1->vol = (set1->r1 - set1->r0) * (set1->g1 - set1->g0) *
+ (set1->b1 - set1->b0);
+ set2->vol = (set2->r1 - set2->r0) * (set2->g1 - set2->g0) *
+ (set2->b1 - set2->b0);
+ return 1;
+}
+
+
+static int
+SplitColorSpace(s, cubes, nColors)
+ ColorimageStatistics *s;
+ Cube *cubes;
+ int nColors;
+{
+ double *vv, temp;
+ register int i;
+ register int n, k;
+
+ vv = Blt_Malloc(sizeof(double) * nColors);
+ assert(vv);
+
+ cubes[0].r0 = cubes[0].g0 = cubes[0].b0 = 0;
+ cubes[0].r1 = cubes[0].g1 = cubes[0].b1 = 32;
+ for (i = 1, n = 0; i < nColors; i++) {
+ if (Cut(cubes + n, cubes + i, s)) {
+ /*
+ * Volume test ensures we won't try to cut one-cell box
+ */
+ vv[n] = vv[i] = 0.0;
+ if (cubes[n].vol > 1) {
+ vv[n] = Variance(cubes + n, s);
+ }
+ if (cubes[i].vol > 1) {
+ vv[i] = Variance(cubes + i, s);
+ }
+ } else {
+ vv[n] = 0.0; /* don't try to split this box again */
+ i--; /* didn't create box i */
+ }
+
+ n = 0;
+ temp = vv[0];
+ for (k = 1; k <= i; k++) {
+ if (vv[k] > temp) {
+ temp = vv[k];
+ n = k;
+ }
+ }
+ if (temp <= 0.0) {
+ i++;
+ fprintf(stderr, "Only got %d boxes\n", i);
+ break;
+ }
+ }
+ Blt_Free(vv);
+ return i;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *--------------------------------------------------------------------
+ */
+static void
+Mark(cubePtr, label, tag)
+ Cube *cubePtr;
+ int label;
+ unsigned int tag[33][33][33];
+{
+ register int r, g, b;
+
+ for (r = R0 + 1; r <= R1; r++) {
+ for (g = G0 + 1; g <= G1; g++) {
+ for (b = B0 + 1; b <= B1; b++) {
+ tag[r][g][b] = label;
+ }
+ }
+ }
+}
+
+static unsigned int *
+CreateColorLookupTable(s, cubes, nColors)
+ ColorimageStatistics *s;
+ Cube *cubes;
+ int nColors;
+{
+ unsigned int *lut;
+ Pix32 color;
+ unsigned int red, green, blue;
+ unsigned int weight;
+ register Cube *cubePtr;
+ register int i;
+
+ lut = Blt_Calloc(sizeof(unsigned int), 33 * 33 * 33);
+ assert(lut);
+
+ color.Alpha = (unsigned char)-1;
+ for (cubePtr = cubes, i = 0; i < nColors; i++, cubePtr++) {
+ weight = Volume(cubePtr, s->wt);
+ if (weight) {
+ red = (Volume(cubePtr, s->mR) / weight) * (NC + 1);
+ green = (Volume(cubePtr, s->mG) / weight) * (NC + 1);
+ blue = (Volume(cubePtr, s->mB) / weight) * (NC + 1);
+ } else {
+ fprintf(stderr, "bogus box %d\n", i);
+ red = green = blue = 0;
+ }
+ color.Red = red >> 8;
+ color.Green = green >> 8;
+ color.Blue = blue >> 8;
+ Mark(cubePtr, color.value, lut);
+ }
+ return lut;
+}
+
+static void
+MapColors(src, dest, lut)
+ Blt_Colorimage src, dest;
+ unsigned int lut[33][33][33];
+{
+
+ /* Apply the color lookup table against the original image */
+ int width, height;
+ int count;
+ Pix32 *srcPtr, *destPtr, *endPtr;
+ unsigned char alpha;
+
+ width = Blt_ColorimageWidth(src);
+ height = Blt_ColorimageHeight(src);
+ count = width * height;
+
+ srcPtr = Blt_ColorimageBits(src);
+ destPtr = Blt_ColorimageBits(dest);
+ for (endPtr = destPtr + count; destPtr < endPtr; srcPtr++, destPtr++) {
+ alpha = srcPtr->Alpha;
+ destPtr->value = lut[srcPtr->Red>>3][srcPtr->Green>>3][srcPtr->Blue>>3];
+ destPtr->Alpha = alpha;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_QuantizeColorimage --
+ *
+ * C Implementation of Wu's Color Quantizer (v. 2) (see Graphics Gems
+ * vol. II, pp. 126-133)
+ *
+ * Author: Xiaolin Wu
+ * Dept. of Computer Science Univ. of Western
+ * Ontario London, Ontario
+ * N6A 5B7
+ * wu@csd.uwo.ca
+ *
+ * Algorithm:
+ * Greedy orthogonal bipartition of RGB space for variance
+ * minimization aided by inclusion-exclusion tricks. For
+ * speed no nearest neighbor search is done. Slightly
+ * better performance can be expected by more
+ * sophisticated but more expensive versions.
+ *
+ * The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
+ * additional documentation and a cure to a previous bug.
+ *
+ * Free to distribute, comments and suggestions are appreciated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_QuantizeColorimage(src, dest, reduceColors)
+ Blt_Colorimage src, dest; /* Source and destination images. */
+ int reduceColors; /* Reduced number of colors. */
+{
+ Cube *cubes;
+ ColorimageStatistics *statistics;
+ int nColors;
+ unsigned int *lut;
+
+ /*
+ * Allocated a structure to hold color statistics.
+ */
+ statistics = GetColorimageStatistics(src);
+ M3d(statistics);
+
+ cubes = Blt_Malloc(sizeof(Cube) * reduceColors);
+ assert(cubes);
+
+ nColors = SplitColorSpace(statistics, cubes, reduceColors);
+ assert(nColors <= reduceColors);
+
+ lut = CreateColorLookupTable(statistics, cubes, nColors);
+ Blt_Free(statistics);
+ Blt_Free(cubes);
+ MapColors(src, dest, lut);
+ Blt_Free(lut);
+ return TCL_OK;
+}
+
+Region2D *
+Blt_SetRegion(x, y, width, height, regionPtr)
+ int x, y, width, height;
+ Region2D *regionPtr;
+{
+ regionPtr->left = x;
+ regionPtr->top = y;
+ regionPtr->right = x + width - 1;
+ regionPtr->bottom = y + height - 1;
+ return regionPtr;
+}
+
+
+/*
+ * Each call to Tk_GetImage returns a pointer to one of the following
+ * structures, which is used as a token by clients (widgets) that
+ * display images.
+ */
+typedef struct TkImageStruct {
+ Tk_Window tkwin; /* Window passed to Tk_GetImage (needed to
+ * "re-get" the image later if the manager
+ * changes). */
+ Display *display; /* Display for tkwin. Needed because when
+ * the image is eventually freed tkwin may
+ * not exist anymore. */
+ struct TkImageMasterStruct *masterPtr;
+ /* Master for this image (identifiers image
+ * manager, for example). */
+ ClientData instanceData;
+ /* One word argument to pass to image manager
+ * when dealing with this image instance. */
+ Tk_ImageChangedProc *changeProc;
+ /* Code in widget to call when image changes
+ * in a way that affects redisplay. */
+ ClientData widgetClientData;
+ /* Argument to pass to changeProc. */
+ struct Image *nextPtr; /* Next in list of all image instances
+ * associated with the same name. */
+
+} TkImage;
+
+/*
+ * For each image master there is one of the following structures,
+ * which represents a name in the image table and all of the images
+ * instantiated from it. Entries in mainPtr->imageTable point to
+ * these structures.
+ */
+typedef struct TkImageMasterStruct {
+ Tk_ImageType *typePtr; /* Information about image type. NULL means
+ * that no image manager owns this image: the
+ * image was deleted. */
+ ClientData masterData; /* One-word argument to pass to image mgr
+ * when dealing with the master, as opposed
+ * to instances. */
+ int width, height; /* Last known dimensions for image. */
+ Blt_HashTable *tablePtr; /* Pointer to hash table containing image
+ * (the imageTable field in some TkMainInfo
+ * structure). */
+ Blt_HashEntry *hPtr; /* Hash entry in mainPtr->imageTable for
+ * this structure (used to delete the hash
+ * entry). */
+ TkImage *instancePtr; /* Pointer to first in list of instances
+ * derived from this name. */
+} TkImageMaster;
+
+
+typedef struct TkPhotoMasterStruct TkPhotoMaster;
+typedef struct TkColorTableStruct TkColorTable;
+
+typedef struct TkPhotoInstanceStruct {
+ TkPhotoMaster *masterPtr; /* Pointer to master for image. */
+ Display *display; /* Display for windows using this instance. */
+ Colormap colormap; /* The image may only be used in windows with
+ * this particular colormap. */
+ struct TkPhotoInstanceStruct *nextPtr;
+ /* Pointer to the next instance in the list
+ * of instances associated with this master. */
+ int refCount; /* Number of instances using this structure. */
+ Tk_Uid palette; /* Palette for these particular instances. */
+ double outputGamma; /* Gamma value for these instances. */
+ Tk_Uid defaultPalette; /* Default palette to use if a palette
+ * is not specified for the master. */
+ TkColorTable *colorTablePtr; /* Pointer to information about colors
+ * allocated for image display in windows
+ * like this one. */
+ Pixmap pixels; /* X pixmap containing dithered image. */
+ int width, height; /* Dimensions of the pixmap. */
+ char *error; /* Error image, used in dithering. */
+ XImage *imagePtr; /* Image structure for converted pixels. */
+ XVisualInfo visualInfo; /* Information about the visual that these
+ * windows are using. */
+ GC gc; /* Graphics context for writing images
+ * to the pixmap. */
+} TkPhotoInstance;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Tk_ImageDeleted --
+ *
+ * Is there any other way to determine if an image has been
+ * deleted?
+ *
+ * Results:
+ * Returns 1 if the image has been deleted, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Tk_ImageIsDeleted(tkImage)
+ Tk_Image tkImage; /* Token for image. */
+{
+ TkImage *imagePtr = (TkImage *) tkImage;
+
+ if (imagePtr->masterPtr == NULL) {
+ return TRUE;
+ }
+ return (imagePtr->masterPtr->typePtr == NULL);
+}
+
+/*LINTLIBRARY*/
+Tk_ImageMaster
+Tk_ImageGetMaster(tkImage)
+ Tk_Image tkImage; /* Token for image. */
+{
+ TkImage *imagePtr = (TkImage *)tkImage;
+
+ return (Tk_ImageMaster) imagePtr->masterPtr;
+}
+
+/*LINTLIBRARY*/
+Tk_ImageType *
+Tk_ImageGetType(tkImage)
+ Tk_Image tkImage; /* Token for image. */
+{
+ TkImage *imagePtr = (TkImage *)tkImage;
+
+ return imagePtr->masterPtr->typePtr;
+}
+
+/*LINTLIBRARY*/
+Pixmap
+Tk_ImageGetPhotoPixmap(tkImage)
+ Tk_Image tkImage; /* Token for image. */
+{
+ TkImage *imagePtr = (TkImage *)tkImage;
+
+ if (strcmp(imagePtr->masterPtr->typePtr->name, "photo") == 0) {
+ TkPhotoInstance *instPtr = (TkPhotoInstance *)imagePtr->instanceData;
+ return instPtr->pixels;
+ }
+ return None;
+}
+
+/*LINTLIBRARY*/
+GC
+Tk_ImageGetPhotoGC(photoImage)
+ Tk_Image photoImage; /* Token for image. */
+{
+ TkImage *imagePtr = (TkImage *) photoImage;
+ if (strcmp(imagePtr->masterPtr->typePtr->name, "photo") == 0) {
+ TkPhotoInstance *instPtr = (TkPhotoInstance *)imagePtr->instanceData;
+ return instPtr->gc;
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TempImageChangedProc
+ *
+ * The image is over-written each time it's resized. We always
+ * resample from the color image we saved when the photo image
+ * was specified (-image option). So we only worry if the image
+ * is deleted.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TempImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+#ifdef notdef
+ fprintf(stderr, "should be redrawing temp image\n");
+#endif
+}
+
+Tk_Image
+Blt_CreateTemporaryImage(interp, tkwin, clientData)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ ClientData clientData;
+{
+ Tk_Image token;
+ char *name; /* Contains image name. */
+
+ if (Tcl_Eval(interp, "image create photo") != TCL_OK) {
+ return NULL;
+ }
+ name = Tcl_GetStringResult(interp);
+ token = Tk_GetImage(interp, tkwin, name, TempImageChangedProc, clientData);
+ if (token == NULL) {
+ return NULL;
+ }
+ return token;
+}
+
+int
+Blt_DestroyTemporaryImage(interp, tkImage)
+ Tcl_Interp *interp;
+ Tk_Image tkImage;
+{
+ if (tkImage != NULL) {
+ if (Tcl_VarEval(interp, "image delete ", Blt_NameOfImage(tkImage),
+ (char *)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tk_FreeImage(tkImage);
+ }
+ return TCL_OK;
+}
+
+char *
+Blt_NameOfImage(tkImage)
+ Tk_Image tkImage;
+{
+ Tk_ImageMaster master;
+
+ master = Tk_ImageGetMaster(tkImage);
+ return Tk_NameOfImage(master);
+}
diff --git a/blt/src/bltImage.h b/blt/src/bltImage.h
new file mode 100644
index 00000000000..3ddfe701fb6
--- /dev/null
+++ b/blt/src/bltImage.h
@@ -0,0 +1,287 @@
+/*
+ * bltImage.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include <X11/Xutil.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+#ifndef _BLT_IMAGE_H
+#define _BLT_IMAGE_H
+
+#define DIV255(i) ((((i) + 1) + (((i) + 1) >> 8) ) >> 8)
+
+#define GAMMA (1.0)
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Pix32 --
+ *
+ * A union representing either a pixel as a RGB triplet or a
+ * single word value.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef union {
+ unsigned int value; /* Lookup table address */
+ struct RGBA {
+ unsigned char red; /* Red intensity 0..255 */
+ unsigned char green; /* Green intensity 0.255 */
+ unsigned char blue; /* Blue intensity 0..255 */
+ unsigned char alpha; /* Alpha-channel for compositing. 0..255 */
+ } rgba;
+ unsigned char channel[4];
+} Pix32;
+
+#define Red rgba.red
+#define Blue rgba.blue
+#define Green rgba.green
+#define Alpha rgba.alpha
+
+
+typedef struct {
+ XColor exact, best;
+ double error;
+ unsigned int freq;
+ int allocated;
+ int index;
+} ColorInfo;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorTable --
+ *
+ * For colormap-ed visuals, this structure contains color lookup
+ * information needed to translate RGB triplets to pixel indices.
+ *
+ * This structure isn't needed for TrueColor or Monochrome visuals.
+ *
+ * DirectColor:
+ * Pixel values for each color channel
+ * StaticColor, PsuedoColor, StaticGray, and GrayScale:
+ * Red represents the 8-bit color. Green and Blue pixel
+ * values are unused.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct ColorTableStruct {
+ double outputGamma; /* Gamma correction value */
+ Display *display; /* Display of colortable. Used to free
+ * colors allocated. */
+ XVisualInfo visualInfo; /* Visual information for window displaying
+ * the image. */
+ Colormap colorMap; /* Colormap used. This may be the default
+ * colormap, or an allocated private map. */
+ int flags;
+ unsigned int red[256], green[256], blue[256];
+
+ /* Array of allocated pixels in colormap */
+ ColorInfo colorInfo[256];
+ ColorInfo *sortedColors[256];
+
+ int nUsedColors, nFreeColors;
+ int nPixels; /* Number of colors in the quantized image */
+ unsigned long int pixelValues[256];
+
+ unsigned int *lut; /* Color lookup table. Used to collect
+ * frequencies of colors and later
+ * colormap indices */
+} *ColorTable;
+
+#define PRIVATE_COLORMAP 1
+#define RGBIndex(r,g,b) (((r)<<10) + ((r)<<6) + (r) + ((g) << 5) + (g) + (b))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_Colorimage --
+ *
+ * The structure below represents a color image. Each pixel
+ * occupies a 32-bit word of memory: one byte for each of the
+ * red, green, and blue color intensities, and another for
+ * alpha-channel image compositing (e.g. transparency).
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct Colorimage {
+ int width, height; /* Dimensions of the image */
+ Pix32 *bits; /* Array of pixels representing the image. */
+} *Blt_Colorimage;
+
+/*
+ * Blt_Colorimage is supposed to be an opaque type.
+ * Use the macros below to access its members.
+ */
+#define Blt_ColorimageHeight(c) ((c)->height)
+#define Blt_ColorimageWidth(c) ((c)->width)
+#define Blt_ColorimageBits(c) ((c)->bits)
+#define Blt_ColorimagePixel(c, x, y) ((c)->bits + ((c)->width * (y)) + (x))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResampleFilterProc --
+ *
+ * A function implementing a 1-D filter.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef double (ResampleFilterProc) _ANSI_ARGS_((double value));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResampleFilter --
+ *
+ * Contains information about a 1-D filter (its support and
+ * the procedure implementing the filter).
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Name of the filter */
+ ResampleFilterProc *proc; /* 1-D filter procedure. */
+ double support; /* Width of 1-D filter */
+} ResampleFilter;
+
+extern ResampleFilter *bltBoxFilterPtr; /* The ubiquitous box filter */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Filter2D --
+ *
+ * Defines a convolution mask for a 2-D filter. Used to smooth or
+ * enhance images.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+ double support; /* Radius of filter */
+ double sum, scale; /* Sum of kernel */
+ double *kernel; /* Array of values (malloc-ed) representing
+ * the discrete 2-D filter. */
+} Filter2D;
+
+/* Prototypes of image routines */
+
+extern void Blt_ColorimageToGreyscale _ANSI_ARGS_((Blt_Colorimage image));
+
+extern void Blt_ColorimageToPhoto _ANSI_ARGS_((Blt_Colorimage image,
+ Tk_PhotoHandle photo));
+
+extern Pixmap Blt_ColorimageToPixmap _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_Colorimage image, ColorTable *colorTablePtr));
+
+extern Blt_Colorimage Blt_ConvolveColorimage _ANSI_ARGS_((
+ Blt_Colorimage srcImage, Filter2D *filter));
+
+extern Blt_Colorimage Blt_CreateColorimage _ANSI_ARGS_((int width,int height));
+
+extern Blt_Colorimage Blt_DrawableToColorimage _ANSI_ARGS_((Tk_Window tkwin,
+ Drawable drawable, int x, int y, int width, int height,
+ double inputGamma));
+
+extern int Blt_GetResampleFilter _ANSI_ARGS_((Tcl_Interp *interp,
+ char *filterName, ResampleFilter **filterPtrPtr));
+
+extern void Blt_FreeColorimage _ANSI_ARGS_((Blt_Colorimage image));
+
+#if HAVE_JPEG
+extern Blt_Colorimage Blt_JPEGToColorimage _ANSI_ARGS_((Tcl_Interp *interp,
+ char *fileName));
+#endif
+
+extern Blt_Colorimage Blt_PhotoToColorimage _ANSI_ARGS_((
+ Tk_PhotoHandle photo));
+
+extern Blt_Colorimage Blt_PhotoRegionToColorimage _ANSI_ARGS_((
+ Tk_PhotoHandle photo, int x, int y, int width, int height));
+
+extern int Blt_QuantizeColorimage _ANSI_ARGS_((Blt_Colorimage src,
+ Blt_Colorimage dest, int nColors));
+
+extern Blt_Colorimage Blt_ResampleColorimage _ANSI_ARGS_((Blt_Colorimage image,
+ int destWidth, int destHeight, ResampleFilter *horzFilterPtr,
+ ResampleFilter *vertFilterPtr));
+
+extern void Blt_ResamplePhoto _ANSI_ARGS_((Tk_PhotoHandle srcPhoto,
+ int x, int y, int width, int height, Tk_PhotoHandle destPhoto,
+ ResampleFilter *horzFilterPtr, ResampleFilter *vertFilterPtr));
+
+extern Blt_Colorimage Blt_ResizeColorimage _ANSI_ARGS_((Blt_Colorimage image,
+ int x, int y, int width, int height, int destWidth, int destHeight));
+
+extern Blt_Colorimage Blt_RotateColorimage _ANSI_ARGS_((Blt_Colorimage image,
+ double theta));
+
+extern void Blt_ResizePhoto _ANSI_ARGS_((Tk_PhotoHandle srcPhoto, int x, int y,
+ int width, int height, Tk_PhotoHandle destPhoto));
+
+extern int Blt_SnapPhoto _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ Drawable drawable, int x, int y, int width, int height, int destWidth,
+ int destHeight, char *photoName, double inputGamma));
+
+extern void Blt_ImageRegion _ANSI_ARGS_((Blt_Colorimage image,
+ Region2D *regionPtr));
+
+extern Region2D *Blt_ColorimageRegion _ANSI_ARGS_((Blt_Colorimage image,
+ Region2D *regionPtr));
+
+extern Region2D *Blt_SetRegion _ANSI_ARGS_((int x, int y, int width,
+ int height, Region2D *regionPtr));
+
+extern ColorTable Blt_CreateColorTable _ANSI_ARGS_((Tk_Window tkwin));
+
+extern ColorTable Blt_DirectColorTable _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_Colorimage image));
+
+extern ColorTable Blt_PseudoColorTable _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_Colorimage image));
+
+extern void Blt_FreeColorTable _ANSI_ARGS_((ColorTable colorTable));
+
+/* Missing routines from the Tk photo C API */
+
+extern int Tk_ImageIsDeleted _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_ImageMaster Tk_ImageGetMaster _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_ImageType *Tk_ImageGetType _ANSI_ARGS_((Tk_Image tkImage));
+extern Pixmap Tk_ImageGetPhotoPixmap _ANSI_ARGS_((Tk_Image photoImage));
+extern GC Tk_ImageGetPhotoGC _ANSI_ARGS_((Tk_Image photoImage));
+
+extern char *Blt_NameOfImage _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_Image Blt_CreateTemporaryImage _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, ClientData clientData));
+extern int Blt_DestroyTemporaryImage _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Image tkImage));
+
+extern GC Blt_GetBitmapGC _ANSI_ARGS_((Tk_Window tkwin));
+extern Pixmap Blt_PhotoImageMask _ANSI_ARGS_((Tk_Window tkwin,
+ Tk_PhotoImageBlock src));
+
+#endif /*_BLT_IMAGE_H*/
diff --git a/blt/src/bltInit.c b/blt/src/bltInit.c
new file mode 100644
index 00000000000..16c9f96f187
--- /dev/null
+++ b/blt/src/bltInit.c
@@ -0,0 +1,680 @@
+
+/*
+ * bltInit.c --
+ *
+ * This module initials the BLT toolkit, registering its commands
+ * with the Tcl/Tk interpreter.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include <bltInt.h>
+
+#ifdef __STDC__
+static Tcl_MathProc MinMathProc, MaxMathProc;
+#endif
+
+#ifdef TCL_ONLY
+#define NO_BELL 1
+#define NO_BITMAP 1
+#define NO_BUSY 1
+#define NO_CONTAINER 1
+#define NO_CUTBUFFER 1
+#define NO_DND 1
+#define NO_DRAGDROP 1
+#define NO_GRAPH 1
+#define NO_HIERBOX 1
+#define NO_HTEXT 1
+#define NO_MOUNTAIN 1
+#define NO_PRINTER 1
+#define NO_TABLE 1
+#define NO_TABSET 1
+#define NO_TABNOTEBOOK 1
+#define NO_TED 1
+#define NO_TILEBUTTON 1
+#define NO_TILEFRAME 1
+#define NO_TILESCROLLBAR 1
+#define NO_WINOP 1
+#define NO_TREEVIEW 1
+#endif
+
+static Tcl_AppInitProc *initProcArr[] =
+{
+#ifndef NO_GRAPH
+ Blt_GraphInit,
+#endif
+#ifndef NO_TABLE
+ Blt_TableInit,
+#endif
+#ifndef NO_HIERBOX
+ Blt_HierboxInit,
+#endif
+#ifndef NO_TABSET
+ Blt_TabsetInit,
+#endif
+#ifndef NO_TABNOTEBOOK
+ Blt_TabnotebookInit,
+#endif
+#ifndef NO_HTEXT
+ Blt_HtextInit,
+#endif
+#ifndef NO_BUSY
+ Blt_BusyInit,
+#endif
+#ifndef NO_WINOP
+ Blt_WinopInit,
+#endif
+#ifndef NO_BITMAP
+ Blt_BitmapInit,
+#endif
+#ifndef NO_BGEXEC
+ Blt_BgexecInit,
+#endif
+#ifndef NO_DRAGDROP
+ Blt_DragDropInit,
+#endif
+#ifndef NO_DND
+ Blt_DndInit,
+#endif
+#ifndef NO_DEBUG
+ Blt_DebugInit,
+#endif
+#ifndef NO_WATCH
+ Blt_WatchInit,
+#endif
+#ifndef NO_CONTAINER
+ Blt_ContainerInit,
+#endif
+#ifndef NO_VECTOR
+ Blt_VectorInit,
+#endif
+#ifndef NO_SPLINE
+ Blt_SplineInit,
+#endif
+#ifndef NO_BELL
+ Blt_BeepInit,
+#endif
+#ifndef NO_CUTBUFFER
+ Blt_CutbufferInit,
+#endif
+#ifndef NO_PRINTER
+ Blt_PrinterInit,
+#endif
+#ifndef NO_TILEFRAME
+ Blt_FrameInit,
+#endif
+#ifndef NO_TILEBUTTON
+ Blt_ButtonInit,
+#endif
+#ifndef NO_TILESCROLLBAR
+ Blt_ScrollbarInit,
+#endif
+#ifndef NO_TREE
+ Blt_TreeInit,
+#endif
+#ifndef NO_TREEVIEW
+ Blt_TreeViewInit,
+#endif
+#if (BLT_MAJOR_VERSION == 3)
+#ifndef NO_MOUNTAIN
+ Blt_MountainInit,
+#endif
+#endif
+#ifndef NO_TED
+ Blt_TedInit,
+#endif
+#ifndef NO_DDE
+ Blt_DdeInit,
+#endif
+ (Tcl_AppInitProc *) NULL
+};
+
+/* ARGSUSED */
+static int
+MinMathProc(clientData, interp, argsPtr, resultPtr)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tcl_Value *argsPtr;
+ Tcl_Value *resultPtr;
+{
+ Tcl_Value *op1Ptr, *op2Ptr;
+
+ op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+ if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+ resultPtr->intValue = MIN(op1Ptr->intValue, op2Ptr->intValue);
+ resultPtr->type = TCL_INT;
+ } else {
+ double a, b;
+
+ a = (op1Ptr->type == TCL_INT)
+ ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+ b = (op2Ptr->type == TCL_INT)
+ ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+ resultPtr->doubleValue = MIN(a, b);
+ resultPtr->type = TCL_DOUBLE;
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+MaxMathProc(clientData, interp, argsPtr, resultPtr)
+ ClientData clientData; /* Not Used. */
+ Tcl_Interp *interp;
+ Tcl_Value *argsPtr;
+ Tcl_Value *resultPtr;
+{
+ Tcl_Value *op1Ptr, *op2Ptr;
+
+ op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+ if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+ resultPtr->intValue = MAX(op1Ptr->intValue, op2Ptr->intValue);
+ resultPtr->type = TCL_INT;
+ } else {
+ double a, b;
+
+ a = (op1Ptr->type == TCL_INT)
+ ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+ b = (op2Ptr->type == TCL_INT)
+ ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+ resultPtr->doubleValue = MAX(a, b);
+ resultPtr->type = TCL_DOUBLE;
+ }
+ return TCL_OK;
+}
+
+#ifndef BLT_LIBRARY
+#ifdef WIN32
+#define BLT_LIBRARY "c:/Program Files/Tcl/lib/blt"##BLT_VERSION
+#else
+#define BLT_LIBRARY "unknown"
+#endif
+#endif
+
+static char libPath[1024] =
+{
+ BLT_LIBRARY
+};
+
+/*
+ * Script to set the BLT library path in the variable global "blt_library"
+ *
+ * Checks the usual locations for a file (bltGraph.pro) from the BLT
+ * library. The places searched in order are
+ *
+ * $BLT_LIBRARY
+ * $BLT_LIBRARY/blt2.4
+ * $BLT_LIBRARY/..
+ * $BLT_LIBRARY/../blt2.4
+ * $blt_libPath
+ * $blt_libPath/blt2.4
+ * $blt_libPath/..
+ * $blt_libPath/../blt2.4
+ * $tcl_library
+ * $tcl_library/blt2.4
+ * $tcl_library/..
+ * $tcl_library/../blt2.4
+ * $env(TCL_LIBRARY)
+ * $env(TCL_LIBRARY)/blt2.4
+ * $env(TCL_LIBRARY)/..
+ * $env(TCL_LIBRARY)/../blt2.4
+ *
+ * The Tcl variable "blt_library" is set to the discovered path.
+ * If the file wasn't found, no error is returned. The actual
+ * usage of $blt_library is purposely deferred so that it can be
+ * set from within a script.
+ */
+
+/* FIXME: Change this to a namespace procedure in 3.0 */
+
+static char initScript[] =
+{"\n\
+global blt_library blt_libPath blt_version tcl_library env\n\
+set blt_library {}\n\
+set path {}\n\
+foreach var { env(BLT_LIBRARY) blt_libPath tcl_library env(TCL_LIBRARY) } { \n\
+ if { ![info exists $var] } { \n\
+ continue \n\
+ } \n\
+ set path [set $var] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file join $path blt$blt_version ] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file dirname [set $var]] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file join $path blt$blt_version ] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+} \n\
+if { $blt_library != \"\" } { \n\
+ global auto_path \n\
+ lappend auto_path $blt_library \n\
+}\n\
+unset var path\n\
+\n"
+};
+
+static int
+GetVersionInfo(interp)
+ Tcl_Interp *interp;
+{
+ Tcl_DString dString;
+ char *value;
+#define EXACT 1
+
+ /*
+ * Check that the versions of Tcl that have been loaded are the
+ * same ones that BLT was compiled against.
+ */
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, TCL_VERSION, EXACT) == NULL) {
+ return TCL_ERROR;
+ }
+#else
+ if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, EXACT) == NULL) {
+ return TCL_ERROR;
+ }
+#endif /* USE_TCL_STUBS */
+
+#ifndef TCL_ONLY
+#ifdef USE_TK_STUBS
+ if (Tk_InitStubs(interp, TK_VERSION, EXACT) == NULL) {
+ return TCL_ERROR;
+ }
+#else
+ if (Tcl_PkgRequire(interp, "Tk", TK_VERSION, EXACT) == NULL) {
+ return TCL_ERROR;
+ }
+#endif /* USE_TK_STUBS */
+#endif /* TCL_ONLY */
+
+ /* Set the version variable. We'll use it in the following script. */
+ value = Tcl_SetVar(interp, "blt_version", BLT_VERSION, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ value = Tcl_SetVar(interp, "blt_patchLevel", BLT_PATCH_LEVEL,
+ TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, libPath, -1);
+#ifdef WIN32
+ {
+ HKEY key;
+ DWORD result;
+#ifndef BLT_REGISTRY_KEY
+#define BLT_REGISTRY_KEY "Software\\BLT\\" BLT_VERSION "\\" TCL_VERSION
+#endif
+ result = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, /* Parent key. */
+ BLT_REGISTRY_KEY, /* Path to sub-key. */
+ 0, /* Reserved. */
+ KEY_READ, /* Security access mask. */
+ &key); /* Resulting key.*/
+
+ if (result == ERROR_SUCCESS) {
+ DWORD size;
+
+ /* Query once to get the size of the string needed */
+ result = RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL, NULL,
+ &size);
+ if (result == ERROR_SUCCESS) {
+ Tcl_DStringSetLength(&dString, size);
+ /* And again to collect the string. */
+ RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL,
+ (LPBYTE)Tcl_DStringValue(&dString), &size);
+ RegCloseKey(key);
+ }
+ }
+ }
+#endif /* WIN32 */
+ value = Tcl_SetVar(interp, "blt_libPath", Tcl_DStringValue(&dString),
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
+ Tcl_DStringFree(&dString);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_Eval(interp, initScript) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#if (TCL_MAJOR_VERSION > 7)
+
+/*LINTLIBRARY*/
+
+#ifdef WIN32
+extern int bltPlatformId;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DllMain --
+ *
+ * This wrapper function is used by Windows to invoke the
+ * initialization code for the DLL.
+ *
+ * Results:
+ * Returns TRUE;
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+BOOL APIENTRY
+DllMain(
+ HINSTANCE hInst, /* Library instance handle. */
+ DWORD reason, /* Reason this function is being called. */
+ LPVOID reserved) /* Not used. */
+{
+ return TRUE;
+}
+
+#endif
+
+EXPORT int
+Blt_Init(interp)
+ Tcl_Interp *interp; /* Interpreter to add extra commands */
+{
+ register Tcl_AppInitProc **p;
+ Tcl_ValueType args[2];
+ Tcl_Namespace *spacePtr;
+
+ if (GetVersionInfo(interp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ spacePtr = NULL;
+#ifndef TCL_ONLY
+ spacePtr = Tcl_CreateNamespace(interp, "blt::tile", NULL,
+ (Tcl_NamespaceDeleteProc *) NULL);
+ if (spacePtr == NULL) {
+ return TCL_ERROR;
+ }
+#endif
+ for (p = initProcArr; *p != NULL; p++) {
+ if ((**p) (interp) != TCL_OK) {
+ Tcl_DeleteNamespace(spacePtr);
+ return TCL_ERROR;
+ }
+ }
+ if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ args[0] = args[1] = TCL_EITHER;
+ Tcl_CreateMathFunc(interp, "min", 2, args, MinMathProc, (ClientData)0);
+ Tcl_CreateMathFunc(interp, "max", 2, args, MaxMathProc, (ClientData)0);
+#ifndef TCL_ONLY
+ Blt_InitEpsCanvasItem(interp);
+#endif
+ return TCL_OK;
+}
+
+#else
+
+/*LINTLIBRARY*/
+EXPORT int
+Blt_Init(interp)
+ Tcl_Interp *interp; /* Interpreter to add extra commands */
+{
+ register Tcl_AppInitProc **p;
+ Tcl_ValueType args[2];
+
+#ifdef ITCL_NAMESPACES
+ Itcl_Namespace dummy, spaceId; /* Token for "blt" namespace
+ * created, used to delete the
+ * namespace on errors. */
+ spaceId = NULL;
+#endif
+ if (GetVersionInfo(interp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#ifdef ITCL_NAMESPACES
+ if (Itcl_CreateNamesp(interp, "blt", (ClientData)0,
+ (Itcl_DeleteProc *) 0, &spaceId) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#ifndef TCL_ONLY
+ if (Itcl_CreateNamesp(interp, "blt::tile", (ClientData)0,
+ (Itcl_DeleteProc *) 0, &dummy) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#endif /* TCL_ONLY */
+#endif /* ITCL_NAMESPACES */
+ if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (p = initProcArr; *p != NULL; p++) {
+ if ((**p) (interp) != TCL_OK) {
+#ifdef ITCL_NAMESPACES
+ if (spaceId != NULL) {
+ Itcl_DeleteNamesp(spaceId);
+ }
+#endif
+ return TCL_ERROR;
+ }
+ }
+ args[0] = args[1] = TCL_EITHER;
+ Tcl_CreateMathFunc(interp, "min", 2, args, MinMathProc, (ClientData)0);
+ Tcl_CreateMathFunc(interp, "max", 2, args, MaxMathProc, (ClientData)0);
+#ifndef TCL_ONLY
+ Blt_InitEpsCanvasItem(interp);
+#endif
+#if (TCL_MAJOR_VERSION > 7)
+ Blt_RegisterArrayType(interp);
+#endif
+ return TCL_OK;
+}
+
+#endif /* TCL_MAJOR_VERION >= 8 */
+
+/*LINTLIBRARY*/
+EXPORT int
+Blt_SafeInit(interp)
+ Tcl_Interp *interp; /* Interpreter to add extra commands */
+{
+ return Blt_Init(interp);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitCmd --
+ *
+ * Given the name of a command, return a pointer to the
+ * clientData field of the command.
+ *
+ * Results:
+ * A standard TCL result. If the command is found, TCL_OK
+ * is returned and clientDataPtr points to the clientData
+ * field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ * If the command is found, clientDataPtr is set to the address
+ * of the clientData of the command. If not found, an error
+ * message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+Tcl_Command
+Blt_InitCmd(interp, nsName, specPtr)
+ Tcl_Interp *interp;
+ char *nsName;
+ Blt_CmdSpec *specPtr;
+{
+ char *cmdPath;
+ Tcl_DString dString;
+ Tcl_Command cmdToken;
+
+ Tcl_DStringInit(&dString);
+#if HAVE_NAMESPACES
+ if (nsName != NULL) {
+ Tcl_DStringAppend(&dString, nsName, -1);
+ }
+ Tcl_DStringAppend(&dString, "::", -1);
+#endif /* HAVE_NAMESPACES */
+ Tcl_DStringAppend(&dString, specPtr->name, -1);
+
+ cmdPath = Tcl_DStringValue(&dString);
+ cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0);
+ if (cmdToken != NULL) {
+ Tcl_DStringFree(&dString);
+ return cmdToken; /* Assume command was already initialized */
+ }
+ cmdToken = Tcl_CreateCommand(interp, cmdPath, specPtr->cmdProc,
+ specPtr->clientData, specPtr->cmdDeleteProc);
+ Tcl_DStringFree(&dString);
+
+#if (HAVE_NAMESPACES) && (TCL_MAJOR_VERSION > 7)
+ {
+ Tcl_Namespace *nsPtr;
+ int dontResetList = 0;
+
+ nsPtr = Tcl_FindNamespace(interp, nsName, (Tcl_Namespace *)NULL,
+ TCL_LEAVE_ERR_MSG);
+ if (nsPtr == NULL) {
+ return NULL;
+ }
+ if (Tcl_Export(interp, nsPtr, specPtr->name, dontResetList) != TCL_OK) {
+ return NULL;
+ }
+ }
+#endif /* TCL_MAJOR_VERSION > 7 */
+ return cmdToken;
+}
+
+#if (TCL_MAJOR_VERSION > 7)
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitObjCmd --
+ *
+ * Given the name of a command, return a pointer to the
+ * clientData field of the command.
+ *
+ * Results:
+ * A standard TCL result. If the command is found, TCL_OK
+ * is returned and clientDataPtr points to the clientData
+ * field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ * If the command is found, clientDataPtr is set to the address
+ * of the clientData of the command. If not found, an error
+ * message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+Tcl_Command
+Blt_InitObjCmd(interp, nsName, specPtr)
+ Tcl_Interp *interp;
+ char *nsName;
+ Blt_ObjCmdSpec *specPtr;
+{
+ char *cmdPath;
+ Tcl_DString dString;
+ Tcl_Command cmdToken;
+ Tcl_Namespace *nsPtr;
+
+ Tcl_DStringInit(&dString);
+ if (nsName != NULL) {
+ Tcl_DStringAppend(&dString, nsName, -1);
+ }
+ Tcl_DStringAppend(&dString, "::", -1);
+ Tcl_DStringAppend(&dString, specPtr->name, -1);
+
+ cmdPath = Tcl_DStringValue(&dString);
+ cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0);
+ if (cmdToken != NULL) {
+ Tcl_DStringFree(&dString);
+ return cmdToken; /* Assume command was already initialized */
+ }
+ cmdToken = Tcl_CreateObjCommand(interp, cmdPath,
+ (Tcl_ObjCmdProc *)specPtr->cmdProc,
+ specPtr->clientData,
+ specPtr->cmdDeleteProc);
+ Tcl_DStringFree(&dString);
+
+ nsPtr = Tcl_FindNamespace(interp, nsName, (Tcl_Namespace *)NULL,
+ TCL_LEAVE_ERR_MSG);
+ if (nsPtr == NULL) {
+ return NULL;
+ }
+ if (Tcl_Export(interp, nsPtr, specPtr->name, FALSE) != TCL_OK) {
+ return NULL;
+ }
+ return cmdToken;
+}
+
+#endif /* TCL_MAJOR_VERSION > 7 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitCmds --
+ *
+ * Given the name of a command, return a pointer to the
+ * clientData field of the command.
+ *
+ * Results:
+ * A standard TCL result. If the command is found, TCL_OK
+ * is returned and clientDataPtr points to the clientData
+ * field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ * If the command is found, clientDataPtr is set to the address
+ * of the clientData of the command. If not found, an error
+ * message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_InitCmds(interp, nsName, specPtr, nCmds)
+ Tcl_Interp *interp;
+ char *nsName;
+ Blt_CmdSpec *specPtr;
+ int nCmds;
+{
+ Blt_CmdSpec *endPtr;
+
+ for (endPtr = specPtr + nCmds; specPtr < endPtr; specPtr++) {
+ if (Blt_InitCmd(interp, nsName, specPtr) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
diff --git a/blt/src/bltInt.h b/blt/src/bltInt.h
new file mode 100644
index 00000000000..d963b3567af
--- /dev/null
+++ b/blt/src/bltInt.h
@@ -0,0 +1,1260 @@
+/*
+ * bltInt.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_INT_H
+#define _BLT_INT_H
+
+#ifdef WIN32
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef STRICT
+#undef WIN32_LEAN_AND_MEAN
+#include <windowsx.h>
+#endif /* WIN32 */
+
+#include <tcl.h>
+#include <tk.h>
+
+#define _VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#define TCL_VERSION_NUMBER _VERSION(TCL_MAJOR_VERSION, TCL_MINOR_VERSION, TCL_RELEASE_SERIAL)
+#define TK_VERSION_NUMBER _VERSION(TK_MAJOR_VERSION, TK_MINOR_VERSION, TK_RELEASE_SERIAL)
+
+#include "bltTkInt.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+
+#ifndef BLT_CONFIG_H
+#ifdef WIN32
+#include "bltWinConfig.h"
+#else
+#include "bltConfig.h"
+#endif
+#define BLT_CONFIG_H 1
+#endif
+
+#include "blt.h"
+#include "bltNsUtil.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif /* HAVE_ERRNO_H */
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif /* HAVE_CTYPE_H */
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif /* HAVE_MEMORY_H */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif /* HAVE_IEEEFP_H */
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef M_PI_2
+#define M_PI_2 1.57079632679489661923
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif /* M_SQRT2 */
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.70710678118654752440
+#endif /* M_SQRT1_2 */
+
+#ifndef SHRT_MAX
+#define SHRT_MAX 0x7FFF
+#endif /* SHRT_MAX */
+
+#ifndef SHRT_MIN
+#define SHRT_MIN -(SHRT_MAX)
+#endif /* SHRT_MAX */
+
+#ifndef USHRT_MAX
+#define USHRT_MAX 0xFFFF
+#endif /* USHRT_MAX */
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif /* INT_MAX */
+
+#ifndef HAVE_FLOAT_H
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DBL_MIN, DBL_MAX --
+ *
+ * DBL_MAX and DBL_MIN are the largest and smaller double
+ * precision numbers that can be represented by the floating
+ * point hardware. If the compiler is ANSI, they can be found in
+ * float.h. Otherwise, we use HUGE_VAL or HUGE to determine
+ * them.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*
+ * Don't want to include __infinity (definition of HUGE_VAL (SC1.x))
+ */
+#ifdef sun
+#define DBL_MAX 1.7976931348623157E+308
+#define DBL_MIN 2.2250738585072014E-308
+#define DBL_EPSILON 2.2204460492503131e-16
+#else
+#ifndef DBL_EPSILON
+#define DBL_EPSILON BLT_DBL_EPSILON
+#endif
+#ifdef HUGE_VAL
+#define DBL_MAX HUGE_VAL
+#define DBL_MIN (1/HUGE_VAL)
+#else
+#ifdef HUGE
+#define DBL_MAX HUGE
+#define DBL_MIN (1/HUGE)
+#else
+/*
+ * Punt: Assume values simple and relatively small
+ */
+#define DBL_MAX 3.40282347E+38
+#define DBL_MIN 1.17549435E-38
+#endif /*HUGE*/
+#endif /*HUGE_VAL*/
+#endif /*sun*/
+#endif /*!HAVE_FLOAT_H*/
+
+#undef INLINE
+#ifdef __GNUC__
+#define INLINE inline
+#else
+#define INLINE
+#endif
+#undef EXPORT
+#define EXPORT
+
+#undef VARARGS
+#ifdef __cplusplus
+#define ANYARGS (...)
+#define VARARGS(first) (first, ...)
+#define VARARGS2(first, second) (first, second, ...)
+#else
+#define ANYARGS ()
+#define VARARGS(first) ()
+#define VARARGS2(first, second) ()
+#endif /* __cplusplus */
+
+#undef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#undef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#undef MIN3
+#define MIN3(a,b,c) (((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c)))
+
+#undef MAX3
+#define MAX3(a,b,c) (((a)>(b))?(((a)>(c))?(a):(c)):(((b)>(c))?(b):(c)))
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * The following are macros replacing math library functions:
+ * "fabs", "fmod", "abs", "rint", and "exp10".
+ *
+ * Although many of these routines may be in your math library,
+ * they aren't used in libtcl.a or libtk.a. This makes it
+ * difficult to dynamically load the BLT library as a shared
+ * object unless the math library is also shared (which isn't
+ * true on several systems). We can avoid the problem by
+ * replacing the "exotic" math routines with macros.
+ *
+ * ----------------------------------------------------------------------
+ */
+#undef ABS
+#define ABS(x) (((x)<0)?(-(x)):(x))
+
+#undef EXP10
+#define EXP10(x) (pow(10.0,(x)))
+
+#undef FABS
+#define FABS(x) (((x)<0.0)?(-(x)):(x))
+
+#undef SIGN
+#define SIGN(x) (((x) < 0.0) ? -1 : 1)
+
+/*
+ * Be careful when using the next two macros. They both assume the floating
+ * point number is less than the size of an int. That means, for example, you
+ * can't use these macros with numbers bigger than than 2^31-1.
+ */
+#undef FMOD
+#define FMOD(x,y) ((x)-(((int)((x)/(y)))*y))
+
+#undef ROUND
+#define ROUND(x) ((int)((x) + (((x)<0.0) ? -0.5 : 0.5)))
+
+#define TRUE 1
+#define FALSE 0
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define UCHAR(c) ((unsigned char) (c))
+
+#undef panic
+#define panic(mesg) Blt_Panic("%s:%d %s", __FILE__, __LINE__, (mesg))
+
+#ifdef HAVE_FINITE
+#else
+#ifdef HAVE_ISFINITE
+#define finite(x) isfinite(x)
+#endif /* HAVE_ISFINITE */
+#endif /* HAVE_FINITE */
+
+/*
+ * Since the Tcl/Tk distribution doesn't perform any asserts, dynamic
+ * loading can fail to find the __assert function. As a workaround,
+ * we'll include our own.
+ */
+#undef assert
+#ifdef NDEBUG
+#define assert(EX) ((void)0)
+#else
+extern void Blt_Assert _ANSI_ARGS_((char *expr, char *file, int line));
+#ifdef __STDC__
+#define assert(EX) (void)((EX) || (Blt_Assert(#EX, __FILE__, __LINE__), 0))
+#else
+#define assert(EX) (void)((EX) || (Blt_Assert("EX", __FILE__, __LINE__), 0))
+#endif /* __STDC__ */
+
+#endif /* NDEBUG */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_CmdSpec --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Name of command */
+ Tcl_CmdProc *cmdProc;
+ Tcl_CmdDeleteProc *cmdDeleteProc;
+ ClientData clientData;
+} Blt_CmdSpec;
+
+#if (TCL_MAJOR_VERSION >= 8)
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_CmdSpec --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Name of command */
+ Tcl_ObjCmdProc *cmdProc;
+ Tcl_CmdDeleteProc *cmdDeleteProc;
+ ClientData clientData;
+} Blt_ObjCmdSpec;
+
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Op --
+ *
+ * Generic function prototype of CmdOptions.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef int (*Blt_Op) _ANSI_ARGS_(ANYARGS);
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_OpSpec --
+ *
+ * Structure to specify a set of operations for a Tcl command.
+ * This is passed to the Blt_GetOp procedure to look
+ * for a function pointer associated with the operation name.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ char *name; /* Name of operation */
+ int minChars; /* Minimum # characters to disambiguate */
+ Blt_Op proc;
+ int minArgs; /* Minimum # args required */
+ int maxArgs; /* Maximum # args required */
+ char *usage; /* Usage message */
+
+} Blt_OpSpec;
+
+typedef enum {
+ BLT_OP_ARG0, /* Op is the first argument. */
+ BLT_OP_ARG1, /* Op is the second argument. */
+ BLT_OP_ARG2, /* Op is the third argument. */
+ BLT_OP_ARG3, /* Op is the fourth argument. */
+ BLT_OP_ARG4 /* Op is the fifth argument. */
+
+} Blt_OpIndex;
+
+#define BLT_OP_LINEAR_SEARCH 1
+#define BLT_OP_BINARY_SEARCH 0
+
+extern Blt_Op Blt_GetOp _ANSI_ARGS_((Tcl_Interp *interp, int nSpecs,
+ Blt_OpSpec *specArr, int operPos, int argc, char **argv, int flags));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+extern Blt_Op Blt_GetOpFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ int nSpecs, Blt_OpSpec *specArr, int operPos, int objc,
+ Tcl_Obj *CONST *objv, int flags));
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Assume we need to declare free if there's no stdlib.h or malloc.h
+ *
+ * ----------------------------------------------------------------------
+ */
+#if !defined(HAVE_STDLIB_H) && !defined(HAVE_MALLOC_H)
+extern void free _ANSI_ARGS_((void *));
+#endif
+
+#define free(x) abc123(x)
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * On some systems "strdup" and "strcasecmp" are in the C library,
+ * but have no declarations in the C header files. Make sure we
+ * supply them here.
+ *
+ * ----------------------------------------------------------------------
+ */
+#ifdef NEED_DECL_STRDUP
+extern char *strdup _ANSI_ARGS_((CONST char *s));
+#endif /* NEED_DECL_STRDUP */
+
+#ifndef HAVE_STRTOLOWER
+extern void strtolower _ANSI_ARGS_((char *s));
+#endif /* HAVE_STRTOLOWER */
+
+#ifdef NEED_DECL_DRAND48
+extern double drand48 _ANSI_ARGS_((void));
+extern void srand48 _ANSI_ARGS_((long seed));
+#endif /* NEED_DECL_DRAND48 */
+
+#ifdef NEED_DECL_STRCASECMP
+extern int strcasecmp _ANSI_ARGS_((CONST char *s1, CONST char *s2));
+#endif /* NEED_DECL_STRCASECMP */
+
+extern int Blt_DictionaryCompare _ANSI_ARGS_((char *s1, char *s2));
+
+EXTERN void Blt_Panic _ANSI_ARGS_(TCL_VARARGS(char *, args));
+
+
+
+/* ---------------------------------------------------------------- */
+
+#define PIXELS_NONNEGATIVE 0
+#define PIXELS_POSITIVE 1
+#define PIXELS_ANY 2
+
+#define COUNT_NONNEGATIVE 0
+#define COUNT_POSITIVE 1
+#define COUNT_ANY 2
+
+#define BLT_SCROLL_MODE_CANVAS (1<<0)
+#define BLT_SCROLL_MODE_LISTBOX (1<<1)
+#define BLT_SCROLL_MODE_HIERBOX (1<<2)
+
+#define RGB_ANTIQUEWHITE1 "#ffefdb"
+#define RGB_BISQUE1 "#ffe4c4"
+#define RGB_BISQUE2 "#eed5b7"
+#define RGB_BISQUE3 "#cdb79e"
+#define RGB_BLACK "#000000"
+#define RGB_BLUE "#0000ff"
+#define RGB_GREEN "#00ff00"
+#define RGB_GREY "#b0b0b0"
+#define RGB_GREY15 "#262626"
+#define RGB_GREY50 "#7f7f7f"
+#define RGB_GREY64 "#a3a3a3"
+#define RGB_GREY70 "#b3b3b3"
+#define RGB_GREY75 "#bfbfbf"
+#define RGB_GREY77 "#c3c3c3"
+#define RGB_GREY82 "#d1d1d1"
+#define RGB_GREY85 "#d9d9d9"
+#define RGB_GREY90 "#e5e5e5"
+#define RGB_GREY95 "#f2f2f2"
+#define RGB_LIGHTBLUE0 "#e4f7ff"
+#define RGB_LIGHTBLUE1 "#bfefff"
+#define RGB_LIGHTBLUE2 "#b2dfee"
+#define RGB_LIGHTSKYBLUE1 "#b0e2ff"
+#define RGB_MAROON "#b03060"
+#define RGB_NAVYBLUE "#000080"
+#define RGB_PINK "#ffc0cb"
+#define RGB_BISQUE1 "#ffe4c4"
+#define RGB_RED "#ff0000"
+#define RGB_WHITE "#ffffff"
+#define RGB_YELLOW "#ffff00"
+
+#ifdef OLD_TK_COLORS
+#define STD_COLOR_NORMAL_BG RGB_BISQUE1
+#define STD_COLOR_ACTIVE_BG RGB_BISQUE2
+#define STD_COLOR_SELECT_BG RGB_LIGHTBLUE2
+#define STD_COLOR_DISABLE_FG RGB_GREY64
+#else
+#define STD_COLOR_NORMAL_BG RGB_GREY85
+#define STD_COLOR_ACTIVE_BG RGB_GREY64
+#define STD_COLOR_SELECT_BG RGB_GREY77
+#define STD_COLOR_DISABLE_FG RGB_GREY64
+#endif /* OLD_TK_COLORS */
+
+#define STD_COLOR_INDICATOR RGB_MAROON
+
+#define STD_COLOR_ACTIVE_FG RGB_BLACK
+#define STD_COLOR_NORMAL_FG RGB_BLACK
+#define STD_COLOR_SELECT_FG RGB_BLACK
+#define STD_COLOR_SHADOW RGB_GREY64
+#define STD_MONO_ACTIVE_BG RGB_BLACK
+#define STD_MONO_ACTIVE_FG RGB_WHITE
+#define STD_MONO_NORMAL_BG RGB_WHITE
+#define STD_MONO_NORMAL_FG RGB_BLACK
+#define STD_MONO_SELECT_BG RGB_BLACK
+#define STD_MONO_SELECT_FG RGB_WHITE
+#define STD_MONO_SHADOW RGB_BLACK
+
+#define STD_SELECT_BORDERWIDTH "2"
+#define STD_BORDERWIDTH "2"
+
+#define STD_FONT_HUGE "*-Helvetica-Medium-R-Normal-*-18-180-*"
+#define STD_FONT_LARGE "*-Helvetica-Medium-R-Normal-*-14-140-*"
+#define STD_FONT "*-Helvetica-Medium-R-Normal-*-12-120-*"
+#define STD_FONT_SMALL "*-Helvetica-Medium-R-Normal-*-10-100-*"
+
+#ifdef WIN32
+#undef STD_FONT
+#undef STD_FONT_SMALL
+#undef STD_FONT_LARGE
+#undef STD_COLOR_NORMAL_BG
+#undef STD_COLOR_NORMAL_FG
+#undef STD_COLOR_TEXT_FG
+#undef STD_COLOR_SELECT_BG
+
+#define STD_FONT "Arial 8"
+#define STD_FONT_SMALL "Arial 6"
+#define STD_FONT_LARGE "Arial 12"
+#define STD_COLOR_NORMAL_BG "SystemButtonFace"
+#define STD_COLOR_NORMAL_FG "SystemButtonText"
+#define STD_COLOR_TEXT_FG "SystemWindowText"
+#define STD_COLOR_SELECT_BG "SystemHighlight"
+#endif /* WIN32 */
+
+#ifdef HAVE_XEXTENDEDMAXREQUESTSIZE
+#define Blt_MaxRequestSize(d) \
+ MAX(XExtendedMaxRequestSize(d), XMaxRequestSize(d))
+#else
+#define Blt_MaxRequestSize(d) XMaxRequestSize(d)
+#endif
+
+#define LineWidth(w) (((w) > 1) ? (w) : 0)
+
+#ifdef TCL_UTF_MAX
+#define HAVE_UTF 1
+extern FILE *Blt_OpenUtfFile _ANSI_ARGS_((char *fileName, char *mode));
+#define fopen(f,m) Blt_OpenUtfFile((f),(m));
+#else
+#define HAVE_UTF 0
+#endif /* TCL_UTF_MAX */
+
+typedef char *DestroyData;
+
+
+#ifndef TK_RELIEF_SOLID
+#define TK_RELIEF_SOLID TK_RELIEF_FLAT
+#endif
+
+/*
+ * Tcl/Tk Backward compatibility section.
+ */
+#if (TCL_MAJOR_VERSION > 7)
+
+#define NO_FLAGS 0
+#define Blt_FindPhoto(interp, name) Tk_FindPhoto(interp, name)
+
+#else
+
+#define Tcl_GetStringResult(interp) ((interp)->result)
+#define Blt_FindPhoto(interp, name) Tk_FindPhoto(name)
+
+#define Tcl_DeleteCommandFromToken(interp, token) \
+ Tcl_DeleteCommand(interp, Tcl_GetCommandName(interp, token))
+
+/*
+ *--------------------------------------------------------------
+ *
+ * The definitions below provide foreward compatibility for
+ * functions and types related to event handling that used to
+ * be in Tk but have moved to Tcl.
+ *
+ *--------------------------------------------------------------
+ */
+
+#define Tcl_IdleProc Tk_IdleProc
+#define Tcl_FileProc Tk_FileProc
+#define Tcl_TimerProc Tk_TimerProc
+#define Tcl_TimerToken Tk_TimerToken
+
+#define Tcl_BackgroundError Tk_BackgroundError
+#define Tcl_CancelIdleCall Tk_CancelIdleCall
+
+#define Tcl_CreateTimerHandler Tk_CreateTimerHandler
+#define Tcl_DeleteTimerHandler Tk_DeleteTimerHandler
+#define Tcl_DoOneEvent Tk_DoOneEvent
+#define Tcl_DoWhenIdle Tk_DoWhenIdle
+#define Tcl_Sleep Tk_Sleep
+
+/* Additional stuff that has moved to Tcl: */
+
+#define Tcl_AfterCmd Tk_AfterCmd
+#define Tcl_EventuallyFree Tk_EventuallyFree
+#define Tcl_FreeProc Tk_FreeProc
+#define Tcl_Preserve Tk_Preserve
+#define Tcl_Release Tk_Release
+
+#endif /* TCL_MAJOR_VERSION > 7 */
+
+typedef int (QSortCompareProc) _ANSI_ARGS_((const void *, const void *));
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Pad --
+ *
+ * Specifies vertical and horizontal padding.
+ *
+ * Padding can be specified on a per side basis. The fields
+ * side1 and side2 refer to the opposite sides, either
+ * horizontally or vertically.
+ *
+ * side1 side2
+ * ----- -----
+ * x | left right
+ * y | top bottom
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ short int side1, side2;
+} Blt_Pad;
+
+#define padLeft padX.side1
+#define padRight padX.side2
+#define padTop padY.side1
+#define padBottom padY.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * The following enumerated values are used as bit flags.
+ * FILL_NONE Neither coordinate plane is specified
+ * FILL_X Horizontal plane.
+ * FILL_Y Vertical plane.
+ * FILL_BOTH Both vertical and horizontal planes.
+ *
+ * ----------------------------------------------------------------------
+ */
+#define FILL_NONE 0
+#define FILL_X 1
+#define FILL_Y 2
+#define FILL_BOTH 3
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Dashes --
+ *
+ * List of dash values (maximum 11 based upon PostScript limit).
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ unsigned char values[12];
+ int offset;
+} Blt_Dashes;
+
+#define LineIsDashed(d) ((d).values[0] != 0)
+
+extern void Blt_SetDashes _ANSI_ARGS_((Display *display, GC gc,
+ Blt_Dashes *dashesPtr));
+extern Blt_Dashes *Blt_GetDashes _ANSI_ARGS_((GC gc));
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Point2D --
+ *
+ * 2-D coordinate.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ double x, y;
+} Point2D;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Point3D --
+ *
+ * 3-D coordinate.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ double x, y, z;
+} Point3d;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Segment2D --
+ *
+ * 2-D line segment.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ Point2D p, q; /* The two end points of the segment. */
+} Segment2D;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Dim2D --
+ *
+ * 2-D dimension.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ short int width, height;
+} Dim2D;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Region2D --
+ *
+ * 2-D region. Used to copy parts of images.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+ int left, right, top, bottom;
+} Region2D;
+
+#define RegionWidth(r) ((r)->right - (r)->left + 1)
+#define RegionHeight(r) ((r)->bottom - (r)->top + 1)
+
+typedef struct {
+ double left, right, top, bottom;
+} Extents2D;
+
+typedef struct {
+ double left, right, top, bottom, front, back;
+} Extents3D;
+
+#define PointInRegion(e,x,y) \
+ (((x) <= (e)->right) && ((x) >= (e)->left) && \
+ ((y) <= (e)->bottom) && ((y) >= (e)->top))
+
+#define PointInRectangle(r,x0,y0) \
+ (((x0) <= (int)((r)->x + (r)->width - 1)) && ((x0) >= (int)(r)->x) && \
+ ((y0) <= (int)((r)->y + (r)->height - 1)) && ((y0) >= (int)(r)->y))
+
+
+/* -------------------------------------------------------------------
+ *
+ * ColorPair --
+ *
+ * Holds a pair of foreground, background colors.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+ XColor *fgColor, *bgColor;
+} ColorPair;
+
+#define COLOR_NONE (XColor *)0
+#define COLOR_DEFAULT (XColor *)1
+#define COLOR_ALLOW_DEFAULTS 1
+
+extern int Blt_GetColorPair _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ char *fgColor, char *bgColor, ColorPair *pairPtr, int colorFlag));
+extern void Blt_FreeColorPair _ANSI_ARGS_((ColorPair *pairPtr));
+
+#define STATE_NORMAL 0
+#define STATE_ACTIVE (1<<0)
+#define STATE_DISABLED (1<<1)
+#define STATE_EMPHASIS (1<<2)
+
+
+#include "bltText.h"
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * X11/Xosdefs.h requires XNOSTDHDRS be set for some systems.
+ * This is a guess. If I can't find STDC headers or unistd.h,
+ * assume that this is non-POSIX and non-STDC environment.
+ * (needed for Encore Umax 3.4 ?)
+ *
+ * ----------------------------------------------------------------------
+ */
+#if !defined(STDC_HEADERS) && !defined(HAVE_UNISTD_H)
+#define XNOSTDHDRS 1
+#endif
+
+extern char *Blt_Itoa _ANSI_ARGS_((int value));
+extern char *Blt_Utoa _ANSI_ARGS_((unsigned int value));
+extern char *Blt_Dtoa _ANSI_ARGS_((Tcl_Interp *interp, double value));
+extern Tcl_Command Blt_InitCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ char *namespace, Blt_CmdSpec *specPtr));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+extern Tcl_Command Blt_InitObjCmd _ANSI_ARGS_((Tcl_Interp *interp,
+ char *namespace, Blt_ObjCmdSpec *specPtr));
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+extern char *Tcl_GetString _ANSI_ARGS_((Tcl_Obj *objPtr));
+extern int Tcl_EvalObjv _ANSI_ARGS_((Tcl_Interp *interp, int objc,
+ Tcl_Obj **objv, int flags));
+#endif /* TCL_VERSION_NUMBER < 8.2.0 */
+#endif /* TCL_VERSION_NUMBER >= 8.0.0 */
+
+#if ((TK_VERSION_NUMBER >= _VERSION(8,0,0)) && \
+ (TK_VERSION_NUMBER < _VERSION(8,1,0)))
+EXTERN int Tk_GetAnchorFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Tk_Anchor *anchorPtr));
+EXTERN int Tk_GetJustifyFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Tk_Justify *justifyPtr));
+EXTERN int Tk_GetReliefFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *reliefPtr));
+EXTERN int Tk_GetMMFromObj _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, double *doublePtr));
+EXTERN int Tk_GetPixelsFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr, int *intPtr));
+EXTERN Tk_3DBorder Tk_Alloc3DBorderFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Pixmap Tk_AllocBitmapFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Tk_Font Tk_AllocFontFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Tk_Cursor Tk_AllocCursorFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN XColor *Tk_AllocColorFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr));
+#endif /* 8.0 */
+
+extern int Blt_InitCmds _ANSI_ARGS_((Tcl_Interp *interp, char *namespace,
+ Blt_CmdSpec *specPtr, int nCmds));
+
+extern int Blt_NaturalSpline _ANSI_ARGS_((Point2D *origPts, int nOrigPts,
+ Point2D *intpPts, int nIntpPts));
+
+extern int Blt_QuadraticSpline _ANSI_ARGS_((Point2D *origPts, int nOrigPts,
+ Point2D *intpPts, int nIntpPts));
+
+extern int Blt_SimplifyLine _ANSI_ARGS_((Point2D *origPts, int low, int high,
+ double tolerance, int indices[]));
+
+extern int Blt_NaturalParametricSpline _ANSI_ARGS_((Point2D *origPts,
+ int nOrigPts, Extents2D *extsPtr, int isClosed, Point2D *intpPts,
+ int nIntpPts));
+
+extern int Blt_CatromParametricSpline _ANSI_ARGS_((Point2D *origPts,
+ int nOrigPts, Point2D *intpPts, int nIntpPts));
+
+extern int Blt_StringToFlag _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int flags));
+extern char *Blt_FlagToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *string, int offset, Tcl_FreeProc **freeProc));
+
+extern void Blt_InitHexTable _ANSI_ARGS_((char *table));
+
+extern GC Blt_GetPrivateGC _ANSI_ARGS_((Tk_Window tkwin, unsigned long gcMask,
+ XGCValues *valuePtr));
+
+extern GC Blt_GetPrivateGCFromDrawable _ANSI_ARGS_((Display *display,
+ Drawable drawable, unsigned long gcMask, XGCValues *valuePtr));
+
+extern void Blt_FreePrivateGC _ANSI_ARGS_((Display *display, GC gc));
+
+extern Tk_Window Blt_FindChild _ANSI_ARGS_((Tk_Window parent, char *name));
+
+extern Tk_Window Blt_FirstChild _ANSI_ARGS_((Tk_Window parent));
+
+extern Tk_Window Blt_NextChild _ANSI_ARGS_((Tk_Window tkwin));
+
+extern void Blt_RelinkWindow _ANSI_ARGS_((Tk_Window tkwin, Tk_Window newParent,
+ int x, int y));
+
+extern Tk_Window Blt_Toplevel _ANSI_ARGS_((Tk_Window tkwin));
+
+extern int Blt_GetPixels _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ char *string, int check, int *valuePtr));
+extern int Blt_GetPosition _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ int *indexPtr));
+extern int Blt_GetCount _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ int check, int *valuePtr));
+
+extern char *Blt_NameOfFill _ANSI_ARGS_((int fill));
+
+extern int Blt_GetXY _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ char *string, int *x, int *y));
+
+extern Point2D Blt_GetProjection _ANSI_ARGS_((int x, int y, Point2D *p,
+ Point2D *q));
+
+extern Tk_OptionParseProc Blt_StringToEnum;
+extern Tk_OptionPrintProc Blt_EnumToString;
+
+extern int Blt_ConfigModified _ANSI_ARGS_(TCL_VARARGS(Tk_ConfigSpec *, specs));
+
+extern void Blt_DStringAppendElements _ANSI_ARGS_(TCL_VARARGS(Tcl_DString *, args));
+
+extern void Blt_MakeTransparentWindowExist _ANSI_ARGS_((Tk_Window tkwin,
+ Window parent, int isBusy));
+
+extern Window Blt_GetParent _ANSI_ARGS_((Display *display, Window tkwin));
+
+extern void Blt_GetBoundingBox _ANSI_ARGS_((int width, int height,
+ double theta, int *widthPtr, int *heightPtr, Point2D *points));
+
+extern void Blt_InitEpsCanvasItem _ANSI_ARGS_((Tcl_Interp *interp));
+
+extern Pixmap Blt_RotateBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap bitmap,
+ int width, int height, double theta, int *widthPtr, int *heightPtr));
+
+extern Pixmap Blt_ScaleBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap srcBitmap,
+ int srcWidth, int srcHeight, int scaledWidth, int scaledHeight));
+
+extern Pixmap Blt_ScaleBitmapRegion _ANSI_ARGS_((Tk_Window tkwin,
+ Pixmap srcBitmap, int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight, Region2D *regionPtr));
+
+extern void Blt_TranslateAnchor _ANSI_ARGS_((int x, int y, int width,
+ int height, Tk_Anchor anchor, int *transXPtr, int *transYPtr));
+
+extern Point2D Blt_TranslatePoint _ANSI_ARGS_((Point2D *pointPtr, int width,
+ int height, Tk_Anchor anchor));
+
+extern int Blt_ConfigureWidgetComponent _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, char *name, char *class, Tk_ConfigSpec *specs,
+ int argc, char **argv, char *widgRec, int flags));
+
+extern void Blt_HSV _ANSI_ARGS_((XColor *colorPtr, double *huePtr,
+ double *valPtr, double *satPtr));
+
+extern void Blt_RGB _ANSI_ARGS_((double hue, double sat, double val,
+ XColor *colorPtr));
+
+extern int Blt_ParseFlag _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window,
+ char *, char *, int));
+extern char *Blt_FlagPrint _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+ Tcl_FreeProc **));
+
+extern Window Blt_GetRealWindowId _ANSI_ARGS_((Tk_Window tkwin));
+extern int Blt_RootX _ANSI_ARGS_((Tk_Window tkwin));
+extern int Blt_RootY _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_MapTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_UnmapTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_RaiseTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_ResizeTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin,
+ int width, int height));
+extern ClientData Blt_GetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin));
+
+extern void Blt_SetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin,
+ ClientData instanceData));
+
+extern void Blt_DeleteWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin));
+
+extern int Blt_AdjustViewport _ANSI_ARGS_((int offset, int worldSize,
+ int windowSize, int scrollUnits, int scrollMode));
+
+extern int Blt_GetScrollInfo _ANSI_ARGS_((Tcl_Interp *interp, int argc,
+ char **argv, int *offsetPtr, int worldSize, int windowSize,
+ int scrollUnits, int scrollMode));
+
+#if (TK_MAJOR_VERSION >= 8)
+extern int Blt_GetScrollInfoFromObj _ANSI_ARGS_((Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST *objv, int *offsetPtr, int worldSize, int windowSize,
+ int scrollUnits, int scrollMode));
+#endif
+
+extern void Blt_UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
+ char *scrollCmd, double firstFract, double lastFract));
+
+extern int Blt_ReparentWindow _ANSI_ARGS_((Display *display, Window window,
+ Window newParent, int x, int y));
+
+#if defined(HAVE_JPEGLIB_H) || defined(HAVE_IJL_H)
+#define HAVE_JPEG 1
+extern int Blt_JPEGToPhoto _ANSI_ARGS_((Tcl_Interp *interp, char *fileName,
+ Tk_PhotoHandle photo));
+#endif /* HAVE_JPEGLIB_H || HAVE_IJL_H */
+
+#define Blt_SetBooleanResult(i, b) \
+ Tcl_SetResult((i), (b) ? "1" : "0", TCL_STATIC)
+
+/*
+ * Define this if you want to be able to tile to the main window "."
+ * This will cause a conflict with Tk if you try to compile and link
+ * statically.
+ */
+#undef TILE_MAINWINDOW
+
+#ifdef WIN32
+#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 0)
+#else
+#define NO_DDE 1
+#endif
+#else
+#define NO_DDE 1
+#define NO_PRINTER 1
+#endif /* WIN32 */
+
+#if (TCL_MAJOR_VERSION == 7)
+#define NO_TREE 1
+#define NO_ARRAY 1
+#define NO_TREEVIEW 1
+#endif
+
+/* #define NO_TED */
+
+#ifndef NO_BEEP
+extern Tcl_AppInitProc Blt_BeepInit;
+#endif
+#ifndef NO_BGEXEC
+extern Tcl_AppInitProc Blt_BgexecInit;
+#endif
+#ifndef NO_BITMAP
+extern Tcl_AppInitProc Blt_BitmapInit;
+#endif
+#ifndef NO_BUSY
+extern Tcl_AppInitProc Blt_BusyInit;
+#endif
+#ifndef NO_CONTAINER
+extern Tcl_AppInitProc Blt_ContainerInit;
+#endif
+#ifndef NO_CUTBUFFER
+extern Tcl_AppInitProc Blt_CutbufferInit;
+#endif
+#ifndef NO_DEBUG
+extern Tcl_AppInitProc Blt_DebugInit;
+#endif
+#ifndef NO_DRAGDROP
+extern Tcl_AppInitProc Blt_DragDropInit;
+#endif
+#ifndef NO_DND
+extern Tcl_AppInitProc Blt_DndInit;
+#endif
+#ifndef NO_GRAPH
+extern Tcl_AppInitProc Blt_GraphInit;
+#endif
+#ifndef NO_HIERBOX
+extern Tcl_AppInitProc Blt_HierboxInit;
+#endif
+#ifndef NO_HIERTABLE
+extern Tcl_AppInitProc Blt_HiertableInit;
+#endif
+#ifndef NO_HTEXT
+extern Tcl_AppInitProc Blt_HtextInit;
+#endif
+#ifdef WIN32
+#ifndef NO_PRINTER
+extern Tcl_AppInitProc Blt_PrinterInit;
+#endif
+#endif
+#ifndef NO_TABLE
+extern Tcl_AppInitProc Blt_TableInit;
+#endif
+#ifndef NO_VECTOR
+extern Tcl_AppInitProc Blt_VectorInit;
+#endif
+#ifndef NO_WINOP
+extern Tcl_AppInitProc Blt_WinopInit;
+#endif
+#ifndef NO_WATCH
+extern Tcl_AppInitProc Blt_WatchInit;
+#endif
+#ifndef NO_SPLINE
+extern Tcl_AppInitProc Blt_SplineInit;
+#endif
+#ifndef NO_TABSET
+extern Tcl_AppInitProc Blt_TabsetInit;
+#endif
+#ifndef NO_TABNOTEBOOK
+extern Tcl_AppInitProc Blt_TabnotebookInit;
+#endif
+#ifndef NO_TREE
+extern Tcl_AppInitProc Blt_TreeInit;
+#endif
+#ifndef NO_TREEVIEW
+extern Tcl_AppInitProc Blt_TreeViewInit;
+#endif
+#ifndef NO_TILEFRAME
+extern Tcl_AppInitProc Blt_FrameInit;
+#endif
+#ifndef NO_TILEBUTTON
+extern Tcl_AppInitProc Blt_ButtonInit;
+#endif
+#ifndef NO_TILESCROLLBAR
+extern Tcl_AppInitProc Blt_ScrollbarInit;
+#endif
+
+#if (BLT_MAJOR_VERSION == 3)
+#ifndef NO_MOUNTAIN
+extern Tcl_AppInitProc Blt_MountainInit;
+#endif
+#endif
+#ifndef NO_TED
+extern Tcl_AppInitProc Blt_TedInit;
+#endif
+
+#ifndef NO_DDE
+extern Tcl_AppInitProc Blt_DdeInit;
+#endif
+
+typedef void *(Blt_MallocProc) _ANSI_ARGS_((size_t size));
+typedef void *(Blt_CallocProc) _ANSI_ARGS_((int nElem, size_t size));
+typedef void *(Blt_ReallocProc) _ANSI_ARGS_((void *ptr, size_t size));
+typedef void (Blt_FreeProc) _ANSI_ARGS_((void *ptr));
+
+EXTERN Blt_MallocProc *Blt_MallocProcPtr;
+EXTERN Blt_FreeProc *Blt_FreeProcPtr;
+
+#define Blt_Malloc(size) (*Blt_MallocProcPtr)(size)
+#define Blt_Free (*Blt_FreeProcPtr)
+
+EXTERN char *Blt_Strdup _ANSI_ARGS_((CONST char *ptr));
+EXTERN void *Blt_Calloc _ANSI_ARGS_((unsigned int nElem, size_t size));
+
+#ifdef WIN32
+#ifdef CHECK_UNICODE_CALLS
+
+#define _UNICODE
+#define UNICODE
+
+#define __TCHAR_DEFINED
+typedef float *_TCHAR;
+
+#define _TCHAR_DEFINED
+typedef float *TCHAR;
+
+#endif /* CHECK_UNICODE_CALLS */
+
+/* DOS Encapsulated PostScript File Header */
+#pragma pack(2)
+typedef struct {
+ BYTE magic[4]; /* Magic number for a DOS EPS file
+ * C5,D0,D3,C6 */
+ DWORD psStart; /* Offset of PostScript section. */
+ DWORD psLength; /* Length of the PostScript section. */
+ DWORD wmfStart; /* Offset of Windows Meta File section. */
+ DWORD wmfLength; /* Length of Meta file section. */
+ DWORD tiffStart; /* Offset of TIFF section. */
+ DWORD tiffLength; /* Length of TIFF section. */
+ WORD checksum; /* Checksum of header. If FFFF, ignore. */
+} DOSEPSHEADER;
+#pragma pack()
+
+/* Aldus Portable Metafile Header */
+#pragma pack(2)
+typedef struct {
+ DWORD key; /* Type of metafile */
+ WORD hmf; /* Unused. Must be NULL. */
+ SMALL_RECT bbox; /* Bounding rectangle */
+ WORD inch; /* Units per inch. */
+ DWORD reserved; /* Unused. */
+ WORD checksum; /* XOR of previous fields (10 32-bit words). */
+} APMHEADER;
+#pragma pack()
+
+extern double hypot(double x, double y);
+extern int Blt_AsyncRead(int fd, char *buffer, unsigned int size);
+extern int Blt_AsyncWrite(int fd, char *buffer, unsigned int size);
+extern void Blt_CreateFileHandler(int fd, int flags, Tcl_FileProc * proc,
+ ClientData clientData);
+extern void Blt_DeleteFileHandler(int fd);
+extern int Blt_GetPlatformId(void);
+extern char *Blt_LastError(void);
+extern int Blt_GetOpenPrinter(Tcl_Interp *interp, const char *id,
+ Drawable *drawablePtr);
+extern int Blt_OpenPrinterDoc(Tcl_Interp *interp, const char *id);
+extern int Blt_ClosePrinterDoc(Tcl_Interp *interp, const char *id);
+extern void Blt_GetPrinterScale(HDC dc, double *xRatio, double *yRatio);
+
+#undef EXPORT
+#define EXPORT __declspec(dllexport)
+
+#ifdef _MSC_VER
+#define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n)
+#define strcasecmp(s1,s2) _stricmp(s1,s2)
+#define finite(x) _finite(x)
+
+#else
+/*
+ * Add missing definitions from windgi.h, windowsx.h, and winspool.h
+ */
+#include <winspool.h>
+#include <windowsx.h>
+#include <wingdi.h>
+#include <missing.h>
+#endif /* _MSC_VER */
+
+#define XCopyArea Blt_EmulateXCopyArea
+#define XCopyPlane Blt_EmulateXCopyPlane
+#define XDrawArcs Blt_EmulateXDrawArcs
+#define XDrawLine Blt_EmulateXDrawLine
+#define XDrawLines Blt_EmulateXDrawLines
+#define XDrawPoints Blt_EmulateXDrawPoints
+#define XDrawRectangle Blt_EmulateXDrawRectangle
+#define XDrawRectangles Blt_EmulateXDrawRectangles
+#define XDrawSegments Blt_EmulateXDrawSegments
+#define XDrawString Blt_EmulateXDrawString
+#define XFillArcs Blt_EmulateXFillArcs
+#define XFillPolygon Blt_EmulateXFillPolygon
+#define XFillRectangle Blt_EmulateXFillRectangle
+#define XFillRectangles Blt_EmulateXFillRectangles
+#define XFree Blt_EmulateXFree
+#define XGetWindowAttributes Blt_EmulateXGetWindowAttributes
+#define XLowerWindow Blt_EmulateXLowerWindow
+#define XMaxRequestSize Blt_EmulateXMaxRequestSize
+#define XRaiseWindow Blt_EmulateXRaiseWindow
+#define XReparentWindow Blt_EmulateXReparentWindow
+#define XSetDashes Blt_EmulateXSetDashes
+#define XUnmapWindow Blt_EmulateXUnmapWindow
+#define XWarpPointer Blt_EmulateXWarpPointer
+
+EXTERN GC Blt_EmulateXCreateGC(Display *display, Drawable drawable,
+ unsigned long mask, XGCValues *valuesPtr);
+EXTERN void Blt_EmulateXCopyArea(Display *display, Drawable src, Drawable dest,
+ GC gc, int src_x, int src_y, unsigned int width, unsigned int height,
+ int dest_x, int dest_y);
+EXTERN void Blt_EmulateXCopyPlane(Display *display, Drawable src,
+ Drawable dest, GC gc, int src_x, int src_y, unsigned int width,
+ unsigned int height, int dest_x, int dest_y, unsigned long plane);
+EXTERN void Blt_EmulateXDrawArcs(Display *display, Drawable drawable, GC gc,
+ XArc *arcArr, int nArcs);
+EXTERN void Blt_EmulateXDrawLine(Display *display, Drawable drawable, GC gc,
+ int x1, int y1, int x2, int y2);
+EXTERN void Blt_EmulateXDrawLines(Display *display, Drawable drawable, GC gc,
+ XPoint *pointArr, int nPoints, int mode);
+EXTERN void Blt_EmulateXDrawPoints(Display *display, Drawable drawable, GC gc,
+ XPoint *pointArr, int nPoints, int mode);
+EXTERN void Blt_EmulateXDrawRectangle(Display *display, Drawable drawable,
+ GC gc, int x, int y, unsigned int width, unsigned int height);
+EXTERN void Blt_EmulateXDrawRectangles(Display *display, Drawable drawable,
+ GC gc, XRectangle *rectArr, int nRects);
+EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable,
+ GC gc, XSegment *segArr, int nSegments);
+EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable,
+ GC gc, XSegment *segArr, int nSegments);
+EXTERN void Blt_EmulateXDrawString(Display *display, Drawable drawable, GC gc,
+ int x, int y, _Xconst char *string, int length);
+EXTERN void Blt_EmulateXFillArcs(Display *display, Drawable drawable, GC gc,
+ XArc *arcArr, int nArcs);
+EXTERN void Blt_EmulateXFillPolygon(Display *display, Drawable drawable,
+ GC gc, XPoint *points, int nPoints, int shape, int mode);
+EXTERN void Blt_EmulateXFillRectangle(Display *display, Drawable drawable,
+ GC gc, int x, int y, unsigned int width, unsigned int height);
+EXTERN void Blt_EmulateXFillRectangles(Display *display, Drawable drawable,
+ GC gc, XRectangle *rectArr, int nRects);
+EXTERN void Blt_EmulateXFree(void *ptr);
+EXTERN Status Blt_EmulateXGetWindowAttributes(Display *display, Window window,
+ XWindowAttributes * attrsPtr);
+EXTERN void Blt_EmulateXLowerWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXMapWindow(Display *display, Window window);
+EXTERN long Blt_EmulateXMaxRequestSize(Display *display);
+EXTERN void Blt_EmulateXRaiseWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXReparentWindow(Display *display, Window window,
+ Window parent, int x, int y);
+EXTERN void Blt_EmulateXSetDashes(Display *display, GC gc, int dashOffset,
+ _Xconst char *dashList, int n);
+EXTERN void Blt_EmulateXUnmapWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXWarpPointer(Display *display, Window srcWindow,
+ Window destWindow, int srcX, int srcY, unsigned int srcWidth,
+ unsigned int srcHeight, int destX, int destY);
+
+EXTERN void Blt_DrawPoint2DLine(Display *display, Drawable drawable, GC gc,
+ Point2D *screenPts, int nScreenPts);
+
+extern unsigned char *Blt_GetBitmapData _ANSI_ARGS_((Display *display,
+ Pixmap bitmap, int width, int height, int *pitchPtr));
+
+extern HFONT Blt_CreateRotatedFont _ANSI_ARGS_((Tk_Window tkwin,
+ unsigned long font, double theta));
+
+extern HPALETTE Blt_GetSystemPalette _ANSI_ARGS_((void));
+
+extern HPEN Blt_GCToPen _ANSI_ARGS_((HDC dc, GC gc));
+
+#endif /* WIN32 */
+
+#endif /*_BLT_INT_H*/
diff --git a/blt/src/bltInterp.h b/blt/src/bltInterp.h
new file mode 100644
index 00000000000..e1f0d2441b3
--- /dev/null
+++ b/blt/src/bltInterp.h
@@ -0,0 +1,376 @@
+/*
+ * bltInterp.h --
+ *
+ * Excerpts from tclInt.h. Used to examine interpreter internals.
+ * Needed by the former (now obsoleted) TclParse* functions.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 1993-1997 Lucent Technologies.
+ * Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ *
+ */
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to command parsing. These are used in
+ * tclParse.c and its clients.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The following data structure is used by various parsing procedures
+ * to hold information about where to store the results of parsing
+ * (e.g. the substituted contents of a quoted argument, or the result
+ * of a nested command). At any given time, the space available
+ * for output is fixed, but a procedure may be called to expand the
+ * space available if the current space runs out.
+ */
+typedef struct ParseValueStruct ParseValue;
+
+struct ParseValueStruct {
+ char *buffer; /* Address of first character in
+ * output buffer. */
+ char *next; /* Place to store next character in
+ * output buffer. */
+ char *end; /* Address of the last usable character
+ * in the buffer. */
+ void (*expandProc) _ANSI_ARGS_((ParseValue *pvPtr, int needed));
+ /* Procedure to call when space runs out;
+ * it will make more space. */
+ ClientData clientData; /* Arbitrary information for use of
+ * expandProc. */
+};
+
+
+/*
+ * The definitions for the LiteralTable and LiteralEntry structures. Each
+ * interpreter contains a LiteralTable. It is used to reduce the storage
+ * needed for all the Tcl objects that hold the literals of scripts compiled
+ * by the interpreter. A literal's object is shared by all the ByteCodes
+ * that refer to the literal. Each distinct literal has one LiteralEntry
+ * entry in the LiteralTable. A literal table is a specialized hash table
+ * that is indexed by the literal's string representation, which may contain
+ * null characters.
+ *
+ * Note that we reduce the space needed for literals by sharing literal
+ * objects both within a ByteCode (each ByteCode contains a local
+ * LiteralTable) and across all an interpreter's ByteCodes (with the
+ * interpreter's global LiteralTable).
+ */
+
+typedef struct LiteralEntryStruct LiteralEntry;
+
+struct LiteralEntryStruct {
+ LiteralEntry *nextPtr; /* Points to next entry in this
+ * hash bucket or NULL if end of
+ * chain. */
+ Tcl_Obj *objPtr; /* Points to Tcl object that
+ * holds the literal's bytes and
+ * length. */
+ int refCount; /* If in an interpreter's global
+ * literal table, the number of
+ * ByteCode structures that share
+ * the literal object; the literal
+ * entry can be freed when refCount
+ * drops to 0. If in a local literal
+ * table, -1. */
+};
+
+typedef struct {
+ LiteralEntry **buckets; /* Pointer to bucket array. Each
+ * element points to first entry in
+ * bucket's hash chain, or NULL. */
+ LiteralEntry *staticBuckets[TCL_SMALL_HASH_TABLE];
+ /* Bucket array used for small
+ * tables to avoid mallocs and
+ * frees. */
+ int numBuckets; /* Total number of buckets allocated
+ * at **buckets. */
+ int numEntries; /* Total number of entries present
+ * in table. */
+ int rebuildSize; /* Enlarge table when numEntries
+ * gets to be this large. */
+ int mask; /* Mask value used in hashing
+ * function. */
+} LiteralTable;
+
+/*
+ * The following structure defines for each Tcl interpreter various
+ * statistics-related information about the bytecode compiler and
+ * interpreter's operation in that interpreter.
+ */
+
+#ifdef TCL_COMPILE_STATS
+typedef struct {
+ long numExecutions; /* Number of ByteCodes executed. */
+ long numCompilations; /* Number of ByteCodes created. */
+ long numByteCodesFreed; /* Number of ByteCodes destroyed. */
+ long instructionCount[256]; /* Number of times each instruction was
+ * executed. */
+
+ double totalSrcBytes; /* Total source bytes ever compiled. */
+ double totalByteCodeBytes; /* Total bytes for all ByteCodes. */
+ double currentSrcBytes; /* Src bytes for all current ByteCodes. */
+ double currentByteCodeBytes;/* Code bytes in all current ByteCodes. */
+
+ long srcCount[32]; /* Source size distribution: # of srcs of
+ * size [2**(n-1)..2**n), n in [0..32). */
+ long byteCodeCount[32]; /* ByteCode size distribution. */
+ long lifetimeCount[32]; /* ByteCode lifetime distribution (ms). */
+
+ double currentInstBytes; /* Instruction bytes-current ByteCodes. */
+ double currentLitBytes; /* Current literal bytes. */
+ double currentExceptBytes; /* Current exception table bytes. */
+ double currentAuxBytes; /* Current auxiliary information bytes. */
+ double currentCmdMapBytes; /* Current src<->code map bytes. */
+
+ long numLiteralsCreated; /* Total literal objects ever compiled. */
+ double totalLitStringBytes; /* Total string bytes in all literals. */
+ double currentLitStringBytes; /* String bytes in current literals. */
+ long literalCount[32]; /* Distribution of literal string sizes. */
+} ByteCodeStats;
+
+#endif /* TCL_COMPILE_STATS */
+
+
+/*
+ *----------------------------------------------------------------
+ * Data structures and procedures related to TclHandles, which
+ * are a very lightweight method of preserving enough information
+ * to determine if an arbitrary malloc'd block has been deleted.
+ *----------------------------------------------------------------
+ */
+
+typedef VOID **TclHandle;
+
+
+/*
+ * The following fills in dummy types for structure refered to
+ * internally by the Tcl interpreter. Since we don't need the actual
+ * size of the structures (they are only pointer references), we'll
+ * simply provide empty opaque types.
+ *
+ */
+typedef struct CallFrameStruct CallFrame;
+typedef struct NamespaceStruct Namespace;
+typedef struct ActiveVarTraceStruct ActiveVarTrace;
+typedef struct ProcStruct Proc;
+typedef struct TraceStruct Trace;
+
+typedef struct TclRegexpStruct TclRegexp;
+typedef struct ExecEnvStruct ExecEnv;
+
+
+/*
+ *----------------------------------------------------------------
+ * This structure defines an interpreter, which is a collection of
+ * commands plus other state information related to interpreting
+ * commands, such as variable storage. Primary responsibility for
+ * this data structure is in tclBasic.c, but almost every Tcl
+ * source file uses something in here.
+ *----------------------------------------------------------------
+ */
+
+typedef struct {
+
+ /*
+ * Note: the first three fields must match exactly the fields in
+ * a Tcl_Interp struct (see tcl.h). If you change one, be sure to
+ * change the other.
+ *
+ * The interpreter's result is held in both the string and the
+ * objResultPtr fields. These fields hold, respectively, the result's
+ * string or object value. The interpreter's result is always in the
+ * result field if that is non-empty, otherwise it is in objResultPtr.
+ * The two fields are kept consistent unless some C code sets
+ * interp->result directly. Programs should not access result and
+ * objResultPtr directly; instead, they should always get and set the
+ * result using procedures such as Tcl_SetObjResult, Tcl_GetObjResult,
+ * and Tcl_GetStringResult. See the SetResult man page for details.
+ */
+
+ char *result; /* If the last command returned a string
+ * result, this points to it. Should not be
+ * accessed directly; see comment above. */
+ Tcl_FreeProc *freeProc; /* Zero means a string result is statically
+ * allocated. TCL_DYNAMIC means string
+ * result was allocated with ckalloc and
+ * should be freed with ckfree. Other values
+ * give address of procedure to invoke to
+ * free the string result. Tcl_Eval must
+ * free it before executing next command. */
+ int errorLine; /* When TCL_ERROR is returned, this gives
+ * the line number in the command where the
+ * error occurred (1 means first line). */
+ Tcl_Obj *objResultPtr; /* If the last command returned an object
+ * result, this points to it. Should not be
+ * accessed directly; see comment above. */
+
+ TclHandle handle; /* Handle used to keep track of when this
+ * interp is deleted. */
+
+ Namespace *globalNsPtr; /* The interpreter's global namespace. */
+ Tcl_HashTable *hiddenCmdTablePtr;
+ /* Hash table used by tclBasic.c to keep
+ * track of hidden commands on a per-interp
+ * basis. */
+ ClientData interpInfo; /* Information used by tclInterp.c to keep
+ * track of master/slave interps on
+ * a per-interp basis. */
+ Tcl_HashTable mathFuncTable;/* Contains all the math functions currently
+ * defined for the interpreter. Indexed by
+ * strings (function names); values have
+ * type (MathFunc *). */
+
+
+
+ /*
+ * Information related to procedures and variables. See tclProc.c
+ * and tclvar.c for usage.
+ */
+
+ int numLevels; /* Keeps track of how many nested calls to
+ * Tcl_Eval are in progress for this
+ * interpreter. It's used to delay deletion
+ * of the table until all Tcl_Eval
+ * invocations are completed. */
+ int maxNestingDepth; /* If numLevels exceeds this value then Tcl
+ * assumes that infinite recursion has
+ * occurred and it generates an error. */
+ CallFrame *framePtr; /* Points to top-most in stack of all nested
+ * procedure invocations. NULL means there
+ * are no active procedures. */
+ CallFrame *varFramePtr; /* Points to the call frame whose variables
+ * are currently in use (same as framePtr
+ * unless an "uplevel" command is
+ * executing). NULL means no procedure is
+ * active or "uplevel 0" is executing. */
+ ActiveVarTrace *activeTracePtr;
+ /* First in list of active traces for
+ * interp, or NULL if no active traces. */
+ int returnCode; /* Completion code to return if current
+ * procedure exits with TCL_RETURN code. */
+ char *errorInfo; /* Value to store in errorInfo if returnCode
+ * is TCL_ERROR. Malloc'ed, may be NULL */
+ char *errorCode; /* Value to store in errorCode if returnCode
+ * is TCL_ERROR. Malloc'ed, may be NULL */
+
+ /*
+ * Information used by Tcl_AppendResult to keep track of partial
+ * results. See Tcl_AppendResult code for details.
+ */
+
+ char *appendResult; /* Storage space for results generated
+ * by Tcl_AppendResult. Malloc-ed. NULL
+ * means not yet allocated. */
+ int appendAvl; /* Total amount of space available at
+ * partialResult. */
+ int appendUsed; /* Number of non-null bytes currently
+ * stored at partialResult. */
+
+ /*
+ * A cache of compiled regular expressions. See Tcl_RegExpCompile
+ * in tclUtil.c for details. THIS CACHE IS OBSOLETE and is only
+ * retained for backward compatibility with Tcl_RegExpCompile.
+ * New code should use the object interface so the Tcl_Obj caches
+ * the compiled expression.
+ */
+
+#define NUM_REGEXPS 5
+ char *patterns[NUM_REGEXPS];/* Strings corresponding to compiled
+ * regular expression patterns. NULL
+ * means that this slot isn't used.
+ * Malloc-ed. */
+ int patLengths[NUM_REGEXPS];/* Number of non-null characters in
+ * corresponding entry in patterns.
+ * -1 means entry isn't used. */
+ TclRegexp *regexps[NUM_REGEXPS];
+ /* Compiled forms of above strings. Also
+ * malloc-ed, or NULL if not in use yet. */
+
+ /*
+ * Information about packages. Used only in tclPkg.c.
+ */
+
+ Tcl_HashTable packageTable; /* Describes all of the packages loaded
+ * in or available to this interpreter.
+ * Keys are package names, values are
+ * (Package *) pointers. */
+ char *packageUnknown; /* Command to invoke during "package
+ * require" commands for packages that
+ * aren't described in packageTable.
+ * Malloc'ed, may be NULL. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ int cmdCount; /* Total number of times a command procedure
+ * has been called for this interpreter. */
+ int evalFlags; /* Flags to control next call to Tcl_Eval.
+ * Normally zero, but may be set before
+ * calling Tcl_Eval. See below for valid
+ * values. */
+ int termOffset; /* Offset of character just after last one
+ * compiled or executed by Tcl_EvalObj. */
+ LiteralTable literalTable; /* Contains LiteralEntry's describing all
+ * Tcl objects holding literals of scripts
+ * compiled by the interpreter. Indexed by
+ * the string representations of literals.
+ * Used to avoid creating duplicate
+ * objects. */
+ int compileEpoch; /* Holds the current "compilation epoch"
+ * for this interpreter. This is
+ * incremented to invalidate existing
+ * ByteCodes when, e.g., a command with a
+ * compile procedure is redefined. */
+ Proc *compiledProcPtr; /* If a procedure is being compiled, a
+ * pointer to its Proc structure; otherwise,
+ * this is NULL. Set by ObjInterpProc in
+ * tclProc.c and used by tclCompile.c to
+ * process local variables appropriately. */
+ char *scriptFile; /* NULL means there is no nested source
+ * command active; otherwise this points to
+ * the name of the file being sourced (it's
+ * not malloc-ed: it points to an argument
+ * to Tcl_EvalFile. */
+ int flags; /* Various flag bits. See below. */
+ long randSeed; /* Seed used for rand() function. */
+ Trace *tracePtr; /* List of traces for this interpreter. */
+ Tcl_HashTable *assocData; /* Hash table for associating data with
+ * this interpreter. Cleaned up when
+ * this interpreter is deleted. */
+ ExecEnv *execEnvPtr; /* Execution environment for Tcl bytecode
+ * execution. Contains a pointer to the
+ * Tcl evaluation stack. */
+ Tcl_Obj *emptyObjPtr; /* Points to an object holding an empty
+ * string. Returned by Tcl_ObjSetVar2 when
+ * variable traces change a variable in a
+ * gross way. */
+ char resultSpace[TCL_RESULT_SIZE + 1];
+ /* Static space holding small results. */
+ Tcl_ThreadId threadId; /* ID of thread that owns the interpreter */
+
+ /*
+ * Statistical information about the bytecode compiler and interpreter's
+ * operation.
+ */
+
+#ifdef TCL_COMPILE_STATS
+ ByteCodeStats stats; /* Holds compilation and execution
+ * statistics for this interpreter. */
+#endif /* TCL_COMPILE_STATS */
+} Interp;
+
+/*
+ * EvalFlag bits for Interp structures:
+ *
+ * TCL_BRACKET_TERM 1 means that the current script is terminated by
+ * a close bracket rather than the end of the string.
+ * TCL_ALLOW_EXCEPTIONS 1 means it's OK for the script to terminate with
+ * a code other than TCL_OK or TCL_ERROR; 0 means
+ * codes other than these should be turned into errors.
+ */
+
+#define TCL_BRACKET_TERM 1
+#define TCL_ALLOW_EXCEPTIONS 4
diff --git a/blt/src/bltList.c b/blt/src/bltList.c
new file mode 100644
index 00000000000..c92c4cfac13
--- /dev/null
+++ b/blt/src/bltList.c
@@ -0,0 +1,588 @@
+/*
+ * bltList.c --
+ *
+ * The module implements generic linked lists.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltList.h"
+
+static struct Blt_ListNodeStruct *
+FindString(listPtr, key)
+ struct Blt_ListStruct *listPtr; /* List to search */
+ char *key; /* Key to match */
+{
+ register struct Blt_ListNodeStruct *nodePtr;
+ char c;
+
+ c = key[0];
+ for (nodePtr = listPtr->headPtr; nodePtr != NULL;
+ nodePtr = nodePtr->nextPtr) {
+ if ((c == nodePtr->key.string[0]) &&
+ (strcmp(key, nodePtr->key.string) == 0)) {
+ return nodePtr;
+ }
+ }
+ return NULL;
+}
+
+static Blt_ListNode
+FindOneWord(listPtr, key)
+ struct Blt_ListStruct *listPtr; /* List to search */
+ char *key; /* Key to match */
+{
+ register struct Blt_ListNodeStruct *nodePtr;
+
+ for (nodePtr = listPtr->headPtr; nodePtr != NULL;
+ nodePtr = nodePtr->nextPtr) {
+ if (key == nodePtr->key.oneWordValue) {
+ return nodePtr;
+ }
+ }
+ return NULL;
+}
+
+static Blt_ListNode
+FindArray(listPtr, key)
+ struct Blt_ListStruct *listPtr; /* List to search */
+ char *key; /* Key to match */
+{
+ register struct Blt_ListNodeStruct *nodePtr;
+ int nBytes;
+
+ nBytes = sizeof(int) * listPtr->type;
+ for (nodePtr = listPtr->headPtr; nodePtr != NULL;
+ nodePtr = nodePtr->nextPtr) {
+ if (memcmp(key, nodePtr->key.words, nBytes) == 0) {
+ return nodePtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeNode --
+ *
+ * Free the memory allocated for the node.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeNode(nodePtr)
+ struct Blt_ListNodeStruct *nodePtr;
+{
+ Blt_Free(nodePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListCreate --
+ *
+ * Creates a new linked list structure and initializes its pointers
+ *
+ * Results:
+ * Returns a pointer to the newly created list structure.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_List
+Blt_ListCreate(type)
+ int type;
+{
+ struct Blt_ListStruct *listPtr;
+
+ listPtr = Blt_Malloc(sizeof(struct Blt_ListStruct));
+ if (listPtr != NULL) {
+ Blt_ListInit(listPtr, type);
+ }
+ return listPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListCreateNode --
+ *
+ * Creates a list node holder. This routine does not insert
+ * the node into the list, nor does it no attempt to maintain
+ * consistency of the keys. For example, more than one node
+ * may use the same key.
+ *
+ * Results:
+ * The return value is the pointer to the newly created node.
+ *
+ * Side Effects:
+ * The key is not copied, only the Uid is kept. It is assumed
+ * this key will not change in the life of the node.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListCreateNode(listPtr, key)
+ struct Blt_ListStruct *listPtr;
+ char *key; /* Unique key to reference object */
+{
+ register struct Blt_ListNodeStruct *nodePtr;
+ int keySize;
+
+ if (listPtr->type == TCL_STRING_KEYS) {
+ keySize = strlen(key) + 1;
+ } else {
+ keySize = sizeof(int) * listPtr->type;
+ }
+ nodePtr = Blt_Calloc(1, sizeof(struct Blt_ListNodeStruct) + keySize - 4);
+ assert(nodePtr);
+ nodePtr->clientData = NULL;
+ nodePtr->nextPtr = nodePtr->prevPtr = NULL;
+ nodePtr->listPtr = listPtr;
+ switch (listPtr->type) {
+ case TCL_STRING_KEYS:
+ strcpy(nodePtr->key.string, key);
+ break;
+ case TCL_ONE_WORD_KEYS:
+ nodePtr->key.oneWordValue = key;
+ break;
+ default:
+ memcpy(nodePtr->key.words, key, keySize);
+ break;
+ }
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListReset --
+ *
+ * Removes all the entries from a list, removing pointers to the
+ * objects and keys (not the objects or keys themselves). The
+ * node counter is reset to zero.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListReset(listPtr)
+ struct Blt_ListStruct *listPtr; /* List to clear */
+{
+ if (listPtr != NULL) {
+ register struct Blt_ListNodeStruct *oldPtr;
+ register struct Blt_ListNodeStruct *nodePtr = listPtr->headPtr;
+
+ while (nodePtr != NULL) {
+ oldPtr = nodePtr;
+ nodePtr = nodePtr->nextPtr;
+ FreeNode(oldPtr);
+ }
+ Blt_ListInit(listPtr, listPtr->type);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDestroy
+ *
+ * Frees all list structures
+ *
+ * Results:
+ * Returns a pointer to the newly created list structure.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDestroy(listPtr)
+ struct Blt_ListStruct *listPtr;
+{
+ if (listPtr != NULL) {
+ Blt_ListReset(listPtr);
+ Blt_Free(listPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListInit --
+ *
+ * Initializes a linked list.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListInit(listPtr, type)
+ struct Blt_ListStruct *listPtr;
+ int type;
+{
+ listPtr->nNodes = 0;
+ listPtr->headPtr = listPtr->tailPtr = NULL;
+ listPtr->type = type;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListLinkAfter --
+ *
+ * Inserts an node following a given node.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListLinkAfter(listPtr, nodePtr, afterPtr)
+ struct Blt_ListStruct *listPtr;
+ struct Blt_ListNodeStruct *nodePtr;
+ struct Blt_ListNodeStruct *afterPtr;
+{
+ if (listPtr->headPtr == NULL) {
+ listPtr->tailPtr = listPtr->headPtr = nodePtr;
+ } else {
+ if (afterPtr == NULL) {
+ /* Prepend to the front of the list */
+ nodePtr->nextPtr = listPtr->headPtr;
+ nodePtr->prevPtr = NULL;
+ listPtr->headPtr->prevPtr = nodePtr;
+ listPtr->headPtr = nodePtr;
+ } else {
+ nodePtr->nextPtr = afterPtr->nextPtr;
+ nodePtr->prevPtr = afterPtr;
+ if (afterPtr == listPtr->tailPtr) {
+ listPtr->tailPtr = nodePtr;
+ } else {
+ afterPtr->nextPtr->prevPtr = nodePtr;
+ }
+ afterPtr->nextPtr = nodePtr;
+ }
+ }
+ nodePtr->listPtr = listPtr;
+ listPtr->nNodes++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListLinkBefore --
+ *
+ * Inserts an node preceding a given node.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListLinkBefore(listPtr, nodePtr, beforePtr)
+ struct Blt_ListStruct *listPtr; /* List to contain new node */
+ struct Blt_ListNodeStruct *nodePtr; /* New node to be inserted */
+ struct Blt_ListNodeStruct *beforePtr; /* Node to link before */
+{
+ if (listPtr->headPtr == NULL) {
+ listPtr->tailPtr = listPtr->headPtr = nodePtr;
+ } else {
+ if (beforePtr == NULL) {
+ /* Append onto the end of the list */
+ nodePtr->nextPtr = NULL;
+ nodePtr->prevPtr = listPtr->tailPtr;
+ listPtr->tailPtr->nextPtr = nodePtr;
+ listPtr->tailPtr = nodePtr;
+ } else {
+ nodePtr->prevPtr = beforePtr->prevPtr;
+ nodePtr->nextPtr = beforePtr;
+ if (beforePtr == listPtr->headPtr) {
+ listPtr->headPtr = nodePtr;
+ } else {
+ beforePtr->prevPtr->nextPtr = nodePtr;
+ }
+ beforePtr->prevPtr = nodePtr;
+ }
+ }
+ nodePtr->listPtr = listPtr;
+ listPtr->nNodes++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListUnlinkNode --
+ *
+ * Unlinks an node from the given list. The node itself is
+ * not deallocated, but only removed from the list.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListUnlinkNode(nodePtr)
+ struct Blt_ListNodeStruct *nodePtr;
+{
+ struct Blt_ListStruct *listPtr;
+
+ listPtr = nodePtr->listPtr;
+ if (listPtr != NULL) {
+ if (listPtr->headPtr == nodePtr) {
+ listPtr->headPtr = nodePtr->nextPtr;
+ }
+ if (listPtr->tailPtr == nodePtr) {
+ listPtr->tailPtr = nodePtr->prevPtr;
+ }
+ if (nodePtr->nextPtr != NULL) {
+ nodePtr->nextPtr->prevPtr = nodePtr->prevPtr;
+ }
+ if (nodePtr->prevPtr != NULL) {
+ nodePtr->prevPtr->nextPtr = nodePtr->nextPtr;
+ }
+ nodePtr->listPtr = NULL;
+ listPtr->nNodes--;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListGetNode --
+ *
+ * Find the first node matching the key given.
+ *
+ * Results:
+ * Returns the pointer to the node. If no node matching
+ * the key given is found, then NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListGetNode(listPtr, key)
+ struct Blt_ListStruct *listPtr; /* List to search */
+ char *key; /* Key to match */
+{
+ if (listPtr != NULL) {
+ switch (listPtr->type) {
+ case TCL_STRING_KEYS:
+ return FindString(listPtr, key);
+ case TCL_ONE_WORD_KEYS:
+ return FindOneWord(listPtr, key);
+ default:
+ return FindArray(listPtr, key);
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDeleteNode --
+ *
+ * Unlinks and deletes the given node.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDeleteNode(nodePtr)
+ struct Blt_ListNodeStruct *nodePtr;
+{
+ Blt_ListUnlinkNode(nodePtr);
+ FreeNode(nodePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDeleteNodeByKey --
+ *
+ * Find the node and free the memory allocated for the node.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDeleteNodeByKey(listPtr, key)
+ struct Blt_ListStruct *listPtr;
+ char *key;
+{
+ struct Blt_ListNodeStruct *nodePtr;
+
+ nodePtr = Blt_ListGetNode(listPtr, key);
+ if (nodePtr != NULL) {
+ Blt_ListDeleteNode(nodePtr);
+ }
+}
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListAppend(listPtr, key, clientData)
+ struct Blt_ListStruct *listPtr;
+ char *key;
+ ClientData clientData;
+{
+ struct Blt_ListNodeStruct *nodePtr;
+
+ nodePtr = Blt_ListCreateNode(listPtr, key);
+ Blt_ListSetValue(nodePtr, clientData);
+ Blt_ListAppendNode(listPtr, nodePtr);
+ return nodePtr;
+}
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListPrepend(listPtr, key, clientData)
+ struct Blt_ListStruct *listPtr;
+ char *key;
+ ClientData clientData;
+{
+ struct Blt_ListNodeStruct *nodePtr;
+
+ nodePtr = Blt_ListCreateNode(listPtr, key);
+ Blt_ListSetValue(nodePtr, clientData);
+ Blt_ListPrependNode(listPtr, nodePtr);
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListGetNthNode --
+ *
+ * Find the node based upon a given position in list.
+ *
+ * Results:
+ * Returns the pointer to the node, if that numbered element
+ * exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListGetNthNode(listPtr, position, direction)
+ struct Blt_ListStruct *listPtr; /* List to traverse */
+ int position; /* Index of node to select from front
+ * or back of the list. */
+ int direction;
+{
+ register struct Blt_ListNodeStruct *nodePtr;
+
+ if (listPtr != NULL) {
+ if (direction > 0) {
+ for (nodePtr = listPtr->headPtr; nodePtr != NULL;
+ nodePtr = nodePtr->nextPtr) {
+ if (position == 0) {
+ return nodePtr;
+ }
+ position--;
+ }
+ } else {
+ for (nodePtr = listPtr->tailPtr; nodePtr != NULL;
+ nodePtr = nodePtr->prevPtr) {
+ if (position == 0) {
+ return nodePtr;
+ }
+ position--;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListSort --
+ *
+ * Find the node based upon a given position in list.
+ *
+ * Results:
+ * Returns the pointer to the node, if that numbered element
+ * exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListSort(listPtr, proc)
+ struct Blt_ListStruct *listPtr; /* List to traverse */
+ Blt_ListCompareProc *proc;
+{
+ struct Blt_ListNodeStruct **nodeArr;
+ register struct Blt_ListNodeStruct *nodePtr;
+ register int i;
+
+ if (listPtr->nNodes < 2) {
+ return;
+ }
+ nodeArr = Blt_Malloc(sizeof(Blt_List) * (listPtr->nNodes + 1));
+ if (nodeArr == NULL) {
+ return; /* Out of memory. */
+ }
+ i = 0;
+ for (nodePtr = listPtr->headPtr; nodePtr != NULL;
+ nodePtr = nodePtr->nextPtr) {
+ nodeArr[i++] = nodePtr;
+ }
+ qsort((char *)nodeArr, listPtr->nNodes,
+ sizeof(struct Blt_ListNodeStruct *), (QSortCompareProc *)proc);
+
+ /* Rethread the list. */
+ nodePtr = nodeArr[0];
+ listPtr->headPtr = nodePtr;
+ nodePtr->prevPtr = NULL;
+ for (i = 1; i < listPtr->nNodes; i++) {
+ nodePtr->nextPtr = nodeArr[i];
+ nodePtr->nextPtr->prevPtr = nodePtr;
+ nodePtr = nodePtr->nextPtr;
+ }
+ listPtr->tailPtr = nodePtr;
+ nodePtr->nextPtr = NULL;
+ Blt_Free(nodeArr);
+}
diff --git a/blt/src/bltList.h b/blt/src/bltList.h
new file mode 100644
index 00000000000..6483151f8d7
--- /dev/null
+++ b/blt/src/bltList.h
@@ -0,0 +1,105 @@
+/*
+ * bltList.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#ifndef _BLT_LIST_H
+#define _BLT_LIST_H
+
+typedef struct Blt_ListStruct *Blt_List;
+typedef struct Blt_ListNodeStruct *Blt_ListNode;
+
+/*
+ * A Blt_ListNode is the container structure for the Blt_List.
+ */
+struct Blt_ListNodeStruct {
+ struct Blt_ListNodeStruct *prevPtr; /* Link to the previous node */
+ struct Blt_ListNodeStruct *nextPtr; /* Link to the next node */
+ ClientData clientData; /* Pointer to the data object */
+ struct Blt_ListStruct *listPtr; /* List to eventually insert node */
+ union { /* Key has one of these forms: */
+ char *oneWordValue; /* One-word value for key. */
+ int *words[1]; /* Multiple integer words for key.
+ * The actual size will be as large
+ * as necessary for this table's
+ * keys. */
+ char string[4]; /* String for key. The actual size
+ * will be as large as needed to hold
+ * the key. */
+ } key; /* MUST BE LAST FIELD IN RECORD!! */
+};
+
+typedef int (Blt_ListCompareProc) _ANSI_ARGS_((Blt_ListNode *node1Ptr,
+ Blt_ListNode *node2Ptr));
+
+/*
+ * A Blt_List is a doubly chained list structure.
+ */
+struct Blt_ListStruct {
+ struct Blt_ListNodeStruct *headPtr; /* Pointer to first element in list */
+ struct Blt_ListNodeStruct *tailPtr; /* Pointer to last element in list */
+ int nNodes; /* Number of node currently in the list. */
+ int type; /* Type of keys in list. */
+};
+
+EXTERN void Blt_ListInit _ANSI_ARGS_((Blt_List list, int type));
+EXTERN void Blt_ListReset _ANSI_ARGS_((Blt_List list));
+EXTERN Blt_List Blt_ListCreate _ANSI_ARGS_((int type));
+EXTERN void Blt_ListDestroy _ANSI_ARGS_((Blt_List list));
+EXTERN Blt_ListNode Blt_ListCreateNode _ANSI_ARGS_((Blt_List list, char *key));
+EXTERN void Blt_ListDeleteNode _ANSI_ARGS_((Blt_ListNode node));
+
+EXTERN Blt_ListNode Blt_ListAppend _ANSI_ARGS_((Blt_List list, char *key,
+ ClientData clientData));
+EXTERN Blt_ListNode Blt_ListPrepend _ANSI_ARGS_((Blt_List list, char *key,
+ ClientData clientData));
+EXTERN void Blt_ListLinkAfter _ANSI_ARGS_((Blt_List list, Blt_ListNode node,
+ Blt_ListNode afterNode));
+EXTERN void Blt_ListLinkBefore _ANSI_ARGS_((Blt_List list, Blt_ListNode node,
+ Blt_ListNode beforeNode));
+EXTERN void Blt_ListUnlinkNode _ANSI_ARGS_((Blt_ListNode node));
+EXTERN Blt_ListNode Blt_ListGetNode _ANSI_ARGS_((Blt_List list, char *key));
+EXTERN void Blt_ListDeleteNodeByKey _ANSI_ARGS_((Blt_List list, char *key));
+EXTERN Blt_ListNode Blt_ListGetNthNode _ANSI_ARGS_((Blt_List list,
+ int position, int direction));
+EXTERN void Blt_ListSort _ANSI_ARGS_((Blt_List list,
+ Blt_ListCompareProc * proc));
+
+#define Blt_ListGetLength(list) \
+ (((list) == NULL) ? 0 : ((struct Blt_ListStruct *)list)->nNodes)
+#define Blt_ListFirstNode(list) \
+ (((list) == NULL) ? NULL : ((struct Blt_ListStruct *)list)->headPtr)
+#define Blt_ListLastNode(list) \
+ (((list) == NULL) ? NULL : ((struct Blt_ListStruct *)list)->tailPtr)
+#define Blt_ListPrevNode(node) ((node)->prevPtr)
+#define Blt_ListNextNode(node) ((node)->nextPtr)
+#define Blt_ListGetKey(node) \
+ (((node)->listPtr->type == TCL_STRING_KEYS) \
+ ? (node)->key.string : (node)->key.oneWordValue)
+#define Blt_ListGetValue(node) ((node)->clientData)
+#define Blt_ListSetValue(node, value) \
+ ((node)->clientData = (ClientData)(value))
+#define Blt_ListAppendNode(list, node) \
+ (Blt_ListLinkBefore((list), (node), (Blt_ListNode)NULL))
+#define Blt_ListPrependNode(list, node) \
+ (Blt_ListLinkAfter((list), (node), (Blt_ListNode)NULL))
+
+#endif /* _BLT_LIST_H */
diff --git a/blt/src/bltNsUtil.c b/blt/src/bltNsUtil.c
new file mode 100644
index 00000000000..eab782c3d0f
--- /dev/null
+++ b/blt/src/bltNsUtil.c
@@ -0,0 +1,728 @@
+/*
+ * bltNsUtil.c --
+ *
+ * This module implements utility procedures for namespaces
+ * in the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltList.h"
+
+/* Namespace related routines */
+
+typedef struct {
+ char *result;
+ Tcl_FreeProc *freeProc;
+ int errorLine;
+ Tcl_HashTable commandTable;
+ Tcl_HashTable mathFuncTable;
+
+ Tcl_HashTable globalTable; /* This is the only field we care about */
+
+ int nLevels;
+ int maxNestingDepth;
+} TclInterp;
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_GetVariableNamespace --
+ *
+ * Returns the namespace context of the vector variable. If NULL,
+ * this indicates that the variable is local to the call frame.
+ *
+ * Note the ever-dangerous manner in which we get this information.
+ * All of these structures are "private". Now who's calling Tcl
+ * an "extension" language?
+ *
+ * Results:
+ * Returns the context of the namespace in an opaque type.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7)
+
+#ifdef ITCL_NAMESPACES
+
+struct VarTrace;
+struct ArraySearch;
+struct NamespCacheRef;
+
+typedef struct VarStruct Var;
+
+struct VarStruct {
+ int valueLength;
+ int valueSpace;
+ union {
+ char *string;
+ Tcl_HashTable *tablePtr;
+ Var *upvarPtr;
+ } value;
+ Tcl_HashEntry *hPtr;
+ int refCount;
+ struct VarTrace *tracePtr;
+ struct ArraySearch *searchPtr;
+ int flags;
+
+ /* >>>>>>>>>> stuff for [incr Tcl] namespaces <<<<<<<<<< */
+
+ char *name;
+ int protection;
+
+ Itcl_Namespace *namesp;
+ struct NamespCacheRef *cacheInfo;
+
+} Var;
+
+
+Tcl_Namespace *
+Tcl_FindNamespace(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ Itcl_Namespace nsToken;
+
+ if (Itcl_FindNamesp(interp, name, 0, &nsToken) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ return NULL;
+ }
+ return (Tcl_Namespace *) nsToken;
+}
+
+Tcl_Namespace *
+Tcl_GetGlobalNamespace(interp)
+ Tcl_Interp *interp;
+{
+ return (Tcl_Namespace *) Itcl_GetGlobalNamesp(interp);
+}
+
+Tcl_Namespace *
+Tcl_GetCurrentNamespace(interp)
+ Tcl_Interp *interp;
+{
+ return (Tcl_Namespace *) Itcl_GetActiveNamesp(interp);
+}
+
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+ Tcl_Interp *interp;
+ Tcl_Command cmdToken;
+{
+ return (Tcl_Namespace *)interp;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ Tcl_Var varToken;
+ Var *varPtr;
+
+ if (Itcl_FindVariable(interp, name, 0, &varToken) != TCL_OK) {
+ return NULL;
+ }
+ varPtr = (Var *) varToken;
+ if (varPtr == NULL) {
+ return NULL;
+ }
+ return (Tcl_Namespace *) varPtr->namesp;
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+ Tcl_Interp *interp;
+ Tcl_Namespace *nsPtr;
+{
+ Itcl_Namespace nsToken = (Itcl_Namespace) nsPtr;
+
+ return (Tcl_CallFrame *) Itcl_ActivateNamesp(interp, nsToken);
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+ Tcl_Interp *interp;
+ Tcl_CallFrame *framePtr;
+{
+ Itcl_DeactivateNamesp(interp, (Itcl_ActiveNamespace) framePtr);
+}
+
+#else
+
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+ Tcl_Interp *interp;
+ Tcl_Command cmdToken;
+{
+ return (Tcl_Namespace *)interp;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ TclInterp *iPtr = (TclInterp *) interp;
+
+ return (Tcl_Namespace *) Tcl_FindHashEntry(&(iPtr->globalTable), name);
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+ Tcl_Interp *interp;
+ Tcl_Namespace *nsPtr; /* Not used. */
+{
+ return NULL;
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+ Tcl_Interp *interp;
+ Tcl_CallFrame *framePtr;
+{
+ /* empty */
+}
+
+Tcl_Namespace *
+Tcl_GetGlobalNamespace(interp)
+ Tcl_Interp *interp;
+{
+ return (Tcl_Namespace *) interp;
+}
+
+Tcl_Namespace *
+Tcl_GetCurrentNamespace(interp)
+ Tcl_Interp *interp;
+{
+ return (Tcl_Namespace *) interp;
+}
+
+Tcl_Namespace *
+Tcl_FindNamespace(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ return (Tcl_Namespace *) interp;
+}
+
+#endif /* ITCL_NAMESPACES */
+
+#else
+
+/*
+ * A Command structure exists for each command in a namespace. The
+ * Tcl_Command opaque type actually refers to these structures.
+ */
+
+typedef struct CompileProcStruct CompileProc;
+typedef struct ImportRefStruct ImportRef;
+
+typedef struct {
+ Tcl_HashEntry *hPtr; /* Pointer to the hash table entry that
+ * refers to this command. The hash table is
+ * either a namespace's command table or an
+ * interpreter's hidden command table. This
+ * pointer is used to get a command's name
+ * from its Tcl_Command handle. NULL means
+ * that the hash table entry has been
+ * removed already (this can happen if
+ * deleteProc causes the command to be
+ * deleted or recreated). */
+ Tcl_Namespace *nsPtr; /* Points to the namespace containing this
+ * command. */
+ int refCount; /* 1 if in command hashtable plus 1 for each
+ * reference from a CmdName Tcl object
+ * representing a command's name in a
+ * ByteCode instruction sequence. This
+ * structure can be freed when refCount
+ * becomes zero. */
+ int cmdEpoch; /* Incremented to invalidate any references
+ * that point to this command when it is
+ * renamed, deleted, hidden, or exposed. */
+ CompileProc *compileProc; /* Procedure called to compile command. NULL
+ * if no compile proc exists for command. */
+ Tcl_ObjCmdProc *objProc; /* Object-based command procedure. */
+ ClientData objClientData; /* Arbitrary value passed to object proc. */
+ Tcl_CmdProc *proc; /* String-based command procedure. */
+ ClientData clientData; /* Arbitrary value passed to string proc. */
+ Tcl_CmdDeleteProc *deleteProc;
+ /* Procedure invoked when deleting command
+ * to, e.g., free all client data. */
+ ClientData deleteData; /* Arbitrary value passed to deleteProc. */
+ int deleted; /* Means that the command is in the process
+ * of being deleted (its deleteProc is
+ * currently executing). Other attempts to
+ * delete the command should be ignored. */
+ ImportRef *importRefPtr; /* List of each imported Command created in
+ * another namespace when this command is
+ * imported. These imported commands
+ * redirect invocations back to this
+ * command. The list is used to remove all
+ * those imported commands when deleting
+ * this "real" command. */
+} Command;
+
+
+struct VarTrace;
+struct ArraySearch;
+
+typedef struct VarStruct Var;
+
+struct VarStruct {
+ union {
+ Tcl_Obj *objPtr;
+ Tcl_HashTable *tablePtr;
+ Var *linkPtr;
+ } value;
+ char *name;
+ Tcl_Namespace *nsPtr;
+ Tcl_HashEntry *hPtr;
+ int refCount;
+ struct VarTrace *tracePtr;
+ struct ArraySearch *searchPtr;
+ int flags;
+};
+
+extern Var *TclLookupVar _ANSI_ARGS_((Tcl_Interp *interp, char *part1,
+ char *part2, int flags, char *mesg, int p1Flags, int p2Flags,
+ Var ** varPtrPtr));
+
+#define VAR_SCALAR 0x1
+#define VAR_ARRAY 0x2
+#define VAR_LINK 0x4
+#define VAR_UNDEFINED 0x8
+#define VAR_IN_HASHTABLE 0x10
+#define VAR_TRACE_ACTIVE 0x20
+#define VAR_ARRAY_ELEMENT 0x40
+#define VAR_NAMESPACE_VAR 0x80
+
+#define VAR_ARGUMENT 0x100
+#define VAR_TEMPORARY 0x200
+#define VAR_RESOLVED 0x400
+
+
+Tcl_HashTable *
+Blt_GetArrayVariableTable(interp, varName, flags)
+ Tcl_Interp *interp;
+ char *varName;
+ int flags;
+{
+ Var *varPtr, *arrayPtr;
+
+ varPtr = TclLookupVar(interp, varName, (char *)NULL, flags, "read",
+ FALSE, FALSE, &arrayPtr);
+ if ((varPtr == NULL) || ((varPtr->flags & VAR_ARRAY) == 0)) {
+ return NULL;
+ }
+ return varPtr->value.tablePtr;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ Var *varPtr;
+
+ varPtr = (Var *) Tcl_FindNamespaceVar(interp, name, (Tcl_Namespace *)
+ NULL, 0);
+ if (varPtr == NULL) {
+ return NULL;
+ }
+ return varPtr->nsPtr;
+}
+
+/*ARGSUSED*/
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+ Tcl_Interp *interp; /* Not used. */
+ Tcl_Command cmdToken;
+{
+ Command *cmdPtr = (Command *)cmdToken;
+
+ return (Tcl_Namespace *)cmdPtr->nsPtr;
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+ Tcl_Interp *interp;
+ Tcl_Namespace *nsPtr;
+{
+ Tcl_CallFrame *framePtr;
+
+ framePtr = Blt_Malloc(sizeof(Tcl_CallFrame));
+ assert(framePtr);
+ if (Tcl_PushCallFrame(interp, framePtr, (Tcl_Namespace *)nsPtr, 0)
+ != TCL_OK) {
+ Blt_Free(framePtr);
+ return NULL;
+ }
+ return framePtr;
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+ Tcl_Interp *interp;
+ Tcl_CallFrame *framePtr;
+{
+ Tcl_PopCallFrame(interp);
+ Blt_Free(framePtr);
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 */
+
+int
+Blt_ParseQualifiedName(interp, qualName, nsPtrPtr, namePtrPtr)
+ Tcl_Interp *interp;
+ CONST char *qualName;
+ Tcl_Namespace **nsPtrPtr;
+ char **namePtrPtr;
+{
+ register char *p, *colon;
+ Tcl_Namespace *nsPtr;
+
+ colon = NULL;
+ p = (char *)(qualName + strlen(qualName));
+ while (--p > qualName) {
+ if ((*p == ':') && (*(p - 1) == ':')) {
+ p++; /* just after the last "::" */
+ colon = p - 2;
+ break;
+ }
+ }
+ if (colon == NULL) {
+ *nsPtrPtr = NULL;
+ *namePtrPtr = (char *)qualName;
+ return TCL_OK;
+ }
+ *colon = '\0';
+ if (qualName[0] == '\0') {
+ nsPtr = Tcl_GetGlobalNamespace(interp);
+ } else {
+ nsPtr = Tcl_FindNamespace(interp, (char *)qualName,
+ (Tcl_Namespace *)NULL, 0);
+ }
+ *colon = ':';
+ if (nsPtr == NULL) {
+ return TCL_ERROR;
+ }
+ *nsPtrPtr = nsPtr;
+ *namePtrPtr = p;
+ return TCL_OK;
+}
+
+char *
+Blt_GetQualifiedName(nsPtr, name, resultPtr)
+ Tcl_Namespace *nsPtr;
+ CONST char *name;
+ Tcl_DString *resultPtr;
+{
+ Tcl_DStringInit(resultPtr);
+#if (TCL_MAJOR_VERSION > 7)
+ if ((nsPtr->fullName[0] != ':') || (nsPtr->fullName[1] != ':') ||
+ (nsPtr->fullName[2] != '\0')) {
+ Tcl_DStringAppend(resultPtr, nsPtr->fullName, -1);
+ }
+#endif
+ Tcl_DStringAppend(resultPtr, "::", -1);
+ Tcl_DStringAppend(resultPtr, (char *)name, -1);
+ return Tcl_DStringValue(resultPtr);
+}
+
+
+#if (TCL_MAJOR_VERSION > 7)
+
+typedef struct {
+ Tcl_HashTable clientTable;
+
+ /* Original clientdata and delete procedure. */
+ ClientData origClientData;
+ Tcl_NamespaceDeleteProc *origDeleteProc;
+
+} Callback;
+
+#ifdef __STDC__
+static Tcl_CmdProc NamespaceDeleteCmd;
+static Tcl_NamespaceDeleteProc NamespaceDeleteNotify;
+#endif
+
+#define NS_DELETE_CMD "#NamespaceDeleteNotifier"
+
+/*ARGSUSED*/
+static int
+NamespaceDeleteCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* */
+ int argc;
+ char **argv;
+{
+ Tcl_AppendResult(interp, "command \"", argv[0], "\" shouldn't be invoked",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+static void
+NamespaceDeleteNotify(clientData)
+ ClientData clientData;
+{
+ Blt_List list;
+ Blt_ListNode node;
+ Tcl_CmdDeleteProc *deleteProc;
+
+ list = (Blt_List)clientData;
+ for (node = Blt_ListFirstNode(list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ deleteProc = (Tcl_CmdDeleteProc *)Blt_ListGetValue(node);
+ clientData = Blt_ListGetKey(node);
+ (*deleteProc) (clientData);
+ }
+ Blt_ListDestroy(list);
+}
+
+void
+Blt_DestroyNsDeleteNotify(interp, nsPtr, clientData)
+ Tcl_Interp *interp;
+ Tcl_Namespace *nsPtr;
+ ClientData clientData;
+{
+ Blt_List list;
+ Blt_ListNode node;
+ char *string;
+ Tcl_CmdInfo cmdInfo;
+
+ string = Blt_Malloc(sizeof(nsPtr->fullName) + strlen(NS_DELETE_CMD) + 4);
+ strcpy(string, nsPtr->fullName);
+ strcat(string, "::");
+ strcat(string, NS_DELETE_CMD);
+ if (!Tcl_GetCommandInfo(interp, string, &cmdInfo)) {
+ goto done;
+ }
+ list = (Blt_List)cmdInfo.clientData;
+ node = Blt_ListGetNode(list, clientData);
+ if (node != NULL) {
+ Blt_ListDeleteNode(node);
+ }
+ done:
+ Blt_Free(string);
+}
+
+int
+Blt_CreateNsDeleteNotify(interp, nsPtr, clientData, deleteProc)
+ Tcl_Interp *interp;
+ Tcl_Namespace *nsPtr;
+ ClientData clientData;
+ Tcl_CmdDeleteProc *deleteProc;
+{
+ Blt_List list;
+ char *string;
+ Tcl_CmdInfo cmdInfo;
+
+ string = Blt_Malloc(sizeof(nsPtr->fullName) + strlen(NS_DELETE_CMD) + 4);
+ strcpy(string, nsPtr->fullName);
+ strcat(string, "::");
+ strcat(string, NS_DELETE_CMD);
+ if (!Tcl_GetCommandInfo(interp, string, &cmdInfo)) {
+ list = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ Blt_CreateCommand(interp, string, NamespaceDeleteCmd, list,
+ NamespaceDeleteNotify);
+ } else {
+ list = (Blt_List)cmdInfo.clientData;
+ }
+ Blt_Free(string);
+ Blt_ListAppend(list, clientData, (ClientData)deleteProc);
+ return TCL_OK;
+}
+
+#endif /* TCL_MAJOR_VERSION > 7 */
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,0,0))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommand --
+ *
+ * Like Tcl_CreateCommand, but creates command in current namespace
+ * instead of global, if one isn't defined. Not a problem with
+ * [incr Tcl] namespaces.
+ *
+ * Results:
+ * The return value is a token for the command, which can
+ * be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
+ Tcl_Interp *interp; /* Token for command interpreter returned by
+ * a previous call to Tcl_CreateInterp. */
+ char *cmdName; /* Name of command. If it contains namespace
+ * qualifiers, the new command is put in the
+ * specified namespace; otherwise it is put
+ * in the global namespace. */
+ Tcl_CmdProc *proc; /* Procedure to associate with cmdName. */
+ ClientData clientData; /* Arbitrary value passed to string proc. */
+ Tcl_CmdDeleteProc *deleteProc;
+ /* If not NULL, gives a procedure to call
+ * when this command is deleted. */
+{
+ return Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc);
+}
+
+/*ARGSUSED*/
+Tcl_Command
+Tcl_FindCommand(interp, cmdName, nsPtr, flags)
+ Tcl_Interp *interp;
+ char *cmdName;
+ Tcl_Namespace *nsPtr; /* Not used. */
+ int flags; /* Not used. */
+{
+ Tcl_HashEntry *hPtr;
+ TclInterp *iPtr = (TclInterp *) interp;
+
+ hPtr = Tcl_FindHashEntry(&iPtr->commandTable, cmdName);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return (Tcl_Command) Tcl_GetHashValue(hPtr);
+}
+
+#endif /* TCL_MAJOR_VERSION <= 7 */
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommand --
+ *
+ * Like Tcl_CreateCommand, but creates command in current namespace
+ * instead of global, if one isn't defined. Not a problem with
+ * [incr Tcl] namespaces.
+ *
+ * Results:
+ * The return value is a token for the command, which can
+ * be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
+ Tcl_Interp *interp; /* Token for command interpreter returned by
+ * a previous call to Tcl_CreateInterp. */
+ char *cmdName; /* Name of command. If it contains namespace
+ * qualifiers, the new command is put in the
+ * specified namespace; otherwise it is put
+ * in the global namespace. */
+ Tcl_CmdProc *proc; /* Procedure to associate with cmdName. */
+ ClientData clientData; /* Arbitrary value passed to string proc. */
+ Tcl_CmdDeleteProc *deleteProc;
+ /* If not NULL, gives a procedure to call
+
+ * when this command is deleted. */
+{
+ register char *p;
+
+ p = cmdName + strlen(cmdName);
+ while (--p > cmdName) {
+ if ((*p == ':') && (*(p - 1) == ':')) {
+ p++; /* just after the last "::" */
+ break;
+ }
+ }
+ if (cmdName == p) {
+ Tcl_DString dString;
+ Tcl_Namespace *nsPtr;
+ Tcl_Command cmdToken;
+
+ Tcl_DStringInit(&dString);
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ Tcl_DStringAppend(&dString, nsPtr->fullName, -1);
+ Tcl_DStringAppend(&dString, "::", -1);
+ Tcl_DStringAppend(&dString, cmdName, -1);
+ cmdToken = Tcl_CreateCommand(interp, Tcl_DStringValue(&dString), proc,
+ clientData, deleteProc);
+ Tcl_DStringFree(&dString);
+ return cmdToken;
+ }
+ return Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommandObj --
+ *
+ * Like Tcl_CreateCommand, but creates command in current namespace
+ * instead of global, if one isn't defined. Not a problem with
+ * [incr Tcl] namespaces.
+ *
+ * Results:
+ * The return value is a token for the command, which can
+ * be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommandObj(interp, cmdName, proc, clientData, deleteProc)
+ Tcl_Interp *interp; /* Token for command interpreter returned by
+ * a previous call to Tcl_CreateInterp. */
+ char *cmdName; /* Name of command. If it contains namespace
+ * qualifiers, the new command is put in the
+ * specified namespace; otherwise it is put
+ * in the global namespace. */
+ Tcl_ObjCmdProc *proc; /* Procedure to associate with cmdName. */
+ ClientData clientData; /* Arbitrary value passed to string proc. */
+ Tcl_CmdDeleteProc *deleteProc;
+ /* If not NULL, gives a procedure to call
+ * when this command is deleted. */
+{
+ register char *p;
+
+ p = cmdName + strlen(cmdName);
+ while (--p > cmdName) {
+ if ((*p == ':') && (*(p - 1) == ':')) {
+ p++; /* just after the last "::" */
+ break;
+ }
+ }
+ if (cmdName == p) {
+ Tcl_DString dString;
+ Tcl_Namespace *nsPtr;
+ Tcl_Command cmdToken;
+
+ Tcl_DStringInit(&dString);
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ Tcl_DStringAppend(&dString, nsPtr->fullName, -1);
+ Tcl_DStringAppend(&dString, "::", -1);
+ Tcl_DStringAppend(&dString, cmdName, -1);
+ cmdToken = Tcl_CreateObjCommand(interp, Tcl_DStringValue(&dString),
+ proc, clientData, deleteProc);
+ Tcl_DStringFree(&dString);
+ return cmdToken;
+ }
+ return Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc);
+}
+#endif /* TCL_VERSION_NUMBER < 8.0.0 */
diff --git a/blt/src/bltNsUtil.h b/blt/src/bltNsUtil.h
new file mode 100644
index 00000000000..16b6614d5b1
--- /dev/null
+++ b/blt/src/bltNsUtil.h
@@ -0,0 +1,119 @@
+/*
+ * bltNsUtil.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef BLT_NS_UTIL_H
+#define BLT_NS_UTIL_H 1
+
+#if defined(ITCL_NAMESPACES) || (TCL_MAJOR_VERSION >= 8)
+#define HAVE_NAMESPACES 1
+#else
+#define HAVE_NAMESPACES 0
+#endif
+
+#if (TCL_MAJOR_VERSION <= 7)
+
+/*
+ * Namespaces and callframes don't exist before Tcl version 8.0.
+ * We'll define them as opaque pointers. In reality, they
+ * point to the interpreter token.
+ */
+typedef struct Tcl_NamespaceStruct Tcl_Namespace;
+typedef struct Tcl_CallFrameStruct *Tcl_CallFrame;
+#endif
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+
+EXTERN Tcl_Command Tcl_FindCommand _ANSI_ARGS_((Tcl_Interp *interp,
+ char *name, Tcl_Namespace *nsPtr, int flags));
+
+#define NS_SEARCH_NONE (0)
+#define NS_SEARCH_CURRENT (1<<0)
+#define NS_SEARCH_GLOBAL (1<<1)
+#define NS_SEARCH_BOTH (NS_SEARCH_GLOBAL | NS_SEARCH_CURRENT)
+
+
+/*
+ * Namespace procedures not prototyped defined in Tcl.h
+ */
+EXTERN Tcl_Namespace *Tcl_GetCurrentNamespace _ANSI_ARGS_((Tcl_Interp *interp));
+
+EXTERN Tcl_Namespace *Tcl_GetGlobalNamespace _ANSI_ARGS_((Tcl_Interp *interp));
+
+#if (TCL_MAJOR_VERSION >= 8)
+EXTERN Tcl_Namespace *Tcl_CreateNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ char *name, ClientData clientData, Tcl_NamespaceDeleteProc *nsDelProc));
+
+EXTERN void Tcl_DeleteNamespace _ANSI_ARGS_((Tcl_Namespace *nsPtr));
+
+EXTERN Tcl_Namespace *Tcl_FindNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ char *name, Tcl_Namespace *context, int flags));
+
+EXTERN int Tcl_Export _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Namespace *nsPtr,
+ char *name, int resetFlag));
+
+EXTERN Tcl_Var Tcl_FindNamespaceVar _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ Tcl_Namespace *contextNsPtr, int flags));
+
+EXTERN void Tcl_PopCallFrame _ANSI_ARGS_((Tcl_Interp *interp));
+
+EXTERN int Tcl_PushCallFrame _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_CallFrame * framePtr, Tcl_Namespace *nsPtr, int isProcCallFrame));
+
+extern Tcl_HashTable *Blt_GetArrayVariableTable _ANSI_ARGS_((
+ Tcl_Interp *interp, char *varName, int flags));
+
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+
+/*
+ * Auxillary procedures
+ */
+EXTERN Tcl_Namespace *Blt_GetVariableNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ char *varName));
+
+EXTERN Tcl_Namespace *Blt_GetCommandNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Command cmdToken));
+
+EXTERN Tcl_CallFrame *Blt_EnterNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Namespace *nsPtr));
+
+EXTERN void Blt_LeaveNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_CallFrame * framePtr));
+
+EXTERN int Blt_ParseQualifiedName _ANSI_ARGS_((Tcl_Interp *interp,
+ CONST char *name, Tcl_Namespace **nsPtrPtr, char **namePtr));
+
+EXTERN char *Blt_GetQualifiedName _ANSI_ARGS_((Tcl_Namespace *nsPtr,
+ CONST char *name,
+ Tcl_DString *resultPtr));
+
+EXTERN Tcl_Command Blt_CreateCommand _ANSI_ARGS_((Tcl_Interp *interp,
+ char *cmdName, Tcl_CmdProc *proc, ClientData clientData,
+ Tcl_CmdDeleteProc *deleteProc));
+
+
+#endif /* BLT_NS_UTIL_H */
diff --git a/blt/src/bltObjConfig.c b/blt/src/bltObjConfig.c
new file mode 100644
index 00000000000..f7c5ee87ba2
--- /dev/null
+++ b/blt/src/bltObjConfig.c
@@ -0,0 +1,2346 @@
+/*
+ * bltObjConfig.c --
+ *
+ * This file contains the Tk_ConfigureWidget procedure. THIS FILE
+ * IS HERE FOR BACKWARD COMPATIBILITY; THE NEW CONFIGURATION
+ * PACKAGE SHOULD BE USED FOR NEW PROJECTS.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "bltInt.h"
+#if (TK_VERSION_NUMBER >= _VERSION(8,0,0))
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "bltObjConfig.h"
+#include "bltTile.h"
+
+#if (TK_VERSION_NUMBER < _VERSION(8,1,0))
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetAnchorFromObj --
+ *
+ * Return a Tk_Anchor value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetAnchorFromObj(interp, objPtr, anchorPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *objPtr; /* The object we are trying to get the
+ * value from. */
+ Tk_Anchor *anchorPtr; /* Where to place the Tk_Anchor that
+ * corresponds to the string value of
+ * objPtr. */
+{
+ return Tk_GetAnchor(interp, Tcl_GetString(objPtr), anchorPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetJustifyFromObj --
+ *
+ * Return a Tk_Justify value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetJustifyFromObj(interp, objPtr, justifyPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *objPtr; /* The object we are trying to get the
+ * value from. */
+ Tk_Justify *justifyPtr; /* Where to place the Tk_Justify that
+ * corresponds to the string value of
+ * objPtr. */
+{
+ return Tk_GetJustify(interp, Tcl_GetString(objPtr), justifyPtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetReliefFromObj --
+ *
+ * Return an integer value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetReliefFromObj(interp, objPtr, reliefPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tcl_Obj *objPtr; /* The object we are trying to get the
+ * value from. */
+ int *reliefPtr; /* Where to place the answer. */
+{
+ return Tk_GetRelief(interp, Tcl_GetString(objPtr), reliefPtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetMMFromObj --
+ *
+ * Attempt to return an mm value from the Tcl object "objPtr". If the
+ * object is not already an mm value, an attempt will be made to convert
+ * it to one.
+ *
+ * Results:
+ * The return value is a standard Tcl object result. If an error occurs
+ * during conversion, an error message is left in the interpreter's
+ * result unless "interp" is NULL.
+ *
+ * Side effects:
+ * If the object is not already a pixel, the conversion will free
+ * any old internal representation.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetMMFromObj(interp, tkwin, objPtr, doublePtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ Tk_Window tkwin;
+ Tcl_Obj *objPtr; /* The object from which to get mms. */
+ double *doublePtr; /* Place to store resulting millimeters. */
+{
+ return Tk_GetScreenMM(interp, tkwin, Tcl_GetString(objPtr), doublePtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetPixelsFromObj --
+ *
+ * Attempt to return a pixel value from the Tcl object "objPtr". If the
+ * object is not already a pixel value, an attempt will be made to convert
+ * it to one.
+ *
+ * Results:
+ * The return value is a standard Tcl object result. If an error occurs
+ * during conversion, an error message is left in the interpreter's
+ * result unless "interp" is NULL.
+ *
+ * Side effects:
+ * If the object is not already a pixel, the conversion will free
+ * any old internal representation.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetPixelsFromObj(interp, tkwin, objPtr, intPtr)
+ Tcl_Interp *interp; /* Used for error reporting if not NULL. */
+ Tk_Window tkwin;
+ Tcl_Obj *objPtr; /* The object from which to get pixels. */
+ int *intPtr; /* Place to store resulting pixels. */
+{
+ return Tk_GetPixels(interp, tkwin, Tcl_GetString(objPtr), intPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_Alloc3DBorderFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Tk_3DBorder structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is a token for a data structure describing a
+ * 3-D border. This token may be passed to procedures such as
+ * Tk_Draw3DRectangle and Tk_Free3DBorder. If an error prevented
+ * the border from being created then NULL is returned and an error
+ * message will be left in the interp's result.
+ *
+ * Side effects:
+ * The border is added to an internal database with a reference
+ * count. For each call to this procedure, there should eventually
+ * be a call to FreeBorderObjProc so that the database is
+ * cleaned up when borders aren't in use anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_3DBorder
+Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr)
+ Tcl_Interp *interp; /* Interp for error results. */
+ Tk_Window tkwin; /* Need the screen the border is used on.*/
+ Tcl_Obj *objPtr; /* Object giving name of color for window
+ * background. */
+{
+ return Tk_Get3DBorder(interp, tkwin, Tcl_GetString(objPtr));
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocBitmapFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Pixmap structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is the X identifer for the desired bitmap
+ * (i.e. a Pixmap with a single plane), unless string couldn't be
+ * parsed correctly. In this case, None is returned and an error
+ * message is left in the interp's result. The caller should never
+ * modify the bitmap that is returned, and should eventually call
+ * Tk_FreeBitmapFromObj when the bitmap is no longer needed.
+ *
+ * Side effects:
+ * The bitmap is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeBitmapFromObj, so that the database can be cleaned up
+ * when bitmaps aren't needed anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Tk_AllocBitmapFromObj(interp, tkwin, objPtr)
+ Tcl_Interp *interp; /* Interp for error results. This may
+ * be NULL. */
+ Tk_Window tkwin; /* Need the screen the bitmap is used on.*/
+ Tcl_Obj *objPtr; /* Object describing bitmap; see manual
+ * entry for legal syntax of string value. */
+{
+ return Tk_GetBitmap(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocFontFromObj --
+ *
+ * Given a string description of a font, map the description to a
+ * corresponding Tk_Font that represents the font.
+ *
+ * Results:
+ * The return value is token for the font, or NULL if an error
+ * prevented the font from being created. If NULL is returned, an
+ * error message will be left in interp's result object.
+ *
+ * Side effects:
+ * The font is added to an internal database with a reference
+ * count. For each call to this procedure, there should eventually
+ * be a call to Tk_FreeFont() or Tk_FreeFontFromObj() so that the
+ * database is cleaned up when fonts aren't in use anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_Font
+Tk_AllocFontFromObj(interp, tkwin, objPtr)
+ Tcl_Interp *interp; /* Interp for database and error return. */
+ Tk_Window tkwin; /* For screen on which font will be used. */
+ Tcl_Obj *objPtr; /* Object describing font, as: named font,
+ * native format, or parseable string. */
+{
+ return Tk_GetFont(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocCursorFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Tk_Cursor structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is the X identifer for the desired cursor,
+ * unless objPtr couldn't be parsed correctly. In this case,
+ * None is returned and an error message is left in the interp's result.
+ * The caller should never modify the cursor that is returned, and
+ * should eventually call Tk_FreeCursorFromObj when the cursor is no
+ * longer needed.
+ *
+ * Side effects:
+ * The cursor is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeCursorFromObj, so that the database can be cleaned up
+ * when cursors aren't needed anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Cursor
+Tk_AllocCursorFromObj(interp, tkwin, objPtr)
+ Tcl_Interp *interp; /* Interp for error results. */
+ Tk_Window tkwin; /* Window in which the cursor will be used.*/
+ Tcl_Obj *objPtr; /* Object describing cursor; see manual
+ * entry for description of legal
+ * syntax of this obj's string rep. */
+{
+ return Tk_GetCursor(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocColorFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * XColor structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is a pointer to an XColor structure that
+ * indicates the red, blue, and green intensities for the color
+ * given by the string in objPtr, and also specifies a pixel value
+ * to use to draw in that color. If an error occurs, NULL is
+ * returned and an error message will be left in interp's result
+ * (unless interp is NULL).
+ *
+ * Side effects:
+ * The color is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeColorFromObj so that the database is cleaned up when colors
+ * aren't in use anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+XColor *
+Tk_AllocColorFromObj(interp, tkwin, objPtr)
+ Tcl_Interp *interp; /* Used only for error reporting. If NULL,
+ * then no messages are provided. */
+ Tk_Window tkwin; /* Window in which the color will be used.*/
+ Tcl_Obj *objPtr; /* Object that describes the color; string
+ * value is a color name such as "red" or
+ * "#ff0000".*/
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ return Tk_GetColor(interp, tkwin, Tk_GetUid(string));
+}
+
+#endif /* 8.0 */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_GetPosition --
+ *
+ * Convert a string representing a numeric position.
+ * A position can be in one of the following forms.
+ *
+ * number - number of the item in the hierarchy, indexed
+ * from zero.
+ * "end" - last position in the hierarchy.
+ *
+ * Results:
+ * A standard Tcl result. If "string" is a valid index, then
+ * *indexPtr is filled with the corresponding numeric index.
+ * If "end" was selected then *indexPtr is set to -1.
+ * Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_GetPositionFromObj(interp, objPtr, indexPtr)
+ Tcl_Interp *interp; /* Interpreter to report results back
+ * to. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representation of the index.
+ * Can be an integer or "end" to refer
+ * to the last index. */
+ int *indexPtr; /* Holds the converted index. */
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ *indexPtr = -1; /* Indicates last position in hierarchy. */
+ } else {
+ int position;
+
+ if (Tcl_GetIntFromObj(interp, objPtr, &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position < 0) {
+ Tcl_AppendResult(interp, "bad position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = position;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPixelsFromObj --
+ *
+ * Like Tk_GetPixelsFromObj, but checks for negative, zero.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetPixelsFromObj(interp, tkwin, objPtr, check, valuePtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Tcl_Obj *objPtr;
+ int check; /* Can be PIXELS_POSITIVE, PIXELS_NONNEGATIVE,
+ * or PIXELS_ANY, */
+ int *valuePtr;
+{
+ int length;
+
+ if (Tk_GetPixelsFromObj(interp, tkwin, objPtr, &length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (length >= SHRT_MAX) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": too big to represent", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (check) {
+ case PIXELS_NONNEGATIVE:
+ if (length < 0) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+
+ case PIXELS_POSITIVE:
+ if (length <= 0) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": must be positive", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+
+ case PIXELS_ANY:
+ break;
+ }
+ *valuePtr = length;
+ return TCL_OK;
+}
+
+int
+Blt_GetPadFromObj(interp, tkwin, objPtr, padPtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ Tcl_Obj *objPtr; /* Pixel value string */
+ Blt_Pad *padPtr;
+{
+ int side1, side2;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc < 1) || (objc > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in padding list",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixelsFromObj(interp, tkwin, objv[0], PIXELS_NONNEGATIVE,
+ &side1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ side2 = side1;
+ if ((objc > 1) &&
+ (Blt_GetPixelsFromObj(interp, tkwin, objv[1], PIXELS_NONNEGATIVE,
+ &side2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ /* Don't update the pad structure until we know both values are okay. */
+ padPtr->side1 = side1;
+ padPtr->side2 = side2;
+ return TCL_OK;
+}
+
+int
+Blt_GetShadowFromObj(interp, tkwin, objPtr, shadowPtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window */
+ Tcl_Obj *objPtr; /* Pixel value string */
+ Shadow *shadowPtr;
+{
+ XColor *colorPtr;
+ int dropOffset;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 2) {
+ Tcl_AppendResult(interp, "wrong # elements in drop shadow value",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ dropOffset = 0;
+ colorPtr = NULL;
+ if (objc > 0) {
+ colorPtr = Tk_AllocColorFromObj(interp, tkwin, objv[0]);
+ if (colorPtr == NULL) {
+ return TCL_ERROR;
+ }
+ dropOffset = 1;
+ if (objc == 2) {
+ if (Blt_GetPixelsFromObj(interp, tkwin, objv[1], PIXELS_NONNEGATIVE,
+ &dropOffset) != TCL_OK) {
+ Tk_FreeColor(colorPtr);
+ return TCL_ERROR;
+ }
+ }
+ }
+ if (shadowPtr->color != NULL) {
+ Tk_FreeColor(shadowPtr->color);
+ }
+ shadowPtr->color = colorPtr;
+ shadowPtr->offset = dropOffset;
+ return TCL_OK;
+}
+
+int
+Blt_GetStateFromObj(interp, objPtr, statePtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tcl_Obj *objPtr; /* Pixel value string */
+ int *statePtr;
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (strcmp(string, "normal") == 0) {
+ *statePtr = STATE_NORMAL;
+ } else if (strcmp(string, "disabled") == 0) {
+ *statePtr = STATE_DISABLED;
+ } else if (strcmp(string, "active") == 0) {
+ *statePtr = STATE_ACTIVE;
+ } else {
+ Tcl_AppendResult(interp, "bad state \"", string,
+ "\": should be normal, active, or disabled", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+char *
+Blt_NameOfState(state)
+ int state;
+{
+ switch (state) {
+ case STATE_ACTIVE:
+ return "active";
+ case STATE_DISABLED:
+ return "disabled";
+ case STATE_NORMAL:
+ return "normal";
+ default:
+ return "???";
+ }
+}
+
+#ifdef notdef /* Replace this routine when Tcl_Obj-based
+ * configuration comes on-line */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfFill --
+ *
+ * Converts the integer representing the fill style into a string.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_NameOfFill(fill)
+ int fill;
+{
+ switch (fill) {
+ case FILL_X:
+ return "x";
+ case FILL_Y:
+ return "y";
+ case FILL_NONE:
+ return "none";
+ case FILL_BOTH:
+ return "both";
+ default:
+ return "unknown value";
+ }
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetFillFromObj --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none" Use neither plane.
+ * "x" X-coordinate plane.
+ * "y" Y-coordinate plane.
+ * "both" Use both coordinate planes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_GetFillFromObj(interp, objPtr, fillPtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tcl_Obj *objPtr; /* Fill style string */
+ int *fillPtr;
+{
+ unsigned int length;
+ char c;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *fillPtr = FILL_NONE;
+ } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) {
+ *fillPtr = FILL_X;
+ } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) {
+ *fillPtr = FILL_Y;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *fillPtr = FILL_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", string,
+ "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetDashesFromObj --
+ *
+ * Converts a Tcl list of dash values into a dash list ready for
+ * use with XSetDashes.
+ *
+ * A valid list dash values can have zero through 11 elements
+ * (PostScript limit). Values must be between 1 and 255. Although
+ * a list of 0 (like the empty string) means no dashes.
+ *
+ * Results:
+ * A standard Tcl result. If the list represented a valid dash
+ * list TCL_OK is returned and *dashesPtr* will contain the
+ * valid dash list. Otherwise, TCL_ERROR is returned and
+ * interp->result will contain an error message.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetDashesFromObj(interp, objPtr, dashesPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+ Blt_Dashes *dashesPtr;
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((string == NULL) || (*string == '\0')) {
+ dashesPtr->values[0] = 0;
+ } else if (strcmp(string, "dash") == 0) { /* 5 2 */
+ dashesPtr->values[0] = 5;
+ dashesPtr->values[1] = 2;
+ dashesPtr->values[2] = 0;
+ } else if (strcmp(string, "dot") == 0) { /* 1 */
+ dashesPtr->values[0] = 1;
+ dashesPtr->values[1] = 0;
+ } else if (strcmp(string, "dashdot") == 0) { /* 2 4 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 0;
+ } else if (strcmp(string, "dashdotdot") == 0) { /* 2 4 2 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 2;
+ dashesPtr->values[4] = 0;
+ } else {
+ int objc;
+ Tcl_Obj **objv;
+ int value;
+ register int i;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 11) { /* This is the postscript limit */
+ Tcl_AppendResult(interp, "too many values in dash list \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Backward compatibility:
+ * Allow list of 0 to turn off dashes
+ */
+ if ((value == 0) && (objc == 1)) {
+ break;
+ }
+ if ((value < 1) || (value > 255)) {
+ Tcl_AppendResult(interp, "dash value \"",
+ Tcl_GetString(objv[i]), "\" is out of range",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ dashesPtr->values[i] = (unsigned char)value;
+ }
+ /* Make sure the array ends with a NUL byte */
+ dashesPtr->values[i] = 0;
+ }
+ return TCL_OK;
+}
+
+char *
+Blt_NameOfSide(side)
+ int side;
+{
+ switch (side) {
+ case SIDE_LEFT:
+ return "left";
+ case SIDE_RIGHT:
+ return "right";
+ case SIDE_BOTTOM:
+ return "bottom";
+ case SIDE_TOP:
+ return "top";
+ }
+ return "unknown side value";
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetSideFromObj --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ * Valid style strings are "left", "right", "top", or "bottom".
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED */
+int
+Blt_GetSideFromObj(interp, objPtr, sidePtr)
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tcl_Obj *objPtr; /* Value string */
+ int *sidePtr; /* (out) Token representing side:
+ * either SIDE_LEFT, SIDE_RIGHT,
+ * SIDE_TOP, or SIDE_BOTTOM. */
+{
+ char c;
+ unsigned int length;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *sidePtr = SIDE_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *sidePtr = SIDE_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+ *sidePtr = SIDE_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+ *sidePtr = SIDE_BOTTOM;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", string,
+ "\": should be left, right, top, or bottom", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToEnum --
+ *
+ * Converts the string into its enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_ObjToEnum(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Vectors of valid strings. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr;
+ char *widgRec; /* Widget record. */
+ int offset; /* Offset of field in record */
+{
+ int *enumPtr = (int *)(widgRec + offset);
+ char c;
+ register char **p;
+ register int i;
+ int count;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ count = 0;
+ for (p = (char **)clientData; *p != NULL; p++) {
+ if ((c == p[0][0]) && (strcmp(string, *p) == 0)) {
+ *enumPtr = count;
+ return TCL_OK;
+ }
+ count++;
+ }
+ *enumPtr = -1;
+
+ Tcl_AppendResult(interp, "bad value \"", string, "\": should be ",
+ (char *)NULL);
+ p = (char **)clientData;
+ if (count > 0) {
+ Tcl_AppendResult(interp, p[0], (char *)NULL);
+ }
+ for (i = 1; i < (count - 1); i++) {
+ Tcl_AppendResult(interp, " ", p[i], ", ", (char *)NULL);
+ }
+ if (count > 1) {
+ Tcl_AppendResult(interp, " or ", p[count - 1], ".", (char *)NULL);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EnumToObj --
+ *
+ * Returns the string associated with the enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+Tcl_Obj *
+Blt_EnumToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* List of strings. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in widget record */
+{
+ int value = *(int *)(widgRec + offset);
+ char **strings = (char **)clientData;
+ char **p;
+ int count;
+
+ count = 0;
+ for (p = strings; *p != NULL; p++) {
+ if (value == count) {
+ return Tcl_NewStringObj(*p, -1);
+ }
+ count++;
+ }
+ return Tcl_NewStringObj("unknown value", -1);
+}
+
+/* Configuration option helper routines */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoConfig --
+ *
+ * This procedure applies a single configuration option
+ * to a widget record.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * WidgRec is modified as indicated by specPtr and value.
+ * The old value is recycled, if that is appropriate for
+ * the value type.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+DoConfig(interp, tkwin, specPtr, objPtr, widgRec)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Window containing widget (needed to
+ * set up X resources). */
+ Blt_ConfigSpec *specPtr; /* Specifier to apply. */
+ Tcl_Obj *objPtr; /* Value to use to fill in widgRec. */
+ char *widgRec; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+{
+ char *ptr;
+ int objIsEmpty;
+
+ objIsEmpty = FALSE;
+ if (objPtr == NULL) {
+ objIsEmpty = TRUE;
+ } else if (specPtr->specFlags & BLT_CONFIG_NULL_OK) {
+ int length;
+
+ if (objPtr->bytes != NULL) {
+ length = objPtr->length;
+ } else {
+ Tcl_GetStringFromObj(objPtr, &length);
+ }
+ objIsEmpty = (length == 0);
+ }
+ do {
+ ptr = widgRec + specPtr->offset;
+ switch (specPtr->type) {
+ case BLT_CONFIG_ANCHOR:
+ {
+ Tk_Anchor anchor;
+
+ if (Tk_GetAnchorFromObj(interp, objPtr, &anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(Tk_Anchor *)ptr = anchor;
+ break;
+ }
+
+ case BLT_CONFIG_BITMAP:
+ {
+ Pixmap newBitmap, oldBitmap;
+
+ if (objIsEmpty) {
+ newBitmap = None;
+ } else {
+ newBitmap = Tk_AllocBitmapFromObj(interp, tkwin, objPtr);
+ if (newBitmap == None) {
+ return TCL_ERROR;
+ }
+ }
+ oldBitmap = *(Pixmap *)ptr;
+ if (oldBitmap != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), oldBitmap);
+ }
+ *(Pixmap *)ptr = newBitmap;
+ break;
+ }
+
+ case BLT_CONFIG_BOOLEAN:
+ {
+ int newBool;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &newBool)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = newBool;
+ break;
+ }
+
+ case BLT_CONFIG_BORDER:
+ {
+ Tk_3DBorder newBorder, oldBorder;
+
+ if (objIsEmpty) {
+ newBorder = NULL;
+ } else {
+ newBorder = Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr);
+ if (newBorder == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ oldBorder = *(Tk_3DBorder *)ptr;
+ if (oldBorder != NULL) {
+ Tk_Free3DBorder(oldBorder);
+ }
+ *(Tk_3DBorder *)ptr = newBorder;
+ break;
+ }
+
+ case BLT_CONFIG_CAP_STYLE:
+ {
+ int cap;
+ char *value;
+
+ value = Tk_GetUid(Tcl_GetString(objPtr));
+ if (Tk_GetCapStyle(interp, value, &cap) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = cap;
+ break;
+ }
+
+ case BLT_CONFIG_COLOR:
+ {
+ XColor *newColor, *oldColor;
+
+ if (objIsEmpty) {
+ newColor = NULL;
+ } else {
+ newColor = Tk_AllocColorFromObj(interp, tkwin, objPtr);
+ if (newColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ oldColor = *(XColor **)ptr;
+ if (oldColor != NULL) {
+ Tk_FreeColor(oldColor);
+ }
+ *(XColor **)ptr = newColor;
+ break;
+ }
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ {
+ Tk_Cursor newCursor, oldCursor;
+
+ if (objIsEmpty) {
+ newCursor = None;
+ } else {
+ newCursor = Tk_AllocCursorFromObj(interp, tkwin, objPtr);
+ if (newCursor == None) {
+ return TCL_ERROR;
+ }
+ }
+ oldCursor = *(Tk_Cursor *)ptr;
+ if (oldCursor != None) {
+ Tk_FreeCursor(Tk_Display(tkwin), oldCursor);
+ }
+ *(Tk_Cursor *)ptr = newCursor;
+ if (specPtr->type == BLT_CONFIG_ACTIVE_CURSOR) {
+ Tk_DefineCursor(tkwin, newCursor);
+ }
+ break;
+ }
+
+ case BLT_CONFIG_CUSTOM:
+ {
+ if ((*(char **)ptr != NULL) &&
+ (specPtr->customPtr->freeProc != NULL)) {
+ (*specPtr->customPtr->freeProc)
+ (specPtr->customPtr->clientData, Tk_Display(tkwin),
+ widgRec, specPtr->offset);
+ *(char **)ptr = NULL;
+ }
+ if (objIsEmpty) {
+ *(char **)ptr = NULL;
+ } else {
+ int result;
+
+ result = (*specPtr->customPtr->parseProc)
+ (specPtr->customPtr->clientData, interp, tkwin, objPtr,
+ widgRec, specPtr->offset);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ break;
+ }
+
+ case BLT_CONFIG_DOUBLE:
+ {
+ double newDouble;
+
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &newDouble)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(double *)ptr = newDouble;
+ break;
+ }
+
+ case BLT_CONFIG_FONT:
+ {
+ Tk_Font newFont, oldFont;
+
+ if (objIsEmpty) {
+ newFont = NULL;
+ } else {
+ newFont = Tk_AllocFontFromObj(interp, tkwin, objPtr);
+ if (newFont == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ oldFont = *(Tk_Font *)ptr;
+ if (oldFont != NULL) {
+ Tk_FreeFont(oldFont);
+ }
+ *(Tk_Font *)ptr = newFont;
+ break;
+ }
+
+ case BLT_CONFIG_INT:
+ {
+ int newInt;
+
+ if (Tcl_GetIntFromObj(interp, objPtr, &newInt) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = newInt;
+ break;
+ }
+
+ case BLT_CONFIG_JOIN_STYLE:
+ {
+ int join;
+ char *value;
+
+ value = Tk_GetUid(Tcl_GetString(objPtr));
+ if (Tk_GetJoinStyle(interp, value, &join) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = join;
+ break;
+ }
+
+ case BLT_CONFIG_JUSTIFY:
+ {
+ Tk_Justify justify;
+
+ if (Tk_GetJustifyFromObj(interp, objPtr, &justify) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(Tk_Justify *)ptr = justify;
+ break;
+ }
+
+ case BLT_CONFIG_MM:
+ {
+ double mm;
+
+ if (Tk_GetMMFromObj(interp, tkwin, objPtr, &mm) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(double *)ptr = mm;
+ break;
+ }
+
+ case BLT_CONFIG_PIXELS:
+ {
+ int pixels;
+
+ if (Tk_GetPixelsFromObj(interp, tkwin, objPtr, &pixels)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = pixels;
+ break;
+ }
+
+ case BLT_CONFIG_RELIEF:
+ {
+ int relief;
+
+ if (Tk_GetReliefFromObj(interp, objPtr, &relief) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = relief;
+ break;
+ }
+
+ case BLT_CONFIG_STRING:
+ {
+ char *oldString, *newString;
+
+ if (objIsEmpty) {
+ newString = NULL;
+ } else {
+ newString = (char *)Blt_Strdup(Tcl_GetString(objPtr));
+ }
+ oldString = *(char **)ptr;
+ if (oldString != NULL) {
+ Blt_Free(oldString);
+ }
+ *(char **)ptr = newString;
+ break;
+ }
+
+ case BLT_CONFIG_UID:
+ {
+ if (objIsEmpty) {
+ *(Tk_Uid *)ptr = NULL;
+ } else {
+ *(Tk_Uid *)ptr = Tk_GetUid(Tcl_GetString(objPtr));
+ }
+ break;
+ }
+
+ case BLT_CONFIG_WINDOW:
+ {
+ Tk_Window tkwin2;
+
+ if (objIsEmpty) {
+ tkwin2 = None;
+ } else {
+ char *path;
+
+ path = Tcl_GetString(objPtr);
+ tkwin2 = Tk_NameToWindow(interp, path, tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ *(Tk_Window *)ptr = tkwin2;
+ break;
+ }
+
+ case BLT_CONFIG_BITFLAG:
+ {
+ int bool;
+ unsigned int flag;
+
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flag = (unsigned int)specPtr->customPtr;
+ *(int *)ptr &= ~flag;
+ if (bool) {
+ *(int *)ptr |= flag;
+ }
+ break;
+ }
+
+ case BLT_CONFIG_DASHES:
+ if (Blt_GetDashesFromObj(interp, objPtr, (Blt_Dashes *)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_DISTANCE:
+ {
+ int newPixels;
+
+ if (Blt_GetPixelsFromObj(interp, tkwin, objPtr,
+ PIXELS_NONNEGATIVE, &newPixels) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = newPixels;
+ break;
+ }
+
+ case BLT_CONFIG_FILL:
+ if (Blt_GetFillFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ case BLT_CONFIG_FLOAT:
+ {
+ double newDouble;
+
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &newDouble)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(float *)ptr = (float)newDouble;
+ break;
+ }
+
+ case BLT_CONFIG_LIST:
+ {
+ char **argv;
+ int argc;
+
+ if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(char ***)ptr = argv;
+ break;
+ }
+
+ case BLT_CONFIG_LISTOBJ:
+ {
+ Tcl_Obj **objv;
+ Tcl_Obj *listObjPtr;
+ int objc;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ listObjPtr = Tcl_NewListObj(objc, objv);
+ Tcl_IncrRefCount(listObjPtr);
+ *(Tcl_Obj **)ptr = listObjPtr;
+ break;
+ }
+
+ case BLT_CONFIG_PAD:
+ if (Blt_GetPadFromObj(interp, tkwin, objPtr, (Blt_Pad *)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ case BLT_CONFIG_POS_DISTANCE:
+ {
+ int newPixels;
+
+ if (Blt_GetPixelsFromObj(interp, tkwin, objPtr,
+ PIXELS_POSITIVE, &newPixels) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = newPixels;
+ break;
+ }
+
+ case BLT_CONFIG_SHADOW:
+ {
+ Shadow *shadowPtr = (Shadow *)ptr;
+
+ if ((shadowPtr != NULL) && (shadowPtr->color != NULL)) {
+ Tk_FreeColor(shadowPtr->color);
+ }
+ if (Blt_GetShadowFromObj(interp, tkwin, objPtr, shadowPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ case BLT_CONFIG_STATE:
+ {
+ if (Blt_GetStateFromObj(interp, objPtr, (int *)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+
+ case BLT_CONFIG_TILE:
+ {
+ Blt_Tile newTile, oldTile;
+
+ if (objIsEmpty) {
+ newTile = None;
+ } else {
+ if (Blt_GetTile(interp, tkwin, Tcl_GetString(objPtr),
+ &newTile) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ oldTile = *(Blt_Tile *)ptr;
+ if (oldTile != NULL) {
+ Blt_FreeTile(oldTile);
+ }
+ *(Blt_Tile *)ptr = newTile;
+ break;
+ }
+
+ case BLT_CONFIG_SIDE:
+ if (Blt_GetSideFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ default:
+ {
+ char buf[200];
+
+ sprintf(buf, "bad config table: unknown type %d",
+ specPtr->type);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ }
+ specPtr++;
+ } while ((specPtr->switchName == NULL) &&
+ (specPtr->type != BLT_CONFIG_END));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatConfigValue --
+ *
+ * This procedure formats the current value of a configuration
+ * option.
+ *
+ * Results:
+ * The return value is the formatted value of the option given
+ * by specPtr and widgRec. If the value is static, so that it
+ * need not be freed, *freeProcPtr will be set to NULL; otherwise
+ * *freeProcPtr will be set to the address of a procedure to
+ * free the result, and the caller must invoke this procedure
+ * when it is finished with the result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigValue(interp, tkwin, specPtr, widgRec)
+ Tcl_Interp *interp; /* Interpreter for use in real conversions. */
+ Tk_Window tkwin; /* Window corresponding to widget. */
+ Blt_ConfigSpec *specPtr; /* Pointer to information describing option.
+ * Must not point to a synonym option. */
+ char *widgRec; /* Pointer to record holding current
+ * values of info for widget. */
+{
+ char *ptr;
+ char *string;
+
+ ptr = widgRec + specPtr->offset;
+ string = "";
+ switch (specPtr->type) {
+ case BLT_CONFIG_ANCHOR:
+ string = Tk_NameOfAnchor(*(Tk_Anchor *)ptr);
+ break;
+
+ case BLT_CONFIG_BITMAP:
+ if (*(Pixmap *)ptr != None) {
+ string = Tk_NameOfBitmap(Tk_Display(tkwin), *(Pixmap *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_BOOLEAN:
+ return Tcl_NewBooleanObj(*(int *)ptr);
+
+ case BLT_CONFIG_BORDER:
+ if (*(Tk_3DBorder *)ptr != NULL) {
+ string = Tk_NameOf3DBorder(*(Tk_3DBorder *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CAP_STYLE:
+ string = Tk_NameOfCapStyle(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_COLOR:
+ if (*(XColor **)ptr != NULL) {
+ string = Tk_NameOfColor(*(XColor **)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ if (*(Tk_Cursor *)ptr != None) {
+ string = Tk_NameOfCursor(Tk_Display(tkwin), *(Tk_Cursor *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CUSTOM:
+ return (*specPtr->customPtr->printProc)(specPtr->customPtr->clientData,
+ interp, tkwin, widgRec, specPtr->offset);
+
+ case BLT_CONFIG_DOUBLE:
+ return Tcl_NewDoubleObj(*(double *)ptr);
+
+ case BLT_CONFIG_FONT:
+ if (*(Tk_Font *)ptr != NULL) {
+ string = Tk_NameOfFont(*(Tk_Font *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_INT:
+ return Tcl_NewIntObj(*(int *)ptr);
+
+ case BLT_CONFIG_JOIN_STYLE:
+ string = Tk_NameOfJoinStyle(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_JUSTIFY:
+ string = Tk_NameOfJustify(*(Tk_Justify *)ptr);
+ break;
+
+ case BLT_CONFIG_MM:
+ return Tcl_NewDoubleObj(*(double *)ptr);
+
+ case BLT_CONFIG_PIXELS:
+ return Tcl_NewIntObj(*(int *)ptr);
+
+ case BLT_CONFIG_RELIEF:
+ string = Tk_NameOfRelief(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_STRING:
+ case BLT_CONFIG_UID:
+ if (*(char **)ptr != NULL) {
+ string = *(char **)ptr;
+ }
+ break;
+
+ case BLT_CONFIG_BITFLAG:
+ {
+ unsigned int flag;
+
+ flag = (*(int *)ptr) & (unsigned int)specPtr->customPtr;
+ return Tcl_NewBooleanObj((flag != 0));
+ }
+
+ case BLT_CONFIG_DASHES:
+ {
+ unsigned char *p;
+ Tcl_Obj *listObjPtr;
+ Blt_Dashes *dashesPtr = (Blt_Dashes *)ptr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for(p = dashesPtr->values; *p != 0; p++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(*p));
+ }
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_DISTANCE:
+ case BLT_CONFIG_POS_DISTANCE:
+ return Tcl_NewIntObj(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_FILL:
+ string = Blt_NameOfFill(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_FLOAT:
+ {
+ double x = *(double *)ptr;
+ return Tcl_NewDoubleObj(x);
+ }
+
+ case BLT_CONFIG_LIST:
+ {
+ Tcl_Obj *objPtr, *listObjPtr;
+ char **p;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (p = *(char ***)ptr; *p != NULL; p++) {
+ objPtr = Tcl_NewStringObj(*p, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_LISTOBJ:
+ return *(Tcl_Obj **)ptr;
+
+ case BLT_CONFIG_PAD:
+ {
+ Blt_Pad *padPtr = (Blt_Pad *)ptr;
+ Tcl_Obj *objPtr, *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ objPtr = Tcl_NewIntObj(padPtr->side1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ objPtr = Tcl_NewIntObj(padPtr->side2);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_SHADOW:
+ {
+ Shadow *shadowPtr = (Shadow *)ptr;
+ Tcl_Obj *objPtr, *listObjPtr;
+
+ if (shadowPtr->color != NULL) {
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ objPtr = Tcl_NewStringObj(Tk_NameOfColor(shadowPtr->color), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ objPtr = Tcl_NewIntObj(shadowPtr->offset);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ return listObjPtr;
+ }
+ }
+
+ case BLT_CONFIG_STATE:
+ string = Blt_NameOfState(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_TILE:
+ string = Blt_NameOfTile((Blt_Tile)ptr);
+ break;
+
+ case BLT_CONFIG_SIDE:
+ string = Blt_NameOfSide(*(int *)ptr);
+ break;
+
+ default:
+ string = "?? unknown type ??";
+ }
+ return Tcl_NewStringObj(string, -1);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FormatConfigInfo --
+ *
+ * Create a valid Tcl list holding the configuration information
+ * for a single configuration option.
+ *
+ * Results:
+ * A Tcl list, dynamically allocated. The caller is expected to
+ * arrange for this list to be freed eventually.
+ *
+ * Side effects:
+ * Memory is allocated.
+ *
+ *--------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigInfo(interp, tkwin, specPtr, widgRec)
+ Tcl_Interp *interp; /* Interpreter to use for things
+ * like floating-point precision. */
+ Tk_Window tkwin; /* Window corresponding to widget. */
+ register Blt_ConfigSpec *specPtr; /* Pointer to information describing
+ * option. */
+ char *widgRec; /* Pointer to record holding current
+ * values of info for widget. */
+{
+ Tcl_Obj *objv[5];
+ Tcl_Obj *emptyObjPtr;
+ register int i;
+
+ emptyObjPtr = Tcl_NewStringObj("", -1);
+ for (i = 0; i < 5; i++) {
+ objv[i] = emptyObjPtr;
+ }
+ if (specPtr->switchName != NULL) {
+ objv[0] = Tcl_NewStringObj(specPtr->switchName, -1);
+ }
+ if (specPtr->dbName != NULL) {
+ objv[1] = Tcl_NewStringObj(specPtr->dbName, -1);
+ }
+ if (specPtr->dbClass != NULL) {
+ objv[2] = Tcl_NewStringObj(specPtr->dbClass, -1);
+ }
+ if (specPtr->defValue != NULL) {
+ objv[3] = Tcl_NewStringObj(specPtr->defValue, -1);
+ }
+ if (specPtr->type == BLT_CONFIG_SYNONYM) {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objv[0]);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objv[1]);
+ return listObjPtr;
+ }
+ objv[4] = FormatConfigValue(interp, tkwin, specPtr, widgRec);
+ return Tcl_NewListObj(5, objv);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FindConfigSpec --
+ *
+ * Search through a table of configuration specs, looking for
+ * one that matches a given switchName.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry, or NULL
+ * if nothing matched. In that case an error message is left
+ * in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_ConfigSpec *
+FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags)
+ Tcl_Interp *interp; /* Used for reporting errors. */
+ Blt_ConfigSpec *specs; /* Pointer to table of configuration
+ * specifications for a widget. */
+ Tcl_Obj *objPtr; /* Name (suitable for use in a "config"
+ * command) identifying particular option. */
+ int needFlags; /* Flags that must be present in matching
+ * entry. */
+ int hateFlags; /* Flags that must NOT be present in
+ * matching entry. */
+{
+ register Blt_ConfigSpec *specPtr;
+ register char c; /* First character of current argument. */
+ Blt_ConfigSpec *matchPtr; /* Matching spec, or NULL. */
+ size_t length;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ c = string[1];
+ length = strlen(string);
+ matchPtr = NULL;
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if (specPtr->switchName == NULL) {
+ continue;
+ }
+ if ((specPtr->switchName[1] != c) ||
+ (strncmp(specPtr->switchName, string, length) != 0)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags) ||
+ (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ if (specPtr->switchName[length] == 0) {
+ matchPtr = specPtr;
+ goto gotMatch;
+ }
+ if (matchPtr != NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "ambiguous option \"", string, "\"",
+ (char *)NULL);
+ }
+ return (Blt_ConfigSpec *)NULL;
+ }
+ matchPtr = specPtr;
+ }
+
+ if (matchPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "unknown option \"", string, "\"",
+ (char *)NULL);
+ }
+ return (Blt_ConfigSpec *)NULL;
+ }
+
+ /*
+ * Found a matching entry. If it's a synonym, then find the
+ * entry that it's a synonym for.
+ */
+
+ gotMatch:
+ specPtr = matchPtr;
+ if (specPtr->type == BLT_CONFIG_SYNONYM) {
+ for (specPtr = specs; ; specPtr++) {
+ if (specPtr->type == BLT_CONFIG_END) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp,
+ "couldn't find synonym for option \"", string,
+ "\"", (char *) NULL);
+ }
+ return (Blt_ConfigSpec *) NULL;
+ }
+ if ((specPtr->dbName == matchPtr->dbName) &&
+ (specPtr->type != BLT_CONFIG_SYNONYM) &&
+ ((specPtr->specFlags & needFlags) == needFlags) &&
+ !(specPtr->specFlags & hateFlags)) {
+ break;
+ }
+ }
+ }
+ return specPtr;
+}
+
+/* Public routines */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ConfigureWidgetFromObj --
+ *
+ * Process command-line options and database options to
+ * fill in fields of a widget record with resources and
+ * other parameters.
+ *
+ * Results:
+ * A standard Tcl return value. In case of an error,
+ * the interp's result will hold an error message.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information
+ * from argc/argv and the option database. Old information
+ * in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ConfigureWidgetFromObj(interp, tkwin, specs, objc, objv, widgRec, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Window containing widget (needed to
+ * set up X resources). */
+ Blt_ConfigSpec *specs; /* Describes legal options. */
+ int objc; /* Number of elements in argv. */
+ Tcl_Obj *CONST *objv; /* Command-line options. */
+ char *widgRec; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. Also,
+ * may have BLT_CONFIG_ARGV_ONLY set. */
+{
+ register Blt_ConfigSpec *specPtr;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's
+ * not considered. */
+
+ if (tkwin == NULL) {
+ /*
+ * Either we're not really in Tk, or the main window was destroyed and
+ * we're on our way out of the application
+ */
+ Tcl_AppendResult(interp, "NULL main window", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * Pass one: scan through all the option specs, replacing strings
+ * with Tk_Uid structs (if this hasn't been done already) and
+ * clearing the BLT_CONFIG_OPTION_SPECIFIED flags.
+ */
+
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if (!(specPtr->specFlags & INIT) && (specPtr->switchName != NULL)) {
+ if (specPtr->dbName != NULL) {
+ specPtr->dbName = Tk_GetUid(specPtr->dbName);
+ }
+ if (specPtr->dbClass != NULL) {
+ specPtr->dbClass = Tk_GetUid(specPtr->dbClass);
+ }
+ if (specPtr->defValue != NULL) {
+ specPtr->defValue = Tk_GetUid(specPtr->defValue);
+ }
+ }
+ specPtr->specFlags =
+ (specPtr->specFlags & ~BLT_CONFIG_OPTION_SPECIFIED) | INIT;
+ }
+
+ /*
+ * Pass two: scan through all of the arguments, processing those
+ * that match entries in the specs.
+ */
+ while (objc > 0) {
+ specPtr = FindConfigSpec(interp, specs, objv[0], needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Process the entry. */
+ if (objc < 2) {
+ Tcl_AppendResult(interp, "value for \"", Tcl_GetString(objv[0]),
+ "\" missing", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (DoConfig(interp, tkwin, specPtr, objv[1], widgRec) != TCL_OK) {
+ char msg[100];
+
+ sprintf(msg, "\n (processing \"%.40s\" option)",
+ specPtr->switchName);
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ specPtr->specFlags |= BLT_CONFIG_OPTION_SPECIFIED;
+ objc -= 2, objv += 2;
+ }
+
+ /*
+ * Pass three: scan through all of the specs again; if no
+ * command-line argument matched a spec, then check for info
+ * in the option database. If there was nothing in the
+ * database, then use the default.
+ */
+
+ if (!(flags & BLT_CONFIG_OBJV_ONLY)) {
+ Tcl_Obj *objPtr;
+
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if ((specPtr->specFlags & BLT_CONFIG_OPTION_SPECIFIED) ||
+ (specPtr->switchName == NULL) ||
+ (specPtr->type == BLT_CONFIG_SYNONYM)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags) ||
+ (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ objPtr = NULL;
+ if (specPtr->dbName != NULL) {
+ char *value;
+
+ value = Tk_GetOption(tkwin, specPtr->dbName, specPtr->dbClass);
+ if (value != NULL) {
+ objPtr = Tcl_NewStringObj(value, -1);
+ }
+ }
+ if (objPtr != NULL) {
+ if (DoConfig(interp, tkwin, specPtr, objPtr, widgRec)
+ != TCL_OK) {
+ char msg[200];
+
+ fprintf(stderr, "obj=%s\n", Tcl_GetString(objPtr));
+ fprintf(stderr, "path=%s\n", Tk_PathName(tkwin));
+ sprintf(msg, "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "database entry for",
+ specPtr->dbName, Tk_PathName(tkwin));
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ } else {
+ if (specPtr->defValue != NULL) {
+ objPtr = Tcl_NewStringObj(specPtr->defValue, -1);
+ } else {
+ objPtr = NULL;
+ }
+ if ((objPtr != NULL) && !(specPtr->specFlags
+ & BLT_CONFIG_DONT_SET_DEFAULT)) {
+ if (DoConfig(interp, tkwin, specPtr, objPtr, widgRec)
+ != TCL_OK) {
+ char msg[200];
+
+ sprintf(msg,
+ "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "default value for",
+ specPtr->dbName, Tk_PathName(tkwin));
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ConfigureInfoFromObj --
+ *
+ * Return information about the configuration options
+ * for a window, and their current values.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp's result will be modified
+ * hold a description of either a single configuration option
+ * available for "widgRec" via "specs", or all the configuration
+ * options available. In the "all" case, the result will
+ * available for "widgRec" via "specs". The result will
+ * be a list, each of whose entries describes one option.
+ * Each entry will itself be a list containing the option's
+ * name for use on command lines, database name, database
+ * class, default value, and current value (empty string
+ * if none). For options that are synonyms, the list will
+ * contain only two values: name and synonym name. If the
+ * "name" argument is non-NULL, then the only information
+ * returned is that for the named argument (i.e. the corresponding
+ * entry in the overall list is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Blt_ConfigureInfoFromObj(interp, tkwin, specs, widgRec, objPtr, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Window corresponding to widgRec. */
+ Blt_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ Tcl_Obj *objPtr; /* If non-NULL, indicates a single option
+ * whose info is to be returned. Otherwise
+ * info is returned for all options. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ register Blt_ConfigSpec *specPtr;
+ int needFlags, hateFlags;
+ char *string;
+ Tcl_Obj *listObjPtr, *valueObjPtr;
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * If information is only wanted for a single configuration
+ * spec, then handle that one spec specially.
+ */
+
+ Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
+ if (objPtr != NULL) {
+ specPtr = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+ valueObjPtr = FormatConfigInfo(interp, tkwin, specPtr, widgRec);
+ Tcl_SetObjResult(interp, valueObjPtr);
+ return TCL_OK;
+ }
+
+ /*
+ * Loop through all the specs, creating a big list with all
+ * their information.
+ */
+ string = NULL; /* Suppress compiler warning. */
+ if (objPtr != NULL) {
+ string = Tcl_GetString(objPtr);
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if ((objPtr != NULL) && (specPtr->switchName != string)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags) ||
+ (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ if (specPtr->switchName == NULL) {
+ continue;
+ }
+ valueObjPtr = FormatConfigInfo(interp, tkwin, specPtr, widgRec);
+ Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureValueFromObj --
+ *
+ * This procedure returns the current value of a configuration
+ * option for a widget.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code (TCL_OK or
+ * TCL_ERROR). The interp's result will be set to hold either the value
+ * of the option given by objPtr (if TCL_OK is returned) or
+ * an error message (if TCL_ERROR is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureValueFromObj(interp, tkwin, specs, widgRec, objPtr, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Window corresponding to widgRec. */
+ Blt_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ Tcl_Obj *objPtr; /* Gives the command-line name for the
+ * option whose value is to be returned. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Blt_ConfigSpec *specPtr;
+ int needFlags, hateFlags;
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+ specPtr = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+ objPtr = FormatConfigValue(interp, tkwin, specPtr, widgRec);
+ Tcl_SetObjResult(interp, objPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeObjOptions --
+ *
+ * Free up all resources associated with configuration options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Any resource in widgRec that is controlled by a configuration
+ * option (e.g. a Tk_3DBorder or XColor) is freed in the appropriate
+ * fashion.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeObjOptions(specs, widgRec, display, needFlags)
+ Blt_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ Display *display; /* X display; needed for freeing some
+ * resources. */
+ int needFlags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ register Blt_ConfigSpec *specPtr;
+ char *ptr;
+
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if ((specPtr->specFlags & needFlags) != needFlags) {
+ continue;
+ }
+ ptr = widgRec + specPtr->offset;
+ switch (specPtr->type) {
+ case BLT_CONFIG_STRING:
+ if (*((char **) ptr) != NULL) {
+ Blt_Free(*((char **) ptr));
+ *((char **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_COLOR:
+ if (*((XColor **) ptr) != NULL) {
+ Tk_FreeColor(*((XColor **) ptr));
+ *((XColor **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_FONT:
+ Tk_FreeFont(*((Tk_Font *) ptr));
+ *((Tk_Font *) ptr) = NULL;
+ break;
+
+ case BLT_CONFIG_BITMAP:
+ if (*((Pixmap *) ptr) != None) {
+ Tk_FreeBitmap(display, *((Pixmap *) ptr));
+ *((Pixmap *) ptr) = None;
+ }
+ break;
+
+ case BLT_CONFIG_BORDER:
+ if (*((Tk_3DBorder *) ptr) != NULL) {
+ Tk_Free3DBorder(*((Tk_3DBorder *) ptr));
+ *((Tk_3DBorder *) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ if (*((Tk_Cursor *) ptr) != None) {
+ Tk_FreeCursor(display, *((Tk_Cursor *) ptr));
+ *((Tk_Cursor *) ptr) = None;
+ }
+ break;
+
+ case BLT_CONFIG_LISTOBJ:
+ Tcl_DecrRefCount(*(Tcl_Obj **)ptr);
+ break;
+
+ case BLT_CONFIG_LIST:
+ {
+ char **argv;
+
+ argv = *(char ***)ptr;
+ if (argv != NULL) {
+ Blt_Free(argv);
+ *(char ***)ptr = NULL;
+ }
+ }
+ break;
+
+ case BLT_CONFIG_TILE:
+ if ((Blt_Tile)ptr != NULL) {
+ Blt_FreeTile((Blt_Tile)ptr);
+ *(Blt_Tile *)ptr = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_CUSTOM:
+ if ((*(char **)ptr != NULL) &&
+ (specPtr->customPtr->freeProc != NULL)) {
+ (*specPtr->customPtr->freeProc)(specPtr->customPtr->clientData,
+ display, widgRec, specPtr->offset);
+ *(char **)ptr = NULL;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ObjConfigModified --
+ *
+ * Given the configuration specifications and one or more option
+ * patterns (terminated by a NULL), indicate if any of the matching
+ * configuration options has been reset.
+ *
+ * Results:
+ * Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ObjConfigModified TCL_VARARGS_DEF(Blt_ConfigSpec *, arg1)
+{
+ va_list argList;
+ Blt_ConfigSpec *specs;
+ register Blt_ConfigSpec *specPtr;
+ register char *option;
+
+ specs = TCL_VARARGS_START(Blt_ConfigSpec *, arg1, argList);
+ while ((option = va_arg(argList, char *)) != NULL) {
+ for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+ if ((Tcl_StringMatch(specPtr->switchName, option)) &&
+ (specPtr->specFlags & BLT_CONFIG_OPTION_SPECIFIED)) {
+ va_end(argList);
+ return 1;
+ }
+ }
+ }
+ va_end(argList);
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureComponentFromObj --
+ *
+ * Configures a component of a widget. This is useful for
+ * widgets that have multiple components which aren't uniquely
+ * identified by a Tk_Window. It allows us, for example, set
+ * resources for axes of the graph widget. The graph really has
+ * only one window, but its convenient to specify components in a
+ * hierarchy of options.
+ *
+ * *graph.x.logScale yes
+ * *graph.Axis.logScale yes
+ * *graph.temperature.scaleSymbols yes
+ * *graph.Element.scaleSymbols yes
+ *
+ * This is really a hack to work around the limitations of the Tk
+ * resource database. It creates a temporary window, needed to
+ * call Tk_ConfigureWidget, using the name of the component.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * A temporary window is created merely to pass to Tk_ConfigureWidget.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureComponentFromObj(interp, parent, name, className, specsPtr,
+ objc, objv, widgRec, flags)
+ Tcl_Interp *interp;
+ Tk_Window parent; /* Window to associate with component */
+ char name[]; /* Name of component */
+ char className[];
+ Blt_ConfigSpec *specsPtr;
+ int objc;
+ Tcl_Obj *CONST *objv;
+ char *widgRec;
+ int flags;
+{
+ Tk_Window tkwin;
+ int result;
+ char *tmpName;
+ int isTemporary = FALSE;
+
+ tmpName = Blt_Strdup(name);
+
+ /* Window name can't start with an upper case letter */
+ tmpName[0] = tolower(name[0]);
+
+ /*
+ * Create component if a child window by the component's name
+ * doesn't already exist.
+ */
+ tkwin = Blt_FindChild(parent, tmpName);
+ if (tkwin == NULL) {
+ tkwin = Tk_CreateWindow(interp, parent, tmpName, (char *)NULL);
+ isTemporary = TRUE;
+ }
+ if (tkwin == NULL) {
+ Tcl_AppendResult(interp, "can't find window in \"",
+ Tk_PathName(parent), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ assert(Tk_Depth(tkwin) == Tk_Depth(parent));
+ Blt_Free(tmpName);
+
+ Tk_SetClass(tkwin, className);
+ result = Blt_ConfigureWidgetFromObj(interp, tkwin, specsPtr, objc, objv,
+ widgRec, flags);
+ if (isTemporary) {
+ Tk_DestroyWindow(tkwin);
+ }
+ return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ObjIsOption --
+ *
+ * Indicates whether objPtr is a valid configuration option
+ * such as -background.
+ *
+ * Results:
+ * Returns 1 is a matching option is found and 0 otherwise.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ObjIsOption(specs, objPtr, flags)
+ Blt_ConfigSpec *specs; /* Describes legal options. */
+ Tcl_Obj *objPtr; /* Command-line option name. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. Also,
+ * may have BLT_CONFIG_ARGV_ONLY set. */
+{
+ register Blt_ConfigSpec *specPtr;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ specPtr = FindConfigSpec((Tcl_Interp *)NULL, specs, objPtr, needFlags, 0);
+ return (specPtr != NULL);
+}
+
+
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
diff --git a/blt/src/bltObjConfig.h b/blt/src/bltObjConfig.h
new file mode 100644
index 00000000000..bb027cf9172
--- /dev/null
+++ b/blt/src/bltObjConfig.h
@@ -0,0 +1,238 @@
+/*
+ * bltObjConfig.h --
+ *
+ * This file contains the Tcl_Obj based versions of the old
+ * Tk_ConfigureWidget procedures.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ */
+
+#ifndef BLT_OBJCONFIG_H
+#define BLT_OBJCONFIG_H
+
+/*
+ * This is a Tcl_Obj based replacement for the widget configuration
+ * functions in Tk.
+ *
+ * What not use the new Tk_Option interface?
+ *
+ * There were design changes in the new Tk_Option interface that
+ * make it unwieldy.
+ *
+ * o You have to dynamically allocate, store, and deallocate
+ * your option table.
+ * o The Tk_FreeConfigOptions routine requires a tkwin argument.
+ * Unfortunately, most widgets save the display pointer and
+ * deference their tkwin when the window is destroyed.
+ * o There's no TK_CONFIG_CUSTOM functionality. This means that
+ * save special options must be saved as strings by
+ * Tk_ConfigureWidget and processed later, thus losing the
+ * benefits of Tcl_Objs. It also make error handling
+ * problematic, since you don't pick up certain errors like
+ *
+ * .widget configure -myoption bad -myoption good
+ *
+ * You will never see the first "bad" value.
+ * o Especially compared to the former Tk_ConfigureWidget calls,
+ * the new interface is overly complex. If there was a big
+ * performance win, it might be worth the effort. But let's
+ * face it, this biggest wins are in processing custom options
+ * values with thousands of elements. Most common resources
+ * (font, color, etc) have string tokens anyways.
+ *
+ * On the other hand, the replacement functions in this file fell
+ * into place quite easily both from the aspect of API writer and
+ * user. The biggest benefit is that you don't need to change lots
+ * of working code just to get the benefits of Tcl_Objs.
+ *
+ */
+
+#define SIDE_TOP (0)
+#define SIDE_RIGHT (1)
+#define SIDE_LEFT (2)
+#define SIDE_BOTTOM (3)
+
+#ifndef Blt_Offset
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+#endif /* Blt_Offset */
+
+typedef int (Blt_OptionParseProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr, char *widgRec,
+ int offset));
+typedef Tcl_Obj *(Blt_OptionPrintProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *widgRec, int offset));
+typedef void (Blt_OptionFreeProc) _ANSI_ARGS_((ClientData clientData,
+ Display *display, char *widgRec, int offset));
+
+typedef struct Blt_CustomOption {
+ Blt_OptionParseProc *parseProc; /* Procedure to call to parse an
+ * option and store it in converted
+ * form. */
+ Blt_OptionPrintProc *printProc; /* Procedure to return a printable
+ * string describing an existing
+ * option. */
+ Blt_OptionFreeProc *freeProc; /* Procedure to free the value. */
+
+ ClientData clientData; /* Arbitrary one-word value used by
+ * option parser: passed to
+ * parseProc and printProc. */
+} Blt_CustomOption;
+
+/*
+ * Structure used to specify information for Tk_ConfigureWidget. Each
+ * structure gives complete information for one option, including
+ * how the option is specified on the command line, where it appears
+ * in the option database, etc.
+ */
+
+typedef struct {
+ int type; /* Type of option, such as BLT_CONFIG_COLOR;
+ * see definitions below. Last option in
+ * table must have type BLT_CONFIG_END. */
+ char *switchName; /* Switch used to specify option in argv.
+ * NULL means this spec is part of a group. */
+ char *dbName; /* Name for option in option database. */
+ char *dbClass; /* Class for option in database. */
+ char *defValue; /* Default value for option if not
+ * specified in command line or database. */
+ int offset; /* Where in widget record to store value;
+ * use Tk_Offset macro to generate values
+ * for this. */
+ int specFlags; /* Any combination of the values defined
+ * below; other bits are used internally
+ * by tkConfig.c. */
+ Blt_CustomOption *customPtr; /* If type is BLT_CONFIG_CUSTOM then this is
+ * a pointer to info about how to parse and
+ * print the option. Otherwise it is
+ * irrelevant. */
+} Blt_ConfigSpec;
+
+/*
+ * Type values for Blt_ConfigSpec structures. See the user
+ * documentation for details.
+ */
+
+typedef enum {
+ BLT_CONFIG_ACTIVE_CURSOR,
+ BLT_CONFIG_ANCHOR,
+ BLT_CONFIG_BITMAP,
+ BLT_CONFIG_BOOLEAN,
+ BLT_CONFIG_BORDER,
+ BLT_CONFIG_CAP_STYLE,
+ BLT_CONFIG_COLOR,
+ BLT_CONFIG_CURSOR,
+ BLT_CONFIG_CUSTOM,
+ BLT_CONFIG_DOUBLE,
+ BLT_CONFIG_FONT,
+ BLT_CONFIG_INT,
+ BLT_CONFIG_JOIN_STYLE,
+ BLT_CONFIG_JUSTIFY,
+ BLT_CONFIG_MM,
+ BLT_CONFIG_PIXELS,
+ BLT_CONFIG_RELIEF,
+ BLT_CONFIG_STRING,
+ BLT_CONFIG_SYNONYM,
+ BLT_CONFIG_UID,
+ BLT_CONFIG_WINDOW,
+
+ BLT_CONFIG_BITFLAG,
+ BLT_CONFIG_DASHES,
+ BLT_CONFIG_DISTANCE, /* */
+ BLT_CONFIG_FILL,
+ BLT_CONFIG_FLOAT,
+ BLT_CONFIG_LIST,
+ BLT_CONFIG_LISTOBJ,
+ BLT_CONFIG_PAD,
+ BLT_CONFIG_POS_DISTANCE, /* */
+ BLT_CONFIG_SHADOW, /* */
+ BLT_CONFIG_SIDE,
+ BLT_CONFIG_STATE,
+ BLT_CONFIG_TILE,
+
+ BLT_CONFIG_END
+} Blt_ConfigTypes;
+
+/*
+ * Possible values for flags argument to Tk_ConfigureWidget:
+ */
+
+#define BLT_CONFIG_OBJV_ONLY 1
+#define BLT_CONFIG_OBJS 0x80
+
+/*
+ * Possible flag values for Blt_ConfigSpec structures. Any bits at
+ * or above BLT_CONFIG_USER_BIT may be used by clients for selecting
+ * certain entries. Before changing any values here, coordinate with
+ * tkOldConfig.c (internal-use-only flags are defined there).
+ */
+
+#define BLT_CONFIG_NULL_OK 1
+#define BLT_CONFIG_COLOR_ONLY 2
+#define BLT_CONFIG_MONO_ONLY 4
+#define BLT_CONFIG_DONT_SET_DEFAULT 8
+#define BLT_CONFIG_OPTION_SPECIFIED 0x10
+#define BLT_CONFIG_USER_BIT 0x100
+
+/*
+ * Values for "flags" field of Blt_ConfigSpec structures. Be sure
+ * to coordinate these values with those defined in tk.h
+ * (BLT_CONFIG_COLOR_ONLY, etc.). There must not be overlap!
+ *
+ * INIT - Non-zero means (char *) things have been
+ * converted to Tk_Uid's.
+ */
+
+#define INIT 0x20
+
+EXTERN int Blt_ConfigureInfoFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_ConfigSpec *specs, char *widgRec,
+ Tcl_Obj *objPtr, int flags));
+EXTERN int Blt_ConfigureValueFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_ConfigSpec *specs, char * widgRec,
+ Tcl_Obj *objPtr, int flags));
+EXTERN int Blt_ConfigureWidgetFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Blt_ConfigSpec *specs, int objc, Tcl_Obj *CONST *objv,
+ char *widgRec, int flags));
+EXTERN int Blt_ConfigureComponentFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, char *name, char *className, Blt_ConfigSpec *specs,
+ int objc, Tcl_Obj *CONST *objv, char *widgRec, int flags));
+
+EXTERN int Blt_ObjConfigModified _ANSI_ARGS_(TCL_VARARGS(Blt_ConfigSpec *, specs));
+EXTERN void Blt_FreeObjOptions _ANSI_ARGS_((Blt_ConfigSpec *specs,
+ char *widgRec, Display *display, int needFlags));
+
+EXTERN int Blt_ObjIsOption _ANSI_ARGS_((Blt_ConfigSpec *specs, Tcl_Obj *objPtr,
+ int flags));
+
+EXTERN int Blt_GetPositionFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *indexPtr));
+
+EXTERN int Blt_GetPixelsFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr, int flags, int *valuePtr));
+
+EXTERN int Blt_GetPadFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr, Blt_Pad *padPtr));
+
+EXTERN int Blt_GetShadowFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr, Shadow *shadowPtr));
+
+EXTERN int Blt_GetStateFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *statePtr));
+
+EXTERN int Blt_GetFillFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *fillPtr));
+
+EXTERN int Blt_GetDashesFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Blt_Dashes *dashesPtr));
+
+
+#endif /* BLT_OBJCONFIG_H */
diff --git a/blt/src/bltParse.c b/blt/src/bltParse.c
new file mode 100644
index 00000000000..9bb89b39e39
--- /dev/null
+++ b/blt/src/bltParse.c
@@ -0,0 +1,541 @@
+/*
+ * tclParse.c --
+ *
+ * Contains a collection of procedures that are used to parse Tcl
+ * commands or parts of commands (like quoted strings or nested
+ * sub-commands).
+ *
+ * Since Tcl 8.1.0 these routines have been replaced by ones that
+ * generate byte-codes. But since these routines are used in
+ * vector expressions, where no such byte-compilation is
+ * necessary, I now include them. In fact, the byte-compiled
+ * versions would be slower since the compiled code typically
+ * runs only one time.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 19941998 Sun Microsystems, Inc.
+ *
+ */
+
+#include <bltInt.h>
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+#include "bltInterp.h"
+
+/*
+ * A table used to classify input characters to assist in parsing
+ * Tcl commands. The table should be indexed with a signed character
+ * using the CHAR_TYPE macro. The character may have a negative
+ * value. The CHAR_TYPE macro takes a pointer to a signed character
+ * and a pointer to the last character in the source string. If the
+ * src pointer is pointing at the terminating null of the string,
+ * CHAR_TYPE returns TCL_COMMAND_END.
+ */
+
+#define STATIC_STRING_SPACE 150
+#define UCHAR(c) ((unsigned char) (c))
+#define TCL_NORMAL 0x01
+#define TCL_SPACE 0x02
+#define TCL_COMMAND_END 0x04
+#define TCL_QUOTE 0x08
+#define TCL_OPEN_BRACKET 0x10
+#define TCL_OPEN_BRACE 0x20
+#define TCL_CLOSE_BRACE 0x40
+#define TCL_BACKSLASH 0x80
+#define TCL_DOLLAR 0x00
+
+/*
+ * The following table assigns a type to each character. Only types
+ * meaningful to Tcl parsing are represented here. The table is
+ * designed to be referenced with either signed or unsigned characters,
+ * so it has 384 entries. The first 128 entries correspond to negative
+ * character values, the next 256 correspond to positive character
+ * values. The last 128 entries are identical to the first 128. The
+ * table is always indexed with a 128-byte offset (the 128th entry
+ * corresponds to a 0 character value).
+ */
+
+static unsigned char tclTypeTable[] =
+{
+ /*
+ * Negative character values, from -128 to -1:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+ * Positive character values, from 0-127:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE,
+ TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL,
+ TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET,
+ TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE,
+ TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+ * Large unsigned character values, from 128-255:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+};
+
+#define CHAR_TYPE(src,last) \
+ (((src)==(last))?TCL_COMMAND_END:(tclTypeTable+128)[(int)*(src)])
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseNestedCmd --
+ *
+ * This procedure parses a nested Tcl command between
+ * brackets, returning the result of the command.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is
+ * TCL_OK unless there was an error while executing the
+ * nested command. If an error occurs then interp->result
+ * contains a standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one processed; this is usually the character just
+ * after the matching close-bracket, or the null character
+ * at the end of the string if the close-bracket was missing
+ * (a missing close bracket is an error). The result returned
+ * by the command is stored in standard fashion in *parsePtr,
+ * null-terminated, with parsePtr->next pointing to the null
+ * character.
+ *
+ * Side effects:
+ * The storage space at *parsePtr may be expanded.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ParseNestedCmd(interp, string, flags, termPtr, parsePtr)
+ Tcl_Interp *interp; /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ char *string; /* Character just after opening bracket. */
+ int flags; /* Flags to pass to nested Tcl_Eval. */
+ char **termPtr; /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr; /* Information about where to place
+ * result of command. */
+{
+ int result, length, shortfall;
+ Interp *iPtr = (Interp *) interp;
+
+ iPtr->evalFlags = flags | TCL_BRACKET_TERM;
+ result = Tcl_Eval(interp, string);
+ *termPtr = (string + iPtr->termOffset);
+ if (result != TCL_OK) {
+ /*
+ * The increment below results in slightly cleaner message in
+ * the errorInfo variable (the close-bracket will appear).
+ */
+
+ if (**termPtr == ']') {
+ *termPtr += 1;
+ }
+ return result;
+ }
+ (*termPtr) += 1;
+ length = strlen(iPtr->result);
+ shortfall = length + 1 - (parsePtr->end - parsePtr->next);
+ if (shortfall > 0) {
+ (*parsePtr->expandProc) (parsePtr, shortfall);
+ }
+ strcpy(parsePtr->next, iPtr->result);
+ parsePtr->next += length;
+
+ Tcl_FreeResult(interp);
+ iPtr->result = iPtr->resultSpace;
+ iPtr->resultSpace[0] = '\0';
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseBraces --
+ *
+ * This procedure scans the information between matching
+ * curly braces.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is
+ * TCL_OK unless there was an error while parsing string.
+ * If an error occurs then interp->result contains a
+ * standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one successfully processed; this is usually the
+ * character just after the matching close-brace. The
+ * information between curly braces is stored in standard
+ * fashion in *parsePtr, null-terminated with parsePtr->next
+ * pointing to the terminating null character.
+ *
+ * Side effects:
+ * The storage space at *parsePtr may be expanded.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Blt_ParseBraces(interp, string, termPtr, parsePtr)
+ Tcl_Interp *interp; /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ char *string; /* Character just after opening bracket. */
+ char **termPtr; /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr; /* Information about where to place
+ * result of command. */
+{
+ int level;
+ register char *src, *dest, *end;
+ register char c;
+ char *lastChar = string + strlen(string);
+
+ src = string;
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ level = 1;
+
+ /*
+ * Copy the characters one at a time to the result area, stopping
+ * when the matching close-brace is found.
+ */
+
+ for (;;) {
+ c = *src;
+ src++;
+
+ if (dest == end) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 20);
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ }
+ *dest = c;
+ dest++;
+
+ if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+ continue;
+ } else if (c == '{') {
+ level++;
+ } else if (c == '}') {
+ level--;
+ if (level == 0) {
+ dest--; /* Don't copy the last close brace. */
+ break;
+ }
+ } else if (c == '\\') {
+ int count;
+
+ /*
+ * Must always squish out backslash-newlines, even when in
+ * braces. This is needed so that this sequence can appear
+ * anywhere in a command, such as the middle of an expression.
+ */
+
+ if (*src == '\n') {
+ dest[-1] = Tcl_Backslash(src - 1, &count);
+ src += count - 1;
+ } else {
+ Tcl_Backslash(src - 1, &count);
+ while (count > 1) {
+ if (dest == end) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 20);
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ }
+ *dest = *src;
+ dest++;
+ src++;
+ count--;
+ }
+ }
+ } else if (c == '\0') {
+ Tcl_AppendResult(interp, "missing close-brace", (char *)NULL);
+ *termPtr = string - 1;
+ return TCL_ERROR;
+ }
+ }
+
+ *dest = '\0';
+ parsePtr->next = dest;
+ *termPtr = src;
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ExpandParseValue --
+ *
+ * This procedure is commonly used as the value of the
+ * expandProc in a ParseValue. It uses malloc to allocate
+ * more space for the result of a parse.
+ *
+ * Results:
+ * The buffer space in *parsePtr is reallocated to something
+ * larger, and if parsePtr->clientData is non-zero the old
+ * buffer is freed. Information is copied from the old
+ * buffer to the new one.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_ExpandParseValue(parsePtr, needed)
+ ParseValue *parsePtr; /* Information about buffer that
+ * must be expanded. If the clientData
+ * in the structure is non-zero, it
+ * means that the current buffer is
+ * dynamically allocated. */
+ int needed; /* Minimum amount of additional space
+ * to allocate. */
+{
+ int size;
+ char *buffer;
+
+ /*
+ * Either double the size of the buffer or add enough new space
+ * to meet the demand, whichever produces a larger new buffer.
+ */
+ size = (parsePtr->end - parsePtr->buffer) + 1;
+ if (size < needed) {
+ size += needed;
+ } else {
+ size += size;
+ }
+ buffer = Blt_Malloc((unsigned int)size);
+
+ /*
+ * Copy from old buffer to new, free old buffer if needed, and
+ * mark new buffer as malloc-ed.
+ */
+ memcpy((VOID *) buffer, (VOID *) parsePtr->buffer,
+ (size_t) (parsePtr->next - parsePtr->buffer));
+ parsePtr->next = buffer + (parsePtr->next - parsePtr->buffer);
+ if (parsePtr->clientData != 0) {
+ Blt_Free(parsePtr->buffer);
+ }
+ parsePtr->buffer = buffer;
+ parsePtr->end = buffer + size - 1;
+ parsePtr->clientData = (ClientData)1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseQuotes --
+ *
+ * This procedure parses a double-quoted string such as a
+ * quoted Tcl command argument or a quoted value in a Tcl
+ * expression. This procedure is also used to parse array
+ * element names within parentheses, or anything else that
+ * needs all the substitutions that happen in quotes.
+ *
+ * Results:
+ * The return value is a standard Tcl result, which is
+ * TCL_OK unless there was an error while parsing the
+ * quoted string. If an error occurs then interp->result
+ * contains a standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one successfully processed; this is usually the
+ * character just after the matching close-quote. The
+ * fully-substituted contents of the quotes are stored in
+ * standard fashion in *parsePtr, null-terminated with
+ * parsePtr->next pointing to the terminating null character.
+ *
+ * Side effects:
+ * The buffer space in parsePtr may be enlarged by calling its
+ * expandProc.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ParseQuotes(interp, string, termChar, flags, termPtr, parsePtr)
+ Tcl_Interp *interp; /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ char *string; /* Character just after opening double-
+ * quote. */
+ int termChar; /* Character that terminates "quoted" string
+ * (usually double-quote, but sometimes
+ * right-paren or something else). */
+ int flags; /* Flags to pass to nested Tcl_Eval calls. */
+ char **termPtr; /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr; /* Information about where to place
+ * fully-substituted result of parse. */
+{
+ register char *src, *dest, c;
+ char *lastChar = string + strlen(string);
+
+ src = string;
+ dest = parsePtr->next;
+
+ for (;;) {
+ if (dest == parsePtr->end) {
+ /*
+ * Target buffer space is about to run out. Make more space.
+ */
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 1);
+ dest = parsePtr->next;
+ }
+ c = *src;
+ src++;
+ if (c == termChar) {
+ *dest = '\0';
+ parsePtr->next = dest;
+ *termPtr = src;
+ return TCL_OK;
+ } else if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+ copy:
+ *dest = c;
+ dest++;
+ continue;
+ } else if (c == '$') {
+ int length;
+ char *value;
+
+ value = Tcl_ParseVar(interp, src - 1, termPtr);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ src = *termPtr;
+ length = strlen(value);
+ if ((parsePtr->end - dest) <= length) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, length);
+ dest = parsePtr->next;
+ }
+ strcpy(dest, value);
+ dest += length;
+ continue;
+ } else if (c == '[') {
+ int result;
+
+ parsePtr->next = dest;
+ result = Blt_ParseNestedCmd(interp, src, flags, termPtr, parsePtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ src = *termPtr;
+ dest = parsePtr->next;
+ continue;
+ } else if (c == '\\') {
+ int nRead;
+
+ src--;
+ *dest = Tcl_Backslash(src, &nRead);
+ dest++;
+ src += nRead;
+ continue;
+ } else if (c == '\0') {
+ char buf[30];
+
+ Tcl_ResetResult(interp);
+ sprintf(buf, "missing %c", termChar);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ *termPtr = string - 1;
+ return TCL_ERROR;
+ } else {
+ goto copy;
+ }
+ }
+}
+
+#endif /* TCL_VERSION_NUMBER >= _VERSION(8,1,0) */
diff --git a/blt/src/bltPool.c b/blt/src/bltPool.c
new file mode 100644
index 00000000000..fed2743ec47
--- /dev/null
+++ b/blt/src/bltPool.c
@@ -0,0 +1,458 @@
+/*
+ * bltPool.c --
+ *
+ * Copyright 2001 Silicon Metrics, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltPool.h"
+
+/*
+ * Blt_Pool --
+ *
+ * Implements a pool memory allocator.
+ *
+ * + It's faster allocating memory since malloc/free are called
+ * only a fraction of the normal times. Fixed size items can
+ * be reused without deallocating/reallocating memory.
+ * + You don't have the extra 8-16 byte overhead per malloc.
+ * - Memory is freed only when the entire pool is destroyed.
+ * - Memory is allocated in chunks. More memory is allocated
+ * than used.
+ * 0 Depending upon allocation/deallocation patterns, locality
+ * may be improved or degraded.
+ *
+ * The pool is a chain of malloc'ed blocks.
+ *
+ * +---------+ +---------+ +---------+
+ * NULL<-| nextPtr |<-| nextPtr |<-| nextPtr |<- headPtr
+ * |---------| |---------| |---------|
+ * | chunk1 | | chunk2 | | chunk3 |
+ * +---------+ | | | |
+ * +---------+ | |
+ * | |
+ * | |
+ * +---------+
+ *
+ * Each chunk contains an integral number of fixed size items.
+ * The number of items doubles until a maximum size is reached
+ * (each subsequent new chunk will be the maximum). Chunks
+ * are allocated only when needed (no more space is available
+ * in the last chunk).
+ *
+ * The chain of blocks is only freed when the entire pool is
+ * destroyed.
+ *
+ * A freelist of unused items also maintained. Each freed item
+ * is prepended to a free list. Before allocating new chunks
+ * the freelist is examined to see if an unused items exist.
+ *
+ * chunk1 chunk2 chunk3
+ * +---------+ +---------+ +---------+
+ * NULL<-| unused | | | | |
+ * +----^----+ +---------+ +---------+
+ * | unused |<-| unused |<-| unused |
+ * +---------+ +---------+ +----^----+
+ * | | | | | unused |
+ * +---------+ | | +----^----+
+ * | | | | |
+ * +---------+ +----|----+
+ * | usused |<- freePtr
+ * +---------+
+ */
+
+#define POOL_MAX_CHUNK_SIZE ((1<<16) - sizeof(Blt_PoolChain))
+
+#ifndef ALIGN
+#define ALIGN(a) \
+ (((size_t)a + (sizeof(void *) - 1)) & (~(sizeof(void *) - 1)))
+#endif /* ALIGN */
+
+static Blt_PoolAllocProc VariablePoolAllocItem;
+static Blt_PoolFreeProc VariablePoolFreeItem;
+static Blt_PoolAllocProc FixedPoolAllocItem;
+static Blt_PoolFreeProc FixedPoolFreeItem;
+static Blt_PoolAllocProc StringPoolAllocItem;
+static Blt_PoolFreeProc StringPoolFreeItem;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VariablePoolAllocItem --
+ *
+ * Returns a new item. First check if there is any more space
+ * left in the current chunk. If there isn't then next check
+ * the free list for unused items. Finally allocate a new
+ * chunk and return its first item.
+ *
+ * Results:
+ * Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ * A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+VariablePoolAllocItem(poolPtr, size)
+ struct Blt_PoolStruct *poolPtr;
+ size_t size; /* Number of bytes to allocate. */
+{
+ Blt_PoolChain *chainPtr;
+ void *memPtr;
+
+ size = ALIGN(size);
+ if (size >= POOL_MAX_CHUNK_SIZE) {
+ /*
+ * Handle oversized requests by allocating a chunk to hold the
+ * single item and immediately placing it into the in-use list.
+ */
+ chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + size);
+ if (poolPtr->headPtr == NULL) {
+ poolPtr->headPtr = chainPtr;
+ } else {
+ chainPtr->nextPtr = poolPtr->headPtr->nextPtr;
+ poolPtr->headPtr->nextPtr = chainPtr;
+ }
+ memPtr = (void *)chainPtr;
+ } else {
+ if (poolPtr->bytesLeft >= size) {
+ poolPtr->bytesLeft -= size;
+ memPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+ } else {
+ poolPtr->waste += poolPtr->bytesLeft;
+ /* Create a new block of items and prepend it to the in-use list */
+ poolPtr->bytesLeft = POOL_MAX_CHUNK_SIZE;
+ /* Allocate the requested chunk size, plus the header */
+ chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+ chainPtr->nextPtr = poolPtr->headPtr;
+ poolPtr->headPtr = chainPtr;
+ /* Peel off a new item. */
+ poolPtr->bytesLeft -= size;
+ memPtr = (char *)(chainPtr + 1) + poolPtr->bytesLeft;
+ }
+ }
+ return memPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VariablePoolFreeItem --
+ *
+ * Placeholder for freeProc routine. The pool memory is
+ * not reclaimed or freed until the entire pool is released.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+VariablePoolFreeItem(poolPtr, item)
+ struct Blt_PoolStruct *poolPtr;
+ void *item;
+{
+ /* Does nothing */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringPoolAllocItem --
+ *
+ * Returns a new item. First check if there is any more space
+ * left in the current chunk. If there isn't then next check
+ * the free list for unused items. Finally allocate a new
+ * chunk and return its first item.
+ *
+ * Results:
+ * Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ * A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+StringPoolAllocItem(poolPtr, size)
+ struct Blt_PoolStruct *poolPtr;
+ size_t size; /* Number of bytes to allocate. */
+{
+ Blt_PoolChain *chainPtr;
+ void *memPtr;
+
+ if (size >= POOL_MAX_CHUNK_SIZE) {
+ /*
+ * Handle oversized requests by allocating a chunk to hold the
+ * single item and immediately placing it into the in-use list.
+ */
+ chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + size);
+ if (poolPtr->headPtr == NULL) {
+ poolPtr->headPtr = chainPtr;
+ } else {
+ chainPtr->nextPtr = poolPtr->headPtr->nextPtr;
+ poolPtr->headPtr->nextPtr = chainPtr;
+ }
+ memPtr = (void *)chainPtr;
+ } else {
+ if (poolPtr->bytesLeft >= size) {
+ poolPtr->bytesLeft -= size;
+ memPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+ } else {
+ poolPtr->waste += poolPtr->bytesLeft;
+ /* Create a new block of items and prepend it to the
+ * in-use list */
+ poolPtr->bytesLeft = POOL_MAX_CHUNK_SIZE;
+ /* Allocate the requested chunk size, plus the header */
+ chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+ chainPtr->nextPtr = poolPtr->headPtr;
+ poolPtr->headPtr = chainPtr;
+ /* Peel off a new item. */
+ poolPtr->bytesLeft -= size;
+ memPtr = (char *)(chainPtr + 1) + poolPtr->bytesLeft;
+ }
+ }
+ return memPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringPoolFreeItem --
+ *
+ * Placeholder for freeProc routine. String pool memory is
+ * not reclaimed or freed until the entire pool is released.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+StringPoolFreeItem(poolPtr, item)
+ struct Blt_PoolStruct *poolPtr;
+ void *item;
+{
+ /* Does nothing */
+}
+
+/*
+ * The fixed size pool is a chain of malloc'ed blocks.
+ *
+ * +---------+ +---------+ +---------+
+ * NULL<-| nextPtr |<-| nextPtr |<-| nextPtr |<- headPtr
+ * |---------| |---------| |---------|
+ * | chunk1 | | chunk2 | | chunk3 |
+ * +---------+ | | | |
+ * +---------+ | |
+ * | |
+ * | |
+ * +---------+
+ *
+ * Each chunk contains an integral number of fixed size items.
+ * The number of items doubles until a maximum size is reached
+ * (each subsequent new chunk will be the maximum). Chunks
+ * are allocated only when needed (no more space is available
+ * in the last chunk).
+ *
+ * The chain of blocks is only freed when the entire pool is
+ * destroyed.
+ *
+ * A freelist of unused items also maintained. Each freed item
+ * is prepended to a free list. Before allocating new chunks
+ * the freelist is examined to see if an unused items exist.
+ *
+ * chunk1 chunk2 chunk3
+ * +---------+ +---------+ +---------+
+ * NULL<-| unused | | | | |
+ * +----^----+ +---------+ +---------+
+ * | unused |<-| unused |<-| unused |
+ * +---------+ +---------+ +----^----+
+ * | | | | | unused |
+ * +---------+ | | +----^----+
+ * | | | | |
+ * +---------+ +----|----+
+ * | usused |<- freePtr
+ * +---------+
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FixedPoolFreeItem --
+ *
+ * Returns a new item. First check if there is any more space
+ * left in the current chunk. If there isn't then next check
+ * the free list for unused items. Finally allocate a new
+ * chunk and return its first item.
+ *
+ * Results:
+ * Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ * A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+FixedPoolAllocItem(poolPtr, size)
+ struct Blt_PoolStruct *poolPtr;
+ size_t size;
+{
+ Blt_PoolChain *chainPtr;
+ void *newPtr;
+
+ size = ALIGN(size);
+ if (poolPtr->itemSize == 0) {
+ poolPtr->itemSize = size;
+ }
+ assert(size == poolPtr->itemSize);
+
+ if (poolPtr->bytesLeft > 0) {
+ poolPtr->bytesLeft -= poolPtr->itemSize;
+ newPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+ } else if (poolPtr->freePtr != NULL) { /* Reuse from the free list. */
+ /* Reuse items on the free list */
+ chainPtr = poolPtr->freePtr;
+ poolPtr->freePtr = chainPtr->nextPtr;
+ newPtr = (void *)chainPtr;
+ } else { /* Allocate another block. */
+
+ /* Create a new block of items and prepend it to the in-use list */
+ poolPtr->bytesLeft = poolPtr->itemSize * (1 << poolPtr->poolSize);
+ if (poolPtr->bytesLeft < POOL_MAX_CHUNK_SIZE) {
+ poolPtr->poolSize++; /* Keep doubling the size of the new
+ * chunk up to a maximum size. */
+ }
+ /* Allocate the requested chunk size, plus the header */
+ chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+ chainPtr->nextPtr = poolPtr->headPtr;
+ poolPtr->headPtr = chainPtr;
+
+ /* Peel off a new item. */
+ poolPtr->bytesLeft -= poolPtr->itemSize;
+ newPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+ }
+ return newPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FixedPoolFreeItem --
+ *
+ * Frees an item. The actual memory is not freed. The item
+ * instead is prepended to a freelist where it may be reclaimed
+ * and used again.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Item is placed on the pool's free list.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FixedPoolFreeItem(poolPtr, item)
+ struct Blt_PoolStruct *poolPtr;
+ void *item;
+{
+ Blt_PoolChain *chainPtr = (Blt_PoolChain *)item;
+
+ /* Prepend the newly deallocated item to the free list. */
+ chainPtr->nextPtr = poolPtr->freePtr;
+ poolPtr->freePtr = chainPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PoolCreate --
+ *
+ * Creates a new memory pool for fixed-size/variable-size/string
+ * items.
+ *
+ * Results:
+ * Returns a pointer to the newly allocated pool.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Blt_Pool
+Blt_PoolCreate(type)
+ int type;
+{
+ struct Blt_PoolStruct *poolPtr;
+
+ poolPtr = Blt_Malloc(sizeof(struct Blt_PoolStruct));
+ switch (type) {
+ case BLT_VARIABLE_SIZE_ITEMS:
+ poolPtr->allocProc = VariablePoolAllocItem;
+ poolPtr->freeProc = VariablePoolFreeItem;
+ break;
+ case BLT_FIXED_SIZE_ITEMS:
+ poolPtr->allocProc = FixedPoolAllocItem;
+ poolPtr->freeProc = FixedPoolFreeItem;
+ break;
+ case BLT_STRING_ITEMS:
+ poolPtr->allocProc = StringPoolAllocItem;
+ poolPtr->freeProc = StringPoolFreeItem;
+ break;
+ }
+ poolPtr->headPtr = poolPtr->freePtr = NULL;
+ poolPtr->waste = poolPtr->bytesLeft = 0;
+ poolPtr->poolSize = poolPtr->itemSize = 0;
+ return poolPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PoolDestroy --
+ *
+ * Destroys the given memory pool. The chain of allocated blocks
+ * are freed. The is the only time that memory is actually freed.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * All memory used by the pool is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_PoolDestroy(poolPtr)
+ struct Blt_PoolStruct *poolPtr;
+{
+ register Blt_PoolChain *chainPtr, *nextPtr;
+
+ for (chainPtr = poolPtr->headPtr; chainPtr != NULL; chainPtr = nextPtr) {
+ nextPtr = chainPtr->nextPtr;
+ Blt_Free(chainPtr);
+ }
+ Blt_Free(poolPtr);
+}
+
diff --git a/blt/src/bltPool.h b/blt/src/bltPool.h
new file mode 100644
index 00000000000..2c36550cde5
--- /dev/null
+++ b/blt/src/bltPool.h
@@ -0,0 +1,36 @@
+#ifndef BLT_POOL_H
+#define BLT_POOL_H
+
+typedef struct Blt_PoolChainStruct {
+ struct Blt_PoolChainStruct *nextPtr;
+} Blt_PoolChain;
+
+#define BLT_STRING_ITEMS 0
+#define BLT_FIXED_SIZE_ITEMS 1
+#define BLT_VARIABLE_SIZE_ITEMS 2
+
+typedef struct Blt_PoolStruct *Blt_Pool;
+
+typedef void *(Blt_PoolAllocProc) _ANSI_ARGS_((Blt_Pool pool, size_t size));
+typedef void (Blt_PoolFreeProc) _ANSI_ARGS_((Blt_Pool pool, void *item));
+
+struct Blt_PoolStruct {
+ Blt_PoolChain *headPtr; /* Chain of malloc'ed chunks. */
+ Blt_PoolChain *freePtr; /* List of deleted items. This is only used
+ * for fixed size items. */
+ size_t poolSize; /* Log2 of # of items in the current block. */
+ size_t itemSize; /* Size of an item. */
+ size_t bytesLeft; /* # of bytes left in the current chunk. */
+ size_t waste;
+
+ Blt_PoolAllocProc *allocProc;
+ Blt_PoolFreeProc *freeProc;
+};
+
+EXTERN Blt_Pool Blt_PoolCreate _ANSI_ARGS_((int type));
+EXTERN void Blt_PoolDestroy _ANSI_ARGS_((Blt_Pool pool));
+
+#define Blt_PoolAllocItem(poolPtr, n) (*((poolPtr)->allocProc))(poolPtr, n)
+#define Blt_PoolFreeItem(poolPtr, item) (*((poolPtr)->freeProc))(poolPtr, item)
+
+#endif /* BLT_POOL_H */
diff --git a/blt/src/bltPs.c b/blt/src/bltPs.c
new file mode 100644
index 00000000000..1ddb0356bde
--- /dev/null
+++ b/blt/src/bltPs.c
@@ -0,0 +1,1491 @@
+
+/*
+ * bltPs.c --
+ *
+ * This module implements general PostScript conversion routines.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltPs.h"
+
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define PS_MAXPATH 1500 /* Maximum number of components in a PostScript
+ * (level 1) path. */
+
+PsToken
+Blt_GetPsToken(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+{
+ struct PsTokenStruct *tokenPtr;
+
+ tokenPtr = Blt_Malloc(sizeof(struct PsTokenStruct));
+ assert(tokenPtr);
+
+ tokenPtr->fontVarName = tokenPtr->colorVarName = NULL;
+ tokenPtr->interp = interp;
+ tokenPtr->tkwin = tkwin;
+ tokenPtr->colorMode = PS_MODE_COLOR;
+ Tcl_DStringInit(&(tokenPtr->dString));
+ return tokenPtr;
+}
+
+void
+Blt_ReleasePsToken(tokenPtr)
+ struct PsTokenStruct *tokenPtr;
+{
+ Tcl_DStringFree(&(tokenPtr->dString));
+ Blt_Free(tokenPtr);
+}
+
+char *
+Blt_PostScriptFromToken(tokenPtr)
+ struct PsTokenStruct *tokenPtr;
+{
+ return Tcl_DStringValue(&(tokenPtr->dString));
+}
+
+char *
+Blt_ScratchBufferFromToken(tokenPtr)
+ struct PsTokenStruct *tokenPtr;
+{
+ return tokenPtr->scratchArr;
+}
+
+void
+Blt_AppendToPostScript
+TCL_VARARGS_DEF(PsToken, arg1)
+{
+ va_list argList;
+ struct PsTokenStruct *tokenPtr;
+ char *string;
+
+ tokenPtr = TCL_VARARGS_START(struct PsTokenStruct, arg1, argList);
+ for (;;) {
+ string = va_arg(argList, char *);
+ if (string == NULL) {
+ break;
+ }
+ Tcl_DStringAppend(&(tokenPtr->dString), string, -1);
+ }
+}
+
+void
+Blt_FormatToPostScript
+TCL_VARARGS_DEF(PsToken, arg1)
+{
+ va_list argList;
+ struct PsTokenStruct *tokenPtr;
+ char *fmt;
+
+ tokenPtr = TCL_VARARGS_START(struct PsTokenStruct, arg1, argList);
+ fmt = va_arg(argList, char *);
+ vsprintf(tokenPtr->scratchArr, fmt, argList);
+ va_end(argList);
+ Tcl_DStringAppend(&(tokenPtr->dString), tokenPtr->scratchArr, -1);
+}
+
+int
+Blt_FileToPostScript(tokenPtr, fileName)
+ struct PsTokenStruct *tokenPtr;
+ char *fileName;
+{
+ Tcl_Channel channel;
+ Tcl_DString dString;
+ Tcl_Interp *interp;
+ char *buf;
+ char *libDir;
+ int nBytes;
+
+ interp = tokenPtr->interp;
+ buf = tokenPtr->scratchArr;
+
+ /*
+ * Read in a standard prolog file from file and append it to the
+ * PostScript output stored in the Tcl_DString in tokenPtr.
+ */
+
+ libDir = Tcl_GetVar(interp, "blt_library", TCL_GLOBAL_ONLY);
+ if (libDir == NULL) {
+ Tcl_AppendResult(interp, "couldn't find BLT script library:",
+ "global variable \"blt_library\" doesn't exist", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, libDir, -1);
+ Tcl_DStringAppend(&dString, "/", -1);
+ Tcl_DStringAppend(&dString, fileName, -1);
+ fileName = Tcl_DStringValue(&dString);
+ Blt_AppendToPostScript(tokenPtr, "\n% including file \"", fileName,
+ "\"\n\n", (char *)NULL);
+
+ channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+ if (channel == NULL) {
+ Tcl_AppendResult(interp, "couldn't open prologue file \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ for(;;) {
+ nBytes = Tcl_Read(channel, buf, PSTOKEN_BUFSIZ);
+ if (nBytes < 0) {
+ Tcl_AppendResult(interp, "error reading prologue file \"",
+ fileName, "\": ", Tcl_PosixError(interp),
+ (char *)NULL);
+ Tcl_Close(interp, channel);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ if (nBytes == 0) {
+ break;
+ }
+ buf[nBytes] = '\0';
+ Blt_AppendToPostScript(tokenPtr, buf, (char *)NULL);
+ }
+ Tcl_DStringFree(&dString);
+ Tcl_Close(interp, channel);
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * XColorToPostScript --
+ *
+ * Convert the a XColor (from its RGB values) to a PostScript
+ * command. If a Tk color map variable exists, it will be
+ * consulted for a PostScript translation based upon the color
+ * name.
+ *
+ * Maps an X color intensity (0 to 2^16-1) to a floating point
+ * value [0..1]. Many versions of Tk don't properly handle the
+ * the lower 8 bits of the color intensity, so we can only
+ * consider the upper 8 bits.
+ *
+ * Results:
+ * The string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+XColorToPostScript(tokenPtr, colorPtr)
+ struct PsTokenStruct *tokenPtr;
+ XColor *colorPtr; /* Color value to be converted */
+{
+ /*
+ * Shift off the lower byte before dividing because some versions
+ * of Tk don't fill the lower byte correctly.
+ */
+ Blt_FormatToPostScript(tokenPtr, "%g %g %g",
+ ((double)(colorPtr->red >> 8) / 255.0),
+ ((double)(colorPtr->green >> 8) / 255.0),
+ ((double)(colorPtr->blue >> 8) / 255.0));
+}
+
+void
+Blt_BackgroundToPostScript(tokenPtr, colorPtr)
+ struct PsTokenStruct *tokenPtr;
+ XColor *colorPtr;
+{
+ /* If the color name exists in Tcl array variable, use that translation */
+ if (tokenPtr->colorVarName != NULL) {
+ char *psColor;
+
+ psColor = Tcl_GetVar2(tokenPtr->interp, tokenPtr->colorVarName,
+ Tk_NameOfColor(colorPtr), 0);
+ if (psColor != NULL) {
+ Blt_AppendToPostScript(tokenPtr, " ", psColor, "\n", (char *)NULL);
+ return;
+ }
+ }
+ XColorToPostScript(tokenPtr, colorPtr);
+ Blt_AppendToPostScript(tokenPtr, " SetBgColor\n", (char *)NULL);
+}
+
+void
+Blt_ForegroundToPostScript(tokenPtr, colorPtr)
+ struct PsTokenStruct *tokenPtr;
+ XColor *colorPtr;
+{
+ /* If the color name exists in Tcl array variable, use that translation */
+ if (tokenPtr->colorVarName != NULL) {
+ char *psColor;
+
+ psColor = Tcl_GetVar2(tokenPtr->interp, tokenPtr->colorVarName,
+ Tk_NameOfColor(colorPtr), 0);
+ if (psColor != NULL) {
+ Blt_AppendToPostScript(tokenPtr, " ", psColor, "\n", (char *)NULL);
+ return;
+ }
+ }
+ XColorToPostScript(tokenPtr, colorPtr);
+ Blt_AppendToPostScript(tokenPtr, " SetFgColor\n", (char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReverseBits --
+ *
+ * Convert a byte from a X image into PostScript image order.
+ * This requires not only the nybbles to be reversed but also
+ * their bit values.
+ *
+ * Results:
+ * The converted byte is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+INLINE static unsigned char
+ReverseBits(byte)
+ register unsigned char byte;
+{
+ byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
+ byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
+ byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
+ return byte;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ByteToHex --
+ *
+ * Convert a byte to its ASCII hexidecimal equivalent.
+ *
+ * Results:
+ * The converted 2 ASCII character string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+INLINE static void
+ByteToHex(byte, string)
+ register unsigned char byte;
+ char *string;
+{
+ static char hexDigits[] = "0123456789ABCDEF";
+
+ string[0] = hexDigits[byte >> 4];
+ string[1] = hexDigits[byte & 0x0F];
+}
+
+#ifdef WIN32
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_BitmapDataToPostScript --
+ *
+ * Output a PostScript image string of the given bitmap image.
+ * It is assumed the image is one bit deep and a zero value
+ * indicates an off-pixel. To convert to PostScript, the bits
+ * need to be reversed from the X11 image order.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript image string is appended.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_BitmapDataToPostScript(
+ struct PsTokenStruct *tokenPtr,
+ Display *display,
+ Pixmap bitmap,
+ int width, int height)
+{
+ register unsigned char byte = 0;
+ register int x, y, bitPos;
+ unsigned long pixel;
+ int byteCount;
+ char string[10];
+ unsigned char *srcBits, *srcPtr;
+ int bytesPerRow;
+
+ srcBits = Blt_GetBitmapData(display, bitmap, width, height, &bytesPerRow);
+ if (srcBits == NULL) {
+ OutputDebugString("Can't get bitmap data");
+ return;
+ }
+ Blt_AppendToPostScript(tokenPtr, "\t<", (char *)NULL);
+ byteCount = bitPos = 0; /* Suppress compiler warning */
+ for (y = height - 1; y >= 0; y--) {
+ srcPtr = srcBits + (bytesPerRow * y);
+ byte = 0;
+ for (x = 0; x < width; x++) {
+ bitPos = x % 8;
+ pixel = (*srcPtr & (0x80 >> bitPos));
+ if (pixel) {
+ byte |= (unsigned char)(1 << bitPos);
+ }
+ if (bitPos == 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ byteCount++;
+ srcPtr++;
+ byte = 0;
+ if (byteCount >= 30) {
+ string[2] = '\n';
+ string[3] = '\t';
+ string[4] = '\0';
+ byteCount = 0;
+ }
+ Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+ }
+ } /* x */
+ if (bitPos != 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+ byteCount++;
+ }
+ } /* y */
+ Blt_Free(srcBits);
+ Blt_AppendToPostScript(tokenPtr, ">\n", (char *)NULL);
+}
+
+#else
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_BitmapDataToPostScript --
+ *
+ * Output a PostScript image string of the given bitmap image.
+ * It is assumed the image is one bit deep and a zero value
+ * indicates an off-pixel. To convert to PostScript, the bits
+ * need to be reversed from the X11 image order.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript image string is appended to interp->result.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height)
+ struct PsTokenStruct *tokenPtr;
+ Display *display;
+ Pixmap bitmap;
+ int width, height;
+{
+ register unsigned char byte = 0;
+ register int x, y, bitPos;
+ unsigned long pixel;
+ XImage *imagePtr;
+ int byteCount;
+ char string[10];
+
+ imagePtr = XGetImage(display, bitmap, 0, 0, width, height, 1, ZPixmap);
+ Blt_AppendToPostScript(tokenPtr, "\t<", (char *)NULL);
+ byteCount = bitPos = 0; /* Suppress compiler warning */
+ for (y = 0; y < height; y++) {
+ byte = 0;
+ for (x = 0; x < width; x++) {
+ pixel = XGetPixel(imagePtr, x, y);
+ bitPos = x % 8;
+ byte |= (unsigned char)(pixel << bitPos);
+ if (bitPos == 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ byteCount++;
+ byte = 0;
+ if (byteCount >= 30) {
+ string[2] = '\n';
+ string[3] = '\t';
+ string[4] = '\0';
+ byteCount = 0;
+ }
+ Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+ }
+ } /* x */
+ if (bitPos != 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+ byteCount++;
+ }
+ } /* y */
+ Blt_AppendToPostScript(tokenPtr, ">\n", (char *)NULL);
+ XDestroyImage(imagePtr);
+}
+
+#endif /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPsData --
+ *
+ * Converts a color image to PostScript RGB (3 components)
+ * or Greyscale (1 component) output. With 3 components, we
+ * assume the "colorimage" operator is available.
+ *
+ * Note that the image converted from bottom to top, to conform
+ * to the PostScript coordinate system.
+ *
+ * Results:
+ * The PostScript data comprising the color image is written
+ * into the dynamic string.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ColorimageToPsData(image, nComponents, resultPtr, prefix)
+ Blt_Colorimage image;
+ int nComponents;
+ Tcl_DString *resultPtr;
+ char *prefix;
+{
+ char string[10];
+ register int count;
+ register int x, y;
+ register Pix32 *pixelPtr;
+ unsigned char byte;
+ int width, height;
+ int offset;
+ int nLines;
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+
+ nLines = 0;
+ count = 0;
+ offset = (height - 1) * width;
+ if (nComponents == 3) {
+ for (y = (height - 1); y >= 0; y--) {
+ pixelPtr = Blt_ColorimageBits(image) + offset;
+ for (x = 0; x < width; x++, pixelPtr++) {
+ if (count == 0) {
+ Tcl_DStringAppend(resultPtr, prefix, -1);
+ Tcl_DStringAppend(resultPtr, " ", -1);
+ }
+ count += 6;
+ ByteToHex(pixelPtr->Red, string);
+ ByteToHex(pixelPtr->Green, string + 2);
+ ByteToHex(pixelPtr->Blue, string + 4);
+ string[6] = '\0';
+ if (count >= 60) {
+ string[6] = '\n';
+ string[7] = '\0';
+ count = 0;
+ nLines++;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ offset -= width;
+ }
+ } else if (nComponents == 1) {
+ for (y = (height - 1); y >= 0; y--) {
+ pixelPtr = Blt_ColorimageBits(image) + offset;
+ for (x = 0; x < width; x++, pixelPtr++) {
+ if (count == 0) {
+ Tcl_DStringAppend(resultPtr, prefix, -1);
+ Tcl_DStringAppend(resultPtr, " ", -1);
+ }
+ count += 2;
+ byte = ~(pixelPtr->Red);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ if (count >= 60) {
+ string[2] = '\n';
+ string[3] = '\0';
+ count = 0;
+ nLines++;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ offset -= width;
+ }
+ }
+ if (count != 0) {
+ Tcl_DStringAppend(resultPtr, "\n", -1);
+ nLines++;
+ }
+ return nLines;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfAtom --
+ *
+ * Wrapper routine for Tk_GetAtomName. Returns NULL instead of
+ * "?bad atom?" if the atom can't be found.
+ *
+ * Results:
+ * The name of the atom is returned if found. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfAtom(tkwin, atom)
+ Tk_Window tkwin;
+ Atom atom;
+{
+ char *result;
+
+ result = Tk_GetAtomName(tkwin, atom);
+ if ((result[0] == '?') && (strcmp(result, "?bad atom?") == 0)) {
+ return NULL;
+ }
+ return result;
+}
+
+
+typedef struct {
+ char *alias;
+ char *fontName;
+} FontMap;
+
+static FontMap psFontMap[] =
+{
+ {"Arial", "Helvetica",},
+ {"AvantGarde", "AvantGarde",},
+ {"Courier New", "Courier",},
+ {"Courier", "Courier",},
+ {"Geneva", "Helvetica",},
+ {"Helvetica", "Helvetica",},
+ {"Monaco", "Courier",},
+ {"New Century Schoolbook", "NewCenturySchlbk",},
+ {"New York", "Times",},
+ {"Palatino", "Palatino",},
+ {"Symbol", "Symbol",},
+ {"Times New Roman", "Times",},
+ {"Times Roman", "Times",},
+ {"Times", "Times",},
+ {"Utopia", "Utopia",},
+ {"ZapfChancery", "ZapfChancery",},
+ {"ZapfDingbats", "ZapfDingbats",},
+};
+
+static int nFontNames = (sizeof(psFontMap) / sizeof(FontMap));
+
+#ifndef WIN32
+/*
+ * -----------------------------------------------------------------
+ *
+ * XFontStructToPostScript --
+ *
+ * Map X11 font to a PostScript font. Currently, only fonts whose
+ * FOUNDRY property are "Adobe" are converted. Simply gets the
+ * XA_FULL_NAME and XA_FAMILY properties and pieces together a
+ * PostScript fontname.
+ *
+ * Results:
+ * Returns the mapped PostScript font name if one is possible.
+ * Otherwise returns NULL.
+ *
+ * -----------------------------------------------------------------
+ */
+static char *
+XFontStructToPostScript(tkwin, fontPtr)
+ Tk_Window tkwin; /* Window to query for atoms */
+ XFontStruct *fontPtr; /* Font structure to map to name */
+{
+ Atom atom;
+ char *fullName, *family, *foundry;
+ register char *src, *dest;
+ int familyLen;
+ char *start;
+ static char string[200]; /* What size? */
+
+ if (XGetFontProperty(fontPtr, XA_FULL_NAME, &atom) == False) {
+ return NULL;
+ }
+ fullName = NameOfAtom(tkwin, atom);
+ if (fullName == NULL) {
+ return NULL;
+ }
+ family = foundry = NULL;
+ if (XGetFontProperty(fontPtr, Tk_InternAtom(tkwin, "FOUNDRY"), &atom)) {
+ foundry = NameOfAtom(tkwin, atom);
+ }
+ if (XGetFontProperty(fontPtr, XA_FAMILY_NAME, &atom)) {
+ family = NameOfAtom(tkwin, atom);
+ }
+ /*
+ * Try to map the font only if the foundry is Adobe
+ */
+ if ((foundry == NULL) || (family == NULL)) {
+ return NULL;
+ }
+ src = NULL;
+ familyLen = strlen(family);
+ if (strncasecmp(fullName, family, familyLen) == 0) {
+ src = fullName + familyLen;
+ }
+ if (strcmp(foundry, "Adobe") != 0) {
+ register int i;
+
+ if (strncasecmp(family, "itc ", 4) == 0) {
+ family += 4; /* Throw out the "itc" prefix */
+ }
+ for (i = 0; i < nFontNames; i++) {
+ if (strcasecmp(family, psFontMap[i].alias) == 0) {
+ family = psFontMap[i].fontName;
+ }
+ }
+ if (i == nFontNames) {
+ family = "Helvetica"; /* Default to a known font */
+ }
+ }
+ /*
+ * PostScript font name is in the form <family>-<type face>
+ */
+ sprintf(string, "%s-", family);
+ dest = start = string + strlen(string);
+
+ /*
+ * Append the type face (part of the full name trailing the family name)
+ * to the the PostScript font name, removing any spaces or dashes
+ *
+ * ex. " Bold Italic" ==> "BoldItalic"
+ */
+ if (src != NULL) {
+ while (*src != '\0') {
+ if ((*src != ' ') && (*src != '-')) {
+ *dest++ = *src;
+ }
+ src++;
+ }
+ }
+ if (dest == start) {
+ --dest; /* Remove '-' to leave just the family name */
+ }
+ *dest = '\0'; /* Make a valid string */
+ return string;
+}
+
+#endif /* !WIN32 */
+
+
+/*
+ * -------------------------------------------------------------------
+ * Routines to convert X drawing functions to PostScript commands.
+ * -------------------------------------------------------------------
+ */
+void
+Blt_ClearBackgroundToPostScript(tokenPtr)
+ struct PsTokenStruct *tokenPtr;
+{
+ Blt_AppendToPostScript(tokenPtr,
+ " 1.0 1.0 1.0 SetBgColor\n",
+ (char *)NULL);
+}
+
+void
+Blt_CapStyleToPostScript(tokenPtr, capStyle)
+ struct PsTokenStruct *tokenPtr;
+ int capStyle;
+{
+ /*
+ * X11:not last = 0, butt = 1, round = 2, projecting = 3
+ * PS: butt = 0, round = 1, projecting = 2
+ */
+ if (capStyle > 0) {
+ capStyle--;
+ }
+ Blt_FormatToPostScript(tokenPtr,
+ "%d setlinecap\n",
+ capStyle);
+}
+
+void
+Blt_JoinStyleToPostScript(tokenPtr, joinStyle)
+ struct PsTokenStruct *tokenPtr;
+ int joinStyle;
+{
+ /*
+ * miter = 0, round = 1, bevel = 2
+ */
+ Blt_FormatToPostScript(tokenPtr,
+ "%d setlinejoin\n",
+ joinStyle);
+}
+
+void
+Blt_LineWidthToPostScript(tokenPtr, lineWidth)
+ struct PsTokenStruct *tokenPtr;
+ int lineWidth;
+{
+ if (lineWidth < 1) {
+ lineWidth = 1;
+ }
+ Blt_FormatToPostScript(tokenPtr,
+ "%d setlinewidth\n",
+ lineWidth);
+}
+
+void
+Blt_LineDashesToPostScript(tokenPtr, dashesPtr)
+ struct PsTokenStruct *tokenPtr;
+ Blt_Dashes *dashesPtr;
+{
+
+ Blt_AppendToPostScript(tokenPtr, "[ ", (char *)NULL);
+ if (dashesPtr != NULL) {
+ unsigned char *p;
+
+ for (p = dashesPtr->values; *p != 0; p++) {
+ Blt_FormatToPostScript(tokenPtr, " %d", *p);
+ }
+ }
+ Blt_AppendToPostScript(tokenPtr, "] 0 setdash\n", (char *)NULL);
+}
+
+void
+Blt_LineAttributesToPostScript(tokenPtr, colorPtr, lineWidth, dashesPtr,
+ capStyle, joinStyle)
+ struct PsTokenStruct *tokenPtr;
+ XColor *colorPtr;
+ int lineWidth;
+ Blt_Dashes *dashesPtr;
+ int capStyle, joinStyle;
+{
+ Blt_JoinStyleToPostScript(tokenPtr, joinStyle);
+ Blt_CapStyleToPostScript(tokenPtr, capStyle);
+ Blt_ForegroundToPostScript(tokenPtr, colorPtr);
+ Blt_LineWidthToPostScript(tokenPtr, lineWidth);
+ Blt_LineDashesToPostScript(tokenPtr, dashesPtr);
+ Blt_AppendToPostScript(tokenPtr, "/DashesProc {} def\n", (char *)NULL);
+}
+
+void
+Blt_RectangleToPostScript(tokenPtr, x, y, width, height)
+ struct PsTokenStruct *tokenPtr;
+ double x, y;
+ int width, height;
+{
+ Blt_FormatToPostScript(tokenPtr,
+ "%g %g %d %d Box fill\n\n",
+ x, y, width, height);
+}
+
+void
+Blt_RegionToPostScript(tokenPtr, x, y, width, height)
+ struct PsTokenStruct *tokenPtr;
+ double x, y;
+ int width, height;
+{
+ Blt_FormatToPostScript(tokenPtr, "%g %g %d %d Box\n\n",
+ x, y, width, height);
+}
+
+void
+Blt_PathToPostScript(tokenPtr, screenPts, nScreenPts)
+ struct PsTokenStruct *tokenPtr;
+ register Point2D *screenPts;
+ int nScreenPts;
+{
+ register Point2D *pointPtr, *endPtr;
+
+ pointPtr = screenPts;
+ Blt_FormatToPostScript(tokenPtr, "newpath %g %g moveto\n",
+ pointPtr->x, pointPtr->y);
+ pointPtr++;
+ endPtr = screenPts + nScreenPts;
+ while (pointPtr < endPtr) {
+ Blt_FormatToPostScript(tokenPtr, "%g %g lineto\n",
+ pointPtr->x, pointPtr->y);
+ pointPtr++;
+ }
+}
+
+void
+Blt_PolygonToPostScript(tokenPtr, screenPts, nScreenPts)
+ struct PsTokenStruct *tokenPtr;
+ Point2D *screenPts;
+ int nScreenPts;
+{
+ Blt_PathToPostScript(tokenPtr, screenPts, nScreenPts);
+ Blt_FormatToPostScript(tokenPtr, "%g %g ", screenPts[0].x, screenPts[0].y);
+ Blt_AppendToPostScript(tokenPtr, " lineto closepath Fill\n", (char *)NULL);
+}
+
+void
+Blt_SegmentsToPostScript(tokenPtr, segPtr, nSegments)
+ struct PsTokenStruct *tokenPtr;
+ register XSegment *segPtr;
+ int nSegments;
+{
+ register int i;
+
+ for (i = 0; i < nSegments; i++, segPtr++) {
+ Blt_FormatToPostScript(tokenPtr, "%d %d moveto\n",
+ segPtr->x1, segPtr->y1);
+ Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n",
+ segPtr->x2, segPtr->y2);
+ Blt_AppendToPostScript(tokenPtr, "DashesProc stroke\n", (char *)NULL);
+ }
+}
+
+
+void
+Blt_RectanglesToPostScript(tokenPtr, rectArr, nRects)
+ struct PsTokenStruct *tokenPtr;
+ XRectangle rectArr[];
+ int nRects;
+{
+ register int i;
+
+ for (i = 0; i < nRects; i++) {
+ Blt_RectangleToPostScript(tokenPtr,
+ (double)rectArr[i].x, (double)rectArr[i].y,
+ (int)rectArr[i].width, (int)rectArr[i].height);
+ }
+}
+
+#ifndef TK_RELIEF_SOLID
+#define TK_RELIEF_SOLID -1 /* Set the an impossible value. */
+#endif /* TK_RELIEF_SOLID */
+
+void
+Blt_Draw3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+ borderWidth, relief)
+ struct PsTokenStruct *tokenPtr;
+ Tk_3DBorder border; /* Token for border to draw. */
+ double x, y; /* Coordinates of rectangle */
+ int width, height; /* Region to be drawn. */
+ int borderWidth; /* Desired width for border, in pixels. */
+ int relief; /* Should be either TK_RELIEF_RAISED or
+ * TK_RELIEF_SUNKEN; indicates position of
+ * interior of window relative to exterior. */
+{
+ TkBorder *borderPtr = (TkBorder *) border;
+ XColor lightColor, darkColor;
+ XColor *lightColorPtr, *darkColorPtr;
+ XColor *topColor, *bottomColor;
+ Point2D points[7];
+ int twiceWidth = (borderWidth * 2);
+
+ if ((width < twiceWidth) || (height < twiceWidth)) {
+ return;
+ }
+ if ((relief == TK_RELIEF_SOLID) ||
+ (borderPtr->lightColor == NULL) || (borderPtr->darkColor == NULL)) {
+ if (relief == TK_RELIEF_SOLID) {
+ darkColor.red = darkColor.blue = darkColor.green = 0x00;
+ lightColor.red = lightColor.blue = lightColor.green = 0x00;
+ relief = TK_RELIEF_SUNKEN;
+ } else {
+ Screen *screenPtr;
+
+ lightColor = *borderPtr->bgColor;
+ screenPtr = Tk_Screen(tokenPtr->tkwin);
+ if (lightColor.pixel == WhitePixelOfScreen(screenPtr)) {
+ darkColor.red = darkColor.blue = darkColor.green = 0x00;
+ } else {
+ darkColor.red = darkColor.blue = darkColor.green = 0xFF;
+ }
+ }
+ lightColorPtr = &lightColor;
+ darkColorPtr = &darkColor;
+ } else {
+ lightColorPtr = borderPtr->lightColor;
+ darkColorPtr = borderPtr->darkColor;
+ }
+
+
+ /*
+ * Handle grooves and ridges with recursive calls.
+ */
+
+ if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
+ int halfWidth, insideOffset;
+
+ halfWidth = borderWidth / 2;
+ insideOffset = borderWidth - halfWidth;
+ Blt_Draw3DRectangleToPostScript(tokenPtr, border, (double)x, (double)y,
+ width, height, halfWidth,
+ (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+ Blt_Draw3DRectangleToPostScript(tokenPtr, border,
+ (double)(x + insideOffset), (double)(y + insideOffset),
+ width - insideOffset * 2, height - insideOffset * 2, halfWidth,
+ (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED : TK_RELIEF_SUNKEN);
+ return;
+ }
+ if (relief == TK_RELIEF_RAISED) {
+ topColor = lightColorPtr;
+ bottomColor = darkColorPtr;
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ topColor = darkColorPtr;
+ bottomColor = lightColorPtr;
+ } else {
+ topColor = bottomColor = borderPtr->bgColor;
+ }
+ Blt_BackgroundToPostScript(tokenPtr, bottomColor);
+ Blt_RectangleToPostScript(tokenPtr, x, y + height - borderWidth, width,
+ borderWidth);
+ Blt_RectangleToPostScript(tokenPtr, x + width - borderWidth, y,
+ borderWidth, height);
+ points[0].x = points[1].x = points[6].x = x;
+ points[0].y = points[6].y = y + height;
+ points[1].y = points[2].y = y;
+ points[2].x = x + width;
+ points[3].x = x + width - borderWidth;
+ points[3].y = points[4].y = y + borderWidth;
+ points[4].x = points[5].x = x + borderWidth;
+ points[5].y = y + height - borderWidth;
+ if (relief != TK_RELIEF_FLAT) {
+ Blt_BackgroundToPostScript(tokenPtr, topColor);
+ }
+ Blt_PolygonToPostScript(tokenPtr, points, 7);
+}
+
+void
+Blt_Fill3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+ borderWidth, relief)
+ struct PsTokenStruct *tokenPtr;
+ Tk_3DBorder border; /* Token for border to draw. */
+ double x, y; /* Coordinates of top-left of border area */
+ int width, height; /* Dimension of border to be drawn. */
+ int borderWidth; /* Desired width for border, in pixels. */
+ int relief; /* Should be either TK_RELIEF_RAISED or
+ * TK_RELIEF_SUNKEN; indicates position of
+ * interior of window relative to exterior. */
+{
+ TkBorder *borderPtr = (TkBorder *) border;
+
+ /*
+ * I'm assuming that the rectangle is to be drawn as a background.
+ * Setting the pen color as foreground or background only affects
+ * the plot when the colormode option is "monochrome".
+ */
+ Blt_BackgroundToPostScript(tokenPtr, borderPtr->bgColor);
+ Blt_RectangleToPostScript(tokenPtr, x, y, width, height);
+ Blt_Draw3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+ borderWidth, relief);
+}
+
+void
+Blt_StippleToPostScript(tokenPtr, display, bitmap)
+ struct PsTokenStruct *tokenPtr;
+ Display *display;
+ Pixmap bitmap;
+{
+ int width, height;
+
+ Tk_SizeOfBitmap(display, bitmap, &width, &height);
+ Blt_FormatToPostScript(tokenPtr,
+ "gsave\n clip\n %d %d\n",
+ width, height);
+ Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height);
+ Blt_AppendToPostScript(tokenPtr,
+ " StippleFill\ngrestore\n",
+ (char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPostScript --
+ *
+ * Translates a color image into 3 component RGB PostScript output.
+ * Uses PS Language Level 2 operator "colorimage".
+ *
+ * Results:
+ * The dynamic string will contain the PostScript output.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorimageToPostScript(tokenPtr, image, x, y)
+ struct PsTokenStruct *tokenPtr;
+ Blt_Colorimage image;
+ double x, y;
+{
+ int width, height;
+ int tmpSize;
+
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+
+ tmpSize = width;
+ if (tokenPtr->colorMode == PS_MODE_COLOR) {
+ tmpSize *= 3;
+ }
+ Blt_FormatToPostScript(tokenPtr, "\n/tmpStr %d string def\n", tmpSize);
+ Blt_AppendToPostScript(tokenPtr, "gsave\n", (char *)NULL);
+ Blt_FormatToPostScript(tokenPtr, " %g %g translate\n", x, y);
+ Blt_FormatToPostScript(tokenPtr, " %d %d scale\n", width, height);
+ Blt_FormatToPostScript(tokenPtr, " %d %d 8\n", width, height);
+ Blt_FormatToPostScript(tokenPtr, " [%d 0 0 %d 0 %d] ", width, -height,
+ height);
+ Blt_AppendToPostScript(tokenPtr,
+ "{\n currentfile tmpStr readhexstring pop\n } ",
+ (char *)NULL);
+ if (tokenPtr->colorMode != PS_MODE_COLOR) {
+ Blt_AppendToPostScript(tokenPtr, "image\n", (char *)NULL);
+ Blt_ColorimageToGreyscale(image);
+ Blt_ColorimageToPsData(image, 1, &(tokenPtr->dString), " ");
+ } else {
+ Blt_AppendToPostScript(tokenPtr,
+ "false 3 colorimage\n",
+ (char *)NULL);
+ Blt_ColorimageToPsData(image, 3, &(tokenPtr->dString), " ");
+ }
+ Blt_AppendToPostScript(tokenPtr,
+ "\ngrestore\n\n",
+ (char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_WindowToPostScript --
+ *
+ * Converts a Tk window to PostScript. If the window could not
+ * be "snapped", then a grey rectangle is drawn in its place.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_WindowToPostScript(tokenPtr, tkwin, x, y)
+ struct PsTokenStruct *tokenPtr;
+ Tk_Window tkwin;
+ double x, y;
+{
+ Blt_Colorimage image;
+ int width, height;
+
+ width = Tk_Width(tkwin);
+ height = Tk_Height(tkwin);
+ image = Blt_DrawableToColorimage(tkwin, Tk_WindowId(tkwin), 0, 0, width,
+ height, GAMMA);
+ if (image == NULL) {
+ /* Can't grab window image so paint the window area grey */
+ Blt_AppendToPostScript(tokenPtr, "% Can't grab window \"",
+ Tk_PathName(tkwin), "\"\n", (char *)NULL);
+ Blt_AppendToPostScript(tokenPtr, "0.5 0.5 0.5 SetBgColor\n",
+ (char *)NULL);
+ Blt_RectangleToPostScript(tokenPtr, x, y, width, height);
+ return;
+ }
+ Blt_ColorimageToPostScript(tokenPtr, image, x, y);
+ Blt_FreeColorimage(image);
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_PhotoToPostScript --
+ *
+ * Output a PostScript image string of the given photo image.
+ * The photo is first converted into a color image and then
+ * translated into PostScript.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript output representing the photo is appended to
+ * the tokenPtr's dynamic string.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_PhotoToPostScript(tokenPtr, photo, x, y)
+ struct PsTokenStruct *tokenPtr;
+ Tk_PhotoHandle photo;
+ double x, y; /* Origin of photo image */
+{
+ Blt_Colorimage image;
+
+ image = Blt_PhotoToColorimage(photo);
+ Blt_ColorimageToPostScript(tokenPtr, image, x, y);
+ Blt_FreeColorimage(image);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_FontToPostScript --
+ *
+ * Map the Tk font to a PostScript font and point size.
+ *
+ * If a Tcl array variable was specified, each element should be
+ * indexed by the X11 font name and contain a list of 1-2
+ * elements; the PostScript font name and the desired point size.
+ * The point size may be omitted and the X font point size will
+ * be used.
+ *
+ * Otherwise, if the foundry is "Adobe", we try to do a plausible
+ * mapping looking at the full name of the font and building a
+ * string in the form of "Family-TypeFace".
+ *
+ * Returns:
+ * None.
+ *
+ * Side Effects:
+ * PostScript commands are output to change the type and the
+ * point size of the current font.
+ *
+ * -----------------------------------------------------------------
+ */
+
+void
+Blt_FontToPostScript(tokenPtr, font)
+ struct PsTokenStruct *tokenPtr;
+ Tk_Font font; /* Tk font to query about */
+{
+ XFontStruct *fontPtr = (XFontStruct *)font;
+ Tcl_Interp *interp = tokenPtr->interp;
+ char *family, *fontName;
+ double pointSize;
+#if (TK_MAJOR_VERSION > 4)
+ register int i;
+#endif /* TK_MAJOR_VERSION > 4 */
+
+ fontName = Tk_NameOfFont(font);
+ pointSize = 12.0;
+ /*
+ * Use the font variable information if it exists.
+ */
+ if (tokenPtr->fontVarName != NULL) {
+ char *fontInfo;
+
+ fontInfo = Tcl_GetVar2(interp, tokenPtr->fontVarName, fontName, 0);
+ if (fontInfo != NULL) {
+ int nProps;
+ char **propArr = NULL;
+
+ if (Tcl_SplitList(interp, fontInfo, &nProps, &propArr) == TCL_OK) {
+ int newSize;
+
+ fontName = propArr[0];
+ if ((nProps == 2) &&
+ (Tcl_GetInt(interp, propArr[1], &newSize) == TCL_OK)) {
+ pointSize = (double)newSize;
+ }
+ }
+ Blt_FormatToPostScript(tokenPtr,
+ "%g /%s SetFont\n",
+ pointSize, fontName);
+ if (propArr != (char **)NULL) {
+ Blt_Free(propArr);
+ }
+ return;
+ }
+ }
+#if (TK_MAJOR_VERSION > 4)
+
+ /*
+ * Otherwise do a quick test to see if it's a PostScript font.
+ * Tk_PostScriptFontName will silently generate a bogus PostScript
+ * font description, so we have to check to see if this is really a
+ * PostScript font.
+ */
+ family = ((TkFont *) fontPtr)->fa.family;
+ for (i = 0; i < nFontNames; i++) {
+ if (strncasecmp(psFontMap[i].alias, family, strlen(psFontMap[i].alias))
+ == 0) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ pointSize = (double)Tk_PostscriptFontName(font, &dString);
+ fontName = Tcl_DStringValue(&dString);
+ Blt_FormatToPostScript(tokenPtr, "%g /%s SetFont\n", pointSize,
+ fontName);
+ Tcl_DStringFree(&dString);
+ return;
+ }
+ }
+
+#endif /* TK_MAJOR_VERSION > 4 */
+
+ /*
+ * Can't find it. Try to use the current point size.
+ */
+ family = NULL;
+ fontName = NULL;
+ pointSize = 12.0;
+
+#ifndef WIN32
+#if (TK_MAJOR_VERSION > 4)
+ /* Can you believe what I have to go through to get an XFontStruct? */
+ fontPtr = XLoadQueryFont(Tk_Display(tokenPtr->tkwin), Tk_NameOfFont(font));
+#endif
+ if (fontPtr != NULL) {
+ unsigned long fontProp;
+
+ if (XGetFontProperty(fontPtr, XA_POINT_SIZE, &fontProp) != False) {
+ pointSize = (double)fontProp / 10.0;
+ }
+ fontName = XFontStructToPostScript(tokenPtr->tkwin, fontPtr);
+#if (TK_MAJOR_VERSION > 4)
+ XFreeFont(Tk_Display(tokenPtr->tkwin), fontPtr);
+#endif /* TK_MAJOR_VERSION > 4 */
+ }
+#endif /* !WIN32 */
+ if ((fontName == NULL) || (fontName[0] == '\0')) {
+ fontName = "Helvetica-Bold"; /* Defaulting to a known PS font */
+ }
+ Blt_FormatToPostScript(tokenPtr, "%g /%s SetFont\n", pointSize, fontName);
+}
+
+static void
+TextLayoutToPostScript(tokenPtr, x, y, textPtr)
+ struct PsTokenStruct *tokenPtr;
+ int x, y;
+ TextLayout *textPtr;
+{
+ char *src, *dst, *end;
+ int count; /* Counts the # of bytes written to
+ * the intermediate scratch buffer. */
+ TextFragment *fragPtr;
+ int i;
+ unsigned char c;
+#if HAVE_UTF
+ Tcl_UniChar ch;
+#endif
+ int limit;
+
+ limit = PSTOKEN_BUFSIZ - 4; /* High water mark for the scratch
+ * buffer. */
+ fragPtr = textPtr->fragArr;
+ for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
+ if (fragPtr->count < 1) {
+ continue;
+ }
+ Blt_AppendToPostScript(tokenPtr, "(", (char *)NULL);
+ count = 0;
+ dst = tokenPtr->scratchArr;
+ src = fragPtr->text;
+ end = fragPtr->text + fragPtr->count;
+ while (src < end) {
+ if (count > limit) {
+ /* Don't let the buffer overflow with a big text string */
+ dst = tokenPtr->scratchArr;
+ dst[count] = '\0';
+ Blt_AppendToPostScript(tokenPtr, dst, (char *)NULL);
+ count = 0;
+ }
+#if HAVE_UTF
+ /*
+ * INTL: For now we just treat the characters as binary
+ * data and dislay the lower byte. Eventually this should
+ * be revised to handle international postscript fonts.
+ */
+ src += Tcl_UtfToUniChar(src, &ch);
+ c = (unsigned char)(ch & 0xff);
+#else
+ c = *src++;
+#endif
+
+ if ((c == '\\') || (c == '(') || (c == ')')) {
+ /*
+ * If special PostScript characters characters "\", "(",
+ * and ")" are contained in the text string, prepend
+ * backslashes to them.
+ */
+ *dst++ = '\\';
+ *dst++ = c;
+ count += 2;
+ } else if ((c < ' ') || (c > '~')) {
+ /*
+ * Present non-printable characters in their octal
+ * representation.
+ */
+ sprintf(dst, "\\%03o", c);
+ dst += 4;
+ count += 4;
+ } else {
+ *dst++ = c;
+ count++;
+ }
+ }
+ tokenPtr->scratchArr[count] = '\0';
+ Blt_AppendToPostScript(tokenPtr, tokenPtr->scratchArr, (char *)NULL);
+ Blt_FormatToPostScript(tokenPtr, ") %d %d %d DrawAdjText\n",
+ fragPtr->width, x + fragPtr->x, y + fragPtr->y);
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TextToPostScript --
+ *
+ * Output PostScript commands to print a text string. The string
+ * may be rotated at any arbitrary angle, and placed according
+ * the anchor type given. The anchor indicates how to interpret
+ * the window coordinates as an anchor for the text bounding box.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Text string is drawn using the given font and GC on the graph
+ * window at the given coordinates, anchor, and rotation
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_TextToPostScript(tokenPtr, string, tsPtr, x, y)
+ struct PsTokenStruct *tokenPtr;
+ char *string; /* String to convert to PostScript */
+ TextStyle *tsPtr; /* Text attribute information */
+ double x, y; /* Window coordinates where to print text */
+{
+ double theta;
+ int bbWidth, bbHeight;
+ TextLayout *textPtr;
+ Point2D anchorPos;
+
+ if ((string == NULL) || (*string == '\0')) { /* Empty string, do nothing */
+ return;
+ }
+ theta = FMOD(tsPtr->theta, (double)360.0);
+ textPtr = Blt_GetTextLayout(string, tsPtr);
+ Blt_GetBoundingBox(textPtr->width, textPtr->height, theta, &bbWidth,
+ &bbHeight, (Point2D *)NULL);
+ /*
+ * Find the center of the bounding box
+ */
+ anchorPos.x = x, anchorPos.y = y;
+ anchorPos = Blt_TranslatePoint(&anchorPos, bbWidth, bbHeight,
+ tsPtr->anchor);
+ anchorPos.x += (bbWidth * 0.5);
+ anchorPos.y += (bbHeight * 0.5);
+
+ /* Initialize text (sets translation and rotation) */
+ Blt_FormatToPostScript(tokenPtr,
+ "%d %d %g %g %g BeginText\n",
+ textPtr->width, textPtr->height,
+ tsPtr->theta, anchorPos.x, anchorPos.y);
+
+ Blt_FontToPostScript(tokenPtr, tsPtr->font);
+
+ /* All coordinates are now relative to what was set by BeginText */
+ if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+ Blt_ForegroundToPostScript(tokenPtr, tsPtr->shadow.color);
+ TextLayoutToPostScript(tokenPtr, tsPtr->shadow.offset,
+ tsPtr->shadow.offset, textPtr);
+ }
+ Blt_ForegroundToPostScript(tokenPtr, (tsPtr->state & STATE_ACTIVE)
+ ? tsPtr->activeColor : tsPtr->color);
+ TextLayoutToPostScript(tokenPtr, 0, 0, textPtr);
+ Blt_Free(textPtr);
+ Blt_AppendToPostScript(tokenPtr, "EndText\n", (char *)NULL);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LineToPostScript --
+ *
+ * Outputs PostScript commands to print a multi-segmented line.
+ * It assumes a procedure DashesProc was previously defined.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Segmented line is printed.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LineToPostScript(tokenPtr, pointPtr, nPoints)
+ struct PsTokenStruct *tokenPtr;
+ register XPoint *pointPtr;
+ int nPoints;
+{
+ register int i;
+
+ if (nPoints <= 0) {
+ return;
+ }
+ Blt_FormatToPostScript(tokenPtr, " newpath %d %d moveto\n",
+ pointPtr->x, pointPtr->y);
+ pointPtr++;
+ for (i = 1; i < (nPoints - 1); i++, pointPtr++) {
+ Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n",
+ pointPtr->x, pointPtr->y);
+ if ((i % PS_MAXPATH) == 0) {
+ Blt_FormatToPostScript(tokenPtr,
+ "DashesProc stroke\n newpath %d %d moveto\n",
+ pointPtr->x, pointPtr->y);
+ }
+ }
+ Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n",
+ pointPtr->x, pointPtr->y);
+ Blt_AppendToPostScript(tokenPtr, "DashesProc stroke\n", (char *)NULL);
+}
+
+void
+Blt_BitmapToPostScript(tokenPtr, display, bitmap, scaleX, scaleY)
+ struct PsTokenStruct *tokenPtr;
+ Display *display;
+ Pixmap bitmap; /* Bitmap to be converted to PostScript */
+ double scaleX, scaleY;
+{
+ int width, height;
+ double scaledWidth, scaledHeight;
+
+ Tk_SizeOfBitmap(display, bitmap, &width, &height);
+ scaledWidth = (double)width * scaleX;
+ scaledHeight = (double)height * scaleY;
+ Blt_AppendToPostScript(tokenPtr, " gsave\n", (char *)NULL);
+ Blt_FormatToPostScript(tokenPtr, " %g %g translate\n",
+ scaledWidth * -0.5, scaledHeight * 0.5);
+ Blt_FormatToPostScript(tokenPtr, " %g %g scale\n",
+ scaledWidth, -scaledHeight);
+ Blt_FormatToPostScript(tokenPtr, " %d %d true [%d 0 0 %d 0 %d] {",
+ width, height, width, -height, height);
+ Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height);
+ Blt_AppendToPostScript(tokenPtr, " } imagemask\n grestore\n",
+ (char *)NULL);
+}
+
+void
+Blt_Segments2DToPostScript(psToken, segPtr, nSegments)
+ PsToken psToken;
+ register Segment2D *segPtr;
+ int nSegments;
+{
+ register Segment2D *endPtr;
+
+ for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
+ Blt_FormatToPostScript(psToken, "%g %g moveto\n",
+ segPtr->p.x, segPtr->p.y);
+ Blt_FormatToPostScript(psToken, " %g %g lineto\n",
+ segPtr->q.x, segPtr->q.y);
+ Blt_AppendToPostScript(psToken, "DashesProc stroke\n", (char *)NULL);
+ }
+}
diff --git a/blt/src/bltPs.h b/blt/src/bltPs.h
new file mode 100644
index 00000000000..356a4275fb3
--- /dev/null
+++ b/blt/src/bltPs.h
@@ -0,0 +1,154 @@
+/*
+ * bltPs.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_PS_H
+#define _BLT_PS_H
+
+#include "bltImage.h"
+
+typedef enum {
+ PS_MODE_MONOCHROME,
+ PS_MODE_GREYSCALE,
+ PS_MODE_COLOR
+} PsColorMode;
+
+typedef struct PsTokenStruct *PsToken;
+
+struct PsTokenStruct {
+ Tcl_Interp *interp; /* Interpreter to report errors to. */
+
+ Tk_Window tkwin; /* Tk_Window used to get font and color
+ * information */
+
+ Tcl_DString dString; /* Dynamic string used to contain the
+ * PostScript generated. */
+
+ char *fontVarName; /* Name of a Tcl array variable to convert
+ * X font names to PostScript fonts. */
+
+ char *colorVarName; /* Name of a Tcl array variable to convert
+ * X color names to PostScript. */
+
+ PsColorMode colorMode; /* Mode: color or greyscale */
+
+#define PSTOKEN_BUFSIZ ((BUFSIZ*2)-1)
+ /*
+ * Utility space for building strings. Currently used to create
+ * PostScript output for the "postscript" command.
+ */
+ char scratchArr[PSTOKEN_BUFSIZ+1];
+};
+
+extern PsToken Blt_GetPsToken _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin));
+
+extern void Blt_ReleasePsToken _ANSI_ARGS_((PsToken psToken));
+
+extern char *Blt_PostScriptFromToken _ANSI_ARGS_((PsToken psToken));
+extern char *Blt_ScratchBufferFromToken _ANSI_ARGS_((PsToken psToken));
+
+extern void Blt_AppendToPostScript _ANSI_ARGS_(TCL_VARARGS(PsToken, psToken));
+
+extern void Blt_FormatToPostScript _ANSI_ARGS_(TCL_VARARGS(PsToken, psToken));
+
+extern void Blt_Draw3DRectangleToPostScript _ANSI_ARGS_((PsToken psToken,
+ Tk_3DBorder border, double x, double y, int width, int height,
+ int borderWidth, int relief));
+
+extern void Blt_Fill3DRectangleToPostScript _ANSI_ARGS_((PsToken psToken,
+ Tk_3DBorder border, double x, double y, int width, int height,
+ int borderWidth, int relief));
+
+extern void Blt_BackgroundToPostScript _ANSI_ARGS_((PsToken psToken,
+ XColor *colorPtr));
+
+extern void Blt_BitmapDataToPostScript _ANSI_ARGS_((PsToken psToken,
+ Display *display, Pixmap bitmap, int width, int height));
+
+extern void Blt_ClearBackgroundToPostScript _ANSI_ARGS_((PsToken psToken));
+
+extern int Blt_ColorimageToPsData _ANSI_ARGS_((Blt_Colorimage image,
+ int nComponents, Tcl_DString * resultPtr, char *prefix));
+
+extern void Blt_ColorimageToPostScript _ANSI_ARGS_((PsToken psToken,
+ Blt_Colorimage image, double x, double y));
+
+extern void Blt_ForegroundToPostScript _ANSI_ARGS_((PsToken psToken,
+ XColor *colorPtr));
+
+extern void Blt_FontToPostScript _ANSI_ARGS_((PsToken psToken, Tk_Font font));
+
+extern void Blt_WindowToPostScript _ANSI_ARGS_((PsToken psToken,
+ Tk_Window tkwin, double x, double y));
+
+extern void Blt_LineDashesToPostScript _ANSI_ARGS_((PsToken psToken,
+ Blt_Dashes *dashesPtr));
+
+extern void Blt_LineWidthToPostScript _ANSI_ARGS_((PsToken psToken,
+ int lineWidth));
+
+extern void Blt_PathToPostScript _ANSI_ARGS_((PsToken psToken,
+ Point2D *screenPts, int nScreenPts));
+
+extern void Blt_PhotoToPostScript _ANSI_ARGS_((PsToken psToken,
+ Tk_PhotoHandle photoToken, double x, double y));
+
+extern void Blt_PolygonToPostScript _ANSI_ARGS_((PsToken psToken,
+ Point2D *screenPts, int nScreenPts));
+
+extern void Blt_LineToPostScript _ANSI_ARGS_((PsToken psToken,
+ XPoint *pointArr, int nPoints));
+
+extern void Blt_TextToPostScript _ANSI_ARGS_((PsToken psToken, char *string,
+ TextStyle *attrPtr, double x, double y));
+
+extern void Blt_RectangleToPostScript _ANSI_ARGS_((PsToken psToken, double x,
+ double y, int width, int height));
+
+extern void Blt_RegionToPostScript _ANSI_ARGS_((PsToken psToken, double x,
+ double y, int width, int height));
+
+extern void Blt_RectanglesToPostScript _ANSI_ARGS_((PsToken psToken,
+ XRectangle *rectArr, int nRects));
+
+extern void Blt_BitmapToPostScript _ANSI_ARGS_((PsToken psToken,
+ Display *display, Pixmap bitmap, double scaleX, double scaleY));
+
+extern void Blt_SegmentsToPostScript _ANSI_ARGS_((PsToken psToken,
+ XSegment *segArr, int nSegs));
+
+extern void Blt_StippleToPostScript _ANSI_ARGS_((PsToken psToken,
+ Display *display, Pixmap bitmap));
+
+extern void Blt_LineAttributesToPostScript _ANSI_ARGS_((PsToken psToken,
+ XColor *colorPtr, int lineWidth, Blt_Dashes *dashesPtr, int capStyle,
+ int joinStyle));
+
+extern int Blt_FileToPostScript _ANSI_ARGS_((PsToken psToken,
+ char *fileName));
+
+extern void Blt_Segments2DToPostScript _ANSI_ARGS_((PsToken psToken,
+ Segment2D *segments, int nSegments));
+
+#endif /* BLT_PS_H */
diff --git a/blt/src/bltSpline.c b/blt/src/bltSpline.c
new file mode 100644
index 00000000000..ae6830bb00a
--- /dev/null
+++ b/blt/src/bltSpline.c
@@ -0,0 +1,1451 @@
+#include "bltInt.h"
+
+typedef double TriDiagonalMatrix[3];
+typedef struct {
+ double b, c, d;
+} Cubic2D;
+
+typedef struct {
+ double b, c, d, e, f;
+} Quint2D;
+
+/*
+ * Quadratic spline parameters
+ */
+#define E1 param[0]
+#define E2 param[1]
+#define V1 param[2]
+#define V2 param[3]
+#define W1 param[4]
+#define W2 param[5]
+#define Z1 param[6]
+#define Z2 param[7]
+#define Y1 param[8]
+#define Y2 param[9]
+
+#ifdef __STDC__
+static Tcl_CmdProc SplineCmd;
+#endif
+
+static INLINE double
+Fabs(x)
+ double x;
+{
+ return ((x < 0.0) ? -x : x);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Search --
+ *
+ * Conducts a binary search for a value. This routine is called
+ * only if key is between x(0) and x(len - 1).
+ *
+ * Results:
+ * Returns the index of the largest value in xtab for which
+ * x[i] < key.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+Search(points, nPoints, key, foundPtr)
+ Point2D points[]; /* Contains the abscissas of the data
+ * points of interpolation. */
+ int nPoints; /* Dimension of x. */
+ double key; /* Value whose relative position in
+ * x is to be located. */
+ int *foundPtr; /* (out) Returns 1 if s is found in
+ * x and 0 otherwise. */
+{
+ int high, low, mid;
+
+ low = 0;
+ high = nPoints - 1;
+
+ while (high >= low) {
+ mid = (high + low) / 2;
+ if (key > points[mid].x) {
+ low = mid + 1;
+ } else if (key < points[mid].x) {
+ high = mid - 1;
+ } else {
+ *foundPtr = 1;
+ return mid;
+ }
+ }
+ *foundPtr = 0;
+ return low;
+}
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * QuadChoose --
+ *
+ * Determines the case needed for the computation of the parame-
+ * ters of the quadratic spline.
+ *
+ * Results:
+ * Returns a case number (1-4) which controls how the parameters
+ * of the quadratic spline are evaluated.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+QuadChoose(p, q, m1, m2, epsilon)
+ Point2D *p; /* Coordinates of one of the points of
+ * interpolation */
+ Point2D *q; /* Coordinates of one of the points of
+ * interpolation */
+ double m1; /* Derivative condition at point P */
+ double m2; /* Derivative condition at point Q */
+ double epsilon; /* Error tolerance used to distinguish
+ * cases when m1 or m2 is relatively
+ * close to the slope or twice the
+ * slope of the line segment joining
+ * the points P and Q. If
+ * epsilon is not 0.0, then epsilon
+ * should be greater than or equal to
+ * machine epsilon. */
+{
+ double slope;
+
+ /* Calculate the slope of the line joining P and Q. */
+ slope = (q->y - p->y) / (q->x - p->x);
+
+ if (slope != 0.0) {
+ double relerr;
+ double mref, mref1, mref2, prod1, prod2;
+
+ prod1 = slope * m1;
+ prod2 = slope * m2;
+
+ /* Find the absolute values of the slopes slope, m1, and m2. */
+ mref = Fabs(slope);
+ mref1 = Fabs(m1);
+ mref2 = Fabs(m2);
+
+ /*
+ * If the relative deviation of m1 or m2 from slope is less than
+ * epsilon, then choose case 2 or case 3.
+ */
+ relerr = epsilon * mref;
+ if ((Fabs(slope - m1) > relerr) && (Fabs(slope - m2) > relerr) &&
+ (prod1 >= 0.0) && (prod2 >= 0.0)) {
+ double prod;
+
+ prod = (mref - mref1) * (mref - mref2);
+ if (prod < 0.0) {
+ /*
+ * l1, the line through (x1,y1) with slope m1, and l2,
+ * the line through (x2,y2) with slope m2, intersect
+ * at a point whose abscissa is between x1 and x2.
+ * The abscissa becomes a knot of the spline.
+ */
+ return 1;
+ }
+ if (mref1 > (mref * 2.0)) {
+ if (mref2 <= ((2.0 - epsilon) * mref)) {
+ return 3;
+ }
+ } else if (mref2 <= (mref * 2.0)) {
+ /*
+ * Both l1 and l2 cross the line through
+ * (x1+x2)/2.0,y1 and (x1+x2)/2.0,y2, which is the
+ * midline of the rectangle formed by P and Q or both
+ * m1 and m2 have signs different than the sign of
+ * slope, or one of m1 and m2 has opposite sign from
+ * slope and l1 and l2 intersect to the left of x1 or
+ * to the right of x2. The point (x1+x2)/2. is a knot
+ * of the spline.
+ */
+ return 2;
+ } else if (mref1 <= ((2.0 - epsilon) * mref)) {
+ /*
+ * In cases 3 and 4, sign(m1)=sign(m2)=sign(slope).
+ * Either l1 or l2 crosses the midline, but not both.
+ * Choose case 4 if mref1 is greater than
+ * (2.-epsilon)*mref; otherwise, choose case 3.
+ */
+ return 3;
+ }
+ /*
+ * If neither l1 nor l2 crosses the midline, the spline
+ * requires two knots between x1 and x2.
+ */
+ return 4;
+ } else {
+ /*
+ * The sign of at least one of the slopes m1 or m2 does not
+ * agree with the sign of *slope*.
+ */
+ if ((prod1 < 0.0) && (prod2 < 0.0)) {
+ return 2;
+ } else if (prod1 < 0.0) {
+ if (mref2 > ((epsilon + 1.0) * mref)) {
+ return 1;
+ } else {
+ return 2;
+ }
+ } else if (mref1 > ((epsilon + 1.0) * mref)) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+ } else if ((m1 * m2) >= 0.0) {
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadCases --
+ *
+ * Computes the knots and other parameters of the spline on the
+ * interval PQ.
+ *
+ *
+ * On input--
+ *
+ * P and Q are the coordinates of the points of interpolation.
+ *
+ * m1 is the slope at P.
+ *
+ * m2 is the slope at Q.
+ *
+ * ncase controls the number and location of the knots.
+ *
+ *
+ * On output--
+ *
+ * (v1,v2),(w1,w2),(z1,z2), and (e1,e2) are the coordinates of
+ * the knots and other parameters of the spline on P.
+ * (e1,e2) and Q are used only if ncase=4.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadCases(p, q, m1, m2, param, which)
+ Point2D *p, *q;
+ double m1, m2;
+ double param[];
+ int which;
+{
+ if ((which == 3) || (which == 4)) { /* Parameters used in both 3 and 4 */
+ double mbar1, mbar2, mbar3, c1, d1, h1, j1, k1;
+
+ c1 = p->x + (q->y - p->y) / m1;
+ d1 = q->x + (p->y - q->y) / m2;
+ h1 = c1 * 2.0 - p->x;
+ j1 = d1 * 2.0 - q->x;
+ mbar1 = (q->y - p->y) / (h1 - p->x);
+ mbar2 = (p->y - q->y) / (j1 - q->x);
+
+ if (which == 4) { /* Case 4. */
+ Y1 = (p->x + c1) / 2.0;
+ V1 = (p->x + Y1) / 2.0;
+ V2 = m1 * (V1 - p->x) + p->y;
+ Z1 = (d1 + q->x) / 2.0;
+ W1 = (q->x + Z1) / 2.0;
+ W2 = m2 * (W1 - q->x) + q->y;
+ mbar3 = (W2 - V2) / (W1 - V1);
+ Y2 = mbar3 * (Y1 - V1) + V2;
+ Z2 = mbar3 * (Z1 - V1) + V2;
+ E1 = (Y1 + Z1) / 2.0;
+ E2 = mbar3 * (E1 - V1) + V2;
+ } else { /* Case 3. */
+ k1 = (p->y - q->y + q->x * mbar2 - p->x * mbar1) / (mbar2 - mbar1);
+ if (Fabs(m1) > Fabs(m2)) {
+ Z1 = (k1 + p->x) / 2.0;
+ } else {
+ Z1 = (k1 + q->x) / 2.0;
+ }
+ V1 = (p->x + Z1) / 2.0;
+ V2 = p->y + m1 * (V1 - p->x);
+ W1 = (q->x + Z1) / 2.0;
+ W2 = q->y + m2 * (W1 - q->x);
+ Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+ }
+ } else if (which == 2) { /* Case 2. */
+ Z1 = (p->x + q->x) / 2.0;
+ V1 = (p->x + Z1) / 2.0;
+ V2 = p->y + m1 * (V1 - p->x);
+ W1 = (Z1 + q->x) / 2.0;
+ W2 = q->y + m2 * (W1 - q->x);
+ Z2 = (V2 + W2) / 2.0;
+ } else { /* Case 1. */
+ double ztwo;
+
+ Z1 = (p->y - q->y + m2 * q->x - m1 * p->x) / (m2 - m1);
+ ztwo = p->y + m1 * (Z1 - p->x);
+ V1 = (p->x + Z1) / 2.0;
+ V2 = (p->y + ztwo) / 2.0;
+ W1 = (Z1 + q->x) / 2.0;
+ W2 = (ztwo + q->y) / 2.0;
+ Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+ }
+}
+
+static int
+QuadSelect(p, q, m1, m2, epsilon, param)
+ Point2D *p, *q;
+ double m1, m2;
+ double epsilon;
+ double param[];
+{
+ int ncase;
+
+ ncase = QuadChoose(p, q, m1, m2, epsilon);
+ QuadCases(p, q, m1, m2, param, ncase);
+ return ncase;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadGetImage --
+ *
+ * -----------------------------------------------------------------------
+ */
+INLINE static double
+QuadGetImage(p1, p2, p3, x1, x2, x3)
+ double p1, p2, p3;
+ double x1, x2, x3;
+{
+ double A, B, C;
+ double y;
+
+ A = x1 - x2;
+ B = x2 - x3;
+ C = x1 - x3;
+
+ y = (p1 * (A * A) + p2 * 2.0 * B * A + p3 * (B * B)) / (C * C);
+ return y;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadSpline --
+ *
+ * Finds the image of a point in x.
+ *
+ * On input
+ *
+ * x Contains the value at which the spline is evaluated.
+ * leftX, leftY
+ * Coordinates of the left-hand data point used in the
+ * evaluation of x values.
+ * rightX, rightY
+ * Coordinates of the right-hand data point used in the
+ * evaluation of x values.
+ * Z1, Z2, Y1, Y2, E2, W2, V2
+ * Parameters of the spline.
+ * ncase Controls the evaluation of the spline by indicating
+ * whether one or two knots were placed in the interval
+ * (xtabs,xtabs1).
+ *
+ * Results:
+ * The image of the spline at x.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadSpline(intp, left, right, param, ncase)
+ Point2D *intp; /* Value at which spline is evaluated */
+ Point2D *left; /* Point to the left of the data point to
+ * be evaluated */
+ Point2D *right; /* Point to the right of the data point to
+ * be evaluated */
+ double param[]; /* Parameters of the spline */
+ int ncase; /* Controls the evaluation of the
+ * spline by indicating whether one or
+ * two knots were placed in the
+ * interval (leftX,rightX) */
+{
+ double y;
+
+ if (ncase == 4) {
+ /*
+ * Case 4: More than one knot was placed in the interval.
+ */
+
+ /*
+ * Determine the location of data point relative to the 1st knot.
+ */
+ if (Y1 > intp->x) {
+ y = QuadGetImage(left->y, V2, Y2, Y1, intp->x, left->x);
+ } else if (Y1 < intp->x) {
+ /*
+ * Determine the location of the data point relative to
+ * the 2nd knot.
+ */
+ if (Z1 > intp->x) {
+ y = QuadGetImage(Y2, E2, Z2, Z1, intp->x, Y1);
+ } else if (Z1 < intp->x) {
+ y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+ } else {
+ y = Z2;
+ }
+ } else {
+ y = Y2;
+ }
+ } else {
+
+ /*
+ * Cases 1, 2, or 3:
+ *
+ * Determine the location of the data point relative to the
+ * knot.
+ */
+ if (Z1 < intp->x) {
+ y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+ } else if (Z1 > intp->x) {
+ y = QuadGetImage(left->y, V2, Z2, Z1, intp->x, left->x);
+ } else {
+ y = Z2;
+ }
+ }
+ intp->y = y;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadSlopes --
+ *
+ * Calculates the derivative at each of the data points. The
+ * slopes computed will insure that an osculatory quadratic
+ * spline will have one additional knot between two adjacent
+ * points of interpolation. Convexity and monotonicity are
+ * preserved wherever these conditions are compatible with the
+ * data.
+ *
+ * Results:
+ * The output array "m" is filled with the derivates at each
+ * data point.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadSlopes(points, m, nPoints)
+ Point2D points[];
+ double *m; /* (out) To be filled with the first
+ * derivative at each data point. */
+ int nPoints; /* Number of data points (dimension of
+ * x, y, and m). */
+{
+ double xbar, xmid, xhat, ydif1, ydif2;
+ double yxmid;
+ double m1, m2;
+ double m1s, m2s;
+ register int i, n, l;
+
+ m1s = m2s = m1 = m2 = 0;
+ for (l = 0, i = 1, n = 2; i < (nPoints - 1); l++, i++, n++) {
+ /*
+ * Calculate the slopes of the two lines joining three
+ * consecutive data points.
+ */
+ ydif1 = points[i].y - points[l].y;
+ ydif2 = points[n].y - points[i].y;
+ m1 = ydif1 / (points[i].x - points[l].x);
+ m2 = ydif2 / (points[n].x - points[i].x);
+ if (i == 1) {
+ m1s = m1, m2s = m2; /* Save slopes of starting point */
+ }
+ /*
+ * If one of the preceding slopes is zero or if they have opposite
+ * sign, assign the value zero to the derivative at the middle
+ * point.
+ */
+ if ((m1 == 0.0) || (m2 == 0.0) || ((m1 * m2) <= 0.0)) {
+ m[i] = 0.0;
+ } else if (Fabs(m1) > Fabs(m2)) {
+ /*
+ * Calculate the slope by extending the line with slope m1.
+ */
+ xbar = ydif2 / m1 + points[i].x;
+ xhat = (xbar + points[n].x) / 2.0;
+ m[i] = ydif2 / (xhat - points[i].x);
+ } else {
+ /*
+ * Calculate the slope by extending the line with slope m2.
+ */
+ xbar = -ydif1 / m2 + points[i].x;
+ xhat = (points[l].x + xbar) / 2.0;
+ m[i] = ydif1 / (points[i].x - xhat);
+ }
+ }
+
+ /* Calculate the slope at the last point, x(n). */
+ i = nPoints - 2;
+ n = nPoints - 1;
+ if ((m1 * m2) < 0.0) {
+ m[n] = m2 * 2.0;
+ } else {
+ xmid = (points[i].x + points[n].x) / 2.0;
+ yxmid = m[i] * (xmid - points[i].x) + points[i].y;
+ m[n] = (points[n].y - yxmid) / (points[n].x - xmid);
+ if ((m[n] * m2) < 0.0) {
+ m[n] = 0.0;
+ }
+ }
+
+ /* Calculate the slope at the first point, x(0). */
+ if ((m1s * m2s) < 0.0) {
+ m[0] = m1s * 2.0;
+ } else {
+ xmid = (points[0].x + points[1].x) / 2.0;
+ yxmid = m[1] * (xmid - points[1].x) + points[1].y;
+ m[0] = (yxmid - points[0].y) / (xmid - points[0].x);
+ if ((m[0] * m1s) < 0.0) {
+ m[0] = 0.0;
+ }
+ }
+
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadEval --
+ *
+ * QuadEval controls the evaluation of an osculatory quadratic
+ * spline. The user may provide his own slopes at the points of
+ * interpolation or use the subroutine 'QuadSlopes' to calculate
+ * slopes which are consistent with the shape of the data.
+ *
+ * ON INPUT--
+ * intpPts must be a nondecreasing vector of points at which the
+ * spline will be evaluated.
+ * origPts contains the abscissas of the data points to be
+ * interpolated. xtab must be increasing.
+ * y contains the ordinates of the data points to be
+ * interpolated.
+ * m contains the slope of the spline at each point of
+ * interpolation.
+ * nPoints number of data points (dimension of xtab and y).
+ * numEval is the number of points of evaluation (dimension of
+ * xval and yval).
+ * epsilon is a relative error tolerance used in subroutine
+ * 'QuadChoose' to distinguish the situation m(i) or
+ * m(i+1) is relatively close to the slope or twice
+ * the slope of the linear segment between xtab(i) and
+ * xtab(i+1). If this situation occurs, roundoff may
+ * cause a change in convexity or monotonicity of the
+ * resulting spline and a change in the case number
+ * provided by 'QuadChoose'. If epsilon is not equal to zero,
+ * then epsilon should be greater than or equal to machine
+ * epsilon.
+ * ON OUTPUT--
+ * yval contains the images of the points in xval.
+ * err is one of the following error codes:
+ * 0 - QuadEval ran normally.
+ * 1 - xval(i) is less than xtab(1) for at least one
+ * i or xval(i) is greater than xtab(num) for at
+ * least one i. QuadEval will extrapolate to provide
+ * function values for these abscissas.
+ * 2 - xval(i+1) < xval(i) for some i.
+ *
+ *
+ * QuadEval calls the following subroutines or functions:
+ * Search
+ * QuadCases
+ * QuadChoose
+ * QuadSpline
+ * -----------------------------------------------------------------------
+ */
+static int
+QuadEval(origPts, nOrigPts, intpPts, nIntpPts, m, epsilon)
+ Point2D origPts[];
+ int nOrigPts;
+ Point2D intpPts[];
+ int nIntpPts;
+ double *m; /* Slope of the spline at each point
+ * of interpolation. */
+ double epsilon; /* Relative error tolerance (see choose) */
+{
+ int error;
+ register int i, j;
+ double param[10];
+ int ncase;
+ int start, end;
+ int l, p;
+ register int n;
+ int found;
+
+ /* Initialize indices and set error result */
+ start = 0;
+ end = nIntpPts - 1;
+ error = 0;
+ l = nOrigPts - 1;
+ p = l - 1;
+ ncase = 1;
+
+ /*
+ * Determine if abscissas of new vector are non-decreasing.
+ */
+ for (j = 1; j < nIntpPts; j++) {
+ if (intpPts[j].x < intpPts[j - 1].x) {
+ return 2;
+ }
+ }
+ /*
+ * Determine if any of the points in xval are LESS than the
+ * abscissa of the first data point.
+ */
+ for (start = 0; start < nIntpPts; start++) {
+ if (intpPts[start].x >= origPts[0].x) {
+ break;
+ }
+ }
+ /*
+ * Determine if any of the points in xval are GREATER than the
+ * abscissa of the l data point.
+ */
+ for (end = nIntpPts - 1; end >= 0; end--) {
+ if (intpPts[end].x <= origPts[l].x) {
+ break;
+ }
+ }
+
+ if (start > 0) {
+ error = 1; /* Set error value to indicate that
+ * extrapolation has occurred. */
+ /*
+ * Calculate the images of points of evaluation whose abscissas
+ * are less than the abscissa of the first data point.
+ */
+ ncase = QuadSelect(origPts, origPts + 1, m[0], m[1], epsilon, param);
+ for (j = 0; j < (start - 1); j++) {
+ QuadSpline(intpPts + j, origPts, origPts + 1, param, ncase);
+ }
+ if (nIntpPts == 1) {
+ return error;
+ }
+ }
+ if ((nIntpPts == 1) && (end != (nIntpPts - 1))) {
+ goto noExtrapolation;
+ }
+
+ /*
+ * Search locates the interval in which the first in-range
+ * point of evaluation lies.
+ */
+
+ i = Search(origPts, nOrigPts, intpPts[start].x, &found);
+
+ n = i + 1;
+ if (n >= nOrigPts) {
+ n = nOrigPts - 1;
+ i = nOrigPts - 2;
+ }
+ /*
+ * If the first in-range point of evaluation is equal to one
+ * of the data points, assign the appropriate value from y.
+ * Continue until a point of evaluation is found which is not
+ * equal to a data point.
+ */
+ if (found) {
+ do {
+ intpPts[start].y = origPts[i].y;
+ start++;
+ if (start >= nIntpPts) {
+ return error;
+ }
+ } while (intpPts[start - 1].x == intpPts[start].x);
+
+ for (;;) {
+ if (intpPts[start].x < origPts[n].x) {
+ break; /* Break out of for-loop */
+ }
+ if (intpPts[start].x == origPts[n].x) {
+ do {
+ intpPts[start].y = origPts[n].y;
+ start++;
+ if (start >= nIntpPts) {
+ return error;
+ }
+ } while (intpPts[start].x == intpPts[start - 1].x);
+ }
+ i++;
+ n++;
+ }
+ }
+ /*
+ * Calculate the images of all the points which lie within
+ * range of the data.
+ */
+ if ((i > 0) || (error != 1)) {
+ ncase = QuadSelect(origPts + i, origPts + n, m[i], m[n],
+ epsilon, param);
+ }
+ for (j = start; j <= end; j++) {
+ /*
+ * If xx(j) - x(n) is negative, do not recalculate
+ * the parameters for this section of the spline since
+ * they are already known.
+ */
+ if (intpPts[j].x == origPts[n].x) {
+ intpPts[j].y = origPts[n].y;
+ continue;
+ } else if (intpPts[j].x > origPts[n].x) {
+ double delta;
+
+ /* Determine that the routine is in the correct part of
+ the spline. */
+ do {
+ i++, n++;
+ delta = intpPts[j].x - origPts[n].x;
+ } while (delta > 0.0);
+
+ if (delta < 0.0) {
+ ncase = QuadSelect(origPts + i, origPts + n, m[i],
+ m[n], epsilon, param);
+ } else if (delta == 0.0) {
+ intpPts[j].y = origPts[n].y;
+ continue;
+ }
+ }
+ QuadSpline(intpPts + j, origPts + i, origPts + n, param, ncase);
+ }
+
+ if (end == (nIntpPts - 1)) {
+ return error;
+ }
+ if ((n == l) && (intpPts[end].x != origPts[l].x)) {
+ goto noExtrapolation;
+ }
+
+ error = 1; /* Set error value to indicate that
+ * extrapolation has occurred. */
+ ncase = QuadSelect(origPts + p, origPts + l, m[p], m[l], epsilon, param);
+
+ noExtrapolation:
+ /*
+ * Calculate the images of the points of evaluation whose
+ * abscissas are greater than the abscissa of the last data point.
+ */
+ for (j = (end + 1); j < nIntpPts; j++) {
+ QuadSpline(intpPts + j, origPts + p, origPts + l, param, ncase);
+ }
+ return error;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Shape preserving quadratic splines
+ * by D.F.Mcallister & J.A.Roulier
+ * Coded by S.L.Dodd & M.Roulier
+ * N.C.State University
+ *
+ * -----------------------------------------------------------------------
+ */
+/*
+ * Driver routine for quadratic spline package
+ * On input--
+ * X,Y Contain n-long arrays of data (x is increasing)
+ * XM Contains m-long array of x values (increasing)
+ * eps Relative error tolerance
+ * n Number of input data points
+ * m Number of output data points
+ * On output--
+ * work Contains the value of the first derivative at each data point
+ * ym Contains the interpolated spline value at each data point
+ */
+int
+Blt_QuadraticSpline(origPts, nOrigPts, intpPts, nIntpPts)
+ Point2D origPts[];
+ int nOrigPts;
+ Point2D intpPts[];
+ int nIntpPts;
+{
+ double epsilon;
+ double *work;
+ int result;
+
+ work = Blt_Malloc(nOrigPts * sizeof(double));
+ assert(work);
+
+ epsilon = 0.0; /* TBA: adjust error via command-line option */
+ /* allocate space for vectors used in calculation */
+ QuadSlopes(origPts, work, nOrigPts);
+ result = QuadEval(origPts, nOrigPts, intpPts, nIntpPts, work, epsilon);
+ Blt_Free(work);
+ if (result > 1) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * Reference:
+ * Numerical Analysis, R. Burden, J. Faires and A. Reynolds.
+ * Prindle, Weber & Schmidt 1981 pp 112
+ *
+ * Parameters:
+ * origPts - vector of points, assumed to be sorted along x.
+ * intpPts - vector of new points.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_NaturalSpline(origPts, nOrigPts, intpPts, nIntpPts)
+ Point2D origPts[];
+ int nOrigPts;
+ Point2D intpPts[];
+ int nIntpPts;
+{
+ Cubic2D *eq;
+ Point2D *iPtr, *endPtr;
+ TriDiagonalMatrix *A;
+ double *dx; /* vector of deltas in x */
+ double x, dy, alpha;
+ int isKnot;
+ register int i, j, n;
+
+ dx = Blt_Malloc(sizeof(double) * nOrigPts);
+ /* Calculate vector of differences */
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dx[i] = origPts[j].x - origPts[i].x;
+ if (dx[i] < 0.0) {
+ return 0;
+ }
+ }
+ n = nOrigPts - 1; /* Number of intervals. */
+ A = Blt_Malloc(sizeof(TriDiagonalMatrix) * nOrigPts);
+ if (A == NULL) {
+ Blt_Free(dx);
+ return 0;
+ }
+ /* Vectors to solve the tridiagonal matrix */
+ A[0][0] = A[n][0] = 1.0;
+ A[0][1] = A[n][1] = 0.0;
+ A[0][2] = A[n][2] = 0.0;
+
+ /* Calculate the intermediate results */
+ for (i = 0, j = 1; j < n; j++, i++) {
+ alpha = 3.0 * ((origPts[j + 1].y / dx[j]) - (origPts[j].y / dx[i]) -
+ (origPts[j].y / dx[j]) + (origPts[i].y / dx[i]));
+ A[j][0] = 2 * (dx[j] + dx[i]) - dx[i] * A[i][1];
+ A[j][1] = dx[j] / A[j][0];
+ A[j][2] = (alpha - dx[i] * A[i][2]) / A[j][0];
+ }
+ eq = Blt_Malloc(sizeof(Cubic2D) * nOrigPts);
+
+ if (eq == NULL) {
+ Blt_Free(A);
+ Blt_Free(dx);
+ return FALSE;
+ }
+ eq[0].c = eq[n].c = 0.0;
+ for (j = n, i = n - 1; i >= 0; i--, j--) {
+ eq[i].c = A[i][2] - A[i][1] * eq[j].c;
+ dy = origPts[i+1].y - origPts[i].y;
+ eq[i].b = (dy) / dx[i] - dx[i] * (eq[j].c + 2.0 * eq[i].c) / 3.0;
+ eq[i].d = (eq[j].c - eq[i].c) / (3.0 * dx[i]);
+ }
+ Blt_Free(A);
+ Blt_Free(dx);
+
+ endPtr = intpPts + nIntpPts;
+ /* Now calculate the new values */
+ for (iPtr = intpPts; iPtr < endPtr; iPtr++) {
+ iPtr->y = 0.0;
+ x = iPtr->x;
+
+ /* Is it outside the interval? */
+ if ((x < origPts[0].x) || (x > origPts[n].x)) {
+ continue;
+ }
+ /* Search for the interval containing x in the point array */
+ i = Search(origPts, nOrigPts, x, &isKnot);
+ if (isKnot) {
+ iPtr->y = origPts[i].y;
+ } else {
+ i--;
+ x -= origPts[i].x;
+ iPtr->y = origPts[i].y +
+ x * (eq[i].b + x * (eq[i].c + x * eq[i].d));
+ }
+ }
+ Blt_Free(eq);
+ return TRUE;
+}
+
+
+static Blt_OpSpec splineOps[] =
+{
+ {"natural", 1, (Blt_Op)Blt_NaturalSpline, 6, 6, "x y splx sply",},
+ {"quadratic", 1, (Blt_Op)Blt_QuadraticSpline, 6, 6, "x y splx sply",},
+};
+static int nSplineOps = sizeof(splineOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+SplineCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ Blt_Vector *x, *y, *splX, *splY;
+ double *xArr, *yArr;
+ register int i;
+ Point2D *origPts, *intpPts;
+ int nOrigPts, nIntpPts;
+
+ proc = Blt_GetOp(interp, nSplineOps, splineOps, BLT_OP_ARG1, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ if ((Blt_GetVector(interp, argv[2], &x) != TCL_OK) ||
+ (Blt_GetVector(interp, argv[3], &y) != TCL_OK) ||
+ (Blt_GetVector(interp, argv[4], &splX) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nOrigPts = Blt_VecLength(x);
+ if (nOrigPts < 3) {
+ Tcl_AppendResult(interp, "length of vector \"", argv[2], "\" is < 3",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 1; i < nOrigPts; i++) {
+ if (Blt_VecData(x)[i] < Blt_VecData(x)[i - 1]) {
+ Tcl_AppendResult(interp, "x vector \"", argv[2],
+ "\" must be monotonically increasing", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ /* Check that all the data points aren't the same. */
+ if (Blt_VecData(x)[i - 1] <= Blt_VecData(x)[0]) {
+ Tcl_AppendResult(interp, "x vector \"", argv[2],
+ "\" must be monotonically increasing", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nOrigPts != Blt_VecLength(y)) {
+ Tcl_AppendResult(interp, "vectors \"", argv[2], "\" and \"", argv[3],
+ " have different lengths", (char *)NULL);
+ return TCL_ERROR;
+ }
+ nIntpPts = Blt_VecLength(splX);
+ if (Blt_GetVector(interp, argv[5], &splY) != TCL_OK) {
+ /*
+ * If the named vector to hold the ordinates of the spline
+ * doesn't exist, create one the same size as the vector
+ * containing the abscissas.
+ */
+ if (Blt_CreateVector(interp, argv[5], nIntpPts, &splY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if (nIntpPts != Blt_VecLength(splY)) {
+ /*
+ * The x and y vectors differ in size. Make the number of ordinates
+ * the same as the number of abscissas.
+ */
+ if (Blt_ResizeVector(splY, nIntpPts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ origPts = Blt_Malloc(sizeof(Point2D) * nOrigPts);
+ if (origPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nOrigPts),
+ "\" points", (char *)NULL);
+ return TCL_ERROR;
+ }
+ intpPts = Blt_Malloc(sizeof(Point2D) * nIntpPts);
+ if (intpPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nIntpPts),
+ "\" points", (char *)NULL);
+ Blt_Free(origPts);
+ return TCL_ERROR;
+ }
+ xArr = Blt_VecData(x);
+ yArr = Blt_VecData(y);
+ for (i = 0; i < nOrigPts; i++) {
+ origPts[i].x = xArr[i];
+ origPts[i].y = yArr[i];
+ }
+ xArr = Blt_VecData(splX);
+ yArr = Blt_VecData(splY);
+ for (i = 0; i < nIntpPts; i++) {
+ intpPts[i].x = xArr[i];
+ intpPts[i].y = yArr[i];
+ }
+ if (!(*proc) (origPts, nOrigPts, intpPts, nIntpPts)) {
+ Tcl_AppendResult(interp, "error generating spline for \"",
+ Blt_NameOfVector(splY), "\"", (char *)NULL);
+ Blt_Free(origPts);
+ Blt_Free(intpPts);
+ return TCL_ERROR;
+ }
+ yArr = Blt_VecData(splY);
+ for (i = 0; i < nIntpPts; i++) {
+ yArr[i] = intpPts[i].y;
+ }
+ Blt_Free(origPts);
+ Blt_Free(intpPts);
+
+ /* Finally update the vector. The size of the vector hasn't
+ * changed, just the data. Reset the vector using TCL_STATIC to
+ * indicate this. */
+ if (Blt_ResetVector(splY, Blt_VecData(splY), Blt_VecLength(splY),
+ Blt_VecSize(splY), TCL_STATIC) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_SplineInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"spline", SplineCmd,};
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+#define SQR(x) ((x)*(x))
+
+typedef struct {
+ double t;
+ double dxdt2;
+ double dydt2;
+} CubicSpline;
+
+
+/*
+ * The following two procedures solve the special linear system which arise
+ * in cubic spline interpolation. If x is assumed cyclic ( x[i]=x[n+i] ) the
+ * equations can be written as (i=0,1,...,n-1):
+ * m[i][0] * x[i-1] + m[i][1] * x[i] + m[i][2] * x[i+1] = b[i] .
+ * In matrix notation one gets A * x = b, where the matrix A is tridiagonal
+ * with additional elements in the upper right and lower left position:
+ * A[i][0] = A_{i,i-1} for i=1,2,...,n-1 and m[0][0] = A_{0,n-1} ,
+ * A[i][1] = A_{i, i } for i=0,1,...,n-1
+ * A[i][2] = A_{i,i+1} for i=0,1,...,n-2 and m[n-1][2] = A_{n-1,0}.
+ * A should be symmetric (A[i+1][0] == A[i][2]) and positive definite.
+ * The size of the system is given in n (n>=1).
+ *
+ * In the first procedure the Cholesky decomposition A = C^T * D * C
+ * (C is upper triangle with unit diagonal, D is diagonal) is calculated.
+ * Return TRUE if decomposition exist.
+ */
+static int
+SolveCubic1(A, n)
+ TriDiagonalMatrix A[];
+ int n;
+{
+ int i;
+ double m_ij, m_n, m_nn, d;
+
+ if (n < 1) {
+ return FALSE; /* Dimension should be at least 1 */
+ }
+ d = A[0][1]; /* D_{0,0} = A_{0,0} */
+ if (d <= 0.0) {
+ return FALSE; /* A (or D) should be positive definite */
+ }
+ m_n = A[0][0]; /* A_{0,n-1} */
+ m_nn = A[n - 1][1]; /* A_{n-1,n-1} */
+ for (i = 0; i < n - 2; i++) {
+ m_ij = A[i][2]; /* A_{i,1} */
+ A[i][2] = m_ij / d; /* C_{i,i+1} */
+ A[i][0] = m_n / d; /* C_{i,n-1} */
+ m_nn -= A[i][0] * m_n; /* to get C_{n-1,n-1} */
+ m_n = -A[i][2] * m_n; /* to get C_{i+1,n-1} */
+ d = A[i + 1][1] - A[i][2] * m_ij; /* D_{i+1,i+1} */
+ if (d <= 0.0) {
+ return FALSE; /* Elements of D should be positive */
+ }
+ A[i + 1][1] = d;
+ }
+ if (n >= 2) { /* Complete last column */
+ m_n += A[n - 2][2]; /* add A_{n-2,n-1} */
+ A[n - 2][0] = m_n / d; /* C_{n-2,n-1} */
+ A[n - 1][1] = d = m_nn - A[n - 2][0] * m_n; /* D_{n-1,n-1} */
+ if (d <= 0.0) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * The second procedure solves the linear system, with the Cholesky
+ * decomposition calculated above (in m[][]) and the right side b given
+ * in x[]. The solution x overwrites the right side in x[].
+ */
+static void
+SolveCubic2(A, spline, nIntervals)
+ TriDiagonalMatrix A[];
+ CubicSpline spline[];
+ int nIntervals;
+{
+ int i;
+ double x, y;
+ int n, m;
+
+ n = nIntervals - 2;
+ m = nIntervals - 1;
+
+ /* Division by transpose of C : b = C^{-T} * b */
+ x = spline[m].dxdt2;
+ y = spline[m].dydt2;
+ for (i = 0; i < n; i++) {
+ spline[i + 1].dxdt2 -= A[i][2] * spline[i].dxdt2; /* C_{i,i+1} * x(i) */
+ spline[i + 1].dydt2 -= A[i][2] * spline[i].dydt2; /* C_{i,i+1} * x(i) */
+ x -= A[i][0] * spline[i].dxdt2; /* C_{i,n-1} * x(i) */
+ y -= A[i][0] * spline[i].dydt2; /* C_{i,n-1} * x(i) */
+ }
+ if (n >= 0) {
+ /* C_{n-2,n-1} * x_{n-1} */
+ spline[m].dxdt2 = x - A[n][0] * spline[n].dxdt2;
+ spline[m].dydt2 = y - A[n][0] * spline[n].dydt2;
+ }
+ /* Division by D: b = D^{-1} * b */
+ for (i = 0; i < nIntervals; i++) {
+ spline[i].dxdt2 /= A[i][1];
+ spline[i].dydt2 /= A[i][1];
+ }
+
+ /* Division by C: b = C^{-1} * b */
+ x = spline[m].dxdt2;
+ y = spline[m].dydt2;
+ if (n >= 0) {
+ /* C_{n-2,n-1} * x_{n-1} */
+ spline[n].dxdt2 -= A[n][0] * x;
+ spline[n].dydt2 -= A[n][0] * y;
+ }
+ for (i = (n - 1); i >= 0; i--) {
+ /* C_{i,i+1} * x_{i+1} + C_{i,n-1} * x_{n-1} */
+ spline[i].dxdt2 -= A[i][2] * spline[i + 1].dxdt2 + A[i][0] * x;
+ spline[i].dydt2 -= A[i][2] * spline[i + 1].dydt2 + A[i][0] * y;
+ }
+}
+
+/*
+ * Find second derivatives (x''(t_i),y''(t_i)) of cubic spline interpolation
+ * through list of points (x_i,y_i). The parameter t is calculated as the
+ * length of the linear stroke. The number of points must be at least 3.
+ * Note: For CLOSED_CONTOURs the first and last point must be equal.
+ */
+static CubicSpline *
+CubicSlopes(points, nPoints, isClosed, unit_x, unit_y)
+ Point2D points[];
+ int nPoints; /* Number of points (nPoints>=3) */
+ int isClosed; /* CLOSED_CONTOUR or OPEN_CONTOUR */
+ double unit_x, unit_y; /* Unit length in x and y (norm=1) */
+{
+ CubicSpline *spline;
+ register CubicSpline *s1, *s2;
+ int n, i;
+ double norm, dx, dy;
+ TriDiagonalMatrix *A; /* The tri-diagonal matrix is saved here. */
+
+ spline = Blt_Malloc(sizeof(CubicSpline) * nPoints);
+ if (spline == NULL) {
+ return NULL;
+ }
+ A = Blt_Malloc(sizeof(TriDiagonalMatrix) * nPoints);
+ if (A == NULL) {
+ Blt_Free(spline);
+ return NULL;
+ }
+ /*
+ * Calculate first differences in (dxdt2[i], dydt2[i]) and interval lengths
+ * in dist[i]:
+ */
+ s1 = spline;
+ for (i = 0; i < nPoints - 1; i++) {
+ s1->dxdt2 = points[i+1].x - points[i].x;
+ s1->dydt2 = points[i+1].y - points[i].y;
+
+ /*
+ * The Norm of a linear stroke is calculated in "normal coordinates"
+ * and used as interval length:
+ */
+ dx = s1->dxdt2 / unit_x;
+ dy = s1->dydt2 / unit_y;
+ s1->t = sqrt(dx * dx + dy * dy);
+
+ s1->dxdt2 /= s1->t; /* first difference, with unit norm: */
+ s1->dydt2 /= s1->t; /* || (dxdt2[i], dydt2[i]) || = 1 */
+ s1++;
+ }
+
+ /*
+ * Setup linear System: Ax = b
+ */
+ n = nPoints - 2; /* Without first and last point */
+ if (isClosed) {
+ /* First and last points must be equal for CLOSED_CONTOURs */
+ spline[nPoints - 1].t = spline[0].t;
+ spline[nPoints - 1].dxdt2 = spline[0].dxdt2;
+ spline[nPoints - 1].dydt2 = spline[0].dydt2;
+ n++; /* Add last point (= first point) */
+ }
+ s1 = spline, s2 = s1 + 1;
+ for (i = 0; i < n; i++) {
+ /* Matrix A, mainly tridiagonal with cyclic second index
+ ("j = j+n mod n")
+ */
+ A[i][0] = s1->t; /* Off-diagonal element A_{i,i-1} */
+ A[i][1] = 2.0 * (s1->t + s2->t); /* A_{i,i} */
+ A[i][2] = s2->t; /* Off-diagonal element A_{i,i+1} */
+
+ /* Right side b_x and b_y */
+ s1->dxdt2 = (s2->dxdt2 - s1->dxdt2) * 6.0;
+ s1->dydt2 = (s2->dydt2 - s1->dydt2) * 6.0;
+
+ /*
+ * If the linear stroke shows a cusp of more than 90 degree,
+ * the right side is reduced to avoid oscillations in the
+ * spline:
+ */
+ /*
+ * The Norm of a linear stroke is calculated in "normal coordinates"
+ * and used as interval length:
+ */
+ dx = s1->dxdt2 / unit_x;
+ dy = s1->dydt2 / unit_y;
+ norm = sqrt(dx * dx + dy * dy) / 8.5;
+ if (norm > 1.0) {
+ /* The first derivative will not be continuous */
+ s1->dxdt2 /= norm;
+ s1->dydt2 /= norm;
+ }
+ s1++, s2++;
+ }
+
+ if (!isClosed) {
+ /* Third derivative is set to zero at both ends */
+ A[0][1] += A[0][0]; /* A_{0,0} */
+ A[0][0] = 0.0; /* A_{0,n-1} */
+ A[n-1][1] += A[n-1][2]; /* A_{n-1,n-1} */
+ A[n-1][2] = 0.0; /* A_{n-1,0} */
+ }
+ /* Solve linear systems for dxdt2[] and dydt2[] */
+
+ if (SolveCubic1(A, n)) { /* Cholesky decomposition */
+ SolveCubic2(A, spline, n); /* A * dxdt2 = b_x */
+ } else { /* Should not happen, but who knows ... */
+ Blt_Free(A);
+ Blt_Free(spline);
+ return NULL;
+ }
+ /* Shift all second derivatives one place right and update the ends. */
+ s2 = spline + n, s1 = s2 - 1;
+ for (/* empty */; s2 > spline; s2--, s1--) {
+ s2->dxdt2 = s1->dxdt2;
+ s2->dydt2 = s1->dydt2;
+ }
+ if (isClosed) {
+ spline[0].dxdt2 = spline[n].dxdt2;
+ spline[0].dydt2 = spline[n].dydt2;
+ } else {
+ /* Third derivative is 0.0 for the first and last interval. */
+ spline[0].dxdt2 = spline[1].dxdt2;
+ spline[0].dydt2 = spline[1].dydt2;
+ spline[n + 1].dxdt2 = spline[n].dxdt2;
+ spline[n + 1].dydt2 = spline[n].dydt2;
+ }
+ Blt_Free( A);
+ return spline;
+}
+
+
+/*
+ * Calculate interpolated values of the spline function (defined via p_cntr
+ * and the second derivatives dxdt2[] and dydt2[]). The number of tabulated
+ * values is n. On an equidistant grid n_intpol values are calculated.
+ */
+static int
+CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline)
+ Point2D origPts[];
+ int nOrigPts;
+ Point2D intpPts[];
+ int nIntpPts;
+ CubicSpline spline[];
+{
+ double t, t_skip, t_max;
+ Point2D p, q;
+ double d, hx, dx0, dx01, hy, dy0, dy01;
+ int i, count;
+ register CubicSpline *s1, *s2;
+
+ /* The length of the total interval */
+ t_max = 0.0;
+ for (i = 0; i < nOrigPts - 1; i++) {
+ t_max += spline[i].t;
+ }
+
+ /* Need a better way of doing this... */
+
+ /* The distance between interpolated points */
+ t_skip = (1. - 1e-7) * t_max / (nIntpPts - 1);
+
+ t = 0.0; /* Splineeter value */
+ q = origPts[0];
+ count = 0;
+
+ intpPts[count++] = q; /* First point. */
+ t += t_skip;
+
+ s1 = spline, s2 = s1 + 1;
+ for (i = 0; i < nOrigPts - 1; i++) {
+ d = s1->t; /* Interval length */
+ p = q;
+ q = origPts[i+1];
+ hx = (q.x - p.x) / d;
+ hy = (q.y - p.y) / d;
+ dx0 = (s2->dxdt2 + 2 * s1->dxdt2) / 6.0;
+ dy0 = (s2->dydt2 + 2 * s1->dydt2) / 6.0;
+ dx01 = (s2->dxdt2 - s1->dxdt2) / (6.0 * d);
+ dy01 = (s2->dydt2 - s1->dydt2) / (6.0 * d);
+ while (t <= s1->t) { /* t in current interval ? */
+ p.x += t * (hx + (t - d) * (dx0 + t * dx01));
+ p.y += t * (hy + (t - d) * (dy0 + t * dy01));
+ intpPts[count++] = p;
+ t += t_skip;
+ }
+ /* Parameter t relative to start of next interval */
+ t -= s1->t;
+ s1++, s2++;
+ }
+ return count;
+}
+
+/*
+ * Generate a cubic spline curve through the points (x_i,y_i) which are
+ * stored in the linked list p_cntr.
+ * The spline is defined as a 2d-function s(t) = (x(t),y(t)), where the
+ * parameter t is the length of the linear stroke.
+ */
+int
+Blt_NaturalParametricSpline(origPts, nOrigPts, extsPtr, isClosed,
+ intpPts, nIntpPts)
+ Point2D origPts[];
+ int nOrigPts;
+ Extents2D *extsPtr;
+ int isClosed;
+ Point2D *intpPts;
+ int nIntpPts;
+{
+ double unit_x, unit_y; /* To define norm (x,y)-plane */
+ CubicSpline *spline;
+ int result;
+
+ if (nOrigPts < 3) {
+ return 0;
+ }
+ if (isClosed) {
+ origPts[nOrigPts].x = origPts[0].x;
+ origPts[nOrigPts].y = origPts[0].y;
+ nOrigPts++;
+ }
+
+ /* Width and height of the grid is used at unit length (2d-norm) */
+ unit_x = extsPtr->right - extsPtr->left;
+ unit_y = extsPtr->bottom - extsPtr->top;
+
+ if (unit_x < FLT_EPSILON) {
+ unit_x = FLT_EPSILON;
+ }
+ if (unit_y < FLT_EPSILON) {
+ unit_y = FLT_EPSILON;
+ }
+ /* Calculate parameters for cubic spline:
+ * t = arc length of interval.
+ * dxdt2 = second derivatives of x with respect to t,
+ * dydt2 = second derivatives of y with respect to t,
+ */
+ spline = CubicSlopes(origPts, nOrigPts, isClosed, unit_x, unit_y);
+ if (spline == NULL) {
+ return 0;
+ }
+ result= CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline);
+ Blt_Free(spline);
+ return result;
+}
+
+
+static void
+CatromCoeffs(p, a, b, c, d)
+ Point2D *p;
+ Point2D *a, *b, *c, *d;
+{
+ a->x = -p[0].x + 3.0 * p[1].x - 3.0 * p[2].x + p[3].x;
+ b->x = 2.0 * p[0].x - 5.0 * p[1].x + 4.0 * p[2].x - p[3].x;
+ c->x = -p[0].x + p[2].x;
+ d->x = 2.0 * p[1].x;
+ a->y = -p[0].y + 3.0 * p[1].y - 3.0 * p[2].y + p[3].y;
+ b->y = 2.0 * p[0].y - 5.0 * p[1].y + 4.0 * p[2].y - p[3].y;
+ c->y = -p[0].y + p[2].y;
+ d->y = 2.0 * p[1].y;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ParametricCatromSpline --
+ *
+ * Computes a spline based upon the data points, returning a new
+ * (larger) coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CatromParametricSpline(points, nPoints, intpPts, nIntpPts)
+ Point2D *points;
+ int nPoints;
+ Point2D *intpPts;
+ int nIntpPts;
+{
+ register int i;
+ Point2D *origPts;
+ double t;
+ int interval;
+ Point2D a, b, c, d;
+
+ assert(nPoints > 0);
+ /*
+ * The spline is computed in screen coordinates instead of data
+ * points so that we can select the abscissas of the interpolated
+ * points from each pixel horizontally across the plotting area.
+ */
+ origPts = Blt_Malloc((nPoints + 4) * sizeof(Point2D));
+ memcpy(origPts + 1, points, sizeof(Point2D) * nPoints);
+
+ origPts[0] = origPts[1];
+ origPts[nPoints + 2] = origPts[nPoints + 1] = origPts[nPoints];
+
+ for (i = 0; i < nIntpPts; i++) {
+ interval = (int)intpPts[i].x;
+ t = intpPts[i].y;
+ assert(interval < nPoints);
+ CatromCoeffs(origPts + interval, &a, &b, &c, &d);
+ intpPts[i].x = (d.x + t * (c.x + t * (b.x + t * a.x))) / 2.0;
+ intpPts[i].y = (d.y + t * (c.y + t * (b.y + t * a.y))) / 2.0;
+ }
+ Blt_Free(origPts);
+ return 1;
+}
diff --git a/blt/src/bltSwitch.c b/blt/src/bltSwitch.c
new file mode 100644
index 00000000000..958af4ad1a1
--- /dev/null
+++ b/blt/src/bltSwitch.c
@@ -0,0 +1,525 @@
+/*
+ * bltSwitch.c --
+ *
+ * This module implements command/argument switch parsing
+ * procedures for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "bltSwitch.h"
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FindSwitchSpec --
+ *
+ * Search through a table of configuration specs, looking for
+ * one that matches a given argvName.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry, or NULL
+ * if nothing matched. In that case an error message is left
+ * in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_SwitchSpec *
+FindSwitchSpec(interp, specs, name, needFlags, hateFlags)
+ Tcl_Interp *interp; /* Used for reporting errors. */
+ Blt_SwitchSpec *specs; /* Pointer to table of configuration
+ * specifications for a widget. */
+ char *name; /* Name (suitable for use in a "switch"
+ * command) identifying particular option. */
+ int needFlags; /* Flags that must be present in matching
+ * entry. */
+ int hateFlags; /* Flags that must NOT be present in
+ * matching entry. */
+{
+ register Blt_SwitchSpec *specPtr;
+ register char c; /* First character of current argument. */
+ Blt_SwitchSpec *matchPtr; /* Matching spec, or NULL. */
+ size_t length;
+
+ c = name[1];
+ length = strlen(name);
+ matchPtr = NULL;
+
+ for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+ if (specPtr->switchName == NULL) {
+ continue;
+ }
+ if ((specPtr->switchName[1] != c)
+ || (strncmp(specPtr->switchName, name, length) != 0)) {
+ continue;
+ }
+ if (((specPtr->flags & needFlags) != needFlags)
+ || (specPtr->flags & hateFlags)) {
+ continue;
+ }
+ if (specPtr->switchName[length] == 0) {
+ return specPtr; /* Stop on a perfect match. */
+ }
+ if (matchPtr != NULL) {
+ Tcl_AppendResult(interp, "ambiguous option \"", name, "\"",
+ (char *) NULL);
+ return (Blt_SwitchSpec *) NULL;
+ }
+ matchPtr = specPtr;
+ }
+
+ if (matchPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown option \"", name, "\"", (char *)NULL);
+ return (Blt_SwitchSpec *) NULL;
+ }
+ return matchPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoSwitch --
+ *
+ * This procedure applies a single configuration option
+ * to a widget record.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * WidgRec is modified as indicated by specPtr and value.
+ * The old value is recycled, if that is appropriate for
+ * the value type.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+DoSwitch(interp, specPtr, string, record)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Blt_SwitchSpec *specPtr; /* Specifier to apply. */
+ char *string; /* Value to use to fill in widgRec. */
+ ClientData record; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+{
+ char *ptr;
+ int isNull;
+ int count;
+
+ isNull = ((*string == '\0') && (specPtr->flags & BLT_SWITCH_NULL_OK));
+ do {
+ ptr = (char *)record + specPtr->offset;
+ switch (specPtr->type) {
+ case BLT_SWITCH_BOOLEAN:
+ if (Tcl_GetBoolean(interp, string, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_INT:
+ if (Tcl_GetInt(interp, string, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_INT_NONNEGATIVE:
+ if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (count < 0) {
+ Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+ "can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *((int *)ptr) = count;
+ break;
+
+ case BLT_SWITCH_INT_POSITIVE:
+ if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (count <= 0) {
+ Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+ "must be positive", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *((int *)ptr) = count;
+
+ case BLT_SWITCH_DOUBLE:
+ if (Tcl_GetDouble(interp, string, (double *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case BLT_SWITCH_STRING:
+ {
+ char *old, *new, **strPtr;
+
+ strPtr = (char **)ptr;
+ if (isNull) {
+ new = NULL;
+ } else {
+ new = Blt_Strdup(string);
+ }
+ old = *strPtr;
+ if (old != NULL) {
+ Blt_Free(old);
+ }
+ *strPtr = new;
+ break;
+ }
+ case BLT_SWITCH_LIST:
+ if (Tcl_SplitList(interp, string, &count, (char ***)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_CUSTOM:
+ if ((*specPtr->customPtr->parseProc) \
+ (specPtr->customPtr->clientData, interp, specPtr->switchName,
+ string, record, specPtr->offset) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ default:
+ Tcl_AppendResult(interp, "bad switch table: unknown type \"",
+ Blt_Itoa(specPtr->type), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ specPtr++;
+ } while ((specPtr->switchName == NULL) &&
+ (specPtr->type != BLT_SWITCH_END));
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ProcessSwitches --
+ *
+ * Process command-line options and database options to
+ * fill in fields of a widget record with resources and
+ * other parameters.
+ *
+ * Results:
+ * Returns the number of arguments comsumed by parsing the
+ * command line. If an error occurred, -1 will be returned
+ * and an error messages can be found as the interpreter
+ * result.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information
+ * from argc/argv and the option database. Old information
+ * in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ProcessSwitches(interp, specs, argc, argv, record, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Blt_SwitchSpec *specs; /* Describes legal options. */
+ int argc; /* Number of elements in argv. */
+ char **argv; /* Command-line options. */
+ char *record; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+ int flags; /* Used to specify additional flags
+ * that must be present in switch specs
+ * for them to be considered. Also,
+ * may have BLT_SWITCH_ARGV_ONLY set. */
+{
+ register int count;
+ char *arg;
+ register Blt_SwitchSpec *specPtr;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's
+ * not considered. */
+
+ needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
+ hateFlags = 0;
+
+ /*
+ * Pass 1: Clear the change flags on all the specs so that we
+ * can check it later.
+ */
+ for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+ specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
+ }
+ /*
+ * Pass 2: Process the arguments that match entries in the specs.
+ * It's an error if the argument doesn't match anything.
+ */
+ for (count = 0; count < argc; count++) {
+ arg = argv[count];
+ if (flags & BLT_SWITCH_OBJV_PARTIAL) {
+ if ((arg[0] != '-') || ((arg[1] == '-') && (argv[2] == '\0'))) {
+ /*
+ * If the argument doesn't start with a '-' (not a switch)
+ * or is '--', stop processing and return the number of
+ * arguments comsumed.
+ */
+ return count;
+ }
+ }
+ specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return -1;
+ }
+ if (specPtr->type == BLT_SWITCH_FLAG) {
+ char *ptr;
+
+ ptr = record + specPtr->offset;
+ *((int *)ptr) |= specPtr->value;
+ } else if (specPtr->type == BLT_SWITCH_VALUE) {
+ char *ptr;
+
+ ptr = record + specPtr->offset;
+ *((int *)ptr) = specPtr->value;
+ } else {
+ if ((count + 1) == argc) {
+ Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
+ (char *) NULL);
+ return -1;
+ }
+ count++;
+ if (DoSwitch(interp, specPtr, argv[count], record) != TCL_OK) {
+ char msg[100];
+
+ sprintf(msg, "\n (processing \"%.40s\" option)",
+ specPtr->switchName);
+ Tcl_AddErrorInfo(interp, msg);
+ return -1;
+ }
+ }
+ specPtr->flags |= BLT_SWITCH_SPECIFIED;
+ }
+ return count;
+}
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ProcessObjSwitches --
+ *
+ * Process command-line options and database options to
+ * fill in fields of a widget record with resources and
+ * other parameters.
+ *
+ * Results:
+ * Returns the number of arguments comsumed by parsing the
+ * command line. If an error occurred, -1 will be returned
+ * and an error messages can be found as the interpreter
+ * result.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information
+ * from argc/argv and the option database. Old information
+ * in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ProcessObjSwitches(interp, specs, objc, objv, record, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Blt_SwitchSpec *specs; /* Describes legal options. */
+ int objc; /* Number of elements in argv. */
+ Tcl_Obj *CONST *objv; /* Command-line options. */
+ char *record; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+ int flags; /* Used to specify additional flags
+ * that must be present in switch specs
+ * for them to be considered. Also,
+ * may have BLT_SWITCH_ARGV_ONLY set. */
+{
+ register Blt_SwitchSpec *specPtr;
+ register int count;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's
+ * not considered. */
+
+ needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
+ hateFlags = 0;
+
+ /*
+ * Pass 1: Clear the change flags on all the specs so that we
+ * can check it later.
+ */
+ for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+ specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
+ }
+ /*
+ * Pass 2: Process the arguments that match entries in the specs.
+ * It's an error if the argument doesn't match anything.
+ */
+ for (count = 0; count < objc; count++) {
+ char *arg;
+
+ arg = Tcl_GetString(objv[count]);
+ if (flags & BLT_SWITCH_OBJV_PARTIAL) {
+ if ((arg[0] != '-') || ((arg[1] == '-') && (arg[2] == '\0'))) {
+ /*
+ * If the argument doesn't start with a '-' (not a switch)
+ * or is '--', stop processing and return the number of
+ * arguments comsumed.
+ */
+ return count;
+ }
+ }
+ specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return -1;
+ }
+ if (specPtr->type == BLT_SWITCH_FLAG) {
+ char *ptr;
+
+ ptr = record + specPtr->offset;
+ *((int *)ptr) |= specPtr->value;
+ } else if (specPtr->type == BLT_SWITCH_VALUE) {
+ char *ptr;
+
+ ptr = record + specPtr->offset;
+ *((int *)ptr) = specPtr->value;
+ } else {
+ count++;
+ if (count == objc) {
+ Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
+ (char *) NULL);
+ return -1;
+ }
+ arg = Tcl_GetString(objv[count]);
+ if (DoSwitch(interp, specPtr, arg, record) != TCL_OK) {
+ char msg[100];
+
+ sprintf(msg, "\n (processing \"%.40s\" option)",
+ specPtr->switchName);
+ Tcl_AddErrorInfo(interp, msg);
+ return -1;
+ }
+ }
+ specPtr->flags |= BLT_SWITCH_SPECIFIED;
+ }
+ return count;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeSwitches --
+ *
+ * Free up all resources associated with switch options.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* ARGSUSED */
+void
+Blt_FreeSwitches(specs, record, needFlags)
+ Blt_SwitchSpec *specs; /* Describes legal options. */
+ char *record; /* Record whose fields contain current
+ * values for options. */
+ int needFlags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ register Blt_SwitchSpec *specPtr;
+
+ for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+ if ((specPtr->flags & needFlags) == needFlags) {
+ char *ptr;
+
+ ptr = record + specPtr->offset;
+ switch (specPtr->type) {
+ case BLT_SWITCH_STRING:
+ case BLT_SWITCH_LIST:
+ if (*((char **) ptr) != NULL) {
+ Blt_Free(*((char **) ptr));
+ *((char **) ptr) = NULL;
+ }
+ break;
+ case BLT_SWITCH_CUSTOM:
+ if ((*(char **)ptr != NULL) &&
+ (specPtr->customPtr->freeProc != NULL)) {
+ (*specPtr->customPtr->freeProc)(ptr);
+ *((char **) ptr) = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SwitchModified --
+ *
+ * Given the configuration specifications and one or more option
+ * patterns (terminated by a NULL), indicate if any of the matching
+ * configuration options has been reset.
+ *
+ * Results:
+ * Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int Blt_SwitchChanged
+TCL_VARARGS_DEF(Blt_SwitchSpec *, arg1)
+{
+ va_list argList;
+ Blt_SwitchSpec *specs;
+ register Blt_SwitchSpec *specPtr;
+ register char *switchName;
+
+ specs = TCL_VARARGS_START(Blt_SwitchSpec *, arg1, argList);
+ while ((switchName = va_arg(argList, char *)) != NULL) {
+ for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+ if ((Tcl_StringMatch(specPtr->switchName, switchName)) &&
+ (specPtr->flags & BLT_SWITCH_SPECIFIED)) {
+ va_end(argList);
+ return 1;
+ }
+ }
+ }
+ va_end(argList);
+ return 0;
+}
diff --git a/blt/src/bltSwitch.h b/blt/src/bltSwitch.h
new file mode 100644
index 00000000000..b53b1ab0813
--- /dev/null
+++ b/blt/src/bltSwitch.h
@@ -0,0 +1,79 @@
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+
+typedef int (Blt_SwitchParseProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *switchName, char *value, char *record,
+ int offset));
+
+typedef void (Blt_SwitchFreeProc) _ANSI_ARGS_((char *ptr));
+
+typedef struct {
+ Blt_SwitchParseProc *parseProc; /* Procedure to parse a switch value
+ * and store it in its converted
+ * form in the data record. */
+ Blt_SwitchFreeProc *freeProc; /* Procedure to free a switch. */
+ ClientData clientData; /* Arbitrary one-word value
+ * used by switch parser,
+ * passed to parseProc. */
+} Blt_SwitchCustom;
+
+/*
+ * Type values for Blt_SwitchSpec structures. See the user
+ * documentation for details.
+ */
+typedef enum {
+ BLT_SWITCH_BOOLEAN, BLT_SWITCH_INT, BLT_SWITCH_INT_POSITIVE,
+ BLT_SWITCH_INT_NONNEGATIVE, BLT_SWITCH_DOUBLE, BLT_SWITCH_STRING,
+ BLT_SWITCH_LIST, BLT_SWITCH_FLAG, BLT_SWITCH_VALUE, BLT_SWITCH_CUSTOM,
+ BLT_SWITCH_END
+} Blt_SwitchTypes;
+
+typedef struct {
+ Blt_SwitchTypes type; /* Type of option, such as BLT_SWITCH_COLOR;
+ * see definitions below. Last option in
+ * table must have type BLT_SWITCH_END. */
+ char *switchName; /* Switch used to specify option in argv.
+ * NULL means this spec is part of a group. */
+ int offset; /* Where in widget record to store value;
+ * use Blt_Offset macro to generate values
+ * for this. */
+ int flags; /* Any combination of the values defined
+ * below. */
+ Blt_SwitchCustom *customPtr; /* If type is BLT_SWITCH_CUSTOM then this is
+ * a pointer to info about how to parse and
+ * print the option. Otherwise it is
+ * irrelevant. */
+ int value;
+} Blt_SwitchSpec;
+
+#define BLT_SWITCH_ARGV_ONLY (1<<0)
+#define BLT_SWITCH_OBJV_ONLY (1<<0)
+#define BLT_SWITCH_ARGV_PARTIAL (1<<1)
+#define BLT_SWITCH_OBJV_PARTIAL (1<<1)
+/*
+ * Possible flag values for Blt_SwitchSpec structures. Any bits at
+ * or above BLT_SWITCH_USER_BIT may be used by clients for selecting
+ * certain entries.
+ */
+#define BLT_SWITCH_NULL_OK (1<<0)
+#define BLT_SWITCH_DONT_SET_DEFAULT (1<<3)
+#define BLT_SWITCH_SPECIFIED (1<<4)
+#define BLT_SWITCH_USER_BIT (1<<8)
+
+extern int Blt_ProcessSwitches _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_SwitchSpec *specs, int argc, char **argv, char *record,
+ int flags));
+
+extern void Blt_FreeSwitches _ANSI_ARGS_((Blt_SwitchSpec *specs, char *record,
+ int flags));
+
+extern int Blt_SwitchChanged _ANSI_ARGS_(TCL_VARARGS(Blt_SwitchSpec *, specs));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+extern int Blt_ProcessObjSwitches _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_SwitchSpec *specPtr, int objc, Tcl_Obj *CONST *objv, char *record,
+ int flags));
+#endif
diff --git a/blt/src/bltTable.c b/blt/src/bltTable.c
new file mode 100644
index 00000000000..5e41e588aff
--- /dev/null
+++ b/blt/src/bltTable.c
@@ -0,0 +1,4956 @@
+/*
+ * bltTable.c --
+ *
+ * This module implements a table-based geometry manager
+ * for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "table" geometry manager was created by George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 3) No way to detect if widget is already a container of another
+ * geometry manager. This one is especially bad with toplevel
+ * widgets, causing the window manager to lock-up trying to handle the
+ * myriads of resize requests.
+ *
+ * Note: This problem continues in Tk 8.x. It's possible for a widget
+ * to be a container for two different geometry managers. Each manager
+ * will set its own requested geometry for the container widget. The
+ * winner sets the geometry last (sometimes ad infinitum).
+ *
+ * 7) Relative sizing of partitions?
+ *
+ */
+
+#include "bltInt.h"
+
+#include "bltTable.h"
+
+#define TABLE_THREAD_KEY "BLT Table Data"
+#define TABLE_DEF_PAD 0
+
+/*
+ * Default values for widget attributes.
+ */
+#define DEF_TABLE_ANCHOR "center"
+#define DEF_TABLE_COLUMNS "0"
+#define DEF_TABLE_FILL "none"
+#define DEF_TABLE_PAD "0"
+#define DEF_TABLE_PROPAGATE "1"
+#define DEF_TABLE_RESIZE "both"
+#define DEF_TABLE_ROWS "0"
+#define DEF_TABLE_SPAN "1"
+#define DEF_TABLE_CONTROL "normal"
+#define DEF_TABLE_WEIGHT "1.0"
+
+#define ENTRY_DEF_PAD 0
+#define ENTRY_DEF_ANCHOR TK_ANCHOR_CENTER
+#define ENTRY_DEF_FILL FILL_NONE
+#define ENTRY_DEF_SPAN 1
+#define ENTRY_DEF_CONTROL CONTROL_NORMAL
+#define ENTRY_DEF_IPAD 0
+
+#define ROWCOL_DEF_RESIZE (RESIZE_BOTH | RESIZE_VIRGIN)
+#define ROWCOL_DEF_PAD 0
+#define ROWCOL_DEF_WEIGHT 1.0
+
+static Tk_Uid rowUid, columnUid;
+
+static void WidgetGeometryProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void WidgetCustodyProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr tableMgrInfo =
+{
+ "table", /* Name of geometry manager used by winfo */
+ WidgetGeometryProc, /* Procedure to for new geometry requests */
+ WidgetCustodyProc, /* Procedure when widget is taken away */
+};
+
+static int StringToLimits _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+
+static char *LimitsToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption limitsOption =
+{
+ StringToLimits, LimitsToString, (ClientData)0
+};
+
+static int StringToResize _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *ResizeToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption resizeOption =
+{
+ StringToResize, ResizeToString, (ClientData)0
+};
+
+static int StringToControl _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *ControlToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption controlOption =
+{
+ StringToControl, ControlToString, (ClientData)0
+};
+
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltDistanceOption;
+
+static Tk_ConfigSpec rowConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+ DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
+ DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
+ TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
+ {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+ DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
+ TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tk_ConfigSpec columnConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+ DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
+ DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
+ TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
+ {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+ DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
+ TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
+ {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec entryConfigSpecs[] =
+{
+ {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
+ DEF_TABLE_ANCHOR, Tk_Offset(Entry, anchor),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL,
+ DEF_TABLE_SPAN, Tk_Offset(Entry, column.span),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-columncontrol", "columnControl", (char *)NULL,
+ DEF_TABLE_CONTROL, Tk_Offset(Entry, column.control),
+ TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
+ {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, column.span), 0},
+ {TK_CONFIG_SYNONYM, "-ccontrol", "columnControl", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, column.control), 0},
+ {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
+ DEF_TABLE_FILL, Tk_Offset(Entry, fill),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_SYNONYM, "-height", "reqHeight", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, reqHeight), 0},
+ {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, padX), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, padY), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-ipadx", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, ipadX), 0, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-ipady", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, ipadY), 0, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-reqheight", "reqHeight", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, reqHeight), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_CUSTOM, "-reqwidth", "reqWidth", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, reqWidth), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL,
+ DEF_TABLE_SPAN, Tk_Offset(Entry, row.span),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-rowcontrol", "rowControl", (char *)NULL,
+ DEF_TABLE_CONTROL, Tk_Offset(Entry, row.control),
+ TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
+ {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, row.span), 0},
+ {TK_CONFIG_SYNONYM, "-rcontrol", "rowControl", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, row.control), 0},
+ {TK_CONFIG_SYNONYM, "-width", "reqWidth", (char *)NULL,
+ (char *)NULL, Tk_Offset(Entry, reqWidth), 0},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec tableConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+ DEF_TABLE_PAD, Tk_Offset(Table, padX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+ DEF_TABLE_PAD, Tk_Offset(Table, padY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_BOOLEAN, "-propagate", (char *)NULL, (char *)NULL,
+ DEF_TABLE_PROPAGATE, Tk_Offset(Table, propagate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Table, reqHeight), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL,
+ (char *)NULL, Tk_Offset(Table, reqWidth), TK_CONFIG_NULL_OK,
+ &limitsOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * Forward declarations
+ */
+static void ArrangeTable _ANSI_ARGS_((ClientData clientData));
+static void DestroyTable _ANSI_ARGS_((DestroyData dataPtr));
+static void DestroyEntry _ANSI_ARGS_((Entry * entryPtr));
+static void TableEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void BinEntry _ANSI_ARGS_((Table *tablePtr, Entry * entryPtr));
+static RowColumn *InitSpan _ANSI_ARGS_((PartitionInfo * infoPtr, int start,
+ int span));
+
+#ifdef __STDC__
+static EntrySearchProc FindEntry;
+static Tcl_CmdProc TableCmd;
+static Tcl_InterpDeleteProc TableInterpDeleteProc;
+static Tk_EventProc WidgetEventProc;
+#endif
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToLimits --
+ *
+ * Converts the list of elements into zero or more pixel values which
+ * determine the range of pixel values possible. An element can be in
+ * any form accepted by Tk_GetPixels. The list has a different meaning
+ * based upon the number of elements.
+ *
+ * # of elements:
+ *
+ * 0 - the limits are reset to the defaults.
+ * 1 - the minimum and maximum values are set to this
+ * value, freezing the range at a single value.
+ * 2 - first element is the minimum, the second is the
+ * maximum.
+ * 3 - first element is the minimum, the second is the
+ * maximum, and the third is the nominal value.
+ *
+ * Any element may be the empty string which indicates the default.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The min and max fields
+ * of the range are set.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToLimits(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Widget of table */
+ char *string; /* New width list */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of limits */
+{
+ Limits *limitsPtr = (Limits *)(widgRec + offset);
+ char **elemArr;
+ int nElem;
+ int limArr[3];
+ Tk_Window winArr[3];
+ int flags;
+
+ elemArr = NULL;
+ nElem = 0;
+
+ /* Initialize limits to default values */
+ limArr[2] = LIMITS_NOM;
+ limArr[1] = LIMITS_MAX;
+ limArr[0] = LIMITS_MIN;
+ winArr[0] = winArr[1] = winArr[2] = NULL;
+ flags = 0;
+
+ if (string != NULL) {
+ int size;
+ int i;
+
+ if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem > 3) {
+ Tcl_AppendResult(interp, "wrong # limits \"", string, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ for (i = 0; i < nElem; i++) {
+ if (elemArr[i][0] == '\0') {
+ continue; /* Empty string: use default value */
+ }
+ flags |= (LIMITS_SET_BIT << i);
+ if ((elemArr[i][0] == '.') &&
+ ((elemArr[i][1] == '\0') || isalpha(UCHAR(elemArr[i][1])))) {
+ Tk_Window tkwin2;
+
+ /* Widget specified: save pointer to widget */
+ tkwin2 = Tk_NameToWindow(interp, elemArr[i], tkwin);
+ if (tkwin2 == NULL) {
+ goto error;
+ }
+ winArr[i] = tkwin2;
+ } else {
+ if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) {
+ goto error;
+ }
+ if ((size < LIMITS_MIN) || (size > LIMITS_MAX)) {
+ Tcl_AppendResult(interp, "bad limits \"", string, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ limArr[i] = size;
+ }
+ }
+ Blt_Free(elemArr);
+ }
+ /*
+ * Check the limits specified. We can't check the requested
+ * size of widgets.
+ */
+ switch (nElem) {
+ case 1:
+ flags |= (LIMITS_SET_MIN | LIMITS_SET_MAX);
+ if (winArr[0] == NULL) {
+ limArr[1] = limArr[0]; /* Set minimum and maximum to value */
+ } else {
+ winArr[1] = winArr[0];
+ }
+ break;
+
+ case 2:
+ if ((winArr[0] == NULL) && (winArr[1] == NULL) &&
+ (limArr[1] < limArr[0])) {
+ Tcl_AppendResult(interp, "bad range \"", string,
+ "\": min > max", (char *)NULL);
+ return TCL_ERROR; /* Minimum is greater than maximum */
+ }
+ break;
+
+ case 3:
+ if ((winArr[0] == NULL) && (winArr[1] == NULL)) {
+ if (limArr[1] < limArr[0]) {
+ Tcl_AppendResult(interp, "bad range \"", string,
+ "\": min > max", (char *)NULL);
+ return TCL_ERROR; /* Minimum is greater than maximum */
+ }
+ if ((winArr[2] == NULL) &&
+ ((limArr[2] < limArr[0]) || (limArr[2] > limArr[1]))) {
+ Tcl_AppendResult(interp, "nominal value \"", string,
+ "\" out of range", (char *)NULL);
+ return TCL_ERROR; /* Nominal is outside of range defined
+ * by minimum and maximum */
+ }
+ }
+ break;
+ }
+ limitsPtr->min = limArr[0];
+ limitsPtr->max = limArr[1];
+ limitsPtr->nom = limArr[2];
+ limitsPtr->wMin = winArr[0];
+ limitsPtr->wMax = winArr[1];
+ limitsPtr->wNom = winArr[2];
+ limitsPtr->flags = flags;
+ return TCL_OK;
+ error:
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResetLimits --
+ *
+ * Resets the limits to their default values.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+INLINE static void
+ResetLimits(limitsPtr)
+ Limits *limitsPtr; /* Limits to be imposed on the value */
+{
+ limitsPtr->flags = 0;
+ limitsPtr->min = LIMITS_MIN;
+ limitsPtr->max = LIMITS_MAX;
+ limitsPtr->nom = LIMITS_NOM;
+ limitsPtr->wNom = limitsPtr->wMax = limitsPtr->wMin = NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetBoundedWidth --
+ *
+ * Bounds a given width value to the limits described in the limit
+ * structure. The initial starting value may be overridden by the
+ * nominal value in the limits.
+ *
+ * Results:
+ * Returns the constrained value.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetBoundedWidth(width, limitsPtr)
+ int width; /* Initial value to be constrained */
+ Limits *limitsPtr; /* Limits to be imposed on the value */
+{
+ /*
+ * Check widgets for requested width values;
+ */
+ if (limitsPtr->wMin != NULL) {
+ limitsPtr->min = Tk_ReqWidth(limitsPtr->wMin);
+ }
+ if (limitsPtr->wMax != NULL) {
+ limitsPtr->max = Tk_ReqWidth(limitsPtr->wMax);
+ }
+ if (limitsPtr->wNom != NULL) {
+ limitsPtr->nom = Tk_ReqWidth(limitsPtr->wNom);
+ }
+ if (limitsPtr->flags & LIMITS_SET_NOM) {
+ width = limitsPtr->nom; /* Override initial value */
+ }
+ if (width < limitsPtr->min) {
+ width = limitsPtr->min; /* Bounded by minimum value */
+ } else if (width > limitsPtr->max) {
+ width = limitsPtr->max; /* Bounded by maximum value */
+ }
+ return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetBoundedHeight --
+ *
+ * Bounds a given value to the limits described in the limit
+ * structure. The initial starting value may be overridden by the
+ * nominal value in the limits.
+ *
+ * Results:
+ * Returns the constrained value.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetBoundedHeight(height, limitsPtr)
+ int height; /* Initial value to be constrained */
+ Limits *limitsPtr; /* Limits to be imposed on the value */
+{
+ /*
+ * Check widgets for requested height values;
+ */
+ if (limitsPtr->wMin != NULL) {
+ limitsPtr->min = Tk_ReqHeight(limitsPtr->wMin);
+ }
+ if (limitsPtr->wMax != NULL) {
+ limitsPtr->max = Tk_ReqHeight(limitsPtr->wMax);
+ }
+ if (limitsPtr->wNom != NULL) {
+ limitsPtr->nom = Tk_ReqHeight(limitsPtr->wNom);
+ }
+ if (limitsPtr->flags & LIMITS_SET_NOM) {
+ height = limitsPtr->nom;/* Override initial value */
+ }
+ if (height < limitsPtr->min) {
+ height = limitsPtr->min;/* Bounded by minimum value */
+ } else if (height > limitsPtr->max) {
+ height = limitsPtr->max;/* Bounded by maximum value */
+ }
+ return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfLimits --
+ *
+ * Convert the values into a list representing the limits.
+ *
+ * Results:
+ * The static string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfLimits(limitsPtr)
+ Limits *limitsPtr;
+{
+ Tcl_DString buffer;
+#define STRING_SPACE 200
+ static char string[STRING_SPACE + 1];
+
+ Tcl_DStringInit(&buffer);
+
+ if (limitsPtr->wMin != NULL) {
+ Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMin));
+ } else if (limitsPtr->flags & LIMITS_SET_MIN) {
+ Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->min));
+ } else {
+ Tcl_DStringAppendElement(&buffer, "");
+ }
+
+ if (limitsPtr->wMax != NULL) {
+ Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMax));
+ } else if (limitsPtr->flags & LIMITS_SET_MAX) {
+ Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->max));
+ } else {
+ Tcl_DStringAppendElement(&buffer, "");
+ }
+
+ if (limitsPtr->wNom != NULL) {
+ Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wNom));
+ } else if (limitsPtr->flags & LIMITS_SET_NOM) {
+ Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->nom));
+ } else {
+ Tcl_DStringAppendElement(&buffer, "");
+ }
+ strncpy(string, Tcl_DStringValue(&buffer), STRING_SPACE);
+ string[STRING_SPACE] = '\0';
+ return string;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LimitsToString --
+ *
+ * Convert the limits of the pixel values allowed into a list.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+LimitsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Row/column structure record */
+ int offset; /* Offset of widget RowColumn record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation routine */
+{
+ Limits *limitsPtr = (Limits *)(widgRec + offset);
+
+ return NameOfLimits(limitsPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToResize --
+ *
+ * Converts the resize mode into its numeric representation. Valid
+ * mode strings are "none", "expand", "shrink", or "both".
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToResize(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Resize style string */
+ char *widgRec; /* Entry structure record */
+ int offset; /* Offset of style in record */
+{
+ int *resizePtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *resizePtr = RESIZE_NONE;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *resizePtr = RESIZE_BOTH;
+ } else if ((c == 'e') && (strncmp(string, "expand", length) == 0)) {
+ *resizePtr = RESIZE_EXPAND;
+ } else if ((c == 's') && (strncmp(string, "shrink", length) == 0)) {
+ *resizePtr = RESIZE_SHRINK;
+ } else {
+ Tcl_AppendResult(interp, "bad resize argument \"", string,
+ "\": should be \"none\", \"expand\", \"shrink\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfResize --
+ *
+ * Converts the resize value into its string representation.
+ *
+ * Results:
+ * Returns a pointer to the static name string.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfResize(resize)
+ int resize;
+{
+ switch (resize & RESIZE_BOTH) {
+ case RESIZE_NONE:
+ return "none";
+ case RESIZE_EXPAND:
+ return "expand";
+ case RESIZE_SHRINK:
+ return "shrink";
+ case RESIZE_BOTH:
+ return "both";
+ default:
+ return "unknown resize value";
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResizeToString --
+ *
+ * Returns resize mode string based upon the resize flags.
+ *
+ * Results:
+ * The resize mode string is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ResizeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Row/column structure record */
+ int offset; /* Offset of resize in RowColumn record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int resize = *(int *)(widgRec + offset);
+
+ return NameOfResize(resize);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToControl --
+ *
+ * Converts the control string into its numeric representation.
+ * Valid control strings are "none", "normal", and "full".
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToControl(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* Control style string */
+ char *widgRec; /* Entry structure record */
+ int offset; /* Offset of style in record */
+{
+ double *controlPtr = (double *)(widgRec + offset);
+ unsigned int length;
+ int bool;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if (Tcl_GetBoolean(NULL, string, &bool) == TCL_OK) {
+ *controlPtr = bool;
+ return TCL_OK;
+ }
+ if ((c == 'n') && (length > 1) &&
+ (strncmp(string, "normal", length) == 0)) {
+ *controlPtr = CONTROL_NORMAL;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(string, "none", length) == 0)) {
+ *controlPtr = CONTROL_NONE;
+ } else if ((c == 'f') && (strncmp(string, "full", length) == 0)) {
+ *controlPtr = CONTROL_FULL;
+ } else {
+ double control;
+
+ if ((Tcl_GetDouble(interp, string, &control) != TCL_OK) ||
+ (control < 0.0)) {
+ Tcl_AppendResult(interp, "bad control argument \"", string,
+ "\": should be \"normal\", \"none\", or \"full\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *controlPtr = control;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfControl --
+ *
+ * Converts the control value into its string representation.
+ *
+ * Results:
+ * Returns a pointer to the static name string.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfControl(control)
+ double control;
+{
+ if (control == CONTROL_NORMAL) {
+ return "normal";
+ } else if (control == CONTROL_NONE) {
+ return "none";
+ } else if (control == CONTROL_FULL) {
+ return "full";
+ } else {
+ static char string[TCL_DOUBLE_SPACE + 1];
+
+ sprintf(string, "%g", control);
+ return string;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ControlToString --
+ *
+ * Returns control mode string based upon the control flags.
+ *
+ * Results:
+ * The control mode string is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ControlToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Row/column structure record */
+ int offset; /* Offset of control in RowColumn record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ double control = *(double *)(widgRec + offset);
+
+ return NameOfControl(control);
+}
+
+
+static void
+EventuallyArrangeTable(tablePtr)
+ Table *tablePtr;
+{
+ if (!(tablePtr->flags & ARRANGE_PENDING)) {
+ tablePtr->flags |= ARRANGE_PENDING;
+ Tcl_DoWhenIdle(ArrangeTable, tablePtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TableEventProc --
+ *
+ * This procedure is invoked by the Tk event handler when the
+ * container widget is reconfigured or destroyed.
+ *
+ * The table will be rearranged at the next idle point if the
+ * container widget has been resized or moved. There's a
+ * distinction made between parent and non-parent container
+ * arrangements. If the container is moved and it's the parent
+ * of the widgets, they're are moved automatically. If it's
+ * not the parent, those widgets need to be moved manually.
+ * This can be a performance hit in rare cases where we're
+ * scrolling the container (by moving the window) and there
+ * are lots of non-child widgets arranged insided.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the table associated with tkwin to have its
+ * layout re-computed and drawn at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TableEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about widget */
+ XEvent *eventPtr; /* Information about event */
+{
+ register Table *tablePtr = clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if ((tablePtr->container.width != Tk_Width(tablePtr->tkwin)) ||
+ (tablePtr->container.height != Tk_Height(tablePtr->tkwin))
+ || (tablePtr->flags & NON_PARENT)) {
+ EventuallyArrangeTable(tablePtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (tablePtr->flags & ARRANGE_PENDING) {
+ Tcl_CancelIdleCall(ArrangeTable, tablePtr);
+ }
+ tablePtr->tkwin = NULL;
+ Tcl_EventuallyFree(tablePtr, DestroyTable);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetEventProc --
+ *
+ * This procedure is invoked by the Tk event handler when
+ * StructureNotify events occur in a widget managed by the table.
+ * For example, when a managed widget is destroyed, it frees the
+ * corresponding entry structure and arranges for the table
+ * layout to be re-computed at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the managed widget was deleted, the Entry structure gets
+ * cleaned up and the table is rearranged.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+WidgetEventProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to Entry structure for widget
+ * referred to by eventPtr. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+ Table *tablePtr = entryPtr->tablePtr;
+
+ if (eventPtr->type == ConfigureNotify) {
+ int borderWidth;
+
+ tablePtr->flags |= REQUEST_LAYOUT;
+ borderWidth = Tk_Changes(entryPtr->tkwin)->border_width;
+ if (entryPtr->borderWidth != borderWidth) {
+ entryPtr->borderWidth = borderWidth;
+ EventuallyArrangeTable(tablePtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ entryPtr->tkwin = NULL;
+ DestroyEntry(entryPtr);
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetCustodyProc --
+ *
+ * This procedure is invoked when a widget has been stolen by
+ * another geometry manager. The information and memory
+ * associated with the widget is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the table to have its layout re-arranged at the
+ * next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+WidgetCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Information about the widget */
+ Tk_Window tkwin; /* Not used. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+ Table *tablePtr = entryPtr->tablePtr;
+
+ if (Tk_IsMapped(entryPtr->tkwin)) {
+ Tk_UnmapWindow(entryPtr->tkwin);
+ }
+ Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+ entryPtr->tkwin = NULL;
+ DestroyEntry(entryPtr);
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetGeometryProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for widgets
+ * managed by the table geometry manager.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the table to have its layout re-computed and
+ * re-arranged at the next idle point.
+ *
+ * ---------------------------------------------------------------------------- */
+/* ARGSUSED */
+static void
+WidgetGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Information about widget that got new
+ * preferred geometry. */
+ Tk_Window tkwin; /* Other Tk-related information about the
+ * widget. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+
+ entryPtr->tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(entryPtr->tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * FindEntry --
+ *
+ * Searches for the table entry corresponding to the given
+ * widget.
+ *
+ * Results:
+ * If a structure associated with the widget exists, a pointer to
+ * that structure is returned. Otherwise NULL.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Entry *
+FindEntry(tablePtr, tkwin)
+ Table *tablePtr;
+ Tk_Window tkwin; /* Widget associated with table entry */
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(tablePtr->entryTable), (char *)tkwin);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return (Entry *) Blt_GetHashValue(hPtr);
+}
+
+
+static int
+GetEntry(interp, tablePtr, string, entryPtrPtr)
+ Tcl_Interp *interp;
+ Table *tablePtr;
+ char *string;
+ Entry **entryPtrPtr;
+{
+ Tk_Window tkwin;
+ Entry *entryPtr;
+
+ tkwin = Tk_NameToWindow(interp, string, tablePtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ entryPtr = FindEntry(tablePtr, tkwin);
+ if (entryPtr == NULL) {
+ Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
+ "\" is not managed by any table", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *entryPtrPtr = entryPtr;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateEntry --
+ *
+ * This procedure creates and initializes a new Entry structure
+ * to hold a widget. A valid widget has a parent widget that is
+ * either a) the container widget itself or b) a mutual ancestor
+ * of the container widget.
+ *
+ * Results:
+ * Returns a pointer to the new structure describing the new
+ * widget entry. If an error occurred, then the return
+ * value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated and initialized for the Entry structure.
+ *
+ * ---------------------------------------------------------------------------- */
+static Entry *
+CreateEntry(tablePtr, tkwin)
+ Table *tablePtr;
+ Tk_Window tkwin;
+{
+ register Entry *entryPtr;
+ int dummy;
+ Tk_Window parent, ancestor;
+
+ /*
+ * Check that this widget can be managed by this table. A valid
+ * widget has a parent widget that either
+ *
+ * 1) is the container widget, or
+ * 2) is a mutual ancestor of the container widget.
+ */
+ ancestor = Tk_Parent(tkwin);
+ for (parent = tablePtr->tkwin; (parent != ancestor) &&
+ (!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) {
+ /* empty */
+ }
+ if (ancestor != parent) {
+ Tcl_AppendResult(tablePtr->interp, "can't manage \"",
+ Tk_PathName(tkwin), "\" in table \"", Tk_PathName(tablePtr->tkwin),
+ "\"", (char *)NULL);
+ return NULL;
+ }
+ entryPtr = Blt_Calloc(1, sizeof(Entry));
+ assert(entryPtr);
+
+ /* Initialize the entry structure */
+
+ entryPtr->tkwin = tkwin;
+ entryPtr->tablePtr = tablePtr;
+ entryPtr->borderWidth = Tk_Changes(tkwin)->border_width;
+ entryPtr->fill = ENTRY_DEF_FILL;
+ entryPtr->row.control = entryPtr->column.control = ENTRY_DEF_CONTROL;
+ entryPtr->anchor = ENTRY_DEF_ANCHOR;
+ entryPtr->row.span = entryPtr->column.span = ENTRY_DEF_SPAN;
+ ResetLimits(&(entryPtr->reqWidth));
+ ResetLimits(&(entryPtr->reqHeight));
+
+ /*
+ * Add the entry to the following data structures.
+ *
+ * 1) A chain of widgets managed by the table.
+ * 2) A hash table of widgets managed by the table.
+ */
+ entryPtr->linkPtr = Blt_ChainAppend(tablePtr->chainPtr, entryPtr);
+ entryPtr->hashPtr = Blt_CreateHashEntry(&(tablePtr->entryTable),
+ (char *)tkwin, &dummy);
+ Blt_SetHashValue(entryPtr->hashPtr, entryPtr);
+
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, WidgetEventProc,
+ entryPtr);
+ Tk_ManageGeometry(tkwin, &tableMgrInfo, (ClientData)entryPtr);
+
+ return entryPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyEntry --
+ *
+ * Removes the Entry structure from the hash table and frees
+ * the memory allocated by it. If the table is still in use
+ * (i.e. was not called from DestoryTable), remove its entries
+ * from the lists of row and column sorted partitions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the entry is freed up.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyEntry(entryPtr)
+ Entry *entryPtr;
+{
+ Table *tablePtr = entryPtr->tablePtr;
+
+ if (entryPtr->row.linkPtr != NULL) {
+ Blt_ChainDeleteLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
+ }
+ if (entryPtr->column.linkPtr != NULL) {
+ Blt_ChainDeleteLink(entryPtr->column.chainPtr,
+ entryPtr->column.linkPtr);
+ }
+ if (entryPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(tablePtr->chainPtr, entryPtr->linkPtr);
+ }
+ if (entryPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(entryPtr->tkwin, StructureNotifyMask,
+ WidgetEventProc, (ClientData)entryPtr);
+ Tk_ManageGeometry(entryPtr->tkwin, (Tk_GeomMgr *)NULL,
+ (ClientData)entryPtr);
+ if ((tablePtr->tkwin != NULL) &&
+ (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin)) {
+ Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+ }
+ if (Tk_IsMapped(entryPtr->tkwin)) {
+ Tk_UnmapWindow(entryPtr->tkwin);
+ }
+ }
+ if (entryPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(tablePtr->entryTable), entryPtr->hashPtr);
+ }
+ Blt_Free(entryPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureEntry --
+ *
+ * This procedure is called to process an argv/argc list, plus the Tk
+ * option database, in order to configure (or reconfigure) one or more
+ * entries. Entries hold information about widgets managed by the
+ * table geometry manager.
+ *
+ * Note: You can query only one widget at a time. But several can be
+ * reconfigured at once.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * The table layout is recomputed and rearranged at the next idle
+ * point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureEntry(tablePtr, interp, entryPtr, argc, argv)
+ Table *tablePtr;
+ Tcl_Interp *interp;
+ Entry *entryPtr;
+ int argc; /* Option-value arguments */
+ char **argv;
+{
+ int oldRowSpan, oldColSpan;
+
+ if (entryPtr->tablePtr != tablePtr) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(entryPtr->tkwin),
+ "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 0) {
+ return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
+ (char *)entryPtr, (char *)NULL, 0);
+ } else if (argc == 1) {
+ return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
+ (char *)entryPtr, argv[0], 0);
+ }
+ oldRowSpan = entryPtr->row.span;
+ oldColSpan = entryPtr->column.span;
+
+ if (Tk_ConfigureWidget(interp, entryPtr->tkwin, entryConfigSpecs,
+ argc, argv, (char *)entryPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((entryPtr->column.span < 1) || (entryPtr->column.span > USHRT_MAX)) {
+ Tcl_AppendResult(interp, "bad column span specified for \"",
+ Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((entryPtr->row.span < 1) || (entryPtr->row.span > USHRT_MAX)) {
+ Tcl_AppendResult(interp, "bad row span specified for \"",
+ Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((oldColSpan != entryPtr->column.span) ||
+ (oldRowSpan != entryPtr->row.span)) {
+ BinEntry(tablePtr, entryPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * PrintEntry --
+ *
+ * Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the widget
+ * attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+PrintEntry(entryPtr, resultPtr)
+ Entry *entryPtr;
+ Tcl_DString *resultPtr;
+{
+ char string[200];
+
+ sprintf(string, " %d,%d ", entryPtr->row.rcPtr->index,
+ entryPtr->column.rcPtr->index);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ Tcl_DStringAppend(resultPtr, Tk_PathName(entryPtr->tkwin), -1);
+ if (entryPtr->ipadX != ENTRY_DEF_PAD) {
+ Tcl_DStringAppend(resultPtr, " -ipadx ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadX), -1);
+ }
+ if (entryPtr->ipadY != ENTRY_DEF_PAD) {
+ Tcl_DStringAppend(resultPtr, " -ipady ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadY), -1);
+ }
+ if (entryPtr->row.span != ENTRY_DEF_SPAN) {
+ Tcl_DStringAppend(resultPtr, " -rowspan ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->row.span), -1);
+ }
+ if (entryPtr->column.span != ENTRY_DEF_SPAN) {
+ Tcl_DStringAppend(resultPtr, " -columnspan ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->column.span), -1);
+ }
+ if (entryPtr->anchor != ENTRY_DEF_ANCHOR) {
+ Tcl_DStringAppend(resultPtr, " -anchor ", -1);
+ Tcl_DStringAppend(resultPtr, Tk_NameOfAnchor(entryPtr->anchor), -1);
+ }
+ if ((entryPtr->padLeft != ENTRY_DEF_PAD) ||
+ (entryPtr->padRight != ENTRY_DEF_PAD)) {
+ Tcl_DStringAppend(resultPtr, " -padx ", -1);
+ sprintf(string, "{%d %d}", entryPtr->padLeft, entryPtr->padRight);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if ((entryPtr->padTop != ENTRY_DEF_PAD) ||
+ (entryPtr->padBottom != ENTRY_DEF_PAD)) {
+ Tcl_DStringAppend(resultPtr, " -pady ", -1);
+ sprintf(string, "{%d %d}", entryPtr->padTop, entryPtr->padBottom);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if (entryPtr->fill != ENTRY_DEF_FILL) {
+ Tcl_DStringAppend(resultPtr, " -fill ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_NameOfFill(entryPtr->fill), -1);
+ }
+ if (entryPtr->column.control != ENTRY_DEF_CONTROL) {
+ Tcl_DStringAppend(resultPtr, " -columncontrol ", -1);
+ Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->column.control), -1);
+ }
+ if (entryPtr->row.control != ENTRY_DEF_CONTROL) {
+ Tcl_DStringAppend(resultPtr, " -rowcontrol ", -1);
+ Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->row.control), -1);
+ }
+ if ((entryPtr->reqWidth.nom != LIMITS_NOM) ||
+ (entryPtr->reqWidth.min != LIMITS_MIN) ||
+ (entryPtr->reqWidth.max != LIMITS_MAX)) {
+ Tcl_DStringAppend(resultPtr, " -reqwidth {", -1);
+ Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqWidth)), -1);
+ Tcl_DStringAppend(resultPtr, "}", -1);
+ }
+ if ((entryPtr->reqHeight.nom != LIMITS_NOM) ||
+ (entryPtr->reqHeight.min != LIMITS_MIN) ||
+ (entryPtr->reqHeight.max != LIMITS_MAX)) {
+ Tcl_DStringAppend(resultPtr, " -reqheight {", -1);
+ Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqHeight)), -1);
+ Tcl_DStringAppend(resultPtr, "}", -1);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoEntry --
+ *
+ * Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the widget
+ * attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoEntry(interp, tablePtr, entryPtr)
+ Tcl_Interp *interp;
+ Table *tablePtr;
+ Entry *entryPtr;
+{
+ Tcl_DString dString;
+
+ if (entryPtr->tablePtr != tablePtr) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(entryPtr->tkwin),
+ "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ PrintEntry(entryPtr, &dString);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateRowColumn --
+ *
+ * Creates and initializes a structure that manages the size of
+ * a row or column in the table. There will be one of these
+ * structures allocated for each row and column in the table,
+ * regardless if a widget is contained in it or not.
+ *
+ * Results:
+ * Returns a pointer to the newly allocated row or column
+ * structure.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+CreateRowColumn()
+{
+ RowColumn *rcPtr;
+
+ rcPtr = Blt_Malloc(sizeof(RowColumn));
+ rcPtr->resize = ROWCOL_DEF_RESIZE;
+ ResetLimits(&(rcPtr->reqSize));
+ rcPtr->nomSize = LIMITS_NOM;
+ rcPtr->pad.side1 = rcPtr->pad.side2 = ROWCOL_DEF_PAD;
+ rcPtr->size = rcPtr->index = rcPtr->minSpan = 0;
+ rcPtr->weight = ROWCOL_DEF_WEIGHT;
+ return rcPtr;
+}
+
+static PartitionInfo *
+ParseRowColumn2(tablePtr, string, numberPtr)
+ Table *tablePtr;
+ char *string;
+ int *numberPtr;
+{
+ char c;
+ int n;
+ PartitionInfo *infoPtr;
+
+ c = tolower(string[0]);
+ if (c == 'c') {
+ infoPtr = &(tablePtr->columnInfo);
+ } else if (c == 'r') {
+ infoPtr = &(tablePtr->rowInfo);
+ } else {
+ Tcl_AppendResult(tablePtr->interp, "bad index \"", string,
+ "\": must start with \"r\" or \"c\"", (char *)NULL);
+ return NULL;
+ }
+ /* Handle row or column configuration queries */
+ if (Tcl_GetInt(tablePtr->interp, string + 1, &n) != TCL_OK) {
+ return NULL;
+ }
+ *numberPtr = (int)n;
+ return infoPtr;
+}
+
+static PartitionInfo *
+ParseRowColumn(tablePtr, string, numberPtr)
+ Table *tablePtr;
+ char *string;
+ int *numberPtr;
+{
+ int n;
+ PartitionInfo *infoPtr;
+
+ infoPtr = ParseRowColumn2(tablePtr, string, &n);
+ if (infoPtr == NULL) {
+ return NULL;
+ }
+ if ((n < 0) || (n >= Blt_ChainGetLength(infoPtr->chainPtr))) {
+ Tcl_AppendResult(tablePtr->interp, "bad ", infoPtr->type, " index \"",
+ string, "\"", (char *)NULL);
+ return NULL;
+ }
+ *numberPtr = (int)n;
+ return infoPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetRowColumn --
+ *
+ * Gets the designated row or column from the table. If the
+ * row or column index is greater than the size of the table,
+ * new rows/columns will be automatically allocated.
+ *
+ * Results:
+ * Returns a pointer to the row or column structure.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+GetRowColumn(infoPtr, n)
+ PartitionInfo *infoPtr;
+ int n;
+{
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+ register int i;
+
+ for (i = Blt_ChainGetLength(infoPtr->chainPtr); i <= n; i++) {
+ rcPtr = CreateRowColumn();
+ rcPtr->index = i;
+ rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
+ }
+ linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, n);
+ if (linkPtr == NULL) {
+ return NULL;
+ }
+ return Blt_ChainGetValue(linkPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DeleteRowColumn --
+ *
+ * Deletes a span of rows/columns from the table. The number of
+ * rows/columns to be deleted is given by span.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size of the column partition array may be extended and
+ * initialized.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DeleteRowColumn(tablePtr, infoPtr, rcPtr)
+ Table *tablePtr;
+ PartitionInfo *infoPtr;
+ RowColumn *rcPtr;
+{
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Entry *entryPtr;
+
+ /*
+ * Remove any entries that start in the row/column to be deleted.
+ * They point to memory that will be freed.
+ */
+ if (infoPtr->type == rowUid) {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (entryPtr->row.rcPtr->index == rcPtr->index) {
+ DestroyEntry(entryPtr);
+ }
+ }
+ } else {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (entryPtr->column.rcPtr->index == rcPtr->index) {
+ DestroyEntry(entryPtr);
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureRowColumn --
+ *
+ * This procedure is called to process an argv/argc list in order to
+ * configure a row or column in the table geometry manager.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result holds an error message.
+ *
+ * Side effects:
+ * Partition configuration options (bounds, resize flags, etc) get
+ * set. New partitions may be created as necessary. The table is
+ * recalculated and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureRowColumn(tablePtr, infoPtr, pattern, argc, argv)
+ Table *tablePtr; /* Table to be configured */
+ PartitionInfo *infoPtr;
+ char *pattern;
+ int argc;
+ char **argv;
+{
+ RowColumn *rcPtr;
+ register Blt_ChainLink *linkPtr;
+ char string[200];
+ int nMatches;
+
+ nMatches = 0;
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ sprintf(string, "%c%d", pattern[0], rcPtr->index);
+ if (Tcl_StringMatch(string, pattern)) {
+ if (argc == 0) {
+ return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
+ infoPtr->configSpecs, (char *)rcPtr, NULL, 0);
+ } else if (argc == 1) {
+ return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
+ infoPtr->configSpecs, (char *)rcPtr, argv[0], 0);
+ } else {
+ if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
+ infoPtr->configSpecs, argc, argv, (char *)rcPtr,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nMatches++;
+ }
+ }
+ if (nMatches == 0) {
+ int n;
+
+ /*
+ * We found no existing partitions matching this pattern, so
+ * see if this designates an new partition (one beyond the
+ * current range).
+ */
+ if ((Tcl_GetInt(NULL, pattern + 1, &n) != TCL_OK) || (n < 0)) {
+ Tcl_AppendResult(tablePtr->interp, "pattern \"", pattern,
+ "\" matches no ", infoPtr->type, " in table \"",
+ Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ rcPtr = GetRowColumn(infoPtr, n);
+ assert(rcPtr);
+ if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
+ infoPtr->configSpecs, argc, argv, (char *)rcPtr,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ EventuallyArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+static void
+PrintRowColumn(interp, infoPtr, rcPtr, resultPtr)
+ Tcl_Interp *interp;
+ PartitionInfo *infoPtr;
+ RowColumn *rcPtr;
+ Tcl_DString *resultPtr;
+{
+ char string[200];
+ char *padFmt, *sizeFmt;
+
+ if (infoPtr->type == rowUid) {
+ padFmt = " -pady {%d %d}";
+ sizeFmt = " -height {%s}";
+ } else {
+ padFmt = " -padx {%d %d}";
+ sizeFmt = " -width {%s}";
+ }
+ if (rcPtr->resize != ROWCOL_DEF_RESIZE) {
+ Tcl_DStringAppend(resultPtr, " -resize ", -1);
+ Tcl_DStringAppend(resultPtr, NameOfResize(rcPtr->resize), -1);
+ }
+ if ((rcPtr->pad.side1 != ROWCOL_DEF_PAD) ||
+ (rcPtr->pad.side2 != ROWCOL_DEF_PAD)) {
+ sprintf(string, padFmt, rcPtr->pad.side1, rcPtr->pad.side2);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if (rcPtr->weight != ROWCOL_DEF_WEIGHT) {
+ Tcl_DStringAppend(resultPtr, " -weight ", -1);
+ Tcl_DStringAppend(resultPtr, Blt_Dtoa(interp, rcPtr->weight), -1);
+ }
+ if ((rcPtr->reqSize.min != LIMITS_MIN) ||
+ (rcPtr->reqSize.nom != LIMITS_NOM) ||
+ (rcPtr->reqSize.max != LIMITS_MAX)) {
+ sprintf(string, sizeFmt, NameOfLimits(&(rcPtr->reqSize)));
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoRowColumn --
+ *
+ * Returns the options of a partition in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the partition
+ * attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoRowColumn(tablePtr, interp, pattern)
+ Table *tablePtr;
+ Tcl_Interp *interp;
+ char *pattern;
+{
+ RowColumn *rcPtr;
+ char string[200];
+ PartitionInfo *infoPtr;
+ char c;
+ Blt_ChainLink *linkPtr, *lastPtr;
+ Tcl_DString dString;
+
+ c = pattern[0];
+ if ((c == 'r') || (c == 'R')) {
+ infoPtr = &(tablePtr->rowInfo);
+ } else {
+ infoPtr = &(tablePtr->columnInfo);
+ }
+ Tcl_DStringInit(&dString);
+ lastPtr = Blt_ChainLastLink(infoPtr->chainPtr);
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ sprintf(string, "%c%d", infoPtr->type[0], rcPtr->index);
+ if (Tcl_StringMatch(string, pattern)) {
+ Tcl_DStringAppend(&dString, string, -1);
+ PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+ if (linkPtr != lastPtr) {
+ Tcl_DStringAppend(&dString, " \\\n", -1);
+ } else {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ }
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InitSpan --
+ *
+ * Checks the size of the column partitions and extends the size if a
+ * larger array is needed.
+ *
+ * Results:
+ * Returns 1 if the column exists. Otherwise 0 is returned and
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * The size of the column partition array may be extended and
+ * initialized.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+InitSpan(infoPtr, start, span)
+ PartitionInfo *infoPtr;
+ int start, span;
+{
+ int length;
+ RowColumn *rcPtr;
+ register int i;
+ Blt_ChainLink *linkPtr;
+
+ length = Blt_ChainGetLength(infoPtr->chainPtr);
+ for (i = length; i < (start + span); i++) {
+ rcPtr = CreateRowColumn();
+ rcPtr->index = i;
+ rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
+ }
+ linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, start);
+ return Blt_ChainGetValue(linkPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Blt_GetTable --
+ *
+ * Searches for a table associated by the path name of the
+ * widget container.
+ *
+ * Errors may occur because
+ * 1) pathName isn't a valid for any Tk widget, or
+ * 2) there's no table associated with that widget as a container.
+ *
+ * Results:
+ * If a table entry exists, a pointer to the Table structure is
+ * returned. Otherwise NULL is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_GetTable(dataPtr, interp, pathName, tablePtrPtr)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ char *pathName; /* Path name of the container widget. */
+ Table **tablePtrPtr;
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(dataPtr->tableTable), (char *)tkwin);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "no table associated with widget \"",
+ pathName, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *tablePtrPtr = (Table *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateTable --
+ *
+ * This procedure creates and initializes a new Table structure
+ * with tkwin as its container widget. The internal structures
+ * associated with the table are initialized.
+ *
+ * Results:
+ * Returns the pointer to the new Table structure describing the
+ * new table geometry manager. If an error occurred, the return
+ * value will be NULL and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * Memory is allocated and initialized, an event handler is set
+ * up to watch tkwin, etc.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Table *
+CreateTable(dataPtr, interp, pathName)
+ TableInterpData *dataPtr;
+ Tcl_Interp *interp; /* Interpreter associated with table. */
+ char *pathName; /* Path name of the container widget to be
+ * associated with the new table. */
+{
+ register Table *tablePtr;
+ Tk_Window tkwin;
+ int dummy;
+ Blt_HashEntry *hPtr;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ tablePtr = Blt_Calloc(1, sizeof(Table));
+ assert(tablePtr);
+ tablePtr->tkwin = tkwin;
+ tablePtr->interp = interp;
+ tablePtr->rowInfo.type = rowUid;
+ tablePtr->rowInfo.configSpecs = rowConfigSpecs;
+ tablePtr->rowInfo.chainPtr = Blt_ChainCreate();
+ tablePtr->columnInfo.type = columnUid;
+ tablePtr->columnInfo.configSpecs = columnConfigSpecs;
+ tablePtr->columnInfo.chainPtr = Blt_ChainCreate();
+ tablePtr->propagate = TRUE;
+
+ tablePtr->arrangeProc = ArrangeTable;
+ Blt_InitHashTable(&(tablePtr->entryTable), BLT_ONE_WORD_KEYS);
+ tablePtr->findEntryProc = FindEntry;
+
+ ResetLimits(&(tablePtr->reqWidth));
+ ResetLimits(&(tablePtr->reqHeight));
+
+ tablePtr->chainPtr = Blt_ChainCreate();
+ tablePtr->rowInfo.list = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ tablePtr->columnInfo.list = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+
+ Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask,
+ TableEventProc, (ClientData)tablePtr);
+ hPtr = Blt_CreateHashEntry(&(dataPtr->tableTable), (char *)tkwin, &dummy);
+ tablePtr->hashPtr = hPtr;
+ tablePtr->tablePtr = &(dataPtr->tableTable);
+ Blt_SetHashValue(hPtr, (ClientData)tablePtr);
+ return tablePtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureTable --
+ *
+ * This procedure is called to process an argv/argc list in order to
+ * configure the table geometry manager.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Table configuration options (-padx, -pady, etc.) get set.
+ * The table is recalculated and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureTable(tablePtr, interp, argc, argv)
+ Table *tablePtr; /* Table to be configured */
+ Tcl_Interp *interp; /* Interpreter to report results back to */
+ int argc;
+ char **argv; /* Option-value pairs */
+{
+ if (argc == 0) {
+ return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
+ (char *)tablePtr, (char *)NULL, 0);
+ } else if (argc == 1) {
+ return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
+ (char *)tablePtr, argv[0], 0);
+ }
+ if (Tk_ConfigureWidget(interp, tablePtr->tkwin, tableConfigSpecs,
+ argc, argv, (char *)tablePtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Arrange for the table layout to be computed at the next idle point. */
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+static void
+PrintTable(tablePtr, resultPtr)
+ Table *tablePtr;
+ Tcl_DString *resultPtr;
+{
+ char string[200];
+
+ if ((tablePtr->padLeft != TABLE_DEF_PAD) ||
+ (tablePtr->padRight != TABLE_DEF_PAD)) {
+ sprintf(string, " -padx {%d %d}", tablePtr->padLeft, tablePtr->padRight);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if ((tablePtr->padTop != TABLE_DEF_PAD) ||
+ (tablePtr->padBottom != TABLE_DEF_PAD)) {
+ sprintf(string, " -pady {%d %d}", tablePtr->padTop, tablePtr->padBottom);
+ Tcl_DStringAppend(resultPtr, string, -1);
+ }
+ if (!tablePtr->propagate) {
+ Tcl_DStringAppend(resultPtr, " -propagate no", -1);
+ }
+ if ((tablePtr->reqWidth.min != LIMITS_MIN) ||
+ (tablePtr->reqWidth.nom != LIMITS_NOM) ||
+ (tablePtr->reqWidth.max != LIMITS_MAX)) {
+ Tcl_DStringAppend(resultPtr, " -reqwidth {%s}", -1);
+ Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqWidth)), -1);
+ }
+ if ((tablePtr->reqHeight.min != LIMITS_MIN) ||
+ (tablePtr->reqHeight.nom != LIMITS_NOM) ||
+ (tablePtr->reqHeight.max != LIMITS_MAX)) {
+ Tcl_DStringAppend(resultPtr, " -reqheight {%s}", -1);
+ Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqHeight)), -1);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyPartitions --
+ *
+ * Clear each of the lists managing the entries. The entries in
+ * the lists of row and column spans are themselves lists which
+ * need to be cleared.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyPartitions(infoPtr)
+ PartitionInfo *infoPtr;
+{
+ if (infoPtr->list != NULL) {
+ Blt_Chain *chainPtr;
+ Blt_ListNode node;
+
+ for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ chainPtr = (Blt_Chain *)Blt_ListGetValue(node);
+ if (chainPtr != NULL) {
+ Blt_ChainDestroy(chainPtr);
+ }
+ }
+ Blt_ListDestroy(infoPtr->list);
+ }
+ if (infoPtr->chainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(rcPtr);
+ }
+ Blt_ChainDestroy(infoPtr->chainPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyTable --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
+ * clean up the Table structure at a safe time (when no-one is using
+ * it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the table geometry manager is freed up.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyTable(dataPtr)
+ DestroyData dataPtr; /* Table structure */
+{
+ Blt_ChainLink *linkPtr;
+ Entry *entryPtr;
+ Table *tablePtr = (Table *)dataPtr;
+
+ /* Release the chain of entries. */
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ entryPtr->linkPtr = NULL; /* Don't disrupt this chain of entries */
+ DestroyEntry(entryPtr);
+ }
+ Blt_ChainDestroy(tablePtr->chainPtr);
+
+ DestroyPartitions(&(tablePtr->rowInfo));
+ DestroyPartitions(&(tablePtr->columnInfo));
+ Blt_DeleteHashTable(&(tablePtr->entryTable));
+ if (tablePtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(tablePtr->tablePtr, tablePtr->hashPtr);
+ }
+ Blt_Free(tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BinEntry --
+ *
+ * Adds the entry to the lists of both row and column spans. The
+ * layout of the table is done in order of partition spans, from
+ * shorted to longest. The widgets spanning a particular number of
+ * partitions are stored in a linked list. Each list is in turn,
+ * contained within a master list.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The entry is added to both the lists of row and columns spans.
+ * This will effect the layout of the widgets.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+BinEntry(tablePtr, entryPtr)
+ Table *tablePtr;
+ Entry *entryPtr;
+{
+ Blt_ListNode node;
+ Blt_List list;
+ Blt_Chain *chainPtr;
+ int key;
+
+ /*
+ * Remove the entry from both row and column lists. It will be
+ * re-inserted into the table at the new position.
+ */
+ if (entryPtr->column.linkPtr != NULL) {
+ Blt_ChainUnlinkLink(entryPtr->column.chainPtr,
+ entryPtr->column.linkPtr);
+ }
+ if (entryPtr->row.linkPtr != NULL) {
+ Blt_ChainUnlinkLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
+ }
+ list = tablePtr->rowInfo.list;
+ key = 0; /* Initialize key to bogus span */
+ for (node = Blt_ListFirstNode(list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ key = (int)Blt_ListGetKey(node);
+ if (entryPtr->row.span <= key) {
+ break;
+ }
+ }
+ if (key != entryPtr->row.span) {
+ Blt_ListNode newNode;
+
+ /*
+ * Create a new list (bucket) to hold entries of that size span
+ * and and link it into the list of buckets.
+ */
+ newNode = Blt_ListCreateNode(list, (char *)entryPtr->row.span);
+ Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
+ Blt_ListLinkBefore(list, newNode, node);
+ node = newNode;
+ }
+ chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+ if (entryPtr->row.linkPtr == NULL) {
+ entryPtr->row.linkPtr = Blt_ChainAppend(chainPtr, entryPtr);
+ } else {
+ Blt_ChainLinkBefore(chainPtr, entryPtr->row.linkPtr, NULL);
+ }
+ entryPtr->row.chainPtr = chainPtr;
+
+ list = tablePtr->columnInfo.list;
+ key = 0;
+ for (node = Blt_ListFirstNode(list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ key = (int)Blt_ListGetKey(node);
+ if (entryPtr->column.span <= key) {
+ break;
+ }
+ }
+ if (key != entryPtr->column.span) {
+ Blt_ListNode newNode;
+
+ /*
+ * Create a new list (bucket) to hold entries of that size span
+ * and and link it into the list of buckets.
+ */
+ newNode = Blt_ListCreateNode(list, (char *)entryPtr->column.span);
+ Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
+ Blt_ListLinkBefore(list, newNode, node);
+ node = newNode;
+ }
+ chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+ /* Add the new entry to the span bucket */
+ if (entryPtr->column.linkPtr == NULL) {
+ entryPtr->column.linkPtr =
+ Blt_ChainAppend(chainPtr, entryPtr);
+ } else {
+ Blt_ChainLinkBefore(chainPtr, entryPtr->column.linkPtr, NULL);
+ }
+ entryPtr->column.chainPtr = chainPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ParseIndex --
+ *
+ * Parse the entry index string and return the row and column numbers
+ * in their respective parameters. The format of a table entry index
+ * is row,column where row is the row number and column is the
+ * column number. Rows and columns are numbered starting from zero.
+ *
+ * Results:
+ * Returns a standard Tcl result. If TCL_OK is returned, the row and
+ * column numbers are returned via rowPtr and columnPtr respectively.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ParseIndex(interp, string, rowPtr, columnPtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *rowPtr, *columnPtr;
+{
+ char *comma;
+ long row, column;
+ int result;
+
+ comma = strchr(string, ',');
+ if (comma == NULL) {
+ Tcl_AppendResult(interp, "bad index \"", string,
+ "\": should be \"row,column\"", (char *)NULL);
+ return TCL_ERROR;
+
+ }
+ *comma = '\0';
+ result = ((Tcl_ExprLong(interp, string, &row) != TCL_OK) ||
+ (Tcl_ExprLong(interp, comma + 1, &column) != TCL_OK));
+ *comma = ','; /* Repair the argument */
+ if (result) {
+ return TCL_ERROR;
+ }
+ if ((row < 0) || (row > USHRT_MAX)) {
+ Tcl_AppendResult(interp, "bad index \"", string,
+ "\": row is out of range", (char *)NULL);
+ return TCL_ERROR;
+
+ }
+ if ((column < 0) || (column > USHRT_MAX)) {
+ Tcl_AppendResult(interp, "bad index \"", string,
+ "\": column is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *rowPtr = (int)row;
+ *columnPtr = (int)column;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ManageEntry --
+ *
+ * Inserts the given widget into the table at a given row and
+ * column position. The widget can already be managed by this or
+ * another table. The widget will be simply moved to the new
+ * location in this table.
+ *
+ * The new widget is inserted into both a hash table (this is
+ * used to locate the information associated with the widget) and
+ * a list (used to indicate relative ordering of widgets).
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred, TCL_ERROR is
+ * returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ * The table is re-computed and arranged at the next idle point.
+ *
+ * ---------------------------------------------------------------------------- */
+static int
+ManageEntry(interp, tablePtr, tkwin, row, column, argc, argv)
+ Tcl_Interp *interp;
+ Table *tablePtr;
+ Tk_Window tkwin;
+ int row, column;
+ int argc;
+ char **argv;
+{
+ Entry *entryPtr;
+ int result = TCL_OK;
+
+ entryPtr = FindEntry(tablePtr, tkwin);
+ if ((entryPtr != NULL) && (entryPtr->tablePtr != tablePtr)) {
+ /* The entry for the widget already exists. If it's
+ * managed by another table, delete it. */
+ DestroyEntry(entryPtr);
+ entryPtr = NULL;
+ }
+ if (entryPtr == NULL) {
+ entryPtr = CreateEntry(tablePtr, tkwin);
+ if (entryPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (argc > 0) {
+ result = Tk_ConfigureWidget(tablePtr->interp, entryPtr->tkwin,
+ entryConfigSpecs, argc, argv, (char *)entryPtr,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ if ((entryPtr->column.span < 1) || (entryPtr->row.span < 1)) {
+ Tcl_AppendResult(tablePtr->interp, "bad span specified for \"",
+ Tk_PathName(tkwin), "\"", (char *)NULL);
+ DestroyEntry(entryPtr);
+ return TCL_ERROR;
+ }
+ entryPtr->column.rcPtr = InitSpan(&(tablePtr->columnInfo), column,
+ entryPtr->column.span);
+ entryPtr->row.rcPtr = InitSpan(&(tablePtr->rowInfo), row,
+ entryPtr->row.span);
+ /*
+ * Insert the entry into both the row and column layout lists
+ */
+ BinEntry(tablePtr, entryPtr);
+
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BuildTable --
+ *
+ * Processes an argv/argc list of table entries to add and configure
+ * new widgets into the table. A table entry consists of the
+ * widget path name, table index, and optional configuration options.
+ * The first argument in the argv list is the name of the table. If
+ * no table exists for the given widget, a new one is created.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred, TCL_ERROR is
+ * returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is allocated, a new table is possibly created, etc.
+ * The table is re-computed and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+BuildTable(tablePtr, interp, argc, argv)
+ Table *tablePtr; /* Table to manage new widgets */
+ Tcl_Interp *interp; /* Interpreter to report errors back to */
+ int argc; /* */
+ char **argv; /* List of widgets, indices, and options */
+{
+ Tk_Window tkwin;
+ int row, column;
+ int nextRow, nextColumn;
+ register int i;
+
+ /* Process any options specific to the table */
+ for (i = 2; i < argc; i += 2) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ }
+ if (i > argc) {
+ i = argc;
+ }
+ if (i > 2) {
+ if (ConfigureTable(tablePtr, interp, i - 2, argv + 2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nextRow = tablePtr->nRows;
+ nextColumn = 0;
+ argc -= i, argv += i;
+ while (argc > 0) {
+ /*
+ * Allow the name of the widget and row/column index to be
+ * specified in any order.
+ */
+ if (argv[0][0] == '.') {
+ tkwin = Tk_NameToWindow(interp, argv[0], tablePtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if ((argc == 1) || (argv[1][0] == '-')) {
+ /* No row,column index, use defaults instead */
+ row = nextRow, column = nextColumn;
+ argc--, argv++;
+ } else {
+ if (ParseIndex(interp, argv[1], &row, &column) != TCL_OK) {
+ return TCL_ERROR; /* Invalid row,column index */
+ }
+ /* Skip over the widget pathname and table index. */
+ argc -= 2, argv += 2;
+ }
+ } else {
+ if (ParseIndex(interp, argv[0], &row, &column) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 1) {
+ Tcl_AppendResult(interp, "missing widget pathname after \"",
+ argv[0], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_NameToWindow(interp, argv[1], tablePtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ /* Skip over the widget pathname and table index. */
+ argc -= 2, argv += 2;
+ }
+
+ /* Find the end of the widget's option-value pairs */
+ for (i = 0; i < argc; i += 2) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ }
+ if (i > argc) {
+ i = argc;
+ }
+ if (ManageEntry(interp, tablePtr, tkwin, row,
+ column, i, argv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nextColumn = column + 1;
+ argc -= i, argv += i;
+ }
+ /* Arrange for the new table layout to be calculated. */
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+
+ Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ParseItem --
+ *
+ * Parses a string representing an item in the table. An item
+ * may be one of the following:
+ * Rn - Row index, where n is the index of row
+ * Cn - Column index, where n is the index of column
+ * r,c - Cell index, where r is the row index and c
+ * is the column index.
+ *
+ * Results:
+ * Returns a standard Tcl result. If no error occurred, TCL_OK is
+ * returned. *RowPtr* will return the row index. *ColumnPtr*
+ * will return the column index. If the row or column index is
+ * not applicable, -1 is returned via *rowPtr* or *columnPtr*.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ParseItem(tablePtr, string, rowPtr, columnPtr)
+ Table *tablePtr;
+ char *string;
+ int *rowPtr, *columnPtr;
+{
+ char c;
+ long partNum;
+
+ c = tolower(string[0]);
+ *rowPtr = *columnPtr = -1;
+ if (c == 'r') {
+ if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((partNum < 0) || (partNum >= tablePtr->nRows)) {
+ Tcl_AppendResult(tablePtr->interp, "row index \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *rowPtr = (int)partNum;
+ } else if (c == 'c') {
+ if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((partNum < 0) || (partNum >= tablePtr->nColumns)) {
+ Tcl_AppendResult(tablePtr->interp, "column index \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *columnPtr = (int)partNum;
+ } else {
+ if (ParseIndex(tablePtr->interp, string,
+ rowPtr, columnPtr) != TCL_OK) {
+ return TCL_ERROR; /* Invalid row,column index */
+ }
+ if ((*rowPtr < 0) || (*rowPtr >= tablePtr->nRows) ||
+ (*columnPtr < 0) || (*columnPtr >= tablePtr->nColumns)) {
+ Tcl_AppendResult(tablePtr->interp, "index \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based upon the
+ * anchor specified. The anchor indicates where the given xy position
+ * is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | | x,y ---+
+ * w center e | |
+ * | | +-----+
+ * sw --- s --- se
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
+ int dx, dy; /* Difference between outer and inner regions
+ */
+ Tk_Anchor anchor; /* Direction of the anchor */
+ int *xPtr, *yPtr;
+{
+ int x, y;
+
+ x = y = 0;
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y = dy;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x = (dx / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Centered */
+ x = (dx / 2);
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x = (dx / 2);
+ y = dy;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x = dx;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x = dx;
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x = dx;
+ y = dy;
+ break;
+ }
+ *xPtr = (*xPtr) + x;
+ *yPtr = (*yPtr) + y;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqWidth --
+ *
+ * Returns the width requested by the widget starting in the
+ * given entry. The requested space also includes any internal
+ * padding which has been designated for this widget.
+ *
+ * The requested width of the widget is always bounded by the limits
+ * set in entryPtr->reqWidth.
+ *
+ * Results:
+ * Returns the requested width of the widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqWidth(entryPtr)
+ Entry *entryPtr;
+{
+ int width;
+
+ width = Tk_ReqWidth(entryPtr->tkwin) + (2 * entryPtr->ipadX);
+ width = GetBoundedWidth(width, &(entryPtr->reqWidth));
+ return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqHeight --
+ *
+ * Returns the height requested by the widget starting in the
+ * given entry. The requested space also includes any internal
+ * padding which has been designated for this widget.
+ *
+ * The requested height of the widget is always bounded by the limits
+ * set in entryPtr->reqHeight.
+ *
+ * Results:
+ * Returns the requested height of the widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqHeight(entryPtr)
+ Entry *entryPtr;
+{
+ int height;
+
+ height = Tk_ReqHeight(entryPtr->tkwin) + (2 * entryPtr->ipadY);
+ height = GetBoundedHeight(height, &(entryPtr->reqHeight));
+ return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetTotalSpan --
+ *
+ * Sums the row/column space requirements for the entire table.
+ *
+ * Results:
+ * Returns the space currently used in the span of partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetTotalSpan(infoPtr)
+ PartitionInfo *infoPtr;
+{
+ register int spaceUsed;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr; /* Start of partitions */
+
+ spaceUsed = 0;
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ spaceUsed += rcPtr->size;
+ }
+ return spaceUsed;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetSpan --
+ *
+ * Determines the space used by rows/columns for an entry.
+ *
+ * Results:
+ * Returns the space currently used in the span of partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetSpan(infoPtr, entryPtr)
+ PartitionInfo *infoPtr;
+ Entry *entryPtr;
+{
+ RowColumn *startPtr;
+ register int spaceUsed;
+ int count;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr; /* Start of partitions */
+ int span; /* Number of partitions spanned */
+
+ if (infoPtr->type == rowUid) {
+ rcPtr = entryPtr->row.rcPtr;
+ span = entryPtr->row.span;
+ } else {
+ rcPtr = entryPtr->column.rcPtr;
+ span = entryPtr->column.span;
+ }
+
+ count = spaceUsed = 0;
+ linkPtr = rcPtr->linkPtr;
+ startPtr = Blt_ChainGetValue(linkPtr);
+ for ( /*empty*/ ; (linkPtr != NULL) && (count < span);
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ spaceUsed += rcPtr->size;
+ count++;
+ }
+ /*
+ * Subtract off the padding on either side of the span, since the
+ * widget can't grow into it.
+ */
+ spaceUsed -= (startPtr->pad.side1 + rcPtr->pad.side2 + infoPtr->ePad);
+ return spaceUsed;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GrowSpan --
+ *
+ * Expand the span by the amount of the extra space needed. This
+ * procedure is used in LayoutPartitions to grow the partitions to
+ * their minimum nominal size, starting from a zero width and height
+ * space.
+ *
+ * This looks more complicated than it really is. The idea is to make
+ * the size of the partitions correspond to the smallest entry
+ * spans. For example, if widget A is in column 1 and widget B spans
+ * both columns 0 and 1, any extra space needed to fit widget B should
+ * come from column 0.
+ *
+ * On the first pass we try to add space to partitions which have not
+ * been touched yet (i.e. have no nominal size). Since the row and
+ * column lists are sorted in ascending order of the number of rows or
+ * columns spanned, the space is distributed amongst the smallest
+ * spans first.
+ *
+ * The second pass handles the case of widgets which have the same
+ * span. For example, if A and B, which span the same number of
+ * partitions are the only widgets to span column 1, column 1 would
+ * grow to contain the bigger of the two slices of space.
+ *
+ * If there is still extra space after the first two passes, this
+ * means that there were no partitions of with no widget spans or the
+ * same order span that could be expanded. The third pass will try to
+ * remedy this by parcelling out the left over space evenly among the
+ * rest of the partitions.
+ *
+ * On each pass, we have to keep iterating over the span, evenly
+ * doling out slices of extra space, because we may hit partition
+ * limits as space is donated. In addition, if there are left over
+ * pixels because of round-off, this will distribute them as evenly as
+ * possible. For the worst case, it will take *span* passes to
+ * expand the span.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The partitions in the span may be expanded.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+GrowSpan(infoPtr, entryPtr, growth)
+ PartitionInfo *infoPtr;
+ Entry *entryPtr;
+ int growth; /* The amount of extra space needed to
+ * grow the span. */
+{
+ register RowColumn *rcPtr;
+ Blt_ChainLink *linkPtr;
+ int spaceLeft, ration;
+ int nOpen; /* # of partitions with space available */
+ register int n;
+ RowColumn *startPtr; /* Starting (column/row) partition */
+ int span; /* Number of partitions in the span */
+
+ if (infoPtr->type == rowUid) {
+ startPtr = entryPtr->row.rcPtr;
+ span = entryPtr->row.span;
+ } else {
+ startPtr = entryPtr->column.rcPtr;
+ span = entryPtr->column.span;
+ }
+
+ /*
+ * ------------------------------------------------------------------------
+ *
+ * Pass 1: First add space to rows/columns that haven't determined their
+ * nominal sizes yet.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+ nOpen = 0;
+ /* Find out how many partitions have no size yet */
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; n < span; n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if ((rcPtr->nomSize == LIMITS_NOM) && (rcPtr->maxSize > rcPtr->size)) {
+ nOpen++;
+ }
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+
+ while ((nOpen > 0) && (growth > 0)) {
+ ration = growth / nOpen;
+ if (ration == 0) {
+ ration = 1;
+ }
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; (n < span) && (growth > 0); n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ spaceLeft = rcPtr->maxSize - rcPtr->size;
+ if ((rcPtr->nomSize == LIMITS_NOM) && (spaceLeft > 0)) {
+ if (ration < spaceLeft) {
+ growth -= ration;
+ rcPtr->size += ration;
+ } else {
+ growth -= spaceLeft;
+ rcPtr->size += spaceLeft;
+ nOpen--;
+ }
+ rcPtr->minSpan = span;
+ rcPtr->control = entryPtr;
+ }
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ }
+
+ /*
+ * ------------------------------------------------------------------------
+ *
+ * Pass 2: Add space to partitions which have the same minimum span
+ *
+ * ------------------------------------------------------------------------
+ */
+
+ nOpen = 0;
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; n < span; n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if ((rcPtr->minSpan == span) && (rcPtr->maxSize > rcPtr->size)) {
+ nOpen++;
+ }
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ while ((nOpen > 0) && (growth > 0)) {
+ ration = growth / nOpen;
+ if (ration == 0) {
+ ration = 1;
+ }
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; (n < span) && (growth > 0); n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ spaceLeft = rcPtr->maxSize - rcPtr->size;
+ if ((rcPtr->minSpan == span) && (spaceLeft > 0)) {
+ if (ration < spaceLeft) {
+ growth -= ration;
+ rcPtr->size += ration;
+ } else {
+ growth -= spaceLeft;
+ rcPtr->size += spaceLeft;
+ nOpen--;
+ }
+ rcPtr->control = entryPtr;
+ }
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ }
+
+ /*
+ * ------------------------------------------------------------------------
+ *
+ * Pass 3: Try to expand all the partitions with space still available
+ *
+ * ------------------------------------------------------------------------
+ */
+
+ /* Find out how many partitions still have space available */
+ nOpen = 0;
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; n < span; n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if ((rcPtr->resize & RESIZE_EXPAND) && (rcPtr->maxSize > rcPtr->size)) {
+ nOpen++;
+ }
+ /* Set the nominal size of the row/column. */
+ rcPtr->nomSize = rcPtr->size;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ while ((nOpen > 0) && (growth > 0)) {
+ ration = growth / nOpen;
+ if (ration == 0) {
+ ration = 1;
+ }
+ linkPtr = startPtr->linkPtr;
+ for (n = 0; (n < span) && (growth > 0); n++) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ if (!(rcPtr->resize & RESIZE_EXPAND)) {
+ continue;
+ }
+ spaceLeft = rcPtr->maxSize - rcPtr->size;
+ if (spaceLeft > 0) {
+ if (ration < spaceLeft) {
+ growth -= ration;
+ rcPtr->size += ration;
+ } else {
+ growth -= spaceLeft;
+ rcPtr->size += spaceLeft;
+ nOpen--;
+ }
+ rcPtr->nomSize = rcPtr->size;
+ rcPtr->control = entryPtr;
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * AdjustPartitions --
+ *
+ * Adjust the span by the amount of the extra space needed. If the
+ * amount (adjustSpace) is negative, shrink the span, otherwise expand
+ * it. Size constraints on the partitions may prevent any or all of
+ * the spacing adjustments.
+ *
+ * This is very much like the GrowSpan procedure, but in this case we
+ * are shrinking or expanding all the (row or column) partitions. It
+ * uses a two pass approach, first giving space to partitions which
+ * not are smaller/larger than their nominal sizes. This is because
+ * constraints on the partitions may cause resizing to be non-linear.
+ *
+ * If there is still extra space, this means that all partitions are
+ * at least to their nominal sizes. The second pass will try to
+ * add/remove the left over space evenly among all the partitions
+ * which still have space available.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The size of the partitions in the span may be increased or decreased.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+AdjustPartitions(infoPtr, adjustment)
+ PartitionInfo *infoPtr; /* Array of (column/row) partitions */
+ int adjustment; /* The amount of extra space to grow or shrink
+ * the span. If negative, it represents the
+ * amount of space to remove */
+{
+ register RowColumn *rcPtr;
+ int ration; /* Amount of space to ration to each
+ * row/column. */
+ int delta; /* Amount of space needed */
+ int spaceLeft; /* Amount of space still available */
+ int size; /* Amount of space requested for a particular
+ * row/column. */
+ int nOpen; /* Number of rows/columns that still can
+ * be adjusted. */
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+ double totalWeight;
+
+ chainPtr = infoPtr->chainPtr;
+
+ /*
+ * ------------------------------------------------------------------------
+ *
+ * Pass 1: First adjust the size of rows/columns that still haven't
+ * reached their nominal size.
+ *
+ * ------------------------------------------------------------------------
+ */
+ delta = adjustment;
+
+ nOpen = 0;
+ totalWeight = 0.0;
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->weight > 0.0) {
+ if (delta < 0) {
+ spaceLeft = rcPtr->size - rcPtr->nomSize;
+ } else {
+ spaceLeft = rcPtr->nomSize - rcPtr->size;
+ }
+ if (spaceLeft > 0) {
+ nOpen++;
+ totalWeight += rcPtr->weight;
+ }
+ }
+ }
+
+ while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
+ ration = (int)(delta / totalWeight);
+ if (ration == 0) {
+ ration = (delta > 0) ? 1 : -1;
+ }
+ for (linkPtr = Blt_ChainFirstLink(chainPtr);
+ (linkPtr != NULL) && (delta != 0);
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->weight > 0.0) {
+ spaceLeft = rcPtr->nomSize - rcPtr->size;
+ if (((delta > 0) && (spaceLeft > 0)) ||
+ ((delta < 0) && (spaceLeft < 0))) {
+ size = (int)(ration * rcPtr->weight);
+ if (size > delta) {
+ size = delta;
+ }
+ if (ABS(size) < ABS(spaceLeft)) {
+ delta -= size;
+ rcPtr->size += size;
+ } else {
+ delta -= spaceLeft;
+ rcPtr->size += spaceLeft;
+ nOpen--;
+ totalWeight -= rcPtr->weight;
+ }
+ }
+ }
+ }
+ }
+ /*
+ * ------------------------------------------------------------------------
+ *
+ * Pass 2: Adjust the partitions with space still available
+ *
+ * ------------------------------------------------------------------------
+ */
+
+ nOpen = 0;
+ totalWeight = 0.0;
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->weight > 0.0) {
+ if (delta > 0) {
+ spaceLeft = rcPtr->maxSize - rcPtr->size;
+ } else {
+ spaceLeft = rcPtr->size - rcPtr->minSize;
+ }
+ if (spaceLeft > 0) {
+ nOpen++;
+ totalWeight += rcPtr->weight;
+ }
+ }
+ }
+ while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
+ ration = (int)(delta / totalWeight);
+ if (ration == 0) {
+ ration = (delta > 0) ? 1 : -1;
+ }
+ linkPtr = Blt_ChainFirstLink(chainPtr);
+ for ( /*empty*/ ; (linkPtr != NULL) && (delta != 0);
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->weight > 0.0) {
+ if (delta > 0) {
+ spaceLeft = rcPtr->maxSize - rcPtr->size;
+ } else {
+ spaceLeft = rcPtr->minSize - rcPtr->size;
+ }
+ if (((delta > 0) && (spaceLeft > 0)) ||
+ ((delta < 0) && (spaceLeft < 0))) {
+ size = (int)(ration * rcPtr->weight);
+ if (size > delta) {
+ size = delta;
+ }
+ if (ABS(size) < ABS(spaceLeft)) {
+ delta -= size;
+ rcPtr->size += size;
+ } else {
+ delta -= spaceLeft;
+ rcPtr->size += spaceLeft;
+ nOpen--;
+ totalWeight -= rcPtr->weight;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResetPartitions --
+ *
+ * Sets/resets the size of each row and column partition to the
+ * minimum limit of the partition (this is usually zero). This routine
+ * gets called when new widgets are added, deleted, or resized.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The size of each partition is re-initialized to its minimum size.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ResetPartitions(tablePtr, infoPtr, limitsProc)
+ Table *tablePtr;
+ PartitionInfo *infoPtr;
+ LimitsProc *limitsProc;
+{
+ register RowColumn *rcPtr;
+ register Blt_ChainLink *linkPtr;
+ int pad, size;
+
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+
+ /*
+ * The constraint procedure below also has the desired side-effect
+ * of setting the minimum, maximum, and nominal values to the
+ * requested size of its associated widget (if one exists).
+ */
+ size = (*limitsProc) (0, &(rcPtr->reqSize));
+
+ pad = PADDING(rcPtr->pad) + infoPtr->ePad;
+ if (rcPtr->reqSize.flags & LIMITS_SET_NOM) {
+
+ /*
+ * This could be done more cleanly. We want to ensure that the
+ * requested nominal size is not overridden when determining the
+ * normal sizes. So temporarily fix min and max to the nominal
+ * size and reset them back later.
+ */
+ rcPtr->minSize = rcPtr->maxSize = rcPtr->size =
+ rcPtr->nomSize = size + pad;
+
+ } else {
+ /* The range defaults to 0..MAXINT */
+ rcPtr->minSize = rcPtr->reqSize.min + pad;
+ rcPtr->maxSize = rcPtr->reqSize.max + pad;
+ rcPtr->nomSize = LIMITS_NOM;
+ rcPtr->size = pad;
+ }
+ rcPtr->minSpan = 0;
+ rcPtr->control = NULL;
+ rcPtr->count = 0;
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SetNominalSizes
+ *
+ * Sets the normal sizes for each partition. The partition size
+ * is the requested widget size plus an amount of padding. In
+ * addition, adjust the min/max bounds of the partition depending
+ * upon the resize flags (whether the partition can be expanded
+ * or shrunk from its normal size).
+ *
+ * Results:
+ * Returns the total space needed for the all the partitions.
+ *
+ * Side Effects:
+ * The nominal size of each partition is set. This is later used
+ * to determine how to shrink or grow the table if the container
+ * can't be resized to accommodate the exact size requirements
+ * of all the partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+SetNominalSizes(tablePtr, infoPtr)
+ Table *tablePtr;
+ PartitionInfo *infoPtr;
+{
+ register RowColumn *rcPtr;
+ Blt_ChainLink *linkPtr;
+ int pad, size, total;
+
+ total = 0;
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ pad = PADDING(rcPtr->pad) + infoPtr->ePad;
+ /*
+ * Restore the real bounds after temporarily setting nominal size.
+ * These values may have been set in ResetPartitions to restrict
+ * the size of the paritition to the requested range.
+ */
+ rcPtr->minSize = rcPtr->reqSize.min + pad;
+ rcPtr->maxSize = rcPtr->reqSize.max + pad;
+
+ size = rcPtr->size;
+ if (size > rcPtr->maxSize) {
+ size = rcPtr->maxSize;
+ } else if (size < rcPtr->minSize) {
+ size = rcPtr->minSize;
+ }
+ if ((infoPtr->ePad > 0) && (size < tablePtr->editPtr->minSize)) {
+ size = tablePtr->editPtr->minSize;
+ }
+ rcPtr->nomSize = rcPtr->size = size;
+
+ /*
+ * If a partition can't be resized (to either expand or shrink), hold
+ * its respective limit at its normal size.
+ */
+ if (!(rcPtr->resize & RESIZE_EXPAND)) {
+ rcPtr->maxSize = rcPtr->nomSize;
+ }
+ if (!(rcPtr->resize & RESIZE_SHRINK)) {
+ rcPtr->minSize = rcPtr->nomSize;
+ }
+ if (rcPtr->control == NULL) {
+ /* If a row/column contains no entries, then its size
+ * should be locked. */
+ if (rcPtr->resize & RESIZE_VIRGIN) {
+ rcPtr->maxSize = rcPtr->minSize = size;
+ } else {
+ if (!(rcPtr->resize & RESIZE_EXPAND)) {
+ rcPtr->maxSize = size;
+ }
+ if (!(rcPtr->resize & RESIZE_SHRINK)) {
+ rcPtr->minSize = size;
+ }
+ }
+ rcPtr->nomSize = size;
+ }
+ total += rcPtr->nomSize;
+ }
+ return total;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LockPartitions
+ *
+ * Sets the maximum size of a row or column, if the partition
+ * has a widget that controls it.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+LockPartitions(infoPtr)
+ PartitionInfo *infoPtr;
+{
+ register RowColumn *rcPtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->control != NULL) {
+ /* Partition is controlled by this widget */
+ rcPtr->maxSize = rcPtr->size;
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LayoutPartitions --
+ *
+ * Calculates the normal space requirements for both the row and
+ * column partitions. Each widget is added in order of the
+ * number of rows or columns spanned, which defines the space needed
+ * among in the partitions spanned.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ *
+ * The sum of normal sizes set here will be used as the normal size
+ * for the container widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+LayoutPartitions(tablePtr)
+ Table *tablePtr;
+{
+ register Blt_ListNode node;
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr;
+ register Entry *entryPtr;
+ int needed, used, total;
+ PartitionInfo *infoPtr;
+
+ infoPtr = &(tablePtr->columnInfo);
+
+ ResetPartitions(tablePtr, infoPtr, GetBoundedWidth);
+
+ for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (entryPtr->column.control != CONTROL_FULL) {
+ continue;
+ }
+ needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
+ 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+ if (needed <= 0) {
+ continue;
+ }
+ used = GetSpan(infoPtr, entryPtr);
+ if (needed > used) {
+ GrowSpan(infoPtr, entryPtr, needed - used);
+ }
+ }
+ }
+
+ LockPartitions(infoPtr);
+
+ for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+
+ needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
+ 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+
+ if (entryPtr->column.control >= 0.0) {
+ needed = (int)(needed * entryPtr->column.control);
+ }
+ if (needed <= 0) {
+ continue;
+ }
+ used = GetSpan(infoPtr, entryPtr);
+ if (needed > used) {
+ GrowSpan(infoPtr, entryPtr, needed - used);
+ }
+ }
+ }
+ total = SetNominalSizes(tablePtr, infoPtr);
+ tablePtr->normal.width = GetBoundedWidth(total, &(tablePtr->reqWidth)) +
+ PADDING(tablePtr->padX) +
+ 2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
+
+ infoPtr = &(tablePtr->rowInfo);
+
+ ResetPartitions(tablePtr, infoPtr, GetBoundedHeight);
+
+ for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (entryPtr->row.control != CONTROL_FULL) {
+ continue;
+ }
+ needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
+ 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+ if (needed <= 0) {
+ continue;
+ }
+ used = GetSpan(infoPtr, entryPtr);
+ if (needed > used) {
+ GrowSpan(infoPtr, entryPtr, needed - used);
+ }
+ }
+ }
+
+ LockPartitions(&(tablePtr->rowInfo));
+
+ for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ chainPtr = Blt_ChainGetValue(node);
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
+ 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+ if (entryPtr->row.control >= 0.0) {
+ needed = (int)(needed * entryPtr->row.control);
+ }
+ if (needed <= 0) {
+ continue;
+ }
+ used = GetSpan(infoPtr, entryPtr);
+ if (needed > used) {
+ GrowSpan(infoPtr, entryPtr, needed - used);
+ }
+ }
+ }
+ total = SetNominalSizes(tablePtr, infoPtr);
+ tablePtr->normal.height = GetBoundedHeight(total, &(tablePtr->reqHeight)) +
+ PADDING(tablePtr->padY) +
+ 2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeEntries
+ *
+ * Places each widget at its proper location. First determines
+ * the size and position of the each widget. It then considers the
+ * following:
+ *
+ * 1. translation of widget position its parent widget.
+ * 2. fill style
+ * 3. anchor
+ * 4. external and internal padding
+ * 5. widget size must be greater than zero
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The size of each partition is re-initialized its minimum size.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ArrangeEntries(tablePtr)
+ Table *tablePtr; /* Table widget structure */
+{
+ register Blt_ChainLink *linkPtr;
+ register Entry *entryPtr;
+ register int spanWidth, spanHeight;
+ int x, y;
+ int winWidth, winHeight;
+ int dx, dy;
+ int maxX, maxY;
+ int extra;
+
+ maxX = tablePtr->container.width -
+ (Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padRight +
+ tablePtr->eTablePad);
+ maxY = tablePtr->container.height -
+ (Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padBottom +
+ tablePtr->eTablePad);
+
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+
+ x = entryPtr->column.rcPtr->offset +
+ entryPtr->column.rcPtr->pad.side1 +
+ entryPtr->padLeft +
+ Tk_Changes(entryPtr->tkwin)->border_width +
+ tablePtr->eEntryPad;
+ y = entryPtr->row.rcPtr->offset +
+ entryPtr->row.rcPtr->pad.side1 +
+ entryPtr->padTop +
+ Tk_Changes(entryPtr->tkwin)->border_width +
+ tablePtr->eEntryPad;
+
+ /*
+ * Unmap any widgets that start beyond of the right edge of
+ * the container.
+ */
+ if ((x >= maxX) || (y >= maxY)) {
+ if (Tk_IsMapped(entryPtr->tkwin)) {
+ if (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin) {
+ Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+ }
+ Tk_UnmapWindow(entryPtr->tkwin);
+ }
+ continue;
+ }
+ extra = 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+ spanWidth = GetSpan(&(tablePtr->columnInfo), entryPtr) -
+ (extra + PADDING(entryPtr->padX));
+ spanHeight = GetSpan(&(tablePtr->rowInfo), entryPtr) -
+ (extra + PADDING(entryPtr->padY));
+
+ winWidth = GetReqWidth(entryPtr);
+ winHeight = GetReqHeight(entryPtr);
+
+ /*
+ *
+ * Compare the widget's requested size to the size of the span.
+ *
+ * 1) If the widget is larger than the span or if the fill flag
+ * is set, make the widget the size of the span. Check that the
+ * new size is within the bounds set for the widget.
+ *
+ * 2) Otherwise, position the widget in the space according to its
+ * anchor.
+ *
+ */
+ if ((spanWidth <= winWidth) || (entryPtr->fill & FILL_X)) {
+ winWidth = spanWidth;
+ if (winWidth > entryPtr->reqWidth.max) {
+ winWidth = entryPtr->reqWidth.max;
+ }
+ }
+ if ((spanHeight <= winHeight) || (entryPtr->fill & FILL_Y)) {
+ winHeight = spanHeight;
+ if (winHeight > entryPtr->reqHeight.max) {
+ winHeight = entryPtr->reqHeight.max;
+ }
+ }
+ dx = dy = 0;
+ if (spanWidth > winWidth) {
+ dx = (spanWidth - winWidth);
+ }
+ if (spanHeight > winHeight) {
+ dy = (spanHeight - winHeight);
+ }
+ if ((dx > 0) || (dy > 0)) {
+ TranslateAnchor(dx, dy, entryPtr->anchor, &x, &y);
+ }
+ /*
+ * Clip the widget at the bottom and/or right edge of the
+ * container.
+ */
+ if (winWidth > (maxX - x)) {
+ winWidth = (maxX - x);
+ }
+ if (winHeight > (maxY - y)) {
+ winHeight = (maxY - y);
+ }
+
+ /*
+ * If the widget is too small (i.e. it has only an external
+ * border) then unmap it.
+ */
+ if ((winWidth < 1) || (winHeight < 1)) {
+ if (Tk_IsMapped(entryPtr->tkwin)) {
+ if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
+ Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+ }
+ Tk_UnmapWindow(entryPtr->tkwin);
+ }
+ continue;
+ }
+
+ /*
+ * Resize and/or move the widget as necessary.
+ */
+ entryPtr->x = x;
+ entryPtr->y = y;
+
+ if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
+ Tk_MaintainGeometry(entryPtr->tkwin, tablePtr->tkwin, x, y,
+ winWidth, winHeight);
+ } else {
+ if ((x != Tk_X(entryPtr->tkwin)) ||
+ (y != Tk_Y(entryPtr->tkwin)) ||
+ (winWidth != Tk_Width(entryPtr->tkwin)) ||
+ (winHeight != Tk_Height(entryPtr->tkwin))) {
+ Tk_MoveResizeWindow(entryPtr->tkwin, x, y, winWidth, winHeight);
+ }
+ if (!Tk_IsMapped(entryPtr->tkwin)) {
+ Tk_MapWindow(entryPtr->tkwin);
+ }
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeTable --
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The widgets in the table are possibly resized and redrawn.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ArrangeTable(clientData)
+ ClientData clientData;
+{
+ Table *tablePtr = clientData;
+ int width, height;
+ int offset;
+ int padX, padY;
+ int outerPad;
+ RowColumn *columnPtr, *rowPtr;
+ Blt_ChainLink *linkPtr;
+
+#ifdef notdef
+ fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin));
+#endif
+ Tcl_Preserve(tablePtr);
+ tablePtr->flags &= ~ARRANGE_PENDING;
+
+ tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad = tablePtr->eTablePad =
+ tablePtr->eEntryPad = 0;
+ if (tablePtr->editPtr != NULL) {
+ tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad =
+ tablePtr->editPtr->gridLineWidth;
+ tablePtr->eTablePad = tablePtr->editPtr->gridLineWidth;
+ tablePtr->eEntryPad = tablePtr->editPtr->entryPad;
+ }
+ /*
+ * If the table has no children anymore, then don't do anything at all:
+ * just leave the container widget's size as-is.
+ */
+ if ((Blt_ChainGetLength(tablePtr->chainPtr) == 0) ||
+ (tablePtr->tkwin == NULL)) {
+ Tcl_Release(tablePtr);
+ return;
+ }
+ if (tablePtr->flags & REQUEST_LAYOUT) {
+ tablePtr->flags &= ~REQUEST_LAYOUT;
+ LayoutPartitions(tablePtr);
+ }
+ /*
+ * Initially, try to fit the partitions exactly into the container
+ * by resizing the container. If the widget's requested size is
+ * different, send a request to the container widget's geometry
+ * manager to resize.
+ */
+ if ((tablePtr->propagate) &&
+ ((tablePtr->normal.width != Tk_ReqWidth(tablePtr->tkwin)) ||
+ (tablePtr->normal.height != Tk_ReqHeight(tablePtr->tkwin)))) {
+ Tk_GeometryRequest(tablePtr->tkwin, tablePtr->normal.width,
+ tablePtr->normal.height);
+ EventuallyArrangeTable(tablePtr);
+ Tcl_Release(tablePtr);
+ return;
+ }
+ /*
+ * Save the width and height of the container so we know when its
+ * size has changed during ConfigureNotify events.
+ */
+ tablePtr->container.width = Tk_Width(tablePtr->tkwin);
+ tablePtr->container.height = Tk_Height(tablePtr->tkwin);
+ outerPad = 2 * (Tk_InternalBorderWidth(tablePtr->tkwin) +
+ tablePtr->eTablePad);
+ padX = outerPad + tablePtr->columnInfo.ePad + PADDING(tablePtr->padX);
+ padY = outerPad + tablePtr->rowInfo.ePad + PADDING(tablePtr->padY);
+
+ width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
+ height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
+
+ /*
+ * If the previous geometry request was not fulfilled (i.e. the size of
+ * the container is different from partitions' space requirements),
+ * try to adjust size of the partitions to fit the widget.
+ */
+ if (tablePtr->container.width != width) {
+ AdjustPartitions(&(tablePtr->columnInfo),
+ tablePtr->container.width - width);
+ width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
+ }
+ if (tablePtr->container.height != height) {
+ AdjustPartitions(&(tablePtr->rowInfo),
+ tablePtr->container.height - height);
+ height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
+ }
+ /*
+ * If after adjusting the size of the partitions the space required
+ * does not equal the size of the widget, do one of the following:
+ *
+ * 1) If it's smaller, center the table in the widget.
+ * 2) If it's bigger, clip the partitions that extend beyond
+ * the edge of the container.
+ *
+ * Set the row and column offsets (including the container's internal
+ * border width). To be used later when positioning the widgets.
+ */
+ offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padLeft +
+ tablePtr->eTablePad;
+ if (width < tablePtr->container.width) {
+ offset += (tablePtr->container.width - width) / 2;
+ }
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->offset = offset + tablePtr->columnInfo.ePad;
+ offset += columnPtr->size;
+ }
+ offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padTop +
+ tablePtr->eTablePad;
+ if (height < tablePtr->container.height) {
+ offset += (tablePtr->container.height - height) / 2;
+ }
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rowPtr = Blt_ChainGetValue(linkPtr);
+ rowPtr->offset = offset + tablePtr->rowInfo.ePad;
+ offset += rowPtr->size;
+ }
+ ArrangeEntries(tablePtr);
+ if (tablePtr->editPtr != NULL) {
+ /* Redraw the editor */
+ (*tablePtr->editPtr->drawProc) (tablePtr->editPtr);
+ }
+ Tcl_Release(tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeOp --
+ *
+ * Forces layout of the table geometry manager. This is useful
+ * mostly for debugging the geometry manager. You can get the
+ * geometry manager to calculate the normal (requested) width and
+ * height of each row and column. Otherwise, you need to first
+ * withdraw the container widget, invoke "update", and then query
+ * the geometry manager.
+ *
+ * Results:
+ * Returns a standard Tcl result. If the table is successfully
+ * rearranged, TCL_OK is returned. Otherwise, TCL_ERROR is returned
+ * and an error message is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArrangeOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int argc;
+ char **argv; /* Path name of container associated with
+ * the table */
+{
+ Table *tablePtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tablePtr->flags |= REQUEST_LAYOUT;
+ ArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the widget attributes
+ * is left in interp->result.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ int length;
+ char c;
+ int n;
+ PartitionInfo *infoPtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
+ (char *)tablePtr, argv[3], 0);
+ }
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if (c == '.') { /* Configure widget */
+ Entry *entryPtr;
+
+ if (GetEntry(interp, tablePtr, argv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, entryPtr->tkwin, entryConfigSpecs,
+ (char *)entryPtr, argv[4], 0);
+ } else if ((c == 'c') && (strncmp(argv[3], "container", length) == 0)) {
+ return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
+ (char *)tablePtr, argv[4], 0);
+ }
+ infoPtr = ParseRowColumn(tablePtr, argv[3], &n);
+ if (infoPtr == NULL) {
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, tablePtr->tkwin, infoPtr->configSpecs,
+ (char *)GetRowColumn(infoPtr, n), argv[4], 0);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the table configuration
+ * option information is left in interp->result.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ int length;
+ char c1, c2;
+ int count;
+ int result;
+ char **items;
+ register int i;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Find the end of the items. Search until we see an option (-).
+ */
+ argc -= 3, argv += 3;
+ for (count = 0; count < argc; count++) {
+ if (argv[count][0] == '-') {
+ break;
+ }
+ }
+ items = argv; /* Save the start of the item list */
+ argc -= count; /* Move beyond the items to the options */
+ argv += count;
+
+ result = TCL_ERROR; /* Suppress compiler warning */
+
+ if (count == 0) {
+ result = ConfigureTable(tablePtr, interp, argc, argv);
+ }
+ for (i = 0; i < count; i++) {
+ c1 = items[i][0];
+ c2 = items[i][1];
+ length = strlen(items[i]);
+ if (c1 == '.') { /* Configure widget */
+ Entry *entryPtr;
+
+ if (GetEntry(interp, tablePtr, items[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = ConfigureEntry(tablePtr, interp, entryPtr, argc, argv);
+ } else if ((c1 == 'r') || (c1 == 'R')) {
+ result = ConfigureRowColumn(tablePtr, &(tablePtr->rowInfo),
+ items[i], argc, argv);
+ } else if ((c1 == 'c') && (c2 == 'o') &&
+ (strncmp(argv[3], "container", length) == 0)) {
+ result = ConfigureTable(tablePtr, interp, argc, argv);
+ } else if ((c1 == 'c') || (c1 == 'C')) {
+ result = ConfigureRowColumn(tablePtr, &(tablePtr->columnInfo),
+ items[i], argc, argv);
+ } else {
+ Tcl_AppendResult(interp, "unknown item \"", items[i],
+ "\": should be widget, row or column index, or \"container\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (result == TCL_ERROR) {
+ break;
+ }
+ if ((i + 1) < count) {
+ Tcl_AppendResult(interp, "\n", (char *)NULL);
+ }
+ }
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the specified rows and/or columns from the table.
+ * Note that the row/column indices can be fixed only after
+ * all the deletions have occurred.
+ *
+ * table delete .f r0 r1 r4 c0
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ char c;
+ Blt_ChainLink *linkPtr, *nextPtr;
+ PartitionInfo *infoPtr;
+ char string[200];
+ int matches;
+ register int i;
+ RowColumn *rcPtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 3; i < argc; i++) {
+ c = tolower(argv[i][0]);
+ if ((c != 'r') && (c != 'c')) {
+ Tcl_AppendResult(interp, "bad index \"", argv[i],
+ "\": must start with \"r\" or \"c\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ matches = 0;
+ for (i = 3; i < argc; i++) {
+ c = tolower(argv[i][0]);
+ infoPtr = (c == 'r') ? &(tablePtr->rowInfo) : &(tablePtr->columnInfo);
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ sprintf(string, "%c%d", argv[i][0], rcPtr->index);
+ if (Tcl_StringMatch(string, argv[i])) {
+ matches++;
+ DeleteRowColumn(tablePtr, infoPtr, rcPtr);
+ Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
+ }
+ }
+ }
+ if (matches > 0) { /* Fix indices */
+ i = 0;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rcPtr->index = i++;
+ }
+ i = 0;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rcPtr->index = i++;
+ }
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * JoinOp --
+ *
+ * Joins the specified span of rows/columns together into a
+ * partition. The row/column indices can be fixed only after
+ * all the deletions have occurred.
+ *
+ * table join .f r0 r3
+ * table join .f c2 c4
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+JoinOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr, *nextPtr, *fromPtr;
+ PartitionInfo *infoPtr, *info2Ptr;
+ Entry *entryPtr;
+ int from, to; /* Indices marking the span of
+ * partitions to be joined together. */
+ int start, end; /* Entry indices. */
+ register int i;
+ RowColumn *rcPtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ infoPtr = ParseRowColumn(tablePtr, argv[3], &from);
+ if (infoPtr == NULL) {
+ return TCL_ERROR;
+ }
+ info2Ptr = ParseRowColumn(tablePtr, argv[4], &to);
+ if (info2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (infoPtr != info2Ptr) {
+ Tcl_AppendResult(interp,
+ "\"from\" and \"to\" must both be rows or columns",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (from >= to) {
+ return TCL_OK; /* No-op. */
+ }
+ fromPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, from);
+ rcPtr = Blt_ChainGetValue(fromPtr);
+
+ /*
+ * ---------------------------------------------------------------
+ *
+ * Reduce the span of all entries that currently cross any of the
+ * trailing rows/columns. Also, if the entry starts in one of
+ * these rows/columns, moved it to the designated "joined"
+ * row/column.
+ *
+ * ---------------------------------------------------------------
+ */
+ if (infoPtr->type == rowUid) {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ start = entryPtr->row.rcPtr->index + 1;
+ end = entryPtr->row.rcPtr->index + entryPtr->row.span - 1;
+ if ((end < from) || ((start > to))) {
+ continue;
+ }
+ entryPtr->row.span -= to - start + 1;
+ if (start >= from) {/* Entry starts in a trailing partition. */
+ entryPtr->row.rcPtr = rcPtr;
+ }
+ }
+ } else {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ start = entryPtr->column.rcPtr->index + 1;
+ end = entryPtr->column.rcPtr->index + entryPtr->column.span - 1;
+ if ((end < from) || ((start > to))) {
+ continue;
+ }
+ entryPtr->column.span -= to - start + 1;
+ if (start >= from) {/* Entry starts in a trailing partition. */
+ entryPtr->column.rcPtr = rcPtr;
+ }
+ }
+ }
+ linkPtr = Blt_ChainNextLink(fromPtr);
+ for (i = from + 1; i <= to; i++) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ DeleteRowColumn(tablePtr, infoPtr, rcPtr);
+ Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
+ linkPtr = nextPtr;
+ }
+ i = 0;
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rcPtr->index = i++;
+ }
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ExtentsOp --
+ *
+ * Returns a list of all the pathnames of the widgets managed by
+ * a table. The table is determined from the name of the
+ * container widget associated with the table.
+ *
+ * table extents .frame r0 c0 container
+ *
+ * Results:
+ * Returns a standard Tcl result. If no error occurred, TCL_OK is
+ * returned and a list of widgets managed by the table is left in
+ * interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExtentsOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return results to. */
+ int argc; /* # of arguments */
+ char **argv; /* Command line arguments. */
+{
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+ RowColumn *c1Ptr, *r1Ptr, *c2Ptr, *r2Ptr;
+ PartitionInfo *infoPtr;
+ int x, y, width, height;
+ char string[200];
+ char c;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = tolower(argv[3][0]);
+ if (c == 'r') {
+ infoPtr = &(tablePtr->rowInfo);
+ } else if (c == 'c') {
+ infoPtr = &(tablePtr->columnInfo);
+ } else {
+ Tcl_AppendResult(interp, "unknown item \"", argv[3],
+ "\": should be widget, row, or column", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ sprintf(string, "%c%d", argv[3][0], rcPtr->index);
+ if (Tcl_StringMatch(string, argv[3])) {
+ if (c == 'r') {
+ r1Ptr = r2Ptr = rcPtr;
+ c1Ptr = GetRowColumn(&(tablePtr->columnInfo), 0);
+ c2Ptr = GetRowColumn(&(tablePtr->columnInfo),
+ tablePtr->nColumns - 1);
+ } else {
+ c1Ptr = c2Ptr = rcPtr;
+ r1Ptr = GetRowColumn(&(tablePtr->rowInfo), 0);
+ r2Ptr = GetRowColumn(&(tablePtr->rowInfo),
+ tablePtr->nRows - 1);
+ }
+ x = c1Ptr->offset;
+ y = r1Ptr->offset;
+ width = c2Ptr->offset + c2Ptr->size - x;
+ height = r2Ptr->offset + r2Ptr->size - y;
+ sprintf(string, "%c%d %d %d %d %d\n", argv[3][0], rcPtr->index,
+ x, y, width, height);
+ Tcl_AppendResult(interp, string, (char *)NULL);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ForgetOp --
+ *
+ * Processes an argv/argc list of widget names and purges their
+ * entries from their respective tables. The widgets are unmapped and
+ * the tables are rearranged at the next idle point. Note that all
+ * the named widgets do not need to exist in the same table.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred, TCL_ERROR is
+ * returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is deallocated (the entry is destroyed), etc. The
+ * affected tables are is re-computed and arranged at the next idle
+ * point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ForgetOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Entry *entryPtr;
+ register int i;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Table *tablePtr;
+ Tk_Window tkwin, mainWindow;
+
+ tablePtr = NULL;
+ mainWindow = Tk_MainWindow(interp);
+ for (i = 2; i < argc; i++) {
+ entryPtr = NULL;
+ tkwin = Tk_NameToWindow(interp, argv[i], mainWindow);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tablePtr = (Table *)Blt_GetHashValue(hPtr);
+ if (tablePtr->interp != interp) {
+ continue;
+ }
+ entryPtr = FindEntry(tablePtr, tkwin);
+ if (entryPtr != NULL) {
+ break;
+ }
+ }
+ if (entryPtr == NULL) {
+ Tcl_AppendResult(interp, "\"", argv[i],
+ "\" is not managed by any table", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_IsMapped(entryPtr->tkwin)) {
+ Tk_UnmapWindow(entryPtr->tkwin);
+ }
+ /* Arrange for the call back here in the loop, because the
+ * widgets may not belong to the same table. */
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ DestroyEntry(entryPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoOp --
+ *
+ * Returns the options of a widget or partition in the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the widget attributes
+ * is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ int result;
+ char c;
+ register int i;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 3; i < argc; i++) {
+ c = argv[i][0];
+ if (c == '.') { /* Entry information */
+ Entry *entryPtr;
+
+ if (GetEntry(interp, tablePtr, argv[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = InfoEntry(interp, tablePtr, entryPtr);
+ } else if ((c == 'r') || (c == 'R') || (c == 'c') || (c == 'C')) {
+ result = InfoRowColumn(tablePtr, interp, argv[i]);
+ } else {
+ Tcl_AppendResult(interp, "unknown item \"", argv[i],
+ "\": should be widget, row, or column", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((i + 1) < argc) {
+ Tcl_AppendResult(interp, "\n", (char *)NULL);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Inserts a span of rows/columns into the table.
+ *
+ * table insert .f r0 2
+ * table insert .f c0 5
+ *
+ * Results:
+ * Returns a standard Tcl result. A list of the widget
+ * attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ long int span;
+ int before;
+ PartitionInfo *infoPtr;
+ RowColumn *rcPtr;
+ register int i;
+ Blt_ChainLink *beforePtr, *linkPtr;
+ int linkBefore;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ linkBefore = TRUE;
+ if (argv[3][0] == '-') {
+ if (strcmp(argv[3], "-before") == 0) {
+ linkBefore = TRUE;
+ argv++; argc--;
+ } else if (strcmp(argv[3], "-after") == 0) {
+ linkBefore = FALSE;
+ argv++; argc--;
+ }
+ }
+ if (argc == 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ "insert ", argv[2], "row|column ?span?", (char *)NULL);
+ return TCL_ERROR;
+ }
+ infoPtr = ParseRowColumn(tablePtr, argv[3], &before);
+ if (infoPtr == NULL) {
+ return TCL_ERROR;
+ }
+ span = 1;
+ if ((argc > 4) && (Tcl_ExprLong(interp, argv[4], &span) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (span < 1) {
+ Tcl_AppendResult(interp, "span value \"", argv[4],
+ "\" can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ beforePtr = Blt_ChainGetNthLink(infoPtr->chainPtr, before);
+ /*
+ * Insert the new rows/columns from the designated point in the
+ * chain.
+ */
+ for (i = 0; i < span; i++) {
+ rcPtr = CreateRowColumn();
+ linkPtr = Blt_ChainNewLink();
+ Blt_ChainSetValue(linkPtr, rcPtr);
+ if (linkBefore) {
+ Blt_ChainLinkBefore(infoPtr->chainPtr, linkPtr, beforePtr);
+ } else {
+ Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, beforePtr);
+ }
+ rcPtr->linkPtr = linkPtr;
+ }
+ i = 0;
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ /* Reset the indices of the trailing rows/columns. */
+ rcPtr->index = i++;
+ }
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SplitOp --
+ *
+ * Splits a single row/column into multiple partitions. Any
+ * widgets that span this row/column will be automatically
+ * corrected to include the new rows/columns.
+ *
+ * table split .f r0 3
+ * table split .f c2 2
+ * Results:
+ * Returns a standard Tcl result. A list of the widget
+ * attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SplitOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ int number, split;
+ int start, end;
+ PartitionInfo *infoPtr;
+ RowColumn *rcPtr;
+ register int i;
+ Blt_ChainLink *afterPtr, *linkPtr;
+ Entry *entryPtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ infoPtr = ParseRowColumn(tablePtr, argv[3], &number);
+ if (infoPtr == NULL) {
+ return TCL_ERROR;
+ }
+ split = 2;
+ if (argc > 4) {
+ if (Tcl_GetInt(interp, argv[4], &split) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (split < 2) {
+ Tcl_AppendResult(interp, "bad split value \"", argv[4],
+ "\": should be 2 or greater", (char *)NULL);
+ return TCL_ERROR;
+ }
+ afterPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, number);
+
+ /*
+ * Append (split - 1) additional rows/columns starting
+ * from the current point in the chain.
+ */
+
+ for (i = 1; i < split; i++) {
+ rcPtr = CreateRowColumn();
+ linkPtr = Blt_ChainNewLink();
+ Blt_ChainSetValue(linkPtr, rcPtr);
+ Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, afterPtr);
+ rcPtr->linkPtr = linkPtr;
+ }
+
+ /*
+ * Also increase the span of all entries that span this
+ * row/column by split - 1.
+ */
+ if (infoPtr->type == rowUid) {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ start = entryPtr->row.rcPtr->index;
+ end = entryPtr->row.rcPtr->index + entryPtr->row.span;
+ if ((start <= number) && (number < end)) {
+ entryPtr->row.span += (split - 1);
+ }
+ }
+ } else {
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ start = entryPtr->column.rcPtr->index;
+ end = entryPtr->column.rcPtr->index + entryPtr->column.span;
+ if ((start <= number) && (number < end)) {
+ entryPtr->column.span += (split - 1);
+ }
+ }
+ }
+ /*
+ * Be careful to renumber the rows or columns only after
+ * processing each entry. Otherwise row/column numbering
+ * will be out of sync with the index.
+ */
+ i = number;
+ for (linkPtr = afterPtr; linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rcPtr->index = i++; /* Renumber the trailing indices. */
+ }
+
+ tablePtr->flags |= REQUEST_LAYOUT;
+ EventuallyArrangeTable(tablePtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RowColumnSearch --
+ *
+ * Searches for the row or column designated by an x or y
+ * coordinate.
+ *
+ * Results:
+ * Returns a pointer to the row/column containing the given point.
+ * If no row/column contains the coordinate, NULL is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+static RowColumn *
+RowColumnSearch(infoPtr, x)
+ PartitionInfo *infoPtr;
+ int x; /* Search coordinate */
+{
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (x > (rcPtr->offset + rcPtr->size)) {
+ break; /* Too far, can't find row/column. */
+ }
+ if (x > rcPtr->offset) {
+ return rcPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LocateOp --
+ *
+ *
+ * Returns the row,column index given a screen coordinate.
+ *
+ * Results:
+ * Returns a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+LocateOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int x, y;
+ RowColumn *rowPtr, *columnPtr;
+ Table *tablePtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixels(interp, tablePtr->tkwin, argv[3], PIXELS_ANY, &x)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixels(interp, tablePtr->tkwin, argv[4], PIXELS_ANY, &y)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ rowPtr = RowColumnSearch(&(tablePtr->rowInfo), y);
+ if (rowPtr == NULL) {
+ return TCL_OK;
+ }
+ columnPtr = RowColumnSearch(&(tablePtr->columnInfo), x);
+ if (columnPtr == NULL) {
+ return TCL_OK;
+ }
+ Tcl_AppendElement(interp, Blt_Itoa(rowPtr->index));
+ Tcl_AppendElement(interp, Blt_Itoa(columnPtr->index));
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ContainersOp --
+ *
+ * Returns a list of tables currently in use. A table is
+ * associated by the name of its container widget. All tables
+ * matching a given pattern are included in this list. If no
+ * pattern is present (argc == 0), all tables are included.
+ *
+ * Results:
+ * Returns a standard Tcl result. If no error occurred, TCL_OK is
+ * returned and a list of tables is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ContainersOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return list of names to */
+ int argc;
+ char **argv; /* Contains 0-1 arguments: search pattern */
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ register Table *tablePtr;
+ char *pattern;
+
+ pattern = NULL;
+ if (argc > 2) {
+ if (argv[2][0] == '-') {
+ unsigned int length;
+
+ length = strlen(argv[2]);
+ if ((length > 1) && (argv[2][1] == 'p') &&
+ (strncmp(argv[2], "-pattern", length) == 0)) {
+ pattern = argv[3];
+ goto search;
+ } else if ((length > 1) && (argv[2][1] == 's') &&
+ (strncmp(argv[2], "-slave", length) == 0)) {
+ Tk_Window tkwin;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "needs widget argument for \"",
+ argv[2], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_NameToWindow(interp, argv[3],
+ Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tablePtr = (Table *)Blt_GetHashValue(hPtr);
+ if (FindEntry(tablePtr, tkwin) != NULL) {
+ Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
+ }
+ }
+ return TCL_OK;
+ } else {
+ Tcl_AppendResult(interp, "bad switch \"", argv[2], "\" : \
+should be \"-pattern\", or \"-slave\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ pattern = argv[2];
+ }
+ }
+ search:
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tablePtr = (Table *)Blt_GetHashValue(hPtr);
+ if (tablePtr->interp == interp) {
+ if ((pattern == NULL) ||
+ (Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), pattern))) {
+ Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SaveOp --
+ *
+ * Returns a list of all the commands necessary to rebuild the
+ * the table. This includes the layout of the widgets and any
+ * row, column, or table options set.
+ *
+ * Results:
+ * Returns a standard Tcl result. If no error occurred, TCL_OK is
+ * returned and a list of widget path names is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SaveOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr, *lastPtr;
+ Entry *entryPtr;
+ PartitionInfo *infoPtr;
+ RowColumn *rcPtr;
+ Tcl_DString dString;
+ int start, last;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "\n# Table widget layout\n\n", -1);
+ Tcl_DStringAppend(&dString, argv[0], -1);
+ Tcl_DStringAppend(&dString, " ", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, " \\\n", -1);
+ lastPtr = Blt_ChainLastLink(tablePtr->chainPtr);
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ PrintEntry(entryPtr, &dString);
+ if (linkPtr != lastPtr) {
+ Tcl_DStringAppend(&dString, " \\\n", -1);
+ }
+ }
+ Tcl_DStringAppend(&dString, "\n\n# Row configuration options\n\n", -1);
+ infoPtr = &(tablePtr->rowInfo);
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ start = Tcl_DStringLength(&dString);
+ Tcl_DStringAppend(&dString, argv[0], -1);
+ Tcl_DStringAppend(&dString, " configure ", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, " r", -1);
+ Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
+ last = Tcl_DStringLength(&dString);
+ PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+ if (Tcl_DStringLength(&dString) == last) {
+ Tcl_DStringSetLength(&dString, start);
+ } else {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ Tcl_DStringAppend(&dString, "\n\n# Column configuration options\n\n", -1);
+ infoPtr = &(tablePtr->columnInfo);
+ for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ start = Tcl_DStringLength(&dString);
+ Tcl_DStringAppend(&dString, argv[0], -1);
+ Tcl_DStringAppend(&dString, " configure ", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, " c", -1);
+ Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
+ last = Tcl_DStringLength(&dString);
+ PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+ if (Tcl_DStringLength(&dString) == last) {
+ Tcl_DStringSetLength(&dString, start);
+ } else {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ start = Tcl_DStringLength(&dString);
+ Tcl_DStringAppend(&dString, "\n\n# Table configuration options\n\n", -1);
+ Tcl_DStringAppend(&dString, argv[0], -1);
+ Tcl_DStringAppend(&dString, " configure ", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+ last = Tcl_DStringLength(&dString);
+ PrintTable(tablePtr, &dString);
+ if (Tcl_DStringLength(&dString) == last) {
+ Tcl_DStringSetLength(&dString, start);
+ } else {
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ * Returns a list of all the pathnames of the widgets managed by
+ * a table geometry manager. The table is given by the path name of a
+ * container widget associated with the table.
+ *
+ * Results:
+ * Returns a standard Tcl result. If no error occurred, TCL_OK is
+ * returned and a list of widget path names is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return list of names to */
+ int argc; /* Number of arguments */
+ char **argv; /* Contains 1-2 arguments: pathname of container
+ * widget associated with the table and search
+ * pattern */
+{
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr;
+ Entry *entryPtr;
+ int rspan, cspan, rstart, cstart;
+ char *pattern;
+ char c;
+ int flags;
+ register int i;
+
+#define MATCH_PATTERN (1<<0) /* Find widgets whose path names
+ * match a given pattern */
+#define MATCH_INDEX_SPAN (1<<1) /* Find widgets that span index */
+#define MATCH_INDEX_START (1<<2) /* Find widgets that start at index */
+
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = 0;
+ pattern = NULL;
+ rspan = cspan = rstart = cstart = 0;
+
+ /* Parse switches and arguments first */
+ for (i = 3; i < argc; i += 2) {
+ if (argv[i][0] == '-') {
+ unsigned int length;
+
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp, "switch \"", argv[i], "\" needs value",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ length = strlen(argv[i]);
+ c = argv[i][1];
+ if ((c == 'p') && (length > 1) &&
+ (strncmp(argv[3], "-pattern", length) == 0)) {
+ flags |= MATCH_PATTERN;
+ pattern = argv[4];
+ } else if ((c == 's') && (length > 2) &&
+ (strncmp(argv[i], "-start", length) == 0)) {
+ flags |= MATCH_INDEX_START;
+ if (ParseItem(tablePtr, argv[i + 1],
+ &rstart, &cstart) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if ((c == 's') && (length > 2) &&
+ (strncmp(argv[i], "-span", length) == 0)) {
+ flags |= MATCH_INDEX_SPAN;
+ if (ParseItem(tablePtr, argv[4],
+ &rspan, &cspan) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad switch \"", argv[3], "\" : \
+should be \"-pattern\", \"-span\", or \"-start\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ if ((i + 1) == argc) {
+ pattern = argv[i];
+ flags |= MATCH_PATTERN;
+ }
+ }
+ }
+
+ /* Then try to match entries with the search criteria */
+
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if ((flags & MATCH_PATTERN) && (pattern != NULL)) {
+ if (Tcl_StringMatch(Tk_PathName(entryPtr->tkwin), pattern)) {
+ goto match;
+ }
+ }
+ if (flags & MATCH_INDEX_SPAN) {
+ if ((rspan >= 0) && ((entryPtr->row.rcPtr->index <= rspan) ||
+ ((entryPtr->row.rcPtr->index + entryPtr->row.span) > rspan))) {
+ goto match;
+ }
+ if ((cspan >= 0) && ((entryPtr->column.rcPtr->index <= cspan) ||
+ ((entryPtr->column.rcPtr->index + entryPtr->column.span)
+ > cspan))) {
+ goto match;
+ }
+ }
+ if (flags & MATCH_INDEX_START) {
+ if ((rstart >= 0) && (entryPtr->row.rcPtr->index == rstart)) {
+ goto match;
+ }
+ if ((cstart >= 0) && (entryPtr->column.rcPtr->index == cstart)) {
+ goto match;
+ }
+ }
+ continue;
+ match:
+ Tcl_AppendElement(interp, Tk_PathName(entryPtr->tkwin));
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Table operations.
+ *
+ * The fields for Blt_OpSpec are as follows:
+ *
+ * - operation name
+ * - minimum number of characters required to disambiguate the operation name.
+ * - function associated with operation.
+ * - minimum number of arguments required.
+ * - maximum number of arguments allowed (0 indicates no limit).
+ * - usage string
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Blt_OpSpec operSpecs[] =
+{
+ {"arrange", 1, (Blt_Op)ArrangeOp, 3, 3, "container",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 5,
+ "container ?row|column|widget? option",},
+ {"configure", 3, (Blt_Op)ConfigureOp, 3, 0,
+ "container ?row|column|widget?... ?option value?...",},
+ {"containers", 3, (Blt_Op)ContainersOp, 2, 4, "?switch? ?arg?",},
+ {"delete", 1, (Blt_Op)DeleteOp, 3, 0,
+ "container row|column ?row|column?",},
+ {"extents", 1, (Blt_Op)ExtentsOp, 4, 4,
+ "container row|column|widget",},
+ {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "widget ?widget?...",},
+ {"info", 3, (Blt_Op)InfoOp, 3, 0,
+ "container ?row|column|widget?...",},
+ {"insert", 3, (Blt_Op)InsertOp, 4, 6,
+ "container ?-before|-after? row|column ?count?",},
+ {"join", 1, (Blt_Op)JoinOp, 5, 5, "container first last",},
+ {"locate", 2, (Blt_Op)LocateOp, 5, 5, "container x y",},
+ {"save", 2, (Blt_Op)SaveOp, 3, 3, "container",},
+ {"search", 2, (Blt_Op)SearchOp, 3, 0, "container ?switch arg?...",},
+ {"split", 2, (Blt_Op)SplitOp, 4, 5, "container row|column div",},
+};
+
+static int nSpecs = sizeof(operSpecs) / sizeof(Blt_OpSpec);
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TableCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to the table geometry manager. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+TableCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ TableInterpData *dataPtr = clientData;
+ Blt_Op proc;
+ int result;
+
+ if ((argc > 1) && (argv[1][0] == '.')) {
+ Table *tablePtr;
+
+ if (Blt_GetTable(clientData, interp, argv[1], &tablePtr) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ tablePtr = CreateTable(dataPtr, interp, argv[1]);
+ if (tablePtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ return BuildTable(tablePtr, interp, argc, argv);
+ }
+ proc = Blt_GetOp(interp, nSpecs, operSpecs, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (dataPtr, interp, argc, argv);
+ return result;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TableInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the table command
+ * is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys all the hash table maintaining the names of the table
+ * geomtry managers.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TableInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+{
+ TableInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Table *tablePtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tablePtr = (Table *)Blt_GetHashValue(hPtr);
+ tablePtr->hashPtr = NULL;
+ DestroyTable((DestroyData)tablePtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->tableTable));
+ Tcl_DeleteAssocData(interp, TABLE_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+static TableInterpData *
+GetTableInterpData(interp)
+ Tcl_Interp *interp;
+{
+ TableInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (TableInterpData *)
+ Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(TableInterpData));
+ assert(dataPtr);
+ Tcl_SetAssocData(interp, TABLE_THREAD_KEY, TableInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->tableTable), BLT_ONE_WORD_KEYS);
+ }
+ return dataPtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Blt_TableInit --
+ *
+ * This procedure is invoked to initialize the Tcl command that
+ * corresponds to the table geometry manager.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds an entry into a global Tcl
+ * associative array.
+ *
+ * ---------------------------------------------------------------------------
+ */
+int
+Blt_TableInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec = {"table", TableCmd, };
+ TableInterpData *dataPtr;
+
+ dataPtr = GetTableInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ rowUid = Tk_GetUid("row");
+ columnUid = Tk_GetUid("column");
+ return TCL_OK;
+}
diff --git a/blt/src/bltTable.h b/blt/src/bltTable.h
new file mode 100644
index 00000000000..edb22f72073
--- /dev/null
+++ b/blt/src/bltTable.h
@@ -0,0 +1,390 @@
+/*
+ * bltTable.h --
+ *
+ * This module implements a table-based geometry manager
+ * for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The table geometry manager was created by George Howlett.
+ */
+
+#ifndef _BLT_TABLE_H
+#define _BLT_TABLE_H
+
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltList.h"
+
+typedef struct {
+ Blt_HashTable tableTable; /* Hash table of table structures keyed by
+ * the address of the reference Tk window */
+} TableInterpData;
+
+
+typedef struct EditorStruct Editor;
+typedef void (EditorDrawProc) _ANSI_ARGS_((Editor *editor));
+typedef void (EditorDestroyProc) _ANSI_ARGS_((DestroyData destroyData));
+
+struct EditorStruct {
+ int gridLineWidth;
+ int buttonHeight;
+ int entryPad;
+ int minSize; /* Minimum size to allow any partition */
+
+ EditorDrawProc *drawProc;
+ EditorDestroyProc *destroyProc;
+};
+
+#define nRows rowInfo.chainPtr->nLinks
+#define nColumns columnInfo.chainPtr->nLinks
+
+/*
+ * Limits --
+ *
+ * Defines the bounding of a size (width or height) in the table.
+ * It may be related to the partition, entry, or table size. The
+ * widget pointers are used to associate sizes with the requested
+ * size of other widgets.
+ */
+
+typedef struct {
+ int flags; /* Flags indicate whether using default
+ * values for limits or not. See flags
+ * below. */
+ int max, min; /* Values for respective limits. */
+ int nom; /* Nominal starting value. */
+ Tk_Window wMax, wMin; /* If non-NULL, represents widgets whose
+ * requested sizes will be set as limits. */
+ Tk_Window wNom; /* If non-NULL represents widget whose
+ * requested size will be the nominal
+ * size. */
+} Limits;
+
+#define LIMITS_SET_BIT 1
+#define LIMITS_SET_MIN (LIMITS_SET_BIT<<0)
+#define LIMITS_SET_MAX (LIMITS_SET_BIT<<1)
+#define LIMITS_SET_NOM (LIMITS_SET_BIT<<2)
+
+#define LIMITS_MIN 0 /* Default minimum limit */
+#define LIMITS_MAX SHRT_MAX/* Default maximum limit */
+#define LIMITS_NOM -1000 /* Default nomimal value. Indicates if a
+ * partition has received any space yet */
+
+typedef int (LimitsProc) _ANSI_ARGS_((int value, Limits *limitsPtr));
+
+/*
+ * Resize --
+ *
+ * These flags indicate in what ways each partition in a table
+ * can be resized from its default dimensions. The normal size of
+ * a row/column is the minimum amount of space needed to hold the
+ * widgets that span it. The table may then be stretched or
+ * shrunk depending if the container is larger or smaller than
+ * the table. This can occur if 1) the user resizes the toplevel
+ * widget, or 2) the container is in turn packed into a larger
+ * widget and the "fill" option is set.
+ *
+ * RESIZE_NONE - No resizing from normal size.
+ * RESIZE_EXPAND - Do not allow the size to decrease.
+ * The size may increase however.
+ * RESIZE_SHRINK - Do not allow the size to increase.
+ * The size may decrease however.
+ * RESIZE_BOTH - Allow the size to increase or
+ * decrease from the normal size.
+ * RESIZE_VIRGIN - Special case of the resize flag. Used to
+ * indicate the initial state of the flag.
+ * Empty rows/columns are treated differently
+ * if this row/column is set.
+ */
+
+#define RESIZE_NONE 0
+#define RESIZE_EXPAND (1<<0)
+#define RESIZE_SHRINK (1<<1)
+#define RESIZE_BOTH (RESIZE_EXPAND | RESIZE_SHRINK)
+#define RESIZE_VIRGIN (1<<2)
+
+/*
+ * Control --
+ */
+#define CONTROL_NORMAL 1.0 /* Consider the widget when
+ * calculating the row heights and
+ * column widths. */
+#define CONTROL_NONE 0.0 /* Ignore the widget. The height and
+ * width of the rows/columns spanned
+ * by this widget will not affected by
+ * the size of the widget.
+ */
+#define CONTROL_FULL -1.0 /* Consider only this widget when
+ * determining the column widths
+ * and row heights of the partitions
+ * it spans. */
+#define EXCL_PAD 0
+#define INCL_PAD 1
+
+typedef struct TableStruct Table;
+typedef struct RowColumnStruct RowColumn;
+
+/*
+ * Entry --
+ *
+ * An entry holds a widget and describes how the widget should
+ * appear in a range of cells.
+ * 1. padding.
+ * 2. how many rows/columns the entry spans.
+ * 3. size bounds for the widget.
+ *
+ * Several entries may start at the same cell in
+ * the table, but a entry can hold only one widget.
+ */
+
+typedef struct {
+ Tk_Window tkwin; /* Widget to be managed. */
+
+ Table *tablePtr; /* Table managing this widget */
+
+ int borderWidth; /* The external border width of
+ * the widget. This is needed to check if
+ * Tk_Changes(tkwin)->border_width changes.
+ */
+
+ int manageWhenNeeded; /* If non-zero, allow joint custody of
+ * the widget. This is for cases
+ * where the same widget may be shared
+ * between two different tables
+ * (e.g. same graph on two different
+ * notebook pages). Claim the widget
+ * only when the table is
+ * mapped. Don't destroy the entry if
+ * the table loses custody of the
+ * widget. */
+
+ Limits reqWidth, reqHeight; /* Bounds for width and height requests
+ * made by the widget. */
+ struct PositionInfo {
+ RowColumn *rcPtr; /* Row or column where this entry starts. */
+
+ int span; /* Number of rows or columns spanned. */
+ double control; /* Weight of widget in the row or column. */
+
+ Blt_ChainLink *linkPtr; /* Link to widget in the chain of spans */
+
+ Blt_Chain *chainPtr; /* Pointer to the chain of spans. */
+ } row, column;
+
+ Tk_Anchor anchor; /* Anchor type: indicates how the
+ * widget is positioned if extra space
+ * is available in the entry */
+
+ Blt_Pad padX; /* Extra padding placed left and right of the
+ * widget. */
+ Blt_Pad padY; /* Extra padding placed above and below the
+ * widget */
+
+ int ipadX, ipadY; /* Extra padding added to the interior of
+ * the widget (i.e. adds to the requested
+ * size of the widget) */
+
+ int fill; /* Indicates how the widget should
+ * fill the span of cells it occupies. */
+
+ int x, y; /* Origin of widget wrt container. */
+
+ Blt_ChainLink *linkPtr; /* Pointer into list of entries. */
+
+ Blt_HashEntry *hashPtr; /* Pointer into table of entries. */
+
+} Entry;
+
+/*
+ * RowColumn --
+ *
+ * Creates a definable space (row or column) in the table. It may
+ * have both requested minimum or maximum values which constrain
+ * the size of it.
+ */
+
+struct RowColumnStruct {
+ int index; /* Index of row or column */
+
+ int size; /* Current size of the partition. This size
+ * is bounded by minSize and maxSize. */
+
+ /*
+ * nomSize and size perform similar duties. I need to keep track
+ * of the amount of space allocated to the partition (using size).
+ * But at the same time, I need to indicate that space can be
+ * parcelled out to this partition. If a nominal size was set for
+ * this partition, I don't want to add space.
+ */
+
+ int nomSize; /* The nominal size (neither expanded
+ * nor shrunk) of the partition based
+ * upon the requested sizes of the
+ * widgets spanning this partition. */
+
+ int minSize, maxSize; /* Size constraints on the partition */
+
+ int offset; /* Offset of the partition (in pixels)
+ * from the origin of the container. */
+
+ int minSpan; /* Minimum spanning widget in
+ * partition. Used for bookkeeping
+ * when growing a span of partitions
+ * */
+
+ double weight; /* Weight of row or column */
+
+ Entry *control; /* Pointer to the entry that is
+ * determining the size of this
+ * partition. This is used to know
+ * when a partition is occupied. */
+
+ int resize; /* Indicates if the partition should
+ * shrink or expand from its nominal
+ * size. */
+
+ Blt_Pad pad; /* Pads the partition beyond its nominal
+ * size */
+
+ Limits reqSize; /* Requested bounds for the size of
+ * the partition. The partition will
+ * not expand or shrink beyond these
+ * limits, regardless of how it was
+ * specified (max widget size). This
+ * includes any extra padding which
+ * may be specified. */
+
+ int maxSpan; /* Maximum spanning widget to consider
+ * when growing a span of partitions.
+ * A value of zero indicates that all
+ * spans should be considered. */
+
+ int count;
+
+ Blt_ChainLink *linkPtr;
+
+};
+
+#define DEF_TBL_RESIZE "both"
+#define DEF_TBL_PAD "0"
+#define DEF_TBL_MAXSPAN "0"
+
+
+/*
+ * This is the default number of elements in the statically
+ * pre-allocated column and row arrays. This number should reflect a
+ * useful number of row and columns, which fit most applications.
+ */
+#define DEF_ARRAY_SIZE 32
+
+typedef Entry *(EntrySearchProc) _ANSI_ARGS_((Table *tablePtr,
+ Tk_Window tkwin));
+
+/*
+ * PartitionInfo --
+ *
+ * Manages the rows or columns of the table. Contains
+ * a chain of partitions (representing the individiual
+ * rows or columns).
+ *
+ */
+typedef struct PartitionInfo {
+ char *type; /* String identifying the type of
+ * partition: "row" or "column". */
+ Blt_Chain *chainPtr;
+ Blt_List list; /* Linked list of bins of widgets
+ * keyed by increasing span. */
+ Tk_ConfigSpec *configSpecs;
+ int reqLength;
+ int ePad; /* Extra padding for row/column
+ * needed to display editor marks */
+} PartitionInfo;
+
+/*
+ * Table structure
+ */
+struct TableStruct {
+ int flags; /* See the flags definitions below. */
+ Tk_Window tkwin; /* The container widget into which
+ * other widgets are arranged. */
+ Tcl_Interp *interp; /* Interpreter associated with all
+ * widgets */
+
+ Blt_Chain *chainPtr; /* Chain of entries in the table. */
+
+ Blt_HashTable entryTable; /* Table of entries. Serves as a
+ * directory to look up entries from
+ * widget their names. */
+ Blt_Pad padX, padY;
+
+ int propagate; /* If non-zero, the table will make a
+ * geometry request on behalf of the
+ * container widget. */
+
+ int eTablePad, eEntryPad;
+
+ PartitionInfo columnInfo;
+ PartitionInfo rowInfo; /* Manages row and column partitions */
+
+ Dim2D container; /* Last known dimenion of the container. */
+ Dim2D normal; /* Normal dimensions of the table */
+ Limits reqWidth, reqHeight; /* Constraints on the table's normal
+ * width and height */
+ Editor *editPtr; /* If non-NULL, indicates that the
+ * table is currently being edited */
+ Tcl_IdleProc *arrangeProc;
+ EntrySearchProc *findEntryProc;
+ Blt_HashEntry *hashPtr; /* Used to delete the table from its
+ * hashtable. */
+ Blt_HashTable *tablePtr;
+};
+
+/*
+ * Table flags definitions
+ */
+#define ARRANGE_PENDING (1<<0) /* A call to ArrangeTable is
+ * pending. This flag allows multiple
+ * layout changes to be requested
+ * before the table is actually
+ * reconfigured. */
+#define REQUEST_LAYOUT (1<<1) /* Get the requested sizes of the
+ * widgets before expanding/shrinking
+ * the size of the container. It's
+ * necessary to recompute the layout
+ * every time a partition or entry is
+ * added, reconfigured, or deleted,
+ * but not when the container is
+ * resized. */
+#define NON_PARENT (1<<2) /* The table is managing widgets that
+ * arern't children of the container.
+ * This requires that they are
+ * manually moved when the container
+ * is moved (a definite performance
+ * hit). */
+/*
+ * Forward declarations
+ */
+
+extern int Blt_GetTable _ANSI_ARGS_((TableInterpData *dataPtr,
+ Tcl_Interp *interp, char *pathName, Table **tablePtrPtr));
+
+#endif /* _BLT_TABLE_H */
diff --git a/blt/src/bltTabnotebook.c b/blt/src/bltTabnotebook.c
new file mode 100644
index 00000000000..0ec10cdabc1
--- /dev/null
+++ b/blt/src/bltTabnotebook.c
@@ -0,0 +1,5712 @@
+/*
+ * bltTabnotebook.c --
+ *
+ * This module implements a tab notebook widget for the BLT toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Tabnotebook widget created by George A. Howlett (gah@bell-labs.com)
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TABNOTEBOOK
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltTile.h"
+
+#if (TK_MAJOR_VERSION == 4)
+#define TK_REPARENTED 0x2000
+#endif
+
+#define INVALID_FAIL 0
+#define INVALID_OK 1
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#define GAP 3
+#define SELECT_PADX 4
+#define SELECT_PADY 2
+#define OUTER_PAD 2
+#define LABEL_PAD 1
+#define LABEL_PADX 2
+#define LABEL_PADY 2
+#define IMAGE_PAD 1
+#define CORNER_OFFSET 3
+
+#define TAB_SCROLL_OFFSET 10
+
+#define SLANT_NONE 0
+#define SLANT_LEFT 1
+#define SLANT_RIGHT 2
+#define SLANT_BOTH (SLANT_LEFT | SLANT_RIGHT)
+
+#define END (-1)
+#define ODD(x) ((x) | 0x01)
+
+#define TABWIDTH(s, t) \
+ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+#define TABHEIGHT(s, t) \
+ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+
+#define VPORTWIDTH(s) \
+ (((s)->side & SIDE_HORIZONTAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+ (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define VPORTHEIGHT(s) \
+ (((s)->side & SIDE_VERTICAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+ (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define GETATTR(t,attr) \
+ (((t)->attr != NULL) ? (t)->attr : (t)->nbPtr->defTabStyle.attr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Internal widget flags:
+ *
+ * TNB_LAYOUT The layout of the widget needs to be
+ * recomputed.
+ *
+ * TNB_REDRAW A redraw request is pending for the widget.
+ *
+ * TNB_SCROLL A scroll request is pending.
+ *
+ * TNB_FOCUS The widget is receiving keyboard events.
+ * Draw the focus highlight border around the
+ * widget.
+ *
+ * TNB_MULTIPLE_TIER Notebook is using multiple tiers.
+ *
+ * TNB_STATIC Notebook does not scroll.
+ *
+ * ---------------------------------------------------------------------------
+ */
+#define TNB_LAYOUT (1<<0)
+#define TNB_REDRAW (1<<1)
+#define TNB_SCROLL (1<<2)
+#define TNB_FOCUS (1<<4)
+
+#define TNB_STATIC (1<<8)
+#define TNB_MULTIPLE_TIER (1<<9)
+
+#define PERFORATION_ACTIVE (1<<10)
+
+#define SIDE_TOP (1<<0)
+#define SIDE_RIGHT (1<<1)
+#define SIDE_LEFT (1<<2)
+#define SIDE_BOTTOM (1<<3)
+
+#define SIDE_VERTICAL (SIDE_LEFT | SIDE_RIGHT)
+#define SIDE_HORIZONTAL (SIDE_TOP | SIDE_BOTTOM)
+
+#define DEF_TNB_ACTIVE_BG_COLOR RGB_GREY90
+#define DEF_TNB_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TNB_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_TNB_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TNB_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TNB_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TNB_BORDER_WIDTH "1"
+#define DEF_TNB_COMMAND (char *)NULL
+#define DEF_TNB_CURSOR (char *)NULL
+#define DEF_TNB_DASHES "1"
+#define DEF_TNB_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TNB_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_TNB_FONT STD_FONT
+#define DEF_TNB_GAP "3"
+#define DEF_TNB_HEIGHT "0"
+#define DEF_TNB_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TNB_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TNB_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_TNB_HIGHLIGHT_WIDTH "2"
+#define DEF_TNB_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TNB_NORMAL_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TNB_OUTER_PAD "3"
+#define DEF_TNB_RELIEF "sunken"
+#define DEF_TNB_ROTATE "0.0"
+#define DEF_TNB_SCROLL_INCREMENT "0"
+#define DEF_TNB_SELECT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TNB_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_TNB_SELECT_BORDER_WIDTH "1"
+#define DEF_TNB_SELECT_CMD (char *)NULL
+#define DEF_TNB_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_TNB_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_TNB_SELECT_MODE "multiple"
+#define DEF_TNB_SELECT_RELIEF "raised"
+#define DEF_TNB_SELECT_PAD "5"
+#define DEF_TNB_SHADOW_COLOR RGB_BLACK
+#define DEF_TNB_SIDE "top"
+#define DEF_TNB_SLANT "none"
+#define DEF_TNB_TAB_BG_COLOR RGB_GREY82
+#define DEF_TNB_TAB_BG_MONO STD_MONO_SELECT_BG
+#define DEF_TNB_TAB_RELIEF "raised"
+#define DEF_TNB_TAKE_FOCUS "1"
+#define DEF_TNB_TEXT_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TNB_TEXT_MONO STD_MONO_NORMAL_FG
+#define DEF_TNB_TEXT_SIDE "left"
+#define DEF_TNB_TIERS "1"
+#define DEF_TNB_TILE (char *)NULL
+#define DEF_TNB_WIDTH "0"
+#define DEF_TNB_SAME_WIDTH "yes"
+#define DEF_TNB_TEAROFF "yes"
+#define DEF_TNB_PAGE_WIDTH "0"
+#define DEF_TNB_PAGE_HEIGHT "0"
+
+#define DEF_TAB_ACTIVE_BG (char *)NULL
+#define DEF_TAB_ACTIVE_FG (char *)NULL
+#define DEF_TAB_ANCHOR "center"
+#define DEF_TAB_BG (char *)NULL
+#define DEF_TAB_COMMAND (char *)NULL
+#define DEF_TAB_DATA (char *)NULL
+#define DEF_TAB_FG (char *)NULL
+#define DEF_TAB_FILL "none"
+#define DEF_TAB_FONT (char *)NULL
+#define DEF_TAB_HEIGHT "0"
+#define DEF_TAB_IMAGE (char *)NULL
+#define DEF_TAB_IPAD "0"
+#define DEF_TAB_PAD "3"
+#define DEF_TAB_PERF_COMMAND (char *)NULL
+#define DEF_TAB_SELECT_BG (char *)NULL
+#define DEF_TAB_SELECT_BORDER_WIDTH "1"
+#define DEF_TAB_SELECT_CMD (char *)NULL
+#define DEF_TAB_SELECT_FG (char *)NULL
+#define DEF_TAB_SHADOW (char *)NULL
+#define DEF_TAB_STATE "normal"
+#define DEF_TAB_STIPPLE "BLT"
+#define DEF_TAB_BIND_TAGS "all"
+#define DEF_TAB_TEXT (char *)NULL
+#define DEF_TAB_VISUAL (char *)NULL
+#define DEF_TAB_WIDTH "0"
+#define DEF_TAB_WINDOW (char *)NULL
+
+typedef struct NotebookStruct Notebook;
+
+static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window));
+static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window));
+
+static Tk_GeomMgr tabMgrInfo =
+{
+ "notebook", /* Name of geometry manager used by winfo */
+ EmbeddedWidgetGeometryProc, /* Procedure to for new geometry requests */
+ EmbeddedWidgetCustodyProc, /* Procedure when window is taken away */
+};
+
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPositiveCountOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltStateOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltUidOption;
+
+static int StringToImage _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *ImageToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToWindow _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *WindowToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSide _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *SideToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSlant _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *SlantToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+/*
+ * Contains a pointer to the widget that's currently being configured.
+ * This is used in the custom configuration parse routine for images.
+ */
+static Notebook *lastNotebookInstance;
+
+static Tk_CustomOption imageOption =
+{
+ StringToImage, ImageToString, (ClientData)&lastNotebookInstance,
+};
+
+static Tk_CustomOption sideOption =
+{
+ StringToSide, SideToString, (ClientData)0,
+};
+
+static Tk_CustomOption windowOption =
+{
+ StringToWindow, WindowToString, (ClientData)0,
+};
+
+static Tk_CustomOption slantOption =
+{
+ StringToSlant, SlantToString, (ClientData)0,
+};
+
+/*
+ * TabImage --
+ *
+ * When multiple instances of an image are displayed in the
+ * same widget, this can be inefficient in terms of both memory
+ * and time. We only need one instance of each image, regardless
+ * of number of times we use it. And searching/deleting instances
+ * can be very slow as the list gets large.
+ *
+ * The workaround, employed below, is to maintain a hash table of
+ * images that maintains a reference count for each image.
+ */
+
+typedef struct TabImageStruct {
+ int refCount; /* Reference counter for this image. */
+ Tk_Image tkImage; /* The Tk image being cached. */
+ int width, height; /* Dimensions of the cached image. */
+ Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */
+
+} *TabImage;
+
+#define ImageHeight(image) ((image)->height)
+#define ImageWidth(image) ((image)->width)
+#define ImageData(image) ((image)->tkImage)
+
+#define TAB_VISIBLE (1<<0)
+#define TAB_REDRAW (1<<2)
+
+typedef struct {
+ char *name; /* Identifier for tab entry */
+ int state; /* State of the tab: Disabled, active, or
+ * normal. */
+ unsigned int flags;
+
+ int tier; /* Index of tier [1..numTiers] containing
+ * this tab. */
+
+ int worldX, worldY; /* Position of the tab in world coordinates. */
+ int worldWidth, worldHeight;/* Dimensions of the tab, corrected for
+ * orientation (-side). It includes the
+ * border, padding, label, etc. */
+ int screenX, screenY;
+ short int screenWidth, screenHeight; /* */
+
+ Notebook *nbPtr; /* Notebook that includes this
+ * tab. Needed for callbacks can pass
+ * only a tab pointer. */
+ Tk_Uid tags;
+
+ /*
+ * Tab label:
+ */
+ Tk_Uid text; /* String displayed as the tab's label. */
+ TabImage image; /* Image displayed as the label. */
+
+ short int textWidth, textHeight;
+ short int labelWidth, labelHeight;
+ Blt_Pad iPadX, iPadY; /* Internal padding around the text */
+
+ Tk_Font font;
+
+ /*
+ * Normal:
+ */
+ XColor *textColor; /* Text color */
+ Tk_3DBorder border; /* Background color and border for tab.*/
+
+ /*
+ * Selected: Tab is currently selected.
+ */
+ XColor *selColor; /* Selected text color */
+ Tk_3DBorder selBorder; /* 3D border of selected folder. */
+
+ /*
+ * Active: Mouse passes over the tab.
+ */
+ Tk_3DBorder activeBorder; /* Active background color. */
+ XColor *activeFgColor; /* Active text color */
+
+ Shadow shadow;
+ Pixmap stipple; /* Stipple for outline of embedded window
+ * when torn off. */
+ /*
+ * Embedded widget information:
+ */
+ Tk_Window tkwin; /* Widget to be mapped when the tab is
+ * selected. If NULL, don't make
+ * space for the page. */
+
+ int reqWidth, reqHeight; /* If non-zero, overrides the
+ * requested dimensions of the
+ * embedded widget. */
+
+ Tk_Window container; /* The window containing the embedded
+ * widget. Does not necessarily have
+ * to be the parent. */
+
+ Tk_Anchor anchor; /* Anchor: indicates how the embedded
+ * widget is positioned within the
+ * extra space on the page. */
+
+ Blt_Pad padX, padY; /* Padding around embedded widget */
+
+ int fill; /* Indicates how the window should
+ * fill the page. */
+
+ /*
+ * Auxillary information:
+ */
+ Tk_Uid command; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ Tk_Uid data; /* This value isn't used in C code.
+ * It may be used by clients in Tcl bindings
+ * to associate extra data (other than the
+ * label or name) with the tab. */
+
+ Blt_ChainLink *linkPtr; /* Pointer to where the tab resides in the
+ * list of tabs. */
+ Tk_Uid perfCommand; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ GC textGC;
+ GC backGC;
+
+ Blt_Tile tile;
+
+} Tab;
+
+static Tk_ConfigSpec tabConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TAB_ACTIVE_BG,
+ Tk_Offset(Tab, activeBorder), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_TAB_ACTIVE_FG,
+ Tk_Offset(Tab, activeFgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_TAB_ANCHOR, Tk_Offset(Tab, anchor), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TAB_BG, Tk_Offset(Tab, border), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_TAB_BIND_TAGS, Tk_Offset(Tab, tags),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-command", "command", "Command",
+ DEF_TAB_COMMAND, Tk_Offset(Tab, command),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "data",
+ DEF_TAB_DATA, Tk_Offset(Tab, data),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_TAB_FILL, Tk_Offset(Tab, fill),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TAB_FG, Tk_Offset(Tab, textColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TAB_FONT, Tk_Offset(Tab, font), 0},
+ {TK_CONFIG_CUSTOM, "-image", "image", "image",
+ DEF_TAB_IMAGE, Tk_Offset(Tab, image),
+ TK_CONFIG_NULL_OK, &imageOption},
+ {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "PadX",
+ DEF_TAB_IPAD, Tk_Offset(Tab, iPadX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "PadY",
+ DEF_TAB_IPAD, Tk_Offset(Tab, iPadY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_TAB_PAD, Tk_Offset(Tab, padX), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_TAB_PAD, Tk_Offset(Tab, padY), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-perforationcommand", "perforationcommand",
+ "PerforationCommand",
+ DEF_TAB_PERF_COMMAND, Tk_Offset(Tab, perfCommand),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_TAB_SELECT_BG, Tk_Offset(Tab, selBorder), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_TAB_SELECT_FG, Tk_Offset(Tab, selColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_TAB_SHADOW, Tk_Offset(Tab, shadow),
+ TK_CONFIG_NULL_OK, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_TAB_STATE, Tk_Offset(Tab, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_TAB_STIPPLE, Tk_Offset(Tab, stipple), 0},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Tab, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_CUSTOM, "-text", "Text", "Text",
+ DEF_TAB_TEXT, Tk_Offset(Tab, text),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-window", "window", "Window",
+ DEF_TAB_WINDOW, Tk_Offset(Tab, tkwin),
+ TK_CONFIG_NULL_OK, &windowOption},
+ {TK_CONFIG_CUSTOM, "-windowheight", "windowHeight", "WindowHeight",
+ DEF_TAB_HEIGHT, Tk_Offset(Tab, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-windowwidth", "windowWidth", "WindowWidth",
+ DEF_TAB_WIDTH, Tk_Offset(Tab, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * TabAttributes --
+ */
+typedef struct {
+ char *name;
+} Perforation;
+
+typedef struct {
+ Tk_Window tkwin; /* Default window to map pages. */
+
+ int reqWidth, reqHeight; /* Requested tab size. */
+ int constWidth;
+ int borderWidth; /* Width of 3D border around the tab's
+ * label. */
+ int pad; /* Extra padding of a tab entry */
+
+ XColor *activeFgColor; /* Active foreground. */
+ Tk_3DBorder activeBorder; /* Active background. */
+ XColor *selColor; /* Selected foreground. */
+ Tk_Font font;
+ XColor *textColor;
+
+ Tk_3DBorder border; /* Normal background. */
+ Tk_3DBorder selBorder; /* Selected background. */
+
+ Blt_Dashes dashes;
+ GC normalGC, activeGC;
+ int relief;
+ char *command;
+ char *perfCommand; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ double rotate;
+ int textSide;
+
+} TabAttributes;
+
+struct NotebookStruct {
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+
+ Tcl_Command cmdToken; /* Token for widget's command. */
+
+ unsigned int flags; /* For bitfield definitions, see below */
+
+ int inset; /* Total width of all borders, including
+ * traversal highlight and 3-D border.
+ * Indicates how much interior stuff must
+ * be offset from outside edges to leave
+ * room for borders. */
+
+ int inset2; /* Total width of 3-D folder border + corner,
+ * Indicates how much interior stuff must
+ * be offset from outside edges of folder.*/
+
+ int yPad; /* Extra offset for selected tab. Only
+ * for single tiers. */
+
+ int pageTop; /* Offset from top of notebook to the
+ * start of the page. */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ Tk_3DBorder border; /* 3D border surrounding the window. */
+ int borderWidth; /* Width of 3D border. */
+ int relief; /* 3D border relief. */
+
+ XColor *shadowColor; /* Shadow color around folder. */
+ /*
+ * Focus highlight ring
+ */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ GC highlightGC; /* GC for focus highlight. */
+
+ char *takeFocus; /* Says whether to select this widget during
+ * tab traveral operations. This value isn't
+ * used in C code, but for the widget's Tcl
+ * bindings. */
+
+
+ int side; /* How notebook is oriented: either SIDE_LEFT,
+ * SIDE_RIGHT, SIDE_TOP, or SIDE_BOTTOM. */
+
+ int slant;
+ int overlap;
+ int gap;
+ int tabWidth, tabHeight;
+ int xSelectPad, ySelectPad; /* Padding around label of the selected tab. */
+ int outerPad; /* Padding around the exterior of the notebook
+ * and folder. */
+
+ TabAttributes defTabStyle; /* Global attribute information specific to
+ * tabs. */
+ Blt_Tile tile;
+
+ int reqWidth, reqHeight; /* Requested dimensions of the notebook
+ * window. */
+ int pageWidth, pageHeight; /* Dimensions of a page in the folder. */
+ int reqPageWidth, reqPageHeight; /* Requested dimensions of a page. */
+
+ int lastX, lastY;
+ /*
+ * Scrolling information:
+ */
+ int worldWidth;
+ int scrollOffset; /* Offset of viewport in world coordinates. */
+ char *scrollCmdPrefix; /* Command strings to control scrollbar.*/
+
+ int scrollUnits; /* Smallest unit of scrolling for tabs. */
+
+ /*
+ * Scanning information:
+ */
+ int scanAnchor; /* Scan anchor in screen coordinates. */
+ int scanOffset; /* Offset of the start of the scan in world
+ * coordinates.*/
+
+
+ int corner; /* Number of pixels to offset next point
+ * when drawing corners of the folder. */
+ int reqTiers; /* Requested number of tiers. Zero means to
+ * dynamically scroll if there are too many
+ * tabs to be display on a single tier. */
+ int nTiers; /* Actual number of tiers. */
+
+ Blt_HashTable imageTable;
+
+
+ Tab *selectPtr; /* The currently selected tab.
+ * (i.e. its page is displayed). */
+
+ Tab *activePtr; /* Tab last located under the pointer.
+ * It is displayed with its active
+ * foreground/background colors. */
+
+ Tab *focusPtr; /* Tab currently receiving focus. */
+
+ Tab *startPtr; /* The first tab on the first tier. */
+
+ Blt_Chain *chainPtr; /* List of tab entries. Used to
+ * arrange placement of tabs. */
+
+ Blt_HashTable tabTable; /* Hash table of tab entries. Used for
+ * lookups of tabs by name. */
+ int nextId;
+
+ int nVisible; /* Number of tabs that are currently visible
+ * in the view port. */
+
+ Blt_BindTable bindTable; /* Tab binding information */
+ Blt_HashTable tagTable; /* Table of bind tags. */
+
+ Perforation perforation;
+ int tearoff;
+};
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "activeBackground",
+ DEF_TNB_ACTIVE_BG_COLOR, Tk_Offset(Notebook, defTabStyle.activeBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "activeBackground",
+ DEF_TNB_ACTIVE_BG_MONO, Tk_Offset(Notebook, defTabStyle.activeBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "activeForeground", DEF_TNB_ACTIVE_FG_COLOR,
+ Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "activeForeground", DEF_TNB_ACTIVE_FG_MONO,
+ Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TNB_BG_MONO, Tk_Offset(Notebook, border), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TNB_BG_COLOR, Tk_Offset(Notebook, border), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_TNB_CURSOR, Tk_Offset(Notebook, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TNB_BORDER_WIDTH, Tk_Offset(Notebook, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_TNB_DASHES, Tk_Offset(Notebook, defTabStyle.dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_SYNONYM, "-fg", "tabForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TNB_FONT, Tk_Offset(Notebook, defTabStyle.font), 0},
+ {TK_CONFIG_SYNONYM, "-foreground", "tabForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_PIXELS, "-gap", "gap", "Gap",
+ DEF_TNB_GAP, Tk_Offset(Notebook, gap),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_TNB_HEIGHT, Tk_Offset(Notebook, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_TNB_HIGHLIGHT_BG_COLOR, Tk_Offset(Notebook, highlightBgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_TNB_HIGHLIGHT_BG_MONO, Tk_Offset(Notebook, highlightBgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_TNB_HIGHLIGHT_COLOR, Tk_Offset(Notebook, highlightColor), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_TNB_HIGHLIGHT_WIDTH, Tk_Offset(Notebook, highlightWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-outerpad", "outerPad", "OuterPad",
+ DEF_TNB_OUTER_PAD, Tk_Offset(Notebook, outerPad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pageheight", "pageHeight", "PageHeight",
+ DEF_TNB_PAGE_HEIGHT, Tk_Offset(Notebook, reqPageHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pagewidth", "pageWidth", "PageWidth",
+ DEF_TNB_PAGE_WIDTH, Tk_Offset(Notebook, reqPageWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-perforationcommand", "perforationcommand",
+ "PerforationCommand",
+ DEF_TAB_PERF_COMMAND, Tk_Offset(Notebook, defTabStyle.perfCommand),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TNB_RELIEF, Tk_Offset(Notebook, relief), 0},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_TNB_ROTATE, Tk_Offset(Notebook, defTabStyle.rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-samewidth", "sameWidth", "SameWidth",
+ DEF_TNB_SAME_WIDTH, Tk_Offset(Notebook, defTabStyle.constWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(Notebook, scrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement",
+ "ScrollIncrement",
+ DEF_TNB_SCROLL_INCREMENT, Tk_Offset(Notebook, scrollUnits),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TNB_SELECT_BG_MONO, Tk_Offset(Notebook, defTabStyle.selBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TNB_SELECT_BG_COLOR, Tk_Offset(Notebook, defTabStyle.selBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ DEF_TNB_SELECT_CMD, Tk_Offset(Notebook, defTabStyle.command),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TNB_SELECT_FG_MONO, Tk_Offset(Notebook, defTabStyle.selColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TNB_SELECT_FG_COLOR, Tk_Offset(Notebook, defTabStyle.selColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-selectpad", "selectPad", "SelectPad",
+ DEF_TNB_SELECT_PAD, Tk_Offset(Notebook, xSelectPad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-shadowcolor", "shadowColor", "ShadowColor",
+ DEF_TNB_SHADOW_COLOR, Tk_Offset(Notebook, shadowColor), 0},
+ {TK_CONFIG_CUSTOM, "-side", "side", "side",
+ DEF_TNB_SIDE, Tk_Offset(Notebook, side),
+ TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+ {TK_CONFIG_CUSTOM, "-slant", "slant", "Slant",
+ DEF_TNB_SLANT, Tk_Offset(Notebook, slant),
+ TK_CONFIG_DONT_SET_DEFAULT, &slantOption},
+ {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+ DEF_TNB_TAB_BG_MONO, Tk_Offset(Notebook, defTabStyle.border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+ DEF_TNB_TAB_BG_COLOR, Tk_Offset(Notebook, defTabStyle.border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-tabborderwidth", "tabBorderWidth", "BorderWidth",
+ DEF_TNB_BORDER_WIDTH, Tk_Offset(Notebook, defTabStyle.borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+ DEF_TNB_TEXT_COLOR, Tk_Offset(Notebook, defTabStyle.textColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+ DEF_TNB_TEXT_MONO, Tk_Offset(Notebook, defTabStyle.textColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-tabrelief", "tabRelief", "TabRelief",
+ DEF_TNB_TAB_RELIEF, Tk_Offset(Notebook, defTabStyle.relief), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_TNB_TAKE_FOCUS, Tk_Offset(Notebook, takeFocus), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-tearoff", "tearoff", "Tearoff",
+ DEF_TNB_TEAROFF, Tk_Offset(Notebook, tearoff),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-textside", "textSide", "TextSide",
+ DEF_TNB_TEXT_SIDE, Tk_Offset(Notebook, defTabStyle.textSide),
+ TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+ {TK_CONFIG_CUSTOM, "-tiers", "tiers", "Tiers",
+ DEF_TNB_TIERS, Tk_Offset(Notebook, reqTiers),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Notebook, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_TNB_WIDTH, Tk_Offset(Notebook, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static void DestroyNotebook _ANSI_ARGS_((DestroyData dataPtr));
+static void DestroyTearoff _ANSI_ARGS_((DestroyData dataPtr));
+static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void TearoffEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void NotebookEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void DrawLabel _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
+ Drawable drawable));
+static void DrawFolder _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
+ Drawable drawable));
+static void DisplayNotebook _ANSI_ARGS_((ClientData clientData));
+static void DisplayTearoff _ANSI_ARGS_((ClientData clientData));
+static void NotebookInstDeletedCmd _ANSI_ARGS_((ClientData clientdata));
+static int NotebookInstCmd _ANSI_ARGS_((ClientData clientdata,
+ Tcl_Interp *interp, int argc, char **argv));
+static void GetWindowRectangle _ANSI_ARGS_((Tab *tabPtr, Tk_Window parent,
+ int tearOff, XRectangle *rectPtr));
+static void ArrangeWindow _ANSI_ARGS_((Tk_Window tkwin, XRectangle *rectPtr,
+ int force));
+static void EventuallyRedraw _ANSI_ARGS_((Notebook *nbPtr));
+static void EventuallyRedrawTearoff _ANSI_ARGS_((Tab *tabPtr));
+static void ComputeLayout _ANSI_ARGS_((Notebook *nbPtr));
+static void DrawOuterBorders _ANSI_ARGS_((Notebook *nbPtr,
+ Drawable drawable));
+
+#ifdef __STDC__
+static Tk_ImageChangedProc ImageChangedProc;
+static Blt_TileChangedProc TileChangedProc;
+static Blt_BindTagProc GetTags;
+static Blt_BindPickProc PickTab;
+static Tcl_IdleProc AdoptWindow;
+static Tcl_CmdProc NotebookCmd;
+#endif /* __STDC__ */
+
+static ClientData
+MakeTag(nbPtr, tagName)
+ Notebook *nbPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&(nbPtr->tagTable), tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WorldToScreen --
+ *
+ * Converts world coordinates to screen coordinates. Note that
+ * the world view is always tabs up.
+ *
+ * Results:
+ * The screen coordinates are returned via *xScreenPtr and *yScreenPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+WorldToScreen(nbPtr, x, y, xScreenPtr, yScreenPtr)
+ Notebook *nbPtr;
+ int x, y;
+ int *xScreenPtr, *yScreenPtr;
+{
+ int sx, sy;
+
+ sx = sy = 0; /* Suppress compiler warning. */
+
+ /* Translate world X-Y to screen coordinates */
+ /*
+ * Note that the world X-coordinate is translated by the selected label's
+ * X padding. This is done only to keep the scroll range is between
+ * 0.0 and 1.0, rather adding/subtracting the pad in various locations.
+ * It may be changed back in the future.
+ */
+ x += (nbPtr->inset +
+ nbPtr->xSelectPad -
+ nbPtr->scrollOffset);
+ y += nbPtr->inset + nbPtr->yPad;
+
+ switch (nbPtr->side) {
+ case SIDE_TOP:
+ sx = x, sy = y; /* Do nothing */
+ break;
+ case SIDE_RIGHT:
+ sx = Tk_Width(nbPtr->tkwin) - y;
+ sy = x;
+ break;
+ case SIDE_LEFT:
+ sx = y, sy = x; /* Flip coordinates */
+ break;
+ case SIDE_BOTTOM:
+ sx = x;
+ sy = Tk_Height(nbPtr->tkwin) - y;
+ break;
+ }
+ *xScreenPtr = sx;
+ *yScreenPtr = sy;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(nbPtr)
+ Notebook *nbPtr;
+{
+ if ((nbPtr->tkwin != NULL) && !(nbPtr->flags & TNB_REDRAW)) {
+ nbPtr->flags |= TNB_REDRAW;
+ Tcl_DoWhenIdle(DisplayNotebook, nbPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawTearoff --
+ *
+ * Queues a request to redraw the tearoff at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawTearoff(tabPtr)
+ Tab *tabPtr;
+{
+ if ((tabPtr->tkwin != NULL) && !(tabPtr->flags & TAB_REDRAW)) {
+ tabPtr->flags |= TAB_REDRAW;
+ Tcl_DoWhenIdle(DisplayTearoff, tabPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ * This routine is called whenever an image displayed in a tab
+ * changes. In this case, we assume that everything will change
+ * and queue a request to re-layout and redraw the entire notebook.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+ Notebook *nbPtr = clientData;
+
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetImage --
+ *
+ * This is a wrapper procedure for Tk_GetImage. The problem is
+ * that if the same image is used repeatedly in the same widget,
+ * the separate instances are saved in a linked list. This makes
+ * it especially slow to destroy the widget. As a workaround,
+ * this routine hashes the image and maintains a reference count
+ * for it.
+ *
+ * Results:
+ * Returns a pointer to the new image.
+ *
+ *----------------------------------------------------------------------
+ */
+static TabImage
+GetImage(nbPtr, interp, tkwin, name)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *name;
+{
+ struct TabImageStruct *imagePtr;
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&(nbPtr->imageTable), name, &isNew);
+ if (isNew) {
+ Tk_Image tkImage;
+ int width, height;
+
+ tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, nbPtr);
+ if (tkImage == NULL) {
+ Blt_DeleteHashEntry(&(nbPtr->imageTable), hPtr);
+ return NULL;
+ }
+ Tk_SizeOfImage(tkImage, &width, &height);
+ imagePtr = Blt_Malloc(sizeof(struct TabImageStruct));
+ imagePtr->tkImage = tkImage;
+ imagePtr->hashPtr = hPtr;
+ imagePtr->refCount = 1;
+ imagePtr->width = width;
+ imagePtr->height = height;
+ Blt_SetHashValue(hPtr, imagePtr);
+ } else {
+ imagePtr = (struct TabImageStruct *)Blt_GetHashValue(hPtr);
+ imagePtr->refCount++;
+ }
+ return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeImage --
+ *
+ * Releases the image if it's not being used anymore by this
+ * widget. Note there may be several uses of the same image
+ * by many tabs.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The reference count is decremented and the image is freed
+ * is it's not being used anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeImage(nbPtr, imagePtr)
+ Notebook *nbPtr;
+ struct TabImageStruct *imagePtr;
+{
+ imagePtr->refCount--;
+ if (imagePtr->refCount == 0) {
+ Blt_DeleteHashEntry(&(nbPtr->imageTable), imagePtr->hashPtr);
+ Tk_FreeImage(imagePtr->tkImage);
+ Blt_Free(imagePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToImage --
+ *
+ * Converts an image name into a Tk image token.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToImage(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Contains a pointer to the notebook containing
+ * this image. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window associated with the notebook. */
+ char *string; /* String representation */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset to field in structure */
+{
+ Notebook *nbPtr = *(Notebook **)clientData;
+ TabImage *imagePtr = (TabImage *) (widgRec + offset);
+ TabImage image;
+
+ image = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ image = GetImage(nbPtr, interp, tkwin, string);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*imagePtr != NULL) {
+ FreeImage(nbPtr, *imagePtr);
+ }
+ *imagePtr = image;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageToString --
+ *
+ * Converts the Tk image back to its string representation (i.e.
+ * its name).
+ *
+ * Results:
+ * The name of the image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ImageToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Pointer to notebook containing image. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Notebook *nbPtr = *(Notebook **)clientData;
+ TabImage *imagePtr = (TabImage *) (widgRec + offset);
+
+ if (*imagePtr == NULL) {
+ return "";
+ }
+ return Blt_GetHashKey(&(nbPtr->imageTable), (*imagePtr)->hashPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToWindow --
+ *
+ * Converts a window name into Tk window.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToWindow(clientData, interp, parent, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window parent; /* Parent window */
+ char *string; /* String representation. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset to field in structure */
+{
+ Tab *tabPtr = (Tab *)widgRec;
+ Tk_Window *tkwinPtr = (Tk_Window *)(widgRec + offset);
+ Tk_Window old, tkwin;
+ Notebook *nbPtr;
+
+ old = *tkwinPtr;
+ tkwin = NULL;
+ nbPtr = tabPtr->nbPtr;
+ if ((string != NULL) && (*string != '\0')) {
+ tkwin = Tk_NameToWindow(interp, string, parent);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (tkwin == old) {
+ return TCL_OK;
+ }
+ /*
+ * Allow only widgets that are children of the notebook to be
+ * embedded into the page. This way we can make assumptions about
+ * the window based upon its parent; either it's the notebook window
+ * or it has been torn off.
+ */
+ parent = Tk_Parent(tkwin);
+ if (parent != nbPtr->tkwin) {
+ Tcl_AppendResult(interp, "can't manage \"", Tk_PathName(tkwin),
+ "\" in notebook \"", Tk_PathName(nbPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_ManageGeometry(tkwin, &tabMgrInfo, tabPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+
+ /*
+ * We need to make the window to exist immediately. If the
+ * window is torn off (placed into another container window),
+ * the timing between the container and the its new child
+ * (this window) gets tricky. This should work for Tk 4.2.
+ */
+ Tk_MakeWindowExist(tkwin);
+ }
+ if (old != NULL) {
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ Tk_DeleteEventHandler(old, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ Tk_ManageGeometry(old, (Tk_GeomMgr *) NULL, tabPtr);
+ Tk_UnmapWindow(old);
+ }
+ *tkwinPtr = tkwin;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowToString --
+ *
+ * Converts the Tk window back to its string representation (i.e.
+ * its name).
+ *
+ * Results:
+ * The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+WindowToString(clientData, parent, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window parent; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Tk_Window tkwin = *(Tk_Window *)(widgRec + offset);
+
+ if (tkwin == NULL) {
+ return "";
+ }
+ return Tk_PathName(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSide --
+ *
+ * Converts "left", "right", "top", "bottom", into a numeric token
+ * designating the side of the notebook which to display tabs.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED */
+static int
+StringToSide(clientData, interp, parent, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window parent; /* Parent window */
+ char *string; /* Option value string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to field in structure */
+{
+ int *sidePtr = (int *)(widgRec + offset);
+ char c;
+ unsigned int length;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *sidePtr = SIDE_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *sidePtr = SIDE_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+ *sidePtr = SIDE_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+ *sidePtr = SIDE_BOTTOM;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", string,
+ "\": should be left, right, top, or bottom", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SideToString --
+ *
+ * Converts the window into its string representation (its name).
+ *
+ * Results:
+ * The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SideToString(clientData, parent, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window parent; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of windows array in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ int side = *(int *)(widgRec + offset);
+
+ switch (side) {
+ case SIDE_LEFT:
+ return "left";
+ case SIDE_RIGHT:
+ return "right";
+ case SIDE_BOTTOM:
+ return "bottom";
+ case SIDE_TOP:
+ return "top";
+ }
+ return "unknown side value";
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSlant --
+ *
+ * Converts the slant style string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none" Both sides are straight.
+ * "left" Left side is slanted.
+ * "right" Right side is slanted.
+ * "both" Both sides are slanted.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSlant(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of attribute. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in widget record. */
+{
+ int *slantPtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *slantPtr = SLANT_NONE;
+ } else if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *slantPtr = SLANT_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *slantPtr = SLANT_RIGHT;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *slantPtr = SLANT_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", string,
+ "\": should be \"none\", \"left\", \"right\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SlantToString --
+ *
+ * Returns the slant style string based upon the slant flags.
+ *
+ * Results:
+ * The slant style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SlantToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record. */
+ int offset; /* Offset of field in widget record. */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int slant = *(int *)(widgRec + offset);
+
+ switch (slant) {
+ case SLANT_LEFT:
+ return "left";
+ case SLANT_RIGHT:
+ return "right";
+ case SLANT_NONE:
+ return "none";
+ case SLANT_BOTH:
+ return "both";
+ default:
+ return "unknown value";
+ }
+}
+
+
+static int
+WorldY(tabPtr)
+ Tab *tabPtr;
+{
+ int tier;
+
+ tier = tabPtr->nbPtr->nTiers - tabPtr->tier;
+ return tier * tabPtr->nbPtr->tabHeight;
+}
+
+static int
+TabIndex(nbPtr, tabPtr)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+{
+ Tab *t2Ptr;
+ int count;
+ Blt_ChainLink *linkPtr;
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ t2Ptr = Blt_ChainGetValue(linkPtr);
+ if (t2Ptr == tabPtr) {
+ return count;
+ }
+ count++;
+ }
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RenumberTiers --
+ *
+ * In multi-tier mode, we need to find the start of the tier
+ * containing the newly selected tab.
+ *
+ * Tiers are draw from the last tier to the first, so that
+ * the the lower-tiered tabs will partially cover the bottoms
+ * of tab directly above it. This simplifies the drawing of
+ * tabs because we don't worry how tabs are clipped by their
+ * neighbors.
+ *
+ * In addition, tabs are re-marked with the correct tier number.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Renumbering the tab's tier will change the vertical placement
+ * of the tab (i.e. shift tiers).
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+RenumberTiers(nbPtr, tabPtr)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+{
+ int tier;
+ Tab *prevPtr;
+ Blt_ChainLink *linkPtr, *lastPtr;
+
+ nbPtr->focusPtr = nbPtr->selectPtr = tabPtr;
+ Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr);
+
+ tier = tabPtr->tier;
+ for (linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); linkPtr != NULL;
+ linkPtr = lastPtr) {
+ lastPtr = Blt_ChainPrevLink(linkPtr);
+ prevPtr = Blt_ChainGetValue(linkPtr);
+ if ((prevPtr == NULL) || (prevPtr->tier != tier)) {
+ break;
+ }
+ tabPtr = prevPtr;
+ }
+ nbPtr->startPtr = tabPtr;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = (tabPtr->tier - tier + 1);
+ if (tabPtr->tier < 1) {
+ tabPtr->tier += nbPtr->nTiers;
+ }
+ tabPtr->worldY = WorldY(tabPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PickTab --
+ *
+ * Searches the tab located within the given screen X-Y coordinates
+ * in the viewport. Note that tabs overlap slightly, so that its
+ * important to search from the innermost tier out.
+ *
+ * Results:
+ * Returns the pointer to the tab. If the pointer isn't contained
+ * by any tab, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static ClientData
+PickTab(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates to test. */
+{
+ Notebook *nbPtr = clientData;
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ tabPtr = nbPtr->selectPtr;
+ if ((nbPtr->tearoff) && (tabPtr != NULL) &&
+ (tabPtr->container == NULL) && (tabPtr->tkwin != NULL)) {
+ int top, bottom, left, right;
+ int sx, sy;
+
+ /* Check first for perforation on the selected tab. */
+ WorldToScreen(nbPtr, tabPtr->worldX + 2,
+ tabPtr->worldY + tabPtr->worldHeight + 4, &sx, &sy);
+ if (nbPtr->side & SIDE_HORIZONTAL) {
+ left = sx - 2;
+ right = left + tabPtr->screenWidth;
+ top = sy - 4;
+ bottom = sy + 4;
+ } else {
+ left = sx - 4;
+ right = sx + 4;
+ top = sy - 2;
+ bottom = top + tabPtr->screenHeight;
+ }
+ if ((x >= left) && (y >= top) && (x < right) && (y < bottom)) {
+ return &nbPtr->perforation;
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (!(tabPtr->flags & TAB_VISIBLE)) {
+ continue;
+ }
+ if ((x >= tabPtr->screenX) && (y >= tabPtr->screenY) &&
+ (x <= (tabPtr->screenX + tabPtr->screenWidth)) &&
+ (y < (tabPtr->screenY + tabPtr->screenHeight))) {
+ return tabPtr;
+ }
+ }
+ return NULL;
+}
+
+static Tab *
+TabLeft(tabPtr)
+ Tab *tabPtr;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *newPtr;
+
+ newPtr = Blt_ChainGetValue(linkPtr);
+ /* Move only if the next tab is on another tier. */
+ if (newPtr->tier == tabPtr->tier) {
+ tabPtr = newPtr;
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabRight(tabPtr)
+ Tab *tabPtr;
+{
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *newPtr;
+
+ newPtr = Blt_ChainGetValue(linkPtr);
+ /* Move only if the next tab is on another tier. */
+ if (newPtr->tier == tabPtr->tier) {
+ tabPtr = newPtr;
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabUp(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Notebook *nbPtr;
+ int x, y;
+ int worldX, worldY;
+
+ nbPtr = tabPtr->nbPtr;
+ worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+ worldY = tabPtr->worldY - (nbPtr->tabHeight / 2);
+ WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ if (tabPtr == NULL) {
+ /*
+ * We might have inadvertly picked the gap between two tabs,
+ * so if the first pick fails, try again a little to the left.
+ */
+ WorldToScreen(nbPtr, worldX + nbPtr->gap, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ }
+ if ((tabPtr == NULL) &&
+ (nbPtr->focusPtr->tier < (nbPtr->nTiers - 1))) {
+ worldY -= nbPtr->tabHeight;
+ WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ }
+ if (tabPtr == NULL) {
+ tabPtr = nbPtr->focusPtr;
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabDown(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Notebook *nbPtr;
+ int x, y;
+ int worldX, worldY;
+
+ nbPtr = tabPtr->nbPtr;
+ worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+ worldY = tabPtr->worldY + (3 * nbPtr->tabHeight) / 2;
+ WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ if (tabPtr == NULL) {
+ /*
+ * We might have inadvertly picked the gap between two tabs,
+ * so if the first pick fails, try again a little to the left.
+ */
+ WorldToScreen(nbPtr, worldX - nbPtr->gap, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ }
+ if ((tabPtr == NULL) && (nbPtr->focusPtr->tier > 2)) {
+ worldY += nbPtr->tabHeight;
+ WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ }
+ if (tabPtr == NULL) {
+ tabPtr = nbPtr->focusPtr;
+ }
+ }
+ return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTab --
+ *
+ * Converts a string representing a tab index into a tab pointer.
+ * The index may be in one of the following forms:
+ *
+ * number Tab at position in the list of tabs.
+ * @x,y Tab closest to the specified X-Y screen coordinates.
+ * "active" Tab mouse is located over.
+ * "focus" Tab is the widget's focus.
+ * "select" Currently selected tab.
+ * "right" Next tab from the focus tab.
+ * "left" Previous tab from the focus tab.
+ * "up" Next tab from the focus tab.
+ * "down" Previous tab from the focus tab.
+ * "end" Last tab in list.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via tabPtrPtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetTab(nbPtr, string, tabPtrPtr, allowNull)
+ Notebook *nbPtr;
+ char *string;
+ Tab **tabPtrPtr;
+ int allowNull; /* Allow NULL tabPtr */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+ int position;
+ char c;
+
+ c = string[0];
+ linkPtr = NULL;
+ tabPtr = NULL;
+ if (nbPtr->focusPtr == NULL) {
+ nbPtr->focusPtr = nbPtr->selectPtr;
+ Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr);
+ }
+ if ((isdigit(UCHAR(c))) &&
+ (Tcl_GetInt(nbPtr->interp, string, &position) == TCL_OK)) {
+ linkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
+ if (linkPtr == NULL) {
+ Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
+ "\" in \"", Tk_PathName(nbPtr->tkwin),
+ "\": no such index", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+ tabPtr = nbPtr->activePtr;
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ tabPtr = (Tab *)Blt_GetCurrentItem(nbPtr->bindTable);
+ if (tabPtr->name == NULL) {
+ /* Selected perforation. */
+ tabPtr = nbPtr->selectPtr;
+ }
+ } else if ((c == 's') && (strcmp(string, "select") == 0)) {
+ tabPtr = nbPtr->selectPtr;
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ tabPtr = nbPtr->focusPtr;
+ } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+ switch (nbPtr->side) {
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ tabPtr = TabLeft(nbPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr = TabDown(nbPtr->focusPtr);
+ break;
+
+ case SIDE_TOP:
+ tabPtr = TabUp(nbPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+ switch (nbPtr->side) {
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ tabPtr = TabRight(nbPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr = TabUp(nbPtr->focusPtr);
+ break;
+
+ case SIDE_TOP:
+ tabPtr = TabDown(nbPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'l') && (strcmp(string, "left") == 0)) {
+ switch (nbPtr->side) {
+ case SIDE_LEFT:
+ tabPtr = TabUp(nbPtr->focusPtr);
+ break;
+
+ case SIDE_RIGHT:
+ tabPtr = TabDown(nbPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ case SIDE_TOP:
+ tabPtr = TabLeft(nbPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'r') && (strcmp(string, "right") == 0)) {
+ switch (nbPtr->side) {
+ case SIDE_LEFT:
+ tabPtr = TabDown(nbPtr->focusPtr);
+ break;
+
+ case SIDE_RIGHT:
+ tabPtr = TabUp(nbPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ case SIDE_TOP:
+ tabPtr = TabRight(nbPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
+ if (linkPtr != NULL) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ }
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(nbPtr->interp, nbPtr->tkwin, string, &x, &y)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ } else {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), string);
+ if (hPtr != NULL) {
+ tabPtr = (Tab *)Blt_GetHashValue(hPtr);
+ }
+ }
+ *tabPtrPtr = tabPtr;
+ Tcl_ResetResult(nbPtr->interp);
+
+ if ((!allowNull) && (tabPtr == NULL)) {
+ Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
+ "\" in \"", Tk_PathName(nbPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+static Tab *
+NextOrLastTab(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr->linkPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr == NULL) {
+ linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+ }
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ }
+ return NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * EmbeddedWidgetEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on embedded widgets contained in the notebook.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When an embedded widget gets deleted, internal structures get
+ * cleaned up. When it gets resized, the notebook is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+EmbeddedWidgetEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ switch (eventPtr->type) {
+ case ConfigureNotify:
+ /*
+ * If the window's requested size changes, redraw the window.
+ * But only if it's currently the selected page.
+ */
+ if ((tabPtr->container == NULL) && (Tk_IsMapped(tabPtr->tkwin)) &&
+ (tabPtr->nbPtr->selectPtr == tabPtr)) {
+ EventuallyRedraw(tabPtr->nbPtr);
+ }
+ break;
+
+ case DestroyNotify:
+ /*
+ * Mark the tab as deleted by dereferencing the Tk window
+ * pointer. Redraw the window only if the tab is currently
+ * visible.
+ */
+ if ((Tk_IsMapped(tabPtr->tkwin)) &&
+ (tabPtr->nbPtr->selectPtr == tabPtr)) {
+ EventuallyRedraw(tabPtr->nbPtr);
+ }
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ tabPtr->tkwin = NULL;
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * EmbeddedWidgetCustodyProc --
+ *
+ * This procedure is invoked when a tab window has been
+ * stolen by another geometry manager. The information and
+ * memory associated with the tab window is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the widget formerly associated with the tab
+ * window to have its layout re-computed and arranged at the
+ * next idle point.
+ *
+ * ---------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Information about the former tab window. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Tab *tabPtr = clientData;
+ Notebook *nbPtr;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ nbPtr = tabPtr->nbPtr;
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ /*
+ * Mark the tab as deleted by dereferencing the Tk window
+ * pointer. Redraw the window only if the tab is currently
+ * visible.
+ */
+ if (tabPtr->tkwin != NULL) {
+ if (Tk_IsMapped(tabPtr->tkwin) && (nbPtr->selectPtr == tabPtr)) {
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ }
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ tabPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * EmbeddedWidgetGeometryProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for tab
+ * windows managed by the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for tkwin, and all its managed siblings, to be
+ * repacked and drawn at the next idle point.
+ *
+ * ------------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Information about window that got new
+ * preferred geometry. */
+ Tk_Window tkwin; /* Other Tk-related information about the
+ * window. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ fprintf(stderr, "%s: line %d \"tkwin is null\"", __FILE__, __LINE__);
+ return;
+ }
+ tabPtr->nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(tabPtr->nbPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTab --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTab(nbPtr, tabPtr)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ if (tabPtr->flags & TAB_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+ }
+ if (tabPtr->container != NULL) {
+ Tk_DestroyWindow(tabPtr->container);
+ }
+ if (tabPtr->tkwin != NULL) {
+ Tk_ManageGeometry(tabPtr->tkwin, (Tk_GeomMgr *)NULL, tabPtr);
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ if (Tk_IsMapped(tabPtr->tkwin)) {
+ Tk_UnmapWindow(tabPtr->tkwin);
+ }
+ }
+ if (tabPtr == nbPtr->activePtr) {
+ nbPtr->activePtr = NULL;
+ }
+ if (tabPtr == nbPtr->selectPtr) {
+ nbPtr->selectPtr = NextOrLastTab(tabPtr);
+ }
+ if (tabPtr == nbPtr->focusPtr) {
+ nbPtr->focusPtr = nbPtr->selectPtr;
+ Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr);
+ }
+ if (tabPtr == nbPtr->startPtr) {
+ nbPtr->startPtr = NULL;
+ }
+ Tk_FreeOptions(tabConfigSpecs, (char *)tabPtr, nbPtr->display, 0);
+ if (tabPtr->text != NULL) {
+ Blt_FreeUid(tabPtr->text);
+ }
+ hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), tabPtr->name);
+ assert(hPtr);
+ Blt_DeleteHashEntry(&(nbPtr->tabTable), hPtr);
+
+ if (tabPtr->image != NULL) {
+ FreeImage(nbPtr, tabPtr->image);
+ }
+ if (tabPtr->name != NULL) {
+ Blt_Free(tabPtr->name);
+ }
+ if (tabPtr->textGC != NULL) {
+ Tk_FreeGC(nbPtr->display, tabPtr->textGC);
+ }
+ if (tabPtr->backGC != NULL) {
+ Tk_FreeGC(nbPtr->display, tabPtr->backGC);
+ }
+ if (tabPtr->command != NULL) {
+ Blt_FreeUid(tabPtr->command);
+ }
+ if (tabPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(nbPtr->chainPtr, tabPtr->linkPtr);
+ }
+ if (tabPtr->tags != NULL) {
+ Blt_FreeUid(tabPtr->tags);
+ }
+ Blt_DeleteBindings(nbPtr->bindTable, tabPtr);
+ Blt_Free(tabPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTab --
+ *
+ * Creates a new tab structure. A tab contains information about
+ * the state of the tab and its embedded window.
+ *
+ * Results:
+ * Returns a pointer to the new tab structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Tab *
+CreateTab(nbPtr)
+ Notebook *nbPtr;
+{
+ Tab *tabPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ char string[200];
+
+ tabPtr = Blt_Calloc(1, sizeof(Tab));
+ assert(tabPtr);
+ tabPtr->nbPtr = nbPtr;
+ sprintf(string, "tab%d", nbPtr->nextId++);
+ tabPtr->name = Blt_Strdup(string);
+ tabPtr->text = Blt_GetUid(string);
+ tabPtr->fill = FILL_NONE;
+ tabPtr->anchor = TK_ANCHOR_CENTER;
+ tabPtr->container = NULL;
+ tabPtr->state = STATE_NORMAL;
+ hPtr = Blt_CreateHashEntry(&(nbPtr->tabTable), string, &isNew);
+ Blt_SetHashValue(hPtr, tabPtr);
+ return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Stub for image change notifications. Since we immediately draw
+ * the image into a pixmap, we don't really care about image changes.
+ *
+ * It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Notebook *nbPtr = clientData;
+
+ if (nbPtr->tkwin != NULL) {
+ EventuallyRedraw(nbPtr);
+ }
+}
+
+static int
+ConfigureTab(nbPtr, tabPtr)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ int labelWidth, labelHeight;
+ Tk_Font font;
+ Tk_3DBorder border;
+
+ font = GETATTR(tabPtr, font);
+ labelWidth = labelHeight = 0;
+ if (tabPtr->text != NULL) {
+ TextStyle ts;
+
+ Blt_InitTextStyle(&ts);
+ ts.font = font;
+ ts.shadow.offset = tabPtr->shadow.offset;
+ ts.padX.side1 = ts.padX.side2 = 2;
+ Blt_GetTextExtents(&ts, tabPtr->text, &labelWidth, &labelHeight);
+ Blt_GetBoundingBox(labelWidth, labelHeight, nbPtr->defTabStyle.rotate,
+ &labelWidth, &labelHeight, (Point2D *)NULL);
+ }
+ tabPtr->textWidth = (short int)labelWidth;
+ tabPtr->textHeight = (short int)labelHeight;
+ if (tabPtr->image != NULL) {
+ int width, height;
+
+ width = ImageWidth(tabPtr->image) + 2 * IMAGE_PAD;
+ height = ImageHeight(tabPtr->image) + 2 * IMAGE_PAD;
+ if (nbPtr->defTabStyle.textSide & SIDE_VERTICAL) {
+ labelWidth += width;
+ labelHeight = MAX(labelHeight, height);
+ } else {
+ labelHeight += height;
+ labelWidth = MAX(labelWidth, width);
+ }
+ }
+ labelWidth += PADDING(tabPtr->iPadX);
+ labelHeight += PADDING(tabPtr->iPadY);
+
+ tabPtr->labelWidth = ODD(labelWidth);
+ tabPtr->labelHeight = ODD(labelHeight);
+
+ newGC = NULL;
+ if (tabPtr->text != NULL) {
+ XColor *colorPtr;
+
+ gcMask = GCForeground | GCFont;
+ colorPtr = GETATTR(tabPtr, textColor);
+ gcValues.foreground = colorPtr->pixel;
+ gcValues.font = Tk_FontId(font);
+ newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+ }
+ if (tabPtr->textGC != NULL) {
+ Tk_FreeGC(nbPtr->display, tabPtr->textGC);
+ }
+ tabPtr->textGC = newGC;
+
+ gcMask = GCForeground | GCStipple | GCFillStyle;
+ gcValues.fill_style = FillStippled;
+ border = GETATTR(tabPtr, border);
+ gcValues.foreground = Tk_3DBorderColor(border)->pixel;
+ gcValues.stipple = tabPtr->stipple;
+ newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+ if (tabPtr->backGC != NULL) {
+ Tk_FreeGC(nbPtr->display, tabPtr->backGC);
+ }
+ tabPtr->backGC = newGC;
+ /*
+ * GC for tiled background.
+ */
+ if (tabPtr->tile != NULL) {
+ Blt_SetTileChangedProc(tabPtr->tile, TileChangedProc, nbPtr);
+ }
+ if (tabPtr->flags & TAB_VISIBLE) {
+ EventuallyRedraw(nbPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TearoffEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on the tearoff widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the tearoff gets deleted, internal structures get
+ * cleaned up. When it gets resized or exposed, it's redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TearoffEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+ (tabPtr->container == NULL)) {
+ return;
+ }
+ switch (eventPtr->type) {
+ case Expose:
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawTearoff(tabPtr);
+ }
+ break;
+
+ case ConfigureNotify:
+ EventuallyRedrawTearoff(tabPtr);
+ break;
+
+ case DestroyNotify:
+ if (tabPtr->flags & TAB_REDRAW) {
+ tabPtr->flags &= ~TAB_REDRAW;
+ Tcl_CancelIdleCall(DisplayTearoff, clientData);
+ }
+ Tk_DestroyWindow(tabPtr->container);
+ tabPtr->container = NULL;
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqWidth --
+ *
+ * Returns the width requested by the embedded tab window and
+ * any requested padding around it. This represents the requested
+ * width of the page.
+ *
+ * Results:
+ * Returns the requested width of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqWidth(tabPtr)
+ Tab *tabPtr;
+{
+ int width;
+
+ if (tabPtr->reqWidth > 0) {
+ width = tabPtr->reqWidth;
+ } else {
+ width = Tk_ReqWidth(tabPtr->tkwin);
+ }
+ width += PADDING(tabPtr->padX) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ if (width < 1) {
+ width = 1;
+ }
+ return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqHeight --
+ *
+ * Returns the height requested by the window and padding around
+ * the window. This represents the requested height of the page.
+ *
+ * Results:
+ * Returns the requested height of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqHeight(tabPtr)
+ Tab *tabPtr;
+{
+ int height;
+
+ if (tabPtr->reqHeight > 0) {
+ height = tabPtr->reqHeight;
+ } else {
+ height = Tk_ReqHeight(tabPtr->tkwin);
+ }
+ height += PADDING(tabPtr->padY) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ if (height < 1) {
+ height = 1;
+ }
+ return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based upon the
+ * anchor specified. The anchor indicates where the given xy position
+ * is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | | x,y ---+
+ * w center e | |
+ * | | +-----+
+ * sw --- s --- se
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
+ int dx, dy; /* Difference between outer and inner regions
+ */
+ Tk_Anchor anchor; /* Direction of the anchor */
+ int *xPtr, *yPtr;
+{
+ int x, y;
+
+ x = y = 0;
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y = dy;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x = (dx / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Centered */
+ x = (dx / 2);
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x = (dx / 2);
+ y = dy;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x = dx;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x = dx;
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x = dx;
+ y = dy;
+ break;
+ }
+ *xPtr = (*xPtr) + x;
+ *yPtr = (*yPtr) + y;
+}
+
+
+static void
+GetWindowRectangle(tabPtr, parent, tearoff, rectPtr)
+ Tab *tabPtr;
+ Tk_Window parent;
+ int tearoff;
+ XRectangle *rectPtr;
+{
+ int pad;
+ Notebook *nbPtr;
+ int cavityWidth, cavityHeight;
+ int width, height;
+ int dx, dy;
+ int x, y;
+
+ nbPtr = tabPtr->nbPtr;
+ pad = nbPtr->inset + nbPtr->inset2;
+
+ if (!tearoff) {
+ switch (nbPtr->side) {
+ case SIDE_RIGHT:
+ case SIDE_BOTTOM:
+ x = nbPtr->inset + nbPtr->inset2;
+ y = nbPtr->inset + nbPtr->inset2;
+ break;
+
+ case SIDE_LEFT:
+ x = nbPtr->pageTop;
+ y = nbPtr->inset + nbPtr->inset2;
+ break;
+
+ case SIDE_TOP:
+ x = nbPtr->inset + nbPtr->inset2;
+ y = nbPtr->pageTop;
+ break;
+ }
+
+ if (nbPtr->side & SIDE_VERTICAL) {
+ cavityWidth = Tk_Width(nbPtr->tkwin) - (nbPtr->pageTop + pad);
+ cavityHeight = Tk_Height(nbPtr->tkwin) - (2 * pad);
+ } else {
+ cavityWidth = Tk_Width(nbPtr->tkwin) - (2 * pad);
+ cavityHeight = Tk_Height(nbPtr->tkwin) - (nbPtr->pageTop + pad);
+ }
+
+ } else {
+ x = nbPtr->inset + nbPtr->inset2;
+#define TEAR_OFF_TAB_SIZE 5
+ y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + nbPtr->outerPad +
+ TEAR_OFF_TAB_SIZE;
+ cavityWidth = Tk_Width(parent) - (2 * pad);
+ cavityHeight = Tk_Height(parent) - (y + pad);
+ }
+ cavityWidth -= PADDING(tabPtr->padX);
+ cavityHeight -= PADDING(tabPtr->padY);
+ if (cavityWidth < 1) {
+ cavityWidth = 1;
+ }
+ if (cavityHeight < 1) {
+ cavityHeight = 1;
+ }
+ width = GetReqWidth(tabPtr);
+ height = GetReqHeight(tabPtr);
+
+ /*
+ * Resize the embedded window is of the following is true:
+ *
+ * 1) It's been torn off.
+ * 2) The -fill option (horizontal or vertical) is set.
+ * 3) the window is bigger than the cavity.
+ */
+ if ((tearoff) || (cavityWidth < width) || (tabPtr->fill & FILL_X)) {
+ width = cavityWidth;
+ }
+ if ((tearoff) || (cavityHeight < height) || (tabPtr->fill & FILL_Y)) {
+ height = cavityHeight;
+ }
+ dx = (cavityWidth - width);
+ dy = (cavityHeight - height);
+ if ((dx > 0) || (dy > 0)) {
+ TranslateAnchor(dx, dy, tabPtr->anchor, &x, &y);
+ }
+ /* Remember that X11 windows must be at least 1 pixel. */
+ if (width < 1) {
+ width = 1;
+ }
+ if (height < 1) {
+ height = 1;
+ }
+ rectPtr->x = (short)(x + tabPtr->padLeft);
+ rectPtr->y = (short)(y + tabPtr->padTop);
+ rectPtr->width = (short)width;
+ rectPtr->height = (short)height;
+}
+
+static void
+ArrangeWindow(tkwin, rectPtr, force)
+ Tk_Window tkwin;
+ XRectangle *rectPtr;
+ int force;
+{
+ if ((force) ||
+ (rectPtr->x != Tk_X(tkwin)) ||
+ (rectPtr->y != Tk_Y(tkwin)) ||
+ (rectPtr->width != Tk_Width(tkwin)) ||
+ (rectPtr->height != Tk_Height(tkwin))) {
+ Tk_MoveResizeWindow(tkwin, rectPtr->x, rectPtr->y,
+ rectPtr->width, rectPtr->height);
+ }
+ if (!Tk_IsMapped(tkwin)) {
+ Tk_MapWindow(tkwin);
+ }
+}
+
+
+/*ARGSUSED*/
+static void
+GetTags(table, object, list)
+ Blt_BindTable table;
+ ClientData object;
+ Blt_List list;
+{
+ Tab *tabPtr = (Tab *)object;
+ Notebook *nbPtr;
+
+ nbPtr = (Notebook *)table->clientData;
+ if (tabPtr->name == NULL) {
+ Blt_ListAppend(list, MakeTag(nbPtr, "Perforation"), 0);
+ } else {
+ Blt_ListAppend(list, MakeTag(nbPtr, tabPtr->name), 0);
+ if (tabPtr->tags != NULL) {
+ int nNames;
+ char **names;
+ register char **p;
+
+ /*
+ * This is a space/time trade-off in favor of space. The tags
+ * are stored as character strings in a hash table. That way,
+ * tabs can share the strings. It's likely that they will. The
+ * down side is that the same string is split over and over again.
+ */
+ if (Tcl_SplitList((Tcl_Interp *)NULL, tabPtr->tags, &nNames,
+ &names) == TCL_OK) {
+ for (p = names; *p != NULL; p++) {
+ Blt_ListAppend(list, MakeTag(nbPtr, *p), 0);
+ }
+ Blt_Free(names);
+ }
+ }
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NotebookEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on notebook widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+NotebookEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Notebook *nbPtr = clientData;
+
+ switch (eventPtr->type) {
+ case Expose:
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedraw(nbPtr);
+ }
+ break;
+
+ case ConfigureNotify:
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ break;
+
+ case FocusIn:
+ case FocusOut:
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ nbPtr->flags |= TNB_FOCUS;
+ } else {
+ nbPtr->flags &= ~TNB_FOCUS;
+ }
+ EventuallyRedraw(nbPtr);
+ }
+ break;
+
+ case DestroyNotify:
+ if (nbPtr->tkwin != NULL) {
+ nbPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(nbPtr->interp, nbPtr->cmdToken);
+ }
+ if (nbPtr->flags & TNB_REDRAW) {
+ Tcl_CancelIdleCall(DisplayNotebook, nbPtr);
+ }
+ Tcl_EventuallyFree(nbPtr, DestroyNotebook);
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyNotebook --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of the widget at a safe
+ * time (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyNotebook(dataPtr)
+ DestroyData dataPtr; /* Pointer to the widget record. */
+{
+ Notebook *nbPtr = (Notebook *)dataPtr;
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (nbPtr->highlightGC != NULL) {
+ Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
+ }
+ if (nbPtr->tile != NULL) {
+ Blt_FreeTile(nbPtr->tile);
+ }
+ if (nbPtr->defTabStyle.activeGC != NULL) {
+ Blt_FreePrivateGC(nbPtr->display, nbPtr->defTabStyle.activeGC);
+ }
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->linkPtr = NULL;
+ DestroyTab(nbPtr, tabPtr);
+ }
+ Blt_ChainDestroy(nbPtr->chainPtr);
+ Blt_DestroyBindingTable(nbPtr->bindTable);
+ Blt_DeleteHashTable(&(nbPtr->tabTable));
+ Blt_DeleteHashTable(&(nbPtr->tagTable));
+ Tk_FreeOptions(configSpecs, (char *)nbPtr, nbPtr->display, 0);
+ Blt_Free(nbPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateNotebook --
+ *
+ * ----------------------------------------------------------------------
+ */
+static Notebook *
+CreateNotebook(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+{
+ Notebook *nbPtr;
+
+ nbPtr = Blt_Calloc(1, sizeof(Notebook));
+ assert(nbPtr);
+
+ Tk_SetClass(tkwin, "Tabnotebook");
+ nbPtr->tkwin = tkwin;
+ nbPtr->display = Tk_Display(tkwin);
+ nbPtr->interp = interp;
+
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ nbPtr->side = SIDE_TOP;
+ nbPtr->borderWidth = nbPtr->highlightWidth = 2;
+ nbPtr->ySelectPad = SELECT_PADY;
+ nbPtr->xSelectPad = SELECT_PADX;
+ nbPtr->relief = TK_RELIEF_SUNKEN;
+ nbPtr->defTabStyle.relief = TK_RELIEF_RAISED;
+ nbPtr->defTabStyle.borderWidth = 1;
+ nbPtr->defTabStyle.constWidth = TRUE;
+ nbPtr->defTabStyle.textSide = SIDE_LEFT;
+ nbPtr->scrollUnits = 2;
+ nbPtr->corner = CORNER_OFFSET;
+ nbPtr->gap = GAP;
+ nbPtr->outerPad = OUTER_PAD;
+ nbPtr->slant = SLANT_NONE;
+ nbPtr->overlap = 0;
+ nbPtr->tearoff = TRUE;
+ nbPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, nbPtr, PickTab,
+ GetTags);
+ nbPtr->chainPtr = Blt_ChainCreate();
+ Blt_InitHashTable(&(nbPtr->tabTable), TCL_STRING_KEYS);
+ Blt_InitHashTable(&(nbPtr->imageTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(nbPtr->tagTable), BLT_STRING_KEYS);
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, nbPtr);
+#endif
+ return nbPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureNotebook --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for nbPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureNotebook(interp, nbPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors. */
+ Notebook *nbPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+ int argc;
+ char **argv;
+ int flags;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+
+ lastNotebookInstance = nbPtr;
+ if (Tk_ConfigureWidget(interp, nbPtr->tkwin, configSpecs, argc, argv,
+ (char *)nbPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ConfigModified(configSpecs, "-width", "-height", "-side", "-gap",
+ "-slant", (char *)NULL)) {
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ }
+ if ((nbPtr->reqHeight > 0) && (nbPtr->reqWidth > 0)) {
+ Tk_GeometryRequest(nbPtr->tkwin, nbPtr->reqWidth,
+ nbPtr->reqHeight);
+ }
+ /*
+ * GC for focus highlight.
+ */
+ gcMask = GCForeground;
+ gcValues.foreground = nbPtr->highlightColor->pixel;
+ newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+ if (nbPtr->highlightGC != NULL) {
+ Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
+ }
+ nbPtr->highlightGC = newGC;
+
+ /*
+ * GC for tiled background.
+ */
+ if (nbPtr->tile != NULL) {
+ Blt_SetTileChangedProc(nbPtr->tile, TileChangedProc, nbPtr);
+ }
+ /*
+ * GC for active line.
+ */
+ gcMask = GCForeground | GCLineWidth | GCLineStyle | GCCapStyle;
+ gcValues.foreground = nbPtr->defTabStyle.activeFgColor->pixel;
+ gcValues.line_width = 0;
+ gcValues.cap_style = CapProjecting;
+ gcValues.line_style = (LineIsDashed(nbPtr->defTabStyle.dashes))
+ ? LineOnOffDash : LineSolid;
+
+ newGC = Blt_GetPrivateGC(nbPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(nbPtr->defTabStyle.dashes)) {
+ nbPtr->defTabStyle.dashes.offset = 2;
+ Blt_SetDashes(nbPtr->display, newGC, &(nbPtr->defTabStyle.dashes));
+ }
+ if (nbPtr->defTabStyle.activeGC != NULL) {
+ Blt_FreePrivateGC(nbPtr->display,
+ nbPtr->defTabStyle.activeGC);
+ }
+ nbPtr->defTabStyle.activeGC = newGC;
+
+ nbPtr->defTabStyle.rotate = FMOD(nbPtr->defTabStyle.rotate, 360.0);
+ if (nbPtr->defTabStyle.rotate < 0.0) {
+ nbPtr->defTabStyle.rotate += 360.0;
+ }
+ nbPtr->inset = nbPtr->highlightWidth + nbPtr->borderWidth + nbPtr->outerPad;
+ if (Blt_ConfigModified(configSpecs, "-font", "-*foreground", "-rotate",
+ "-*background", "-side", (char *)NULL)) {
+ Blt_ChainLink *linkPtr;
+ Tab *tabPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ ConfigureTab(nbPtr, tabPtr);
+ }
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ }
+ nbPtr->inset2 = nbPtr->defTabStyle.borderWidth + nbPtr->corner;
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Notebook operations
+ *
+ * --------------------------------------------------------------
+ */
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Selects the tab to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ActivateOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (argv[2][0] == '\0') {
+ tabPtr = NULL;
+ } else if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr != NULL) && (tabPtr->state == STATE_DISABLED)) {
+ tabPtr = NULL;
+ }
+ if (tabPtr != nbPtr->activePtr) {
+ nbPtr->activePtr = tabPtr;
+ EventuallyRedraw(nbPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ if (argc == 2) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(nbPtr->tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, nbPtr->bindTable,
+ MakeTag(nbPtr, argv[2]), argc - 3, argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ lastNotebookInstance = nbPtr;
+ return Tk_ConfigureValue(interp, nbPtr->tkwin, configSpecs,
+ (char *)nbPtr, argv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for nbPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ lastNotebookInstance = nbPtr;
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
+ (char *)nbPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
+ (char *)nbPtr, argv[2], 0);
+ }
+ if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes tab from the set. Deletes either a range of
+ * tabs or a single node.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *firstPtr, *lastPtr;
+
+ lastPtr = NULL;
+ if (GetTab(nbPtr, argv[2], &firstPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((argc == 4) &&
+ (GetTab(nbPtr, argv[3], &lastPtr, INVALID_FAIL) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (lastPtr == NULL) {
+ DestroyTab(nbPtr, firstPtr);
+ } else {
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr, *nextLinkPtr;
+
+ tabPtr = NULL; /* Suppress compiler warning. */
+
+ /* Make sure that the first tab is before the last. */
+ for (linkPtr = firstPtr->linkPtr; linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr == lastPtr) {
+ break;
+ }
+ }
+ if (tabPtr != lastPtr) {
+ return TCL_OK;
+ }
+ linkPtr = firstPtr->linkPtr;
+ while (linkPtr != NULL) {
+ nextLinkPtr = Blt_ChainNextLink(linkPtr);
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ DestroyTab(nbPtr, tabPtr);
+ linkPtr = nextLinkPtr;
+ if (tabPtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FocusOp --
+ *
+ * Selects the tab to get focus.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FocusOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr != NULL) {
+ nbPtr->focusPtr = tabPtr;
+ Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr);
+ EventuallyRedraw(nbPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Converts a string representing a tab index.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each index found. If an index could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr != NULL) {
+ Tcl_SetResult(interp, Blt_Itoa(TabIndex(nbPtr, tabPtr)),
+ TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IdOp --
+ *
+ * Converts a tab index into the tab identifier.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each index found. If an index could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IdOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr == NULL) {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Add new entries into a tab set.
+ *
+ * .t insert end label option-value label option-value...
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr, *beforeLinkPtr;
+ char c;
+
+ c = argv[2][0];
+ if ((c == 'e') && (strcmp(argv[2], "end") == 0)) {
+ beforeLinkPtr = NULL;
+ } else if (isdigit(UCHAR(c))) {
+ int position;
+
+ if (Tcl_GetInt(interp, argv[2], &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position < 0) {
+ beforeLinkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+ } else if (position > Blt_ChainGetLength(nbPtr->chainPtr)) {
+ beforeLinkPtr = NULL;
+ } else {
+ beforeLinkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
+ }
+ } else {
+ Tab *beforePtr;
+
+ if (GetTab(nbPtr, argv[2], &beforePtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ beforeLinkPtr = beforePtr->linkPtr;
+ }
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ tabPtr = CreateTab(nbPtr);
+ if (tabPtr == NULL) {
+ return TCL_ERROR;
+ }
+ lastNotebookInstance = nbPtr;
+ if (Blt_ConfigureWidgetComponent(interp, nbPtr->tkwin, tabPtr->name,
+ "Tab", tabConfigSpecs, argc - 3, argv + 3, (char *)tabPtr, 0)
+ != TCL_OK) {
+ DestroyTab(nbPtr, tabPtr);
+ return TCL_ERROR;
+ }
+ if (ConfigureTab(nbPtr, tabPtr) != TCL_OK) {
+ DestroyTab(nbPtr, tabPtr);
+ return TCL_ERROR;
+ }
+ linkPtr = Blt_ChainNewLink();
+ if (beforeLinkPtr == NULL) {
+ Blt_ChainAppendLink(nbPtr->chainPtr, linkPtr);
+ } else {
+ Blt_ChainLinkBefore(nbPtr->chainPtr, linkPtr, beforeLinkPtr);
+ }
+ tabPtr->linkPtr = linkPtr;
+ Blt_ChainSetValue(linkPtr, tabPtr);
+ Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+ return TCL_OK;
+
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+static void
+PercentSubst(nbPtr, tabPtr, command, resultPtr)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ char *command;
+ Tcl_DString *resultPtr;
+{
+ register char *last, *p;
+ /*
+ * Get the full path name of the node, in case we need to
+ * substitute for it.
+ */
+ Tcl_DStringInit(resultPtr);
+ for (last = p = command; *p != '\0'; p++) {
+ if (*p == '%') {
+ char *string;
+ char buf[3];
+
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ *p = '%';
+ }
+ switch (*(p + 1)) {
+ case '%': /* Percent sign */
+ string = "%";
+ break;
+ case 'W': /* Widget name */
+ string = Tk_PathName(nbPtr->tkwin);
+ break;
+ case 'i': /* Tab Index */
+ string = Blt_Itoa(TabIndex(nbPtr, tabPtr));
+ break;
+ case 'n': /* Tab name */
+ string = tabPtr->name;
+ break;
+ default:
+ if (*(p + 1) == '\0') {
+ p--;
+ }
+ buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+ string = buf;
+ break;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ p++;
+ last = p + 1;
+ }
+ }
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeOp --
+ *
+ * This procedure is called to invoke a selection command.
+ *
+ * .h invoke index
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvokeOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+ char *command;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ Tcl_Preserve(tabPtr);
+ command = GETATTR(tabPtr, command);
+ if (command != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ PercentSubst(nbPtr, tabPtr, command, &dString);
+ result = Tcl_GlobalEval(nbPtr->interp,
+ Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_Release(tabPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ * Moves a tab to a new location.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr, *linkPtr;
+ int before;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ if ((argv[3][0] == 'b') && (strcmp(argv[3], "before") == 0)) {
+ before = 1;
+ } else if ((argv[3][0] == 'a') && (strcmp(argv[3], "after") == 0)) {
+ before = 0;
+ } else {
+ Tcl_AppendResult(interp, "bad key word \"", argv[3],
+ "\": should be \"after\" or \"before\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetTab(nbPtr, argv[4], &linkPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr == linkPtr) {
+ return TCL_OK;
+ }
+ Blt_ChainUnlinkLink(nbPtr->chainPtr, tabPtr->linkPtr);
+ if (before) {
+ Blt_ChainLinkBefore(nbPtr->chainPtr, tabPtr->linkPtr,
+ linkPtr->linkPtr);
+ } else {
+ Blt_ChainLinkAfter(nbPtr->chainPtr, tabPtr->linkPtr,
+ linkPtr->linkPtr);
+ }
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y; /* Screen coordinates of the test point. */
+ Tab *tabPtr;
+
+ if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[2], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (nbPtr->nVisible > 0) {
+ tabPtr = (Tab *)PickTab(nbPtr, x, y);
+ if (tabPtr != NULL) {
+ Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * This procedure is called to selecta tab.
+ *
+ * .h select index
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ if ((nbPtr->selectPtr != NULL) && (nbPtr->selectPtr != tabPtr) &&
+ (nbPtr->selectPtr->tkwin != NULL)) {
+ if (nbPtr->selectPtr->container == NULL) {
+ if (Tk_IsMapped(nbPtr->selectPtr->tkwin)) {
+ Tk_UnmapWindow(nbPtr->selectPtr->tkwin);
+ }
+ } else {
+ /* Redraw now unselected container. */
+ EventuallyRedrawTearoff(nbPtr->selectPtr);
+ }
+ }
+ nbPtr->selectPtr = tabPtr;
+ if ((nbPtr->nTiers > 1) && (tabPtr->tier != nbPtr->startPtr->tier)) {
+ RenumberTiers(nbPtr, tabPtr);
+ Blt_PickCurrentItem(nbPtr->bindTable);
+ }
+ nbPtr->flags |= (TNB_SCROLL);
+ if (tabPtr->container != NULL) {
+ EventuallyRedrawTearoff(tabPtr);
+ }
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+static int
+ViewOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int width;
+
+ width = VPORTWIDTH(nbPtr);
+ if (argc == 2) {
+ double fract;
+
+ /*
+ * Note: we are bounding the fractions between 0.0 and 1.0 to
+ * support the "canvas"-style of scrolling.
+ */
+
+ fract = (double)nbPtr->scrollOffset / nbPtr->worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(nbPtr->scrollOffset),
+ nbPtr->worldWidth, width, nbPtr->scrollUnits,
+ BLT_SCROLL_MODE_CANVAS) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nbPtr->flags |= TNB_SCROLL;
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+
+static void
+AdoptWindow(clientData)
+ ClientData clientData;
+{
+ Tab *tabPtr = clientData;
+ int x, y;
+ Notebook *nbPtr = tabPtr->nbPtr;
+
+ x = nbPtr->inset + nbPtr->inset2 + tabPtr->padLeft;
+#define TEAR_OFF_TAB_SIZE 5
+ y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
+ nbPtr->outerPad + TEAR_OFF_TAB_SIZE + tabPtr->padTop;
+ Blt_RelinkWindow(tabPtr->tkwin, tabPtr->container, x, y);
+ Tk_MapWindow(tabPtr->tkwin);
+}
+
+static void
+DestroyTearoff(dataPtr)
+ DestroyData dataPtr;
+{
+ Tab *tabPtr = (Tab *)dataPtr;
+
+ if (tabPtr->container != NULL) {
+ Notebook *nbPtr;
+ Tk_Window tkwin;
+ nbPtr = tabPtr->nbPtr;
+
+ tkwin = tabPtr->container;
+ if (tabPtr->flags & TAB_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+ }
+ Tk_DeleteEventHandler(tkwin, StructureNotifyMask, TearoffEventProc,
+ tabPtr);
+ if (tabPtr->tkwin != NULL) {
+ XRectangle rect;
+
+ GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
+ Blt_RelinkWindow(tabPtr->tkwin, nbPtr->tkwin, rect.x, rect.y);
+ if (tabPtr == nbPtr->selectPtr) {
+ ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+ } else {
+ Tk_UnmapWindow(tabPtr->tkwin);
+ }
+ }
+ Tk_DestroyWindow(tkwin);
+ tabPtr->container = NULL;
+ }
+}
+
+static int
+CreateTearoff(nbPtr, name, tabPtr)
+ Notebook *nbPtr;
+ char *name;
+ Tab *tabPtr;
+{
+ Tk_Window tkwin;
+ int width, height;
+
+ tkwin = Tk_CreateWindowFromPath(nbPtr->interp, nbPtr->tkwin, name,
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ tabPtr->container = tkwin;
+ if (Tk_WindowId(tkwin) == None) {
+ Tk_MakeWindowExist(tkwin);
+ }
+ Tk_SetClass(tkwin, "Tearoff");
+ Tk_CreateEventHandler(tkwin, (ExposureMask | StructureNotifyMask),
+ TearoffEventProc, tabPtr);
+ if (Tk_WindowId(tabPtr->tkwin) == None) {
+ Tk_MakeWindowExist(tabPtr->tkwin);
+ }
+ width = Tk_Width(tabPtr->tkwin);
+ if (width < 2) {
+ width = (tabPtr->reqWidth > 0)
+ ? tabPtr->reqWidth : Tk_ReqWidth(tabPtr->tkwin);
+ }
+ width += PADDING(tabPtr->padX) + 2 *
+ Tk_Changes(tabPtr->tkwin)->border_width;
+ width += 2 * (nbPtr->inset2 + nbPtr->inset);
+#define TEAR_OFF_TAB_SIZE 5
+ height = Tk_Height(tabPtr->tkwin);
+ if (height < 2) {
+ height = (tabPtr->reqHeight > 0)
+ ? tabPtr->reqHeight : Tk_ReqHeight(tabPtr->tkwin);
+ }
+ height += PADDING(tabPtr->padY) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ height += nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
+ TEAR_OFF_TAB_SIZE + nbPtr->outerPad;
+ Tk_GeometryRequest(tkwin, width, height);
+ Tk_UnmapWindow(tabPtr->tkwin);
+ /* Tk_MoveWindow(tabPtr->tkwin, 0, 0); */
+ Tcl_SetResult(nbPtr->interp, Tk_PathName(tkwin), TCL_VOLATILE);
+#ifdef WIN32
+ AdoptWindow(tabPtr);
+#else
+ Tcl_DoWhenIdle(AdoptWindow, tabPtr);
+#endif
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabCgetOp --
+ *
+ * .h tab cget index option
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabCgetOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ lastNotebookInstance = nbPtr;
+ return Tk_ConfigureValue(interp, nbPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, argv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the options for
+ * one or more tabs in the widget.
+ *
+ * .h tab configure index ?index...? ?option value?...
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TabConfigureOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int nTabs, nOpts, result;
+ char **options;
+ register int i;
+ Tab *tabPtr;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR; /* Can't find node. */
+ }
+ }
+ nTabs = i; /* Number of tab indices specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + i; /* Start of options in argv */
+
+ for (i = 0; i < nTabs; i++) {
+ GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL);
+ if (argc == 1) {
+ return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, (char *)NULL, 0);
+ } else if (argc == 2) {
+ return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, argv[2], 0);
+ }
+ Tcl_Preserve(tabPtr);
+ lastNotebookInstance = nbPtr;
+ result = Tk_ConfigureWidget(interp, nbPtr->tkwin, tabConfigSpecs,
+ nOpts, options, (char *)tabPtr, TK_CONFIG_ARGV_ONLY);
+ if (result == TCL_OK) {
+ result = ConfigureTab(nbPtr, tabPtr);
+ }
+ Tcl_Release(tabPtr);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (tabPtr->flags & TAB_VISIBLE) {
+ nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+ EventuallyRedraw(nbPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabDockallOp --
+ *
+ * .h tab dockall
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabDockallOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabNamesOp --
+ *
+ * .h tab names pattern
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabNamesOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (argc == 3) {
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, tabPtr->name);
+ }
+ } else {
+ register int i;
+
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(tabPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, tabPtr->name);
+ break;
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabTearoffOp --
+ *
+ * .h tab tearoff index ?title?
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabTearoffOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+ int result;
+ Tk_Window tkwin;
+
+ result = TCL_OK;
+
+ if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+ (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK; /* No-op */
+ }
+ if (argc == 4) {
+ Tk_Window parent;
+
+ parent = (tabPtr->container == NULL)
+ ? nbPtr->tkwin : tabPtr->container;
+ Tcl_SetResult(nbPtr->interp, Tk_PathName(parent), TCL_VOLATILE);
+ return TCL_OK;
+ }
+ Tcl_Preserve(tabPtr);
+ result = TCL_OK;
+
+ tkwin = Tk_NameToWindow(interp, argv[4], nbPtr->tkwin);
+ Tcl_ResetResult(interp);
+
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ if ((tkwin != nbPtr->tkwin) && (tabPtr->container == NULL)) {
+ result = CreateTearoff(nbPtr, argv[4], tabPtr);
+ }
+ Tcl_Release(tabPtr);
+ EventuallyRedraw(nbPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabOp --
+ *
+ * This procedure handles tab operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec tabOps[] =
+{
+ {"cget", 2, (Blt_Op)TabCgetOp, 5, 5, "nameOrIndex option",},
+ {"configure", 2, (Blt_Op)TabConfigureOp, 4, 0,
+ "nameOrIndex ?option value?...",},
+ {"dockall", 1, (Blt_Op)TabDockallOp, 3, 3, "" },
+ {"names", 1, (Blt_Op)TabNamesOp, 3, 0, "?pattern...?",},
+ {"tearoff", 1, (Blt_Op)TabTearoffOp, 4, 5, "index ?parent?",},
+};
+
+static int nTabOps = sizeof(tabOps) / sizeof(Blt_OpSpec);
+
+static int
+TabOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nTabOps, tabOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (nbPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationHighlightOp --
+ *
+ * This procedure is called to highlight the perforation.
+ *
+ * .h perforation highlight boolean
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationHighlightOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int bool;
+
+ if (Tcl_GetBoolean(interp, argv[3], &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (bool) {
+ nbPtr->flags |= PERFORATION_ACTIVE;
+ } else {
+ nbPtr->flags &= ~PERFORATION_ACTIVE;
+ }
+ EventuallyRedraw(nbPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationInvokeOp --
+ *
+ * This procedure is called to invoke a perforation command.
+ *
+ * .t perforation invoke
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationInvokeOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+
+ if (nbPtr->selectPtr != NULL) {
+ char *cmd;
+
+ cmd = GETATTR(nbPtr->selectPtr, perfCommand);
+ if (cmd != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ PercentSubst(nbPtr, nbPtr->selectPtr, cmd, &dString);
+ Tcl_Preserve(nbPtr);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_Release(nbPtr);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationOp --
+ *
+ * This procedure handles tab operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec perforationOps[] =
+{
+ {"highlight", 1, (Blt_Op)PerforationHighlightOp, 4, 4, "boolean" },
+ {"invoke", 1, (Blt_Op)PerforationInvokeOp, 3, 3, "",},
+};
+
+static int nPerforationOps = sizeof(perforationOps) / sizeof(Blt_OpSpec);
+
+static int
+PerforationOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nPerforationOps, perforationOps, BLT_OP_ARG2,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (nbPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ * Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y;
+ char c;
+ unsigned int length;
+ int oper;
+
+#define SCAN_MARK 1
+#define SCAN_DRAGTO 2
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
+ oper = SCAN_MARK;
+ } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
+ oper = SCAN_DRAGTO;
+ } else {
+ Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
+ "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, nbPtr->tkwin, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (oper == SCAN_MARK) {
+ if (nbPtr->side & SIDE_VERTICAL) {
+ nbPtr->scanAnchor = y;
+ } else {
+ nbPtr->scanAnchor = x;
+ }
+ nbPtr->scanOffset = nbPtr->scrollOffset;
+ } else {
+ int offset, delta;
+
+ if (nbPtr->side & SIDE_VERTICAL) {
+ delta = nbPtr->scanAnchor - y;
+ } else {
+ delta = nbPtr->scanAnchor - x;
+ }
+ offset = nbPtr->scanOffset + (10 * delta);
+ offset = Blt_AdjustViewport(offset, nbPtr->worldWidth,
+ VPORTWIDTH(nbPtr), nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS);
+ nbPtr->scrollOffset = offset;
+ nbPtr->flags |= TNB_SCROLL;
+ EventuallyRedraw(nbPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr != NULL) {
+ int left, right, width;
+
+ width = VPORTWIDTH(nbPtr);
+ left = nbPtr->scrollOffset + nbPtr->xSelectPad;
+ right = nbPtr->scrollOffset + width - nbPtr->xSelectPad;
+
+ /* If the tab is partially obscured, scroll so that it's
+ * entirely in view. */
+ if (tabPtr->worldX < left) {
+ nbPtr->scrollOffset = tabPtr->worldX;
+ if (TabIndex(nbPtr, tabPtr) > 0) {
+ nbPtr->scrollOffset -= TAB_SCROLL_OFFSET;
+ }
+ } else if ((tabPtr->worldX + tabPtr->worldWidth) >= right) {
+ Blt_ChainLink *linkPtr;
+
+ nbPtr->scrollOffset = tabPtr->worldX + tabPtr->worldWidth -
+ (width - 2 * nbPtr->xSelectPad);
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *nextPtr;
+
+ nextPtr = Blt_ChainGetValue(linkPtr);
+ if (nextPtr->tier == tabPtr->tier) {
+ nbPtr->scrollOffset += TAB_SCROLL_OFFSET;
+ }
+ }
+ }
+ nbPtr->flags |= TNB_SCROLL;
+ EventuallyRedraw(nbPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SizeOp(nbPtr, interp, argc, argv)
+ Notebook *nbPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tcl_SetResult(interp, Blt_Itoa(Blt_ChainGetLength(nbPtr->chainPtr)),
+ TCL_VOLATILE);
+ return TCL_OK;
+}
+
+
+static int
+CountTabs(nbPtr)
+ Notebook *nbPtr;
+{
+ int count;
+ int width, height;
+ Blt_ChainLink *linkPtr;
+ register Tab *tabPtr;
+ register int pageWidth, pageHeight;
+ int labelWidth, labelHeight;
+ int tabWidth, tabHeight;
+
+ pageWidth = pageHeight = 0;
+ count = 0;
+
+ labelWidth = labelHeight = 0;
+
+ /*
+ * Pass 1: Figure out the maximum area needed for a label and a
+ * page. Both the label and page dimensions are adjusted
+ * for orientation. In addition, reset the visibility
+ * flags and reorder the tabs.
+ */
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+
+ /* Reset visibility flag and order of tabs. */
+
+ tabPtr->flags &= ~TAB_VISIBLE;
+ count++;
+
+ if (tabPtr->tkwin != NULL) {
+ width = GetReqWidth(tabPtr);
+ if (pageWidth < width) {
+ pageWidth = width;
+ }
+ height = GetReqHeight(tabPtr);
+ if (pageHeight < height) {
+ pageHeight = height;
+ }
+ }
+ if (labelWidth < tabPtr->labelWidth) {
+ labelWidth = tabPtr->labelWidth;
+ }
+ if (labelHeight < tabPtr->labelHeight) {
+ labelHeight = tabPtr->labelHeight;
+ }
+ }
+
+ nbPtr->overlap = 0;
+
+ /*
+ * Pass 2: Set the individual sizes of each tab. This is different
+ * for constant and variable width tabs. Add the extra space
+ * needed for slanted tabs, now that we know maximum tab
+ * height.
+ */
+ if (nbPtr->defTabStyle.constWidth) {
+ int slant;
+
+ tabWidth = 2 * nbPtr->inset2;
+ tabHeight = nbPtr->inset2 /* + 4 */;
+
+ if (nbPtr->side & SIDE_VERTICAL) {
+ tabWidth += labelHeight;
+ tabHeight += labelWidth;
+ slant = labelWidth;
+ } else {
+ tabWidth += labelWidth;
+ tabHeight += labelHeight;
+ slant = labelHeight;
+ }
+ if (nbPtr->slant & SLANT_LEFT) {
+ tabWidth += slant;
+ nbPtr->overlap += tabHeight / 2;
+ }
+ if (nbPtr->slant & SLANT_RIGHT) {
+ tabWidth += slant;
+ nbPtr->overlap += tabHeight / 2;
+ }
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->worldWidth = tabWidth;
+ tabPtr->worldHeight = tabHeight;
+ }
+ } else {
+ int slant;
+
+ tabWidth = tabHeight = 0;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+
+ width = 2 * nbPtr->inset2;
+ height = nbPtr->inset2 /* + 4 */;
+ if (nbPtr->side & SIDE_VERTICAL) {
+ width += tabPtr->labelHeight;
+ height += labelWidth;
+ slant = labelWidth;
+ } else {
+ width += tabPtr->labelWidth;
+ height += labelHeight;
+ slant = labelHeight;
+ }
+ width += (nbPtr->slant & SLANT_LEFT) ? slant : nbPtr->corner;
+ width += (nbPtr->slant & SLANT_RIGHT) ? slant : nbPtr->corner;
+
+ tabPtr->worldWidth = width; /* + 2 * (nbPtr->corner + nbPtr->xSelectPad) */ ;
+ tabPtr->worldHeight = height;
+
+ if (tabWidth < width) {
+ tabWidth = width;
+ }
+ if (tabHeight < height) {
+ tabHeight = height;
+ }
+ }
+ if (nbPtr->slant & SLANT_LEFT) {
+ nbPtr->overlap += tabHeight / 2;
+ }
+ if (nbPtr->slant & SLANT_RIGHT) {
+ nbPtr->overlap += tabHeight / 2;
+ }
+ }
+
+ nbPtr->tabWidth = tabWidth;
+ nbPtr->tabHeight = tabHeight;
+
+ /*
+ * Let the user override any page dimension.
+ */
+ nbPtr->pageWidth = pageWidth;
+ nbPtr->pageHeight = pageHeight;
+ if (nbPtr->reqPageWidth > 0) {
+ nbPtr->pageWidth = nbPtr->reqPageWidth;
+ }
+ if (nbPtr->reqPageHeight > 0) {
+ nbPtr->pageHeight = nbPtr->reqPageHeight;
+ }
+ return count;
+}
+
+
+static void
+WidenTabs(nbPtr, startPtr, nTabs, adjustment)
+ Notebook *nbPtr;
+ Tab *startPtr;
+ int nTabs;
+ int adjustment;
+{
+ register Tab *tabPtr;
+ register int i;
+ int ration;
+ Blt_ChainLink *linkPtr;
+ int x;
+
+ x = startPtr->tier;
+ while (adjustment > 0) {
+ ration = adjustment / nTabs;
+ if (ration == 0) {
+ ration = 1;
+ }
+ linkPtr = startPtr->linkPtr;
+ for (i = 0; (linkPtr != NULL) && (i < nTabs) && (adjustment > 0); i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ adjustment -= ration;
+ tabPtr->worldWidth += ration;
+ assert(x == tabPtr->tier);
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ }
+ /*
+ * Go back and reset the world X-coordinates of the tabs,
+ * now that their widths have changed.
+ */
+ x = 0;
+ linkPtr = startPtr->linkPtr;
+ for (i = 0; (i < nTabs) && (linkPtr != NULL); i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+}
+
+
+static void
+AdjustTabSizes(nbPtr, nTabs)
+ Notebook *nbPtr;
+ int nTabs;
+{
+ int tabsPerTier;
+ int total, count, extra;
+ Tab *startPtr, *nextPtr;
+ Blt_ChainLink *linkPtr;
+ register Tab *tabPtr;
+ int x, maxWidth;
+
+ tabsPerTier = (nTabs + (nbPtr->nTiers - 1)) / nbPtr->nTiers;
+ x = 0;
+ maxWidth = 0;
+ if (nbPtr->defTabStyle.constWidth) {
+ register int i;
+
+ linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+ count = 1;
+ while (linkPtr != NULL) {
+ for (i = 0; i < tabsPerTier; i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = count;
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ if (x > maxWidth) {
+ maxWidth = x;
+ }
+ if (linkPtr == NULL) {
+ goto done;
+ }
+ }
+ count++;
+ x = 0;
+ }
+ }
+ done:
+ /* Add to tab widths to fill out row. */
+ if (((nTabs % tabsPerTier) != 0) && (nbPtr->defTabStyle.constWidth)) {
+ return;
+ }
+ startPtr = NULL;
+ count = total = 0;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ /*empty*/ ) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (startPtr == NULL) {
+ startPtr = tabPtr;
+ }
+ count++;
+ total += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ if (linkPtr != NULL) {
+ nextPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr->tier == nextPtr->tier) {
+ continue;
+ }
+ }
+ total += nbPtr->overlap;
+ extra = nbPtr->worldWidth - total;
+ assert(count > 0);
+ if (extra > 0) {
+ WidenTabs(nbPtr, startPtr, count, extra);
+ }
+ count = total = 0;
+ startPtr = NULL;
+ }
+}
+
+/*
+ *
+ * tabWidth = textWidth + gap + (2 * (pad + outerBW));
+ *
+ * tabHeight = textHeight + 2 * (pad + outerBW) + topMargin;
+ *
+ */
+static void
+ComputeLayout(nbPtr)
+ Notebook *nbPtr;
+{
+ int width, height;
+ Blt_ChainLink *linkPtr;
+ Tab *tabPtr;
+ int x, extra;
+ int nTiers, nTabs;
+
+ nbPtr->nTiers = 0;
+ nbPtr->pageTop = 0;
+ nbPtr->worldWidth = 1;
+ nbPtr->yPad = 0;
+
+ nTiers = 0;
+ nTabs = CountTabs(nbPtr);
+ if (nTabs == 0) {
+ return;
+ }
+ /* Reset the pointers to the selected and starting tab. */
+ if (nbPtr->selectPtr == NULL) {
+ linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+ if (linkPtr != NULL) {
+ nbPtr->selectPtr = Blt_ChainGetValue(linkPtr);
+ }
+ }
+ if (nbPtr->startPtr == NULL) {
+ nbPtr->startPtr = nbPtr->selectPtr;
+ }
+ if (nbPtr->focusPtr == NULL) {
+ nbPtr->focusPtr = nbPtr->selectPtr;
+ Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr);
+ }
+ width = Tk_Width(nbPtr->tkwin) - (2 * nbPtr->inset) -
+ nbPtr->xSelectPad - nbPtr->corner;
+ height = Tk_Height(nbPtr->tkwin) - 2 *
+ (nbPtr->corner + nbPtr->xSelectPad);
+
+ if (nbPtr->side & SIDE_VERTICAL) {
+ int temp;
+
+ temp = width, width = height, height = temp;
+ }
+ nbPtr->flags |= TNB_STATIC;
+ if (nbPtr->reqTiers > 1) {
+ int total, maxWidth;
+
+ /* Static multiple tier mode. */
+
+ /* Sum tab widths and determine the number of tiers needed. */
+ nTiers = 1;
+ total = x = 0;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((x + tabPtr->worldWidth) > width) {
+ nTiers++;
+ x = 0;
+ }
+ tabPtr->worldX = x;
+ tabPtr->tier = nTiers;
+ extra = tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ total += extra, x += extra;
+ }
+ maxWidth = width;
+
+ if (nTiers > nbPtr->reqTiers) {
+ /*
+ * The tabs do not fit into the requested number of tiers.
+ * Go into scrolling mode.
+ */
+ width = ((total + nbPtr->tabWidth) / nbPtr->reqTiers);
+ x = 0;
+ nTiers = 1;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = nTiers;
+ /*
+ * Keep adding tabs to a tier until we overfill it.
+ */
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ if (x > width) {
+ nTiers++;
+ if (x > maxWidth) {
+ maxWidth = x;
+ }
+ x = 0;
+ }
+ }
+ nbPtr->flags &= ~TNB_STATIC;
+ }
+ nbPtr->worldWidth = maxWidth;
+ nbPtr->nTiers = nTiers;
+
+ if (nTiers > 1) {
+ AdjustTabSizes(nbPtr, nTabs);
+ }
+ if (nbPtr->flags & TNB_STATIC) {
+ nbPtr->worldWidth = VPORTWIDTH(nbPtr);
+ } else {
+ /* Do you add an offset ? */
+ nbPtr->worldWidth += (nbPtr->xSelectPad + nbPtr->corner);
+ }
+ nbPtr->worldWidth += nbPtr->overlap;
+ if (nbPtr->selectPtr != NULL) {
+ RenumberTiers(nbPtr, nbPtr->selectPtr);
+ }
+ } else {
+ /*
+ * Scrollable single tier mode.
+ */
+ nTiers = 1;
+ x = 0;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = nTiers;
+ tabPtr->worldX = x;
+ tabPtr->worldY = 0;
+ x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+ }
+ nbPtr->worldWidth = x + nbPtr->corner - nbPtr->gap +
+ nbPtr->xSelectPad + nbPtr->overlap;
+ nbPtr->flags &= ~TNB_STATIC;
+ }
+ if (nTiers == 1) {
+ nbPtr->yPad = nbPtr->ySelectPad;
+ }
+ nbPtr->nTiers = nTiers;
+ nbPtr->pageTop = nbPtr->inset + nbPtr->yPad /* + 4 */ +
+ (nbPtr->nTiers * nbPtr->tabHeight) + nbPtr->inset2;
+
+ if (nbPtr->side & SIDE_VERTICAL) {
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenWidth = (short int)nbPtr->tabHeight;
+ tabPtr->screenHeight = (short int)tabPtr->worldWidth;
+ }
+ } else {
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenWidth = (short int)tabPtr->worldWidth;
+ tabPtr->screenHeight = (short int)nbPtr->tabHeight;
+ }
+ }
+}
+
+static void
+ComputeVisibleTabs(nbPtr)
+ Notebook *nbPtr;
+{
+ int nVisibleTabs;
+ register Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ nbPtr->nVisible = 0;
+ if (Blt_ChainGetLength(nbPtr->chainPtr) == 0) {
+ return;
+ }
+ nVisibleTabs = 0;
+ if (nbPtr->flags & TNB_STATIC) {
+
+ /* Static multiple tier mode. */
+
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->flags |= TAB_VISIBLE;
+ nVisibleTabs++;
+ }
+ } else {
+ int width, offset;
+ /*
+ * Scrollable (single or multiple) tier mode.
+ */
+ offset = nbPtr->scrollOffset - (nbPtr->outerPad + nbPtr->xSelectPad);
+ width = VPORTWIDTH(nbPtr) + nbPtr->scrollOffset +
+ 2 * nbPtr->outerPad;
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((tabPtr->worldX >= width) ||
+ ((tabPtr->worldX + tabPtr->worldWidth) < offset)) {
+ tabPtr->flags &= ~TAB_VISIBLE;
+ } else {
+ tabPtr->flags |= TAB_VISIBLE;
+ nVisibleTabs++;
+ }
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenX = tabPtr->screenY = -1000;
+ if (tabPtr->flags & TAB_VISIBLE) {
+ WorldToScreen(nbPtr, tabPtr->worldX, tabPtr->worldY,
+ &(tabPtr->screenX), &(tabPtr->screenY));
+ switch (nbPtr->side) {
+ case SIDE_RIGHT:
+ tabPtr->screenX -= nbPtr->tabHeight;
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr->screenY -= nbPtr->tabHeight;
+ break;
+ }
+ }
+ }
+ nbPtr->nVisible = nVisibleTabs;
+ Blt_PickCurrentItem(nbPtr->bindTable);
+}
+
+
+static void
+Draw3DFolder(nbPtr, tabPtr, drawable, side, pointArr, nPoints)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+ int side;
+ XPoint pointArr[];
+ int nPoints;
+{
+ GC gc;
+ int relief, borderWidth;
+ Tk_3DBorder border;
+
+ if (tabPtr == nbPtr->selectPtr) {
+ border = GETATTR(tabPtr, selBorder);
+ } else if (tabPtr->border != NULL) {
+ border = tabPtr->border;
+ } else {
+ border = nbPtr->defTabStyle.border;
+ }
+ relief = nbPtr->defTabStyle.relief;
+ if ((side == SIDE_RIGHT) || (side == SIDE_TOP)) {
+ borderWidth = -nbPtr->defTabStyle.borderWidth;
+ if (relief == TK_RELIEF_SUNKEN) {
+ relief = TK_RELIEF_RAISED;
+ } else if (relief == TK_RELIEF_RAISED) {
+ relief = TK_RELIEF_SUNKEN;
+ }
+ } else {
+ borderWidth = nbPtr->defTabStyle.borderWidth;
+ }
+ /* Draw the outline of the folder. */
+ gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
+ XDrawLines(nbPtr->display, drawable, gc, pointArr, nPoints,
+ CoordModeOrigin);
+ /* And the folder itself. */
+ if (tabPtr->tile != NULL) {
+#ifdef notdef
+ Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+ borderWidth, relief);
+#endif
+ Blt_TilePolygon(nbPtr->tkwin, drawable, tabPtr->tile, pointArr,
+ nPoints);
+#ifdef notdef
+ Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+ borderWidth, relief);
+#endif
+ } else {
+ Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+ borderWidth, relief);
+ }
+}
+
+/*
+ * x,y
+ * |1|2|3| 4 |3|2|1|
+ *
+ * 1. tab border width
+ * 2. corner offset
+ * 3. label pad
+ * 4. label width
+ *
+ *
+ */
+static void
+DrawLabel(nbPtr, tabPtr, drawable)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ int x, y, dx, dy;
+ int tx, ty, ix, iy;
+ int imgWidth, imgHeight;
+ int active, selected;
+ XColor *fgColor, *bgColor;
+ Tk_3DBorder border;
+ GC gc;
+
+ if (!(tabPtr->flags & TAB_VISIBLE)) {
+ return;
+ }
+ x = tabPtr->screenX;
+ y = tabPtr->screenY;
+
+ active = (nbPtr->activePtr == tabPtr);
+ selected = (nbPtr->selectPtr == tabPtr);
+
+ fgColor = GETATTR(tabPtr, textColor);
+ border = GETATTR(tabPtr, border);
+ if (selected) {
+ border = GETATTR(tabPtr, selBorder);
+ }
+ bgColor = Tk_3DBorderColor(border);
+ if (active) {
+ Tk_3DBorder activeBorder;
+
+ activeBorder = GETATTR(tabPtr, activeBorder);
+ bgColor = Tk_3DBorderColor(activeBorder);
+ }
+ dx = (tabPtr->screenWidth - tabPtr->labelWidth) / 2;
+ dy = (tabPtr->screenHeight - tabPtr->labelHeight) / 2;
+
+
+ /*
+ * The label position is computed with screen coordinates. This
+ * is because both text and image components are oriented in
+ * screen coordinate space, and not according to the orientation
+ * of the tabs themselves. That's why we have to consider the
+ * side when correcting for left/right slants.
+ */
+ switch (nbPtr->side) {
+ case SIDE_TOP:
+ case SIDE_BOTTOM:
+ if (nbPtr->slant == SLANT_LEFT) {
+ x += nbPtr->overlap;
+ } else if (nbPtr->slant == SLANT_RIGHT) {
+ x -= nbPtr->overlap;
+ }
+ break;
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ if (nbPtr->slant == SLANT_LEFT) {
+ y += nbPtr->overlap;
+ } else if (nbPtr->slant == SLANT_RIGHT) {
+ y -= nbPtr->overlap;
+ }
+ break;
+ }
+
+ /*
+ * Draw the active or normal background color over the entire
+ * label area. This includes both the tab's text and image.
+ * The rectangle should be 2 pixels wider/taller than this
+ * area. So if the label consists of just an image, we get an
+ * halo around the image when the tab is active.
+ */
+ gc = Tk_GCForColor(bgColor, drawable);
+ XFillRectangle(nbPtr->display, drawable, gc, x + dx, y + dy,
+ tabPtr->labelWidth, tabPtr->labelHeight);
+
+ if ((nbPtr->flags & TNB_FOCUS) && (nbPtr->focusPtr == tabPtr)) {
+ XDrawRectangle(nbPtr->display, drawable, nbPtr->defTabStyle.activeGC,
+ x + dx, y + dy, tabPtr->labelWidth - 1, tabPtr->labelHeight - 1);
+ }
+ tx = ty = ix = iy = 0; /* Suppress compiler warning. */
+
+ imgWidth = imgHeight = 0;
+ if (tabPtr->image != NULL) {
+ imgWidth = ImageWidth(tabPtr->image);
+ imgHeight = ImageHeight(tabPtr->image);
+ }
+ switch (nbPtr->defTabStyle.textSide) {
+ case SIDE_LEFT:
+ tx = x + dx + tabPtr->iPadX.side1;
+ ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+ ix = tx + tabPtr->textWidth + IMAGE_PAD;
+ iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+ break;
+ case SIDE_RIGHT:
+ ix = x + dx + tabPtr->iPadX.side1 + IMAGE_PAD;
+ iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+ tx = ix + imgWidth;
+ ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+ break;
+ case SIDE_BOTTOM:
+ iy = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+ ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+ ty = iy + imgHeight;
+ tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+ break;
+ case SIDE_TOP:
+ tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+ ty = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+ ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+ iy = ty + tabPtr->textHeight;
+ break;
+ }
+ if (tabPtr->image != NULL) {
+ Tk_RedrawImage(ImageData(tabPtr->image), 0, 0, imgWidth, imgHeight,
+ drawable, ix, iy);
+ }
+ if (tabPtr->text != NULL) {
+ TextStyle ts;
+ XColor *activeColor;
+
+ activeColor = fgColor;
+ if (selected) {
+ activeColor = GETATTR(tabPtr, selColor);
+ } else if (active) {
+ activeColor = GETATTR(tabPtr, activeFgColor);
+ }
+ Blt_SetDrawTextStyle(&ts, GETATTR(tabPtr, font), tabPtr->textGC,
+ fgColor, activeColor, tabPtr->shadow.color,
+ nbPtr->defTabStyle.rotate, TK_ANCHOR_NW, TK_JUSTIFY_LEFT,
+ 0, tabPtr->shadow.offset);
+ ts.state = tabPtr->state;
+ ts.border = border;
+ ts.padX.side1 = ts.padX.side2 = 2;
+ if (selected || active) {
+ ts.state |= STATE_ACTIVE;
+ }
+ Blt_DrawText(nbPtr->tkwin, drawable, tabPtr->text, &ts, tx, ty);
+ }
+}
+
+
+static void
+DrawPerforation(nbPtr, tabPtr, drawable)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ XPoint pointArr[2];
+ int x, y;
+ int segmentWidth, max;
+ Tk_3DBorder border, perfBorder;
+
+ if ((tabPtr->container != NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ WorldToScreen(nbPtr, tabPtr->worldX + 2,
+ tabPtr->worldY + tabPtr->worldHeight + 2, &x, &y);
+ border = GETATTR(tabPtr, selBorder);
+ segmentWidth = 3;
+ if (nbPtr->flags & PERFORATION_ACTIVE) {
+ perfBorder = GETATTR(tabPtr, activeBorder);
+ } else {
+ perfBorder = GETATTR(tabPtr, selBorder);
+ }
+ if (nbPtr->side & SIDE_HORIZONTAL) {
+ pointArr[0].x = x;
+ pointArr[0].y = pointArr[1].y = y;
+ max = tabPtr->screenX + tabPtr->screenWidth - 2;
+ Tk_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
+ x - 2, y - 4, tabPtr->screenWidth, 8, 0, TK_RELIEF_FLAT);
+ while (pointArr[0].x < max) {
+ pointArr[1].x = pointArr[0].x + segmentWidth;
+ if (pointArr[1].x > max) {
+ pointArr[1].x = max;
+ }
+ Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
+ TK_RELIEF_RAISED);
+ pointArr[0].x += 2 * segmentWidth;
+ }
+ } else {
+ pointArr[0].x = pointArr[1].x = x;
+ pointArr[0].y = y;
+ max = tabPtr->screenY + tabPtr->screenHeight - 2;
+ Tk_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
+ x - 4, y - 2, 8, tabPtr->screenHeight, 0, TK_RELIEF_FLAT);
+ while (pointArr[0].y < max) {
+ pointArr[1].y = pointArr[0].y + segmentWidth;
+ if (pointArr[1].y > max) {
+ pointArr[1].y = max;
+ }
+ Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
+ TK_RELIEF_RAISED);
+ pointArr[0].y += 2 * segmentWidth;
+ }
+ }
+}
+
+#define NextPoint(px, py) \
+ pointPtr->x = (px), pointPtr->y = (py), pointPtr++, nPoints++
+
+#define BottomLeft(px, py) \
+ NextPoint((px) + nbPtr->corner, (py)), \
+ NextPoint((px), (py) - nbPtr->corner)
+
+#define TopLeft(px, py) \
+ NextPoint((px), (py) + nbPtr->corner), \
+ NextPoint((px) + nbPtr->corner, (py))
+
+#define TopRight(px, py) \
+ NextPoint((px) - nbPtr->corner, (py)), \
+ NextPoint((px), (py) + nbPtr->corner)
+
+#define BottomRight(px, py) \
+ NextPoint((px), (py) - nbPtr->corner), \
+ NextPoint((px) - nbPtr->corner, (py))
+
+
+/*
+ * From the left edge:
+ *
+ * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
+ *
+ * a. highlight ring
+ * b. notebook 3D border
+ * c. outer gap
+ * d. page border
+ * e. page corner
+ * f. gap + select pad
+ * g. label pad x (worldX)
+ * h. internal pad x
+ * i. label width
+ * j. rest of page width
+ *
+ * worldX, worldY
+ * |
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ *
+ */
+static void
+DrawFolder(nbPtr, tabPtr, drawable)
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ XPoint pointArr[16];
+ XPoint *pointPtr;
+ int width, height;
+ int left, bottom, right, top, yBot, yTop;
+ int x, y;
+ register int i;
+ int nPoints;
+
+ width = VPORTWIDTH(nbPtr);
+ height = VPORTHEIGHT(nbPtr);
+
+ x = tabPtr->worldX;
+ y = tabPtr->worldY;
+
+ nPoints = 0;
+ pointPtr = pointArr;
+
+ /* Remember these are all world coordinates. */
+ /*
+ * x Left side of tab.
+ * y Top of tab.
+ * yTop Top of folder.
+ * yBot Bottom of the tab.
+ * left Left side of the folder.
+ * right Right side of the folder.
+ * top Top of folder.
+ * bottom Bottom of folder.
+ */
+ left = nbPtr->scrollOffset - nbPtr->xSelectPad;
+ right = left + width;
+ yTop = y + tabPtr->worldHeight;
+ yBot = nbPtr->pageTop - (nbPtr->inset + nbPtr->yPad);
+ top = yBot - nbPtr->inset2 /* - 4 */;
+
+ if (nbPtr->pageHeight == 0) {
+ bottom = yBot + 2 * nbPtr->corner;
+ } else {
+ bottom = height - nbPtr->yPad - 1;
+ }
+ if (tabPtr != nbPtr->selectPtr) {
+
+ /*
+ * Case 1: Unselected tab
+ *
+ * * 3+ . . +4
+ * 2+ +5
+ * . .
+ * 1+ +6
+ * 0+ . . +7
+ *
+ */
+
+ if (nbPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + nbPtr->tabHeight, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+ x += tabPtr->worldWidth;
+ if (nbPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - nbPtr->tabHeight, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+ } else if (!(tabPtr->flags & TAB_VISIBLE)) {
+
+ /*
+ * Case 2: Selected tab not visible in viewport. Draw folder only.
+ *
+ * * 3+ . . +4
+ * 2+ +5
+ * . .
+ * 1+ +6
+ * 0+------+7
+ *
+ */
+
+ TopLeft(left, top);
+ TopRight(right, top);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ } else {
+ int flags;
+ int tabWidth;
+
+ x -= nbPtr->xSelectPad;
+ y -= nbPtr->yPad;
+ tabWidth = tabPtr->worldWidth + 2 * nbPtr->xSelectPad;
+
+#define CLIP_NONE 0
+#define CLIP_LEFT (1<<0)
+#define CLIP_RIGHT (1<<1)
+ flags = 0;
+ if (x < left) {
+ flags |= CLIP_LEFT;
+ }
+ if ((x + tabWidth) > right) {
+ flags |= CLIP_RIGHT;
+ }
+ switch (flags) {
+ case CLIP_NONE:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+---------+7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+ . . . . . . . . . . . . +11
+ */
+
+ if (x < (left + nbPtr->corner)) {
+ NextPoint(left, top);
+ } else {
+ TopLeft(left, top);
+ }
+ if (nbPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yTop);
+ NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+ } else {
+ NextPoint(x, top);
+ TopLeft(x, y);
+ }
+ x += tabWidth;
+ if (nbPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+ NextPoint(x, yTop);
+ } else {
+ TopRight(x, y);
+ NextPoint(x, top);
+ }
+ if (x > (right - nbPtr->corner)) {
+ NextPoint(right, top + nbPtr->corner);
+ } else {
+ TopRight(right, top);
+ }
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+
+ case CLIP_LEFT:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 2+--------+7 . . . .+8
+ * 1+ . . . +0 +9
+ * . .
+ * . .
+ * 13+ +10
+ * 12+ . . . .+11
+ */
+
+ NextPoint(left, yBot);
+ if (nbPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+
+ x += tabWidth;
+ if (nbPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, top);
+ } else {
+ TopRight(x, y);
+ NextPoint(x, top);
+ }
+ if (x > (right - nbPtr->corner)) {
+ NextPoint(right, top + nbPtr->corner);
+ } else {
+ TopRight(right, top);
+ }
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+
+ case CLIP_RIGHT:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 9+ . . +10
+ * 8+ +11
+ * . .
+ * . .
+ * 6+ . . . .7+---------+12
+ * 5+ 0+ . . . +13
+ * . .
+ * . .
+ * 4+ +1
+ * 3+ . . . +2
+ */
+
+ NextPoint(right, yBot);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ if (x < (left + nbPtr->corner)) {
+ NextPoint(left, top);
+ } else {
+ TopLeft(left, top);
+ }
+ NextPoint(x, top);
+
+ if (nbPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yTop);
+ NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+ } else {
+ TopLeft(x, y);
+ }
+ x += tabWidth;
+ if (nbPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+ break;
+
+ case (CLIP_LEFT | CLIP_RIGHT):
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . . . . . . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+---------------------+7
+ * 2+ 0+ +9 .+8
+ * . .
+ * . .
+ * 13+ +10
+ * 12+ . . . +11
+ */
+
+ NextPoint(left, yBot);
+ if (nbPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+ x += tabPtr->worldWidth;
+ if (nbPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+ NextPoint(right, yBot);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+ }
+ }
+ NextPoint(pointArr[0].x, pointArr[0].y);
+ for (i = 0; i < nPoints; i++) {
+ WorldToScreen(nbPtr, pointArr[i].x, pointArr[i].y, &x, &y);
+ pointArr[i].x = x;
+ pointArr[i].y = y;
+ }
+ Draw3DFolder(nbPtr, tabPtr, drawable, nbPtr->side, pointArr, nPoints);
+ DrawLabel(nbPtr, tabPtr, drawable);
+ if (tabPtr->container != NULL) {
+ XRectangle rect;
+
+ /* Draw a rectangle covering the spot representing the window */
+ GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
+ XFillRectangles(nbPtr->display, drawable, tabPtr->backGC,
+ &rect, 1);
+ }
+}
+
+static void
+DrawOuterBorders(nbPtr, drawable)
+ Notebook *nbPtr;
+ Drawable drawable;
+{
+ /*
+ * Draw 3D border just inside of the focus highlight ring. We
+ * draw the border even if the relief is flat so that any tabs
+ * that hang over the edge will be clipped.
+ */
+ if (nbPtr->borderWidth > 0) {
+ Tk_Draw3DRectangle(nbPtr->tkwin, drawable, nbPtr->border,
+ nbPtr->highlightWidth, nbPtr->highlightWidth,
+ Tk_Width(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
+ Tk_Height(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
+ nbPtr->borderWidth, nbPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if (nbPtr->highlightWidth > 0) {
+ XColor *color;
+ GC gc;
+
+ color = (nbPtr->flags & TNB_FOCUS)
+ ? nbPtr->highlightColor : nbPtr->highlightBgColor;
+ gc = Tk_GCForColor(color, drawable);
+ Tk_DrawFocusHighlight(nbPtr->tkwin, gc, nbPtr->highlightWidth,
+ drawable);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayNotebook --
+ *
+ * This procedure is invoked to display the widget.
+ *
+ * Recomputes the layout of the widget if necessary. This is
+ * necessary if the world coordinate system has changed.
+ * Sets the vertical and horizontal scrollbars. This is done
+ * here since the window width and height are needed for the
+ * scrollbar calculations.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayNotebook(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Notebook *nbPtr = clientData;
+ Pixmap drawable;
+ int width, height;
+
+ nbPtr->flags &= ~TNB_REDRAW;
+ if (nbPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (nbPtr->flags & TNB_LAYOUT) {
+ ComputeLayout(nbPtr);
+ nbPtr->flags &= ~TNB_LAYOUT;
+ }
+ if ((nbPtr->reqHeight == 0) || (nbPtr->reqWidth == 0)) {
+ width = height = 0;
+ if (nbPtr->side & SIDE_VERTICAL) {
+ height = nbPtr->worldWidth;
+ } else {
+ width = nbPtr->worldWidth;
+ }
+ if (nbPtr->reqWidth > 0) {
+ width = nbPtr->reqWidth;
+ } else if (nbPtr->pageWidth > 0) {
+ width = nbPtr->pageWidth;
+ }
+ if (nbPtr->reqHeight > 0) {
+ height = nbPtr->reqHeight;
+ } else if (nbPtr->pageHeight > 0) {
+ height = nbPtr->pageHeight;
+ }
+ if (nbPtr->side & SIDE_VERTICAL) {
+ width += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
+ height += nbPtr->inset + nbPtr->inset2;
+ } else {
+ height += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
+ width += nbPtr->inset + nbPtr->inset2;
+ }
+ if ((Tk_ReqWidth(nbPtr->tkwin) != width) ||
+ (Tk_ReqHeight(nbPtr->tkwin) != height)) {
+ Tk_GeometryRequest(nbPtr->tkwin, width, height);
+ }
+ }
+ if (nbPtr->flags & TNB_SCROLL) {
+ width = VPORTWIDTH(nbPtr);
+ nbPtr->scrollOffset = Blt_AdjustViewport(nbPtr->scrollOffset,
+ nbPtr->worldWidth, width, nbPtr->scrollUnits,
+ BLT_SCROLL_MODE_CANVAS);
+ if (nbPtr->scrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(nbPtr->interp, nbPtr->scrollCmdPrefix,
+ (double)nbPtr->scrollOffset / nbPtr->worldWidth,
+ (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth);
+ }
+ ComputeVisibleTabs(nbPtr);
+ nbPtr->flags &= ~TNB_SCROLL;
+ }
+ if (!Tk_IsMapped(nbPtr->tkwin)) {
+ return;
+ }
+ height = Tk_Height(nbPtr->tkwin);
+ drawable = Tk_GetPixmap(nbPtr->display, Tk_WindowId(nbPtr->tkwin),
+ Tk_Width(nbPtr->tkwin), Tk_Height(nbPtr->tkwin),
+ Tk_Depth(nbPtr->tkwin));
+
+ /*
+ * Clear the background either by tiling a pixmap or filling with
+ * a solid color. Tiling takes precedence.
+ */
+ if (nbPtr->tile != NULL) {
+ Blt_SetTileOrigin(nbPtr->tkwin, nbPtr->tile, 0, 0);
+ Blt_TileRectangle(nbPtr->tkwin, drawable, nbPtr->tile, 0, 0,
+ Tk_Width(nbPtr->tkwin), height);
+ } else {
+ Tk_Fill3DRectangle(nbPtr->tkwin, drawable, nbPtr->border, 0, 0,
+ Tk_Width(nbPtr->tkwin), height, 0, TK_RELIEF_FLAT);
+ }
+
+ if (nbPtr->nVisible > 0) {
+ register int i;
+ register Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = nbPtr->startPtr->linkPtr;
+ for (i = 0; i < Blt_ChainGetLength(nbPtr->chainPtr); i++) {
+ linkPtr = Blt_ChainPrevLink(linkPtr);
+ if (linkPtr == NULL) {
+ linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
+ }
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((tabPtr != nbPtr->selectPtr) &&
+ (tabPtr->flags & TAB_VISIBLE)) {
+ DrawFolder(nbPtr, tabPtr, drawable);
+ }
+ }
+ DrawFolder(nbPtr, nbPtr->selectPtr, drawable);
+ if (nbPtr->tearoff) {
+ DrawPerforation(nbPtr, nbPtr->selectPtr, drawable);
+ }
+
+ if ((nbPtr->selectPtr->tkwin != NULL) &&
+ (nbPtr->selectPtr->container == NULL)) {
+ XRectangle rect;
+
+ GetWindowRectangle(nbPtr->selectPtr, nbPtr->tkwin, FALSE, &rect);
+ ArrangeWindow(nbPtr->selectPtr->tkwin, &rect, 0);
+ }
+ }
+ DrawOuterBorders(nbPtr, drawable);
+ XCopyArea(nbPtr->display, drawable, Tk_WindowId(nbPtr->tkwin),
+ nbPtr->highlightGC, 0, 0, Tk_Width(nbPtr->tkwin), height, 0, 0);
+ Tk_FreePixmap(nbPtr->display, drawable);
+}
+
+/*
+ * From the left edge:
+ *
+ * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
+ *
+ * a. highlight ring
+ * b. notebook 3D border
+ * c. outer gap
+ * d. page border
+ * e. page corner
+ * f. gap + select pad
+ * g. label pad x (worldX)
+ * h. internal pad x
+ * i. label width
+ * j. rest of page width
+ *
+ * worldX, worldY
+ * |
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ *
+ */
+static void
+DisplayTearoff(clientData)
+ ClientData clientData;
+{
+ Notebook *nbPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+ XPoint pointArr[16];
+ XPoint *pointPtr;
+ int width, height;
+ int left, bottom, right, top;
+ int x, y;
+ int nPoints;
+ Tk_Window tkwin;
+ Tk_Window parent;
+ XRectangle rect;
+
+ tabPtr = clientData;
+ if (tabPtr == NULL) {
+ return;
+ }
+ tabPtr->flags &= ~TAB_REDRAW;
+ nbPtr = tabPtr->nbPtr;
+ if (nbPtr->tkwin == NULL) {
+ return;
+ }
+ tkwin = tabPtr->container;
+ drawable = Tk_WindowId(tkwin);
+
+ /*
+ * Clear the background either by tiling a pixmap or filling with
+ * a solid color. Tiling takes precedence.
+ */
+ if (nbPtr->tile != NULL) {
+ Blt_SetTileOrigin(tkwin, nbPtr->tile, 0, 0);
+ Blt_TileRectangle(tkwin, drawable, nbPtr->tile, 0, 0, Tk_Width(tkwin),
+ Tk_Height(tkwin));
+ } else {
+ Tk_Fill3DRectangle(tkwin, drawable, nbPtr->border, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
+ }
+
+ width = Tk_Width(tkwin) - 2 * nbPtr->inset;
+ height = Tk_Height(tkwin) - 2 * nbPtr->inset;
+ x = nbPtr->inset + nbPtr->gap + nbPtr->corner;
+ y = nbPtr->inset;
+
+ left = nbPtr->inset;
+ right = nbPtr->inset + width;
+ top = nbPtr->inset + nbPtr->corner + nbPtr->xSelectPad;
+ bottom = nbPtr->inset + height;
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ */
+
+ nPoints = 0;
+ pointPtr = pointArr;
+
+ TopLeft(left, top);
+ NextPoint(x, top);
+ TopLeft(x, y);
+ x += tabPtr->worldWidth;
+ TopRight(x, y);
+ NextPoint(x, top);
+ TopRight(right, top);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ NextPoint(pointArr[0].x, pointArr[0].y);
+ Draw3DFolder(nbPtr, tabPtr, drawable, SIDE_TOP, pointArr, nPoints);
+
+ parent = (tabPtr->container == NULL) ? nbPtr->tkwin : tabPtr->container;
+ GetWindowRectangle(tabPtr, parent, TRUE, &rect);
+ ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+
+ /* Draw 3D border. */
+ if ((nbPtr->borderWidth > 0) && (nbPtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(tkwin, drawable, nbPtr->border, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), nbPtr->borderWidth,
+ nbPtr->relief);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NotebookCmd --
+ *
+ * This procedure is invoked to process the "notebook" command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec notebookOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+ {"bind", 1, (Blt_Op)BindOp, 2, 5, "index ?sequence command?",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "first ?last?",},
+ {"focus", 1, (Blt_Op)FocusOp, 3, 3, "index",},
+ {"highlight", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+ {"id", 2, (Blt_Op)IdOp, 3, 3, "index",},
+ {"index", 3, (Blt_Op)IndexOp, 3, 5, "string",},
+ {"insert", 3, (Blt_Op)InsertOp, 3, 0, "index ?option value?",},
+ {"invoke", 3, (Blt_Op)InvokeOp, 3, 3, "index",},
+ {"move", 1, (Blt_Op)MoveOp, 5, 5, "name after|before index",},
+ {"nearest", 1, (Blt_Op)NearestOp, 4, 4, "x y",},
+ {"perforation", 1, (Blt_Op)PerforationOp, 2, 0, "args",},
+ {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+ {"see", 3, (Blt_Op)SeeOp, 3, 3, "index",},
+ {"select", 3, (Blt_Op)SelectOp, 3, 3, "index",},
+ {"size", 2, (Blt_Op)SizeOp, 2, 2, "",},
+ {"tab", 1, (Blt_Op)TabOp, 2, 0, "oper args",},
+ {"view", 1, (Blt_Op)ViewOp, 2, 5,
+ "?moveto fract? ?scroll number what?",},
+};
+
+static int nNotebookOps = sizeof(notebookOps) / sizeof(Blt_OpSpec);
+
+static int
+NotebookInstCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ Notebook *nbPtr = clientData;
+ int result;
+
+ proc = Blt_GetOp(interp, nNotebookOps, notebookOps, BLT_OP_ARG1, argc,
+ argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(nbPtr);
+ result = (*proc) (nbPtr, interp, argc, argv);
+ Tcl_Release(nbPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotebookInstDeletedCmd --
+ *
+ * This procedure can be called if the window was destroyed
+ * (tkwin will be NULL) and the command was deleted
+ * automatically. In this case, we need to do nothing.
+ *
+ * Otherwise this routine was called because the command was
+ * deleted. Then we need to clean-up and destroy the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotebookInstDeletedCmd(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Notebook *nbPtr = clientData;
+
+ if (nbPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = nbPtr->tkwin;
+ nbPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * NotebookCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * See the user documentation.
+ *
+ * -----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+NotebookCmd(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. */
+{
+ Notebook *nbPtr;
+ Tk_Window tkwin;
+ unsigned int mask;
+ Tcl_CmdInfo cmdInfo;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ nbPtr = CreateNotebook(interp, tkwin);
+ if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ Tk_DestroyWindow(nbPtr->tkwin);
+ return TCL_ERROR;
+ }
+ mask = (ExposureMask | StructureNotifyMask | FocusChangeMask);
+ Tk_CreateEventHandler(tkwin, mask, NotebookEventProc, nbPtr);
+ nbPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], NotebookInstCmd,
+ nbPtr, NotebookInstDeletedCmd);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(nbPtr->tkwin, nbPtr->cmdToken);
+#endif
+
+ /*
+ * Try to invoke a procedure to initialize various bindings on
+ * tabs. Source the file containing the procedure now if the
+ * procedure isn't currently defined. We deferred this to now so
+ * that the user could set the variable "blt_library" within the
+ * script.
+ */
+ if (!Tcl_GetCommandInfo(interp, "blt::TabnotebookInit", &cmdInfo)) {
+ static char initCmd[] =
+ "source [file join $blt_library tabnotebook.tcl]";
+
+ if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) {
+ char info[200];
+
+ sprintf(info, "\n (while loading bindings for %s)", argv[0]);
+ Tcl_AddErrorInfo(interp, info);
+ Tk_DestroyWindow(nbPtr->tkwin);
+ return TCL_ERROR;
+ }
+ }
+ if (Tcl_VarEval(interp, "blt::TabnotebookInit ", argv[1], (char *)NULL)
+ != TCL_OK) {
+ Tk_DestroyWindow(nbPtr->tkwin);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(nbPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+int
+Blt_TabnotebookInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "tabnotebook", NotebookCmd,
+ };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_TABNOTEBOOK */
diff --git a/blt/src/bltTabset.c b/blt/src/bltTabset.c
new file mode 100644
index 00000000000..8daec77bc21
--- /dev/null
+++ b/blt/src/bltTabset.c
@@ -0,0 +1,5882 @@
+/*
+ * bltTabset.c --
+ *
+ * This module implements a tabset widget for the BLT toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Tabset widget created by George A. Howlett (gah@bell-labs.com)
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TABSET
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltTile.h"
+
+#if (TK_MAJOR_VERSION == 4)
+#define TK_REPARENTED 0x2000
+#endif
+
+#define INVALID_FAIL 0
+#define INVALID_OK 1
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#define GAP 3
+#define SELECT_PADX 4
+#define SELECT_PADY 2
+#define OUTER_PAD 2
+#define LABEL_PAD 1
+#define LABEL_PADX 2
+#define LABEL_PADY 2
+#define IMAGE_PAD 1
+#define CORNER_OFFSET 3
+
+#define TAB_SCROLL_OFFSET 10
+
+#define SLANT_NONE 0
+#define SLANT_LEFT 1
+#define SLANT_RIGHT 2
+#define SLANT_BOTH (SLANT_LEFT | SLANT_RIGHT)
+
+#define END (-1)
+#define ODD(x) ((x) | 0x01)
+
+#define TABWIDTH(s, t) \
+ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+#define TABHEIGHT(s, t) \
+ ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+
+#define VPORTWIDTH(s) \
+ (((s)->side & SIDE_HORIZONTAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+ (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define VPORTHEIGHT(s) \
+ (((s)->side & SIDE_VERTICAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+ (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define GETATTR(t,attr) \
+ (((t)->attr != NULL) ? (t)->attr : (t)->setPtr->defTabStyle.attr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Internal widget flags:
+ *
+ * TABSET_LAYOUT The layout of the widget needs to be
+ * recomputed.
+ *
+ * TABSET_REDRAW A redraw request is pending for the widget.
+ *
+ * TABSET_SCROLL A scroll request is pending.
+ *
+ * TABSET_FOCUS The widget is receiving keyboard events.
+ * Draw the focus highlight border around the
+ * widget.
+ *
+ * TABSET_MULTIPLE_TIER Tabset is using multiple tiers.
+ *
+ * TABSET_STATIC Tabset does not scroll.
+ *
+ * ---------------------------------------------------------------------------
+ */
+#define TABSET_LAYOUT (1<<0)
+#define TABSET_REDRAW (1<<1)
+#define TABSET_SCROLL (1<<2)
+#define TABSET_FOCUS (1<<4)
+
+#define TABSET_STATIC (1<<8)
+#define TABSET_MULTIPLE_TIER (1<<9)
+
+#define PERFORATION_ACTIVE (1<<10)
+
+#define SIDE_TOP (1<<0)
+#define SIDE_RIGHT (1<<1)
+#define SIDE_LEFT (1<<2)
+#define SIDE_BOTTOM (1<<3)
+
+#define SIDE_VERTICAL (SIDE_LEFT | SIDE_RIGHT)
+#define SIDE_HORIZONTAL (SIDE_TOP | SIDE_BOTTOM)
+
+#define DEF_TABSET_ACTIVE_BG_COLOR RGB_GREY90
+#define DEF_TABSET_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TABSET_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_TABSET_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TABSET_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TABSET_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TABSET_BORDER_WIDTH "1"
+#define DEF_TABSET_COMMAND (char *)NULL
+#define DEF_TABSET_CURSOR (char *)NULL
+#define DEF_TABSET_DASHES "1"
+#define DEF_TABSET_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TABSET_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_TABSET_FONT STD_FONT
+#define DEF_TABSET_GAP "3"
+#define DEF_TABSET_HEIGHT "0"
+#define DEF_TABSET_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TABSET_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TABSET_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_TABSET_HIGHLIGHT_WIDTH "2"
+#define DEF_TABSET_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TABSET_NORMAL_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TABSET_OUTER_PAD "3"
+#define DEF_TABSET_RELIEF "sunken"
+#define DEF_TABSET_ROTATE "0.0"
+#define DEF_TABSET_SCROLL_INCREMENT "0"
+#define DEF_TABSET_SELECT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TABSET_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_TABSET_SELECT_BORDER_WIDTH "1"
+#define DEF_TABSET_SELECT_CMD (char *)NULL
+#define DEF_TABSET_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_TABSET_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_TABSET_SELECT_MODE "multiple"
+#define DEF_TABSET_SELECT_RELIEF "raised"
+#define DEF_TABSET_SELECT_PAD "5"
+#define DEF_TABSET_SHADOW_COLOR RGB_BLACK
+#define DEF_TABSET_SIDE "top"
+#define DEF_TABSET_SLANT "none"
+#define DEF_TABSET_TAB_BG_COLOR RGB_GREY82
+#define DEF_TABSET_TAB_BG_MONO STD_MONO_SELECT_BG
+#define DEF_TABSET_TAB_RELIEF "raised"
+#define DEF_TABSET_TAKE_FOCUS "1"
+#define DEF_TABSET_TEXT_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TABSET_TEXT_MONO STD_MONO_NORMAL_FG
+#define DEF_TABSET_TEXT_SIDE "left"
+#define DEF_TABSET_TIERS "1"
+#define DEF_TABSET_TILE (char *)NULL
+#define DEF_TABSET_WIDTH "0"
+#define DEF_TABSET_SAME_WIDTH "yes"
+#define DEF_TABSET_TEAROFF "yes"
+#define DEF_TABSET_PAGE_WIDTH "0"
+#define DEF_TABSET_PAGE_HEIGHT "0"
+
+#define DEF_TAB_ACTIVE_BG (char *)NULL
+#define DEF_TAB_ACTIVE_FG (char *)NULL
+#define DEF_TAB_ANCHOR "center"
+#define DEF_TAB_BG (char *)NULL
+#define DEF_TAB_COMMAND (char *)NULL
+#define DEF_TAB_DATA (char *)NULL
+#define DEF_TAB_FG (char *)NULL
+#define DEF_TAB_FILL "none"
+#define DEF_TAB_FONT (char *)NULL
+#define DEF_TAB_HEIGHT "0"
+#define DEF_TAB_IMAGE (char *)NULL
+#define DEF_TAB_IPAD "0"
+#define DEF_TAB_PAD "3"
+#define DEF_TAB_PERF_COMMAND (char *)NULL
+#define DEF_TAB_SELECT_BG (char *)NULL
+#define DEF_TAB_SELECT_BORDER_WIDTH "1"
+#define DEF_TAB_SELECT_CMD (char *)NULL
+#define DEF_TAB_SELECT_FG (char *)NULL
+#define DEF_TAB_SHADOW (char *)NULL
+#define DEF_TAB_STATE "normal"
+#define DEF_TAB_STIPPLE "BLT"
+#define DEF_TAB_BIND_TAGS "all"
+#define DEF_TAB_TEXT (char *)NULL
+#define DEF_TAB_VISUAL (char *)NULL
+#define DEF_TAB_WIDTH "0"
+#define DEF_TAB_WINDOW (char *)NULL
+
+typedef struct TabsetStruct Tabset;
+
+static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window));
+static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window));
+
+static Tk_GeomMgr tabMgrInfo =
+{
+ "tabset", /* Name of geometry manager used by winfo */
+ EmbeddedWidgetGeometryProc, /* Procedure to for new geometry requests */
+ EmbeddedWidgetCustodyProc, /* Procedure when window is taken away */
+};
+
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPositiveCountOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltStateOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltUidOption;
+
+static int StringToImage _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *ImageToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToWindow _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *WindowToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSide _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *SideToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSlant _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *SlantToString _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtrPtr));
+
+/*
+ * Contains a pointer to the widget that's currently being configured.
+ * This is used in the custom configuration parse routine for images.
+ */
+static Tabset *tabSet;
+
+static Tk_CustomOption imageOption =
+{
+ StringToImage, ImageToString, (ClientData)&tabSet,
+};
+
+static Tk_CustomOption sideOption =
+{
+ StringToSide, SideToString, (ClientData)0,
+};
+
+static Tk_CustomOption windowOption =
+{
+ StringToWindow, WindowToString, (ClientData)0,
+};
+
+static Tk_CustomOption slantOption =
+{
+ StringToSlant, SlantToString, (ClientData)0,
+};
+
+/*
+ * TabImage --
+ *
+ * When multiple instances of an image are displayed in the
+ * same widget, this can be inefficient in terms of both memory
+ * and time. We only need one instance of each image, regardless
+ * of number of times we use it. And searching/deleting instances
+ * can be very slow as the list gets large.
+ *
+ * The workaround, employed below, is to maintain a hash table of
+ * images that maintains a reference count for each image.
+ */
+
+typedef struct TabImageStruct {
+ int refCount; /* Reference counter for this image. */
+ Tk_Image tkImage; /* The Tk image being cached. */
+ int width, height; /* Dimensions of the cached image. */
+ Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */
+
+} *TabImage;
+
+#define ImageHeight(image) ((image)->height)
+#define ImageWidth(image) ((image)->width)
+#define ImageData(image) ((image)->tkImage)
+
+#define TAB_VISIBLE (1<<0)
+#define TAB_REDRAW (1<<2)
+
+typedef struct {
+ char *name; /* Identifier for tab entry */
+ int state; /* State of the tab: Disabled, active, or
+ * normal. */
+ unsigned int flags;
+
+ int tier; /* Index of tier [1..numTiers] containing
+ * this tab. */
+
+ int worldX, worldY; /* Position of the tab in world coordinates. */
+ int worldWidth, worldHeight;/* Dimensions of the tab, corrected for
+ * orientation (-side). It includes the
+ * border, padding, label, etc. */
+ int screenX, screenY;
+ short int screenWidth, screenHeight; /* */
+
+ Tabset *setPtr; /* Tabset that includes this
+ * tab. Needed for callbacks can pass
+ * only a tab pointer. */
+ Tk_Uid tags;
+
+ /*
+ * Tab label:
+ */
+ Tk_Uid text; /* String displayed as the tab's label. */
+ TabImage image; /* Image displayed as the label. */
+
+ short int textWidth, textHeight;
+ short int labelWidth, labelHeight;
+ Blt_Pad iPadX, iPadY; /* Internal padding around the text */
+
+ Tk_Font font;
+
+ /*
+ * Normal:
+ */
+ XColor *textColor; /* Text color */
+ Tk_3DBorder border; /* Background color and border for tab.*/
+
+ /*
+ * Selected: Tab is currently selected.
+ */
+ XColor *selColor; /* Selected text color */
+ Tk_3DBorder selBorder; /* 3D border of selected folder. */
+
+ /*
+ * Active: Mouse passes over the tab.
+ */
+ Tk_3DBorder activeBorder; /* Active background color. */
+ XColor *activeFgColor; /* Active text color */
+
+ Shadow shadow;
+ Pixmap stipple; /* Stipple for outline of embedded window
+ * when torn off. */
+ /*
+ * Embedded widget information:
+ */
+ Tk_Window tkwin; /* Widget to be mapped when the tab is
+ * selected. If NULL, don't make
+ * space for the page. */
+
+ int reqWidth, reqHeight; /* If non-zero, overrides the
+ * requested dimensions of the
+ * embedded widget. */
+
+ Tk_Window container; /* The window containing the embedded
+ * widget. Does not necessarily have
+ * to be the parent. */
+
+ Tk_Anchor anchor; /* Anchor: indicates how the embedded
+ * widget is positioned within the
+ * extra space on the page. */
+
+ Blt_Pad padX, padY; /* Padding around embedded widget */
+
+ int fill; /* Indicates how the window should
+ * fill the page. */
+
+ /*
+ * Auxillary information:
+ */
+ Tk_Uid command; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ Tk_Uid data; /* This value isn't used in C code.
+ * It may be used by clients in Tcl bindings
+ * to associate extra data (other than the
+ * label or name) with the tab. */
+
+ Blt_ChainLink *linkPtr; /* Pointer to where the tab resides in the
+ * list of tabs. */
+ Tk_Uid perfCommand; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ GC textGC;
+ GC backGC;
+
+ Blt_Tile tile;
+
+} Tab;
+
+static Tk_ConfigSpec tabConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TAB_ACTIVE_BG,
+ Tk_Offset(Tab, activeBorder), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_TAB_ACTIVE_FG,
+ Tk_Offset(Tab, activeFgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_TAB_ANCHOR, Tk_Offset(Tab, anchor), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TAB_BG, Tk_Offset(Tab, border), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_TAB_BIND_TAGS, Tk_Offset(Tab, tags),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-command", "command", "Command",
+ DEF_TAB_COMMAND, Tk_Offset(Tab, command),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "data",
+ DEF_TAB_DATA, Tk_Offset(Tab, data),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_TAB_FILL, Tk_Offset(Tab, fill),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TAB_FG, Tk_Offset(Tab, textColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TAB_FONT, Tk_Offset(Tab, font), 0},
+ {TK_CONFIG_CUSTOM, "-image", "image", "image",
+ DEF_TAB_IMAGE, Tk_Offset(Tab, image),
+ TK_CONFIG_NULL_OK, &imageOption},
+ {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "PadX",
+ DEF_TAB_IPAD, Tk_Offset(Tab, iPadX),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "PadY",
+ DEF_TAB_IPAD, Tk_Offset(Tab, iPadY),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+ DEF_TAB_PAD, Tk_Offset(Tab, padX), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+ DEF_TAB_PAD, Tk_Offset(Tab, padY), 0, &bltPadOption},
+ {TK_CONFIG_CUSTOM, "-perforationcommand", "perforationcommand",
+ "PerforationCommand",
+ DEF_TAB_PERF_COMMAND, Tk_Offset(Tab, perfCommand),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_TAB_SELECT_BG, Tk_Offset(Tab, selBorder), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_TAB_SELECT_FG, Tk_Offset(Tab, selColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+ DEF_TAB_SHADOW, Tk_Offset(Tab, shadow),
+ TK_CONFIG_NULL_OK, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-state", "state", "State",
+ DEF_TAB_STATE, Tk_Offset(Tab, state),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+ {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_TAB_STIPPLE, Tk_Offset(Tab, stipple), 0},
+ {TK_CONFIG_CUSTOM, "-text", "Text", "Text",
+ DEF_TAB_TEXT, Tk_Offset(Tab, text),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_CUSTOM, "-window", "window", "Window",
+ DEF_TAB_WINDOW, Tk_Offset(Tab, tkwin),
+ TK_CONFIG_NULL_OK, &windowOption},
+ {TK_CONFIG_CUSTOM, "-windowheight", "windowHeight", "WindowHeight",
+ DEF_TAB_HEIGHT, Tk_Offset(Tab, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-windowwidth", "windowWidth", "WindowWidth",
+ DEF_TAB_WIDTH, Tk_Offset(Tab, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * TabAttributes --
+ */
+typedef struct {
+ char *name;
+} Perforation;
+
+typedef struct {
+ Tk_Window tkwin; /* Default window to map pages. */
+
+ int reqWidth, reqHeight; /* Requested tab size. */
+ int constWidth;
+ int borderWidth; /* Width of 3D border around the tab's
+ * label. */
+ int pad; /* Extra padding of a tab entry */
+
+ XColor *activeFgColor; /* Active foreground. */
+ Tk_3DBorder activeBorder; /* Active background. */
+ XColor *selColor; /* Selected foreground. */
+ Tk_Font font;
+ XColor *textColor;
+
+ Tk_3DBorder border; /* Normal background. */
+ Tk_3DBorder selBorder; /* Selected background. */
+
+ Blt_Dashes dashes;
+ GC normalGC, activeGC;
+ int relief;
+ char *command;
+ char *perfCommand; /* Command (malloc-ed) invoked when the tab
+ * is selected */
+ double rotate;
+ int textSide;
+
+} TabAttributes;
+
+struct TabsetStruct {
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+
+ Tcl_Command cmdToken; /* Token for widget's command. */
+
+ unsigned int flags; /* For bitfield definitions, see below */
+
+ int inset; /* Total width of all borders, including
+ * traversal highlight and 3-D border.
+ * Indicates how much interior stuff must
+ * be offset from outside edges to leave
+ * room for borders. */
+
+ int inset2; /* Total width of 3-D folder border + corner,
+ * Indicates how much interior stuff must
+ * be offset from outside edges of folder.*/
+
+ int yPad; /* Extra offset for selected tab. Only
+ * for single tiers. */
+
+ int pageTop; /* Offset from top of tabset to the
+ * start of the page. */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ Tk_3DBorder border; /* 3D border surrounding the window. */
+ int borderWidth; /* Width of 3D border. */
+ int relief; /* 3D border relief. */
+
+ XColor *shadowColor; /* Shadow color around folder. */
+ /*
+ * Focus highlight ring
+ */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ GC highlightGC; /* GC for focus highlight. */
+
+ char *takeFocus; /* Says whether to select this widget during
+ * tab traveral operations. This value isn't
+ * used in C code, but for the widget's Tcl
+ * bindings. */
+
+
+ int side; /* How tabset is oriented: either SIDE_LEFT,
+ * SIDE_RIGHT, SIDE_TOP, or SIDE_BOTTOM. */
+
+ int slant;
+ int overlap;
+ int gap;
+ int tabWidth, tabHeight;
+ int xSelectPad, ySelectPad; /* Padding around label of the selected tab. */
+ int outerPad; /* Padding around the exterior of the tabset
+ * and folder. */
+
+ TabAttributes defTabStyle; /* Global attribute information specific to
+ * tabs. */
+ Blt_Tile tile;
+
+ int reqWidth, reqHeight; /* Requested dimensions of the tabset
+ * window. */
+ int pageWidth, pageHeight; /* Dimensions of a page in the folder. */
+ int reqPageWidth, reqPageHeight; /* Requested dimensions of a page. */
+
+ int lastX, lastY;
+ /*
+ * Scrolling information:
+ */
+ int worldWidth;
+ int scrollOffset; /* Offset of viewport in world coordinates. */
+ char *scrollCmdPrefix; /* Command strings to control scrollbar.*/
+
+ int scrollUnits; /* Smallest unit of scrolling for tabs. */
+
+ /*
+ * Scanning information:
+ */
+ int scanAnchor; /* Scan anchor in screen coordinates. */
+ int scanOffset; /* Offset of the start of the scan in world
+ * coordinates.*/
+
+
+ int corner; /* Number of pixels to offset next point
+ * when drawing corners of the folder. */
+ int reqTiers; /* Requested number of tiers. Zero means to
+ * dynamically scroll if there are too many
+ * tabs to be display on a single tier. */
+ int nTiers; /* Actual number of tiers. */
+
+ Blt_HashTable imageTable;
+
+
+ Tab *selectPtr; /* The currently selected tab.
+ * (i.e. its page is displayed). */
+
+ Tab *activePtr; /* Tab last located under the pointer.
+ * It is displayed with its active
+ * foreground/background colors. */
+
+ Tab *focusPtr; /* Tab currently receiving focus. */
+
+ Tab *startPtr; /* The first tab on the first tier. */
+
+ Blt_Chain *chainPtr; /* List of tab entries. Used to
+ * arrange placement of tabs. */
+
+ Blt_HashTable tabTable; /* Hash table of tab entries. Used for
+ * lookups of tabs by name. */
+
+ int nVisible; /* Number of tabs that are currently visible
+ * in the view port. */
+
+ Blt_BindTable bindTable; /* Tab binding information */
+ Blt_HashTable tagTable; /* Table of bind tags. */
+
+ Perforation perforation;
+ int tearoff;
+};
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "activeBackground",
+ DEF_TABSET_ACTIVE_BG_COLOR, Tk_Offset(Tabset, defTabStyle.activeBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "activeBackground",
+ DEF_TABSET_ACTIVE_BG_MONO, Tk_Offset(Tabset, defTabStyle.activeBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "activeForeground", DEF_TABSET_ACTIVE_FG_COLOR,
+ Tk_Offset(Tabset, defTabStyle.activeFgColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "activeForeground", DEF_TABSET_ACTIVE_FG_MONO,
+ Tk_Offset(Tabset, defTabStyle.activeFgColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TABSET_BG_MONO, Tk_Offset(Tabset, border), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TABSET_BG_COLOR, Tk_Offset(Tabset, border), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_TABSET_CURSOR, Tk_Offset(Tabset, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TABSET_BORDER_WIDTH, Tk_Offset(Tabset, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_TABSET_DASHES, Tk_Offset(Tabset, defTabStyle.dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_SYNONYM, "-fg", "tabForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TABSET_FONT, Tk_Offset(Tabset, defTabStyle.font), 0},
+ {TK_CONFIG_SYNONYM, "-foreground", "tabForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_PIXELS, "-gap", "gap", "Gap",
+ DEF_TABSET_GAP, Tk_Offset(Tabset, gap),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+ DEF_TABSET_HEIGHT, Tk_Offset(Tabset, reqHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_TABSET_HIGHLIGHT_BG_COLOR, Tk_Offset(Tabset, highlightBgColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_TABSET_HIGHLIGHT_BG_MONO, Tk_Offset(Tabset, highlightBgColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_TABSET_HIGHLIGHT_COLOR, Tk_Offset(Tabset, highlightColor), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_TABSET_HIGHLIGHT_WIDTH, Tk_Offset(Tabset, highlightWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-outerpad", "outerPad", "OuterPad",
+ DEF_TABSET_OUTER_PAD, Tk_Offset(Tabset, outerPad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pageheight", "pageHeight", "PageHeight",
+ DEF_TABSET_PAGE_HEIGHT, Tk_Offset(Tabset, reqPageHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pagewidth", "pageWidth", "PageWidth",
+ DEF_TABSET_PAGE_WIDTH, Tk_Offset(Tabset, reqPageWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_STRING, "-perforationcommand", "perforationcommand",
+ "PerforationCommand",
+ DEF_TAB_PERF_COMMAND, Tk_Offset(Tabset, defTabStyle.perfCommand),
+ TK_CONFIG_NULL_OK, &bltUidOption},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TABSET_RELIEF, Tk_Offset(Tabset, relief), 0},
+ {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+ DEF_TABSET_ROTATE, Tk_Offset(Tabset, defTabStyle.rotate),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-samewidth", "sameWidth", "SameWidth",
+ DEF_TABSET_SAME_WIDTH, Tk_Offset(Tabset, defTabStyle.constWidth),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
+ (char *)NULL, Tk_Offset(Tabset, scrollCmdPrefix), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement",
+ "ScrollIncrement",
+ DEF_TABSET_SCROLL_INCREMENT, Tk_Offset(Tabset, scrollUnits),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TABSET_SELECT_BG_MONO, Tk_Offset(Tabset, defTabStyle.selBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TABSET_SELECT_BG_COLOR, Tk_Offset(Tabset, defTabStyle.selBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ DEF_TABSET_SELECT_CMD, Tk_Offset(Tabset, defTabStyle.command),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TABSET_SELECT_FG_MONO, Tk_Offset(Tabset, defTabStyle.selColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TABSET_SELECT_FG_COLOR, Tk_Offset(Tabset, defTabStyle.selColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-selectpad", "selectPad", "SelectPad",
+ DEF_TABSET_SELECT_PAD, Tk_Offset(Tabset, xSelectPad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-shadowcolor", "shadowColor", "ShadowColor",
+ DEF_TABSET_SHADOW_COLOR, Tk_Offset(Tabset, shadowColor), 0},
+ {TK_CONFIG_CUSTOM, "-side", "side", "side",
+ DEF_TABSET_SIDE, Tk_Offset(Tabset, side),
+ TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+ {TK_CONFIG_CUSTOM, "-slant", "slant", "Slant",
+ DEF_TABSET_SLANT, Tk_Offset(Tabset, slant),
+ TK_CONFIG_DONT_SET_DEFAULT, &slantOption},
+ {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+ DEF_TABSET_TAB_BG_MONO, Tk_Offset(Tabset, defTabStyle.border),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+ DEF_TABSET_TAB_BG_COLOR, Tk_Offset(Tabset, defTabStyle.border),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_CUSTOM, "-tabborderwidth", "tabBorderWidth", "BorderWidth",
+ DEF_TABSET_BORDER_WIDTH, Tk_Offset(Tabset, defTabStyle.borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+ DEF_TABSET_TEXT_COLOR, Tk_Offset(Tabset, defTabStyle.textColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+ DEF_TABSET_TEXT_MONO, Tk_Offset(Tabset, defTabStyle.textColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-tabrelief", "tabRelief", "TabRelief",
+ DEF_TABSET_TAB_RELIEF, Tk_Offset(Tabset, defTabStyle.relief), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_TABSET_TAKE_FOCUS, Tk_Offset(Tabset, takeFocus), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-tearoff", "tearoff", "Tearoff",
+ DEF_TABSET_TEAROFF, Tk_Offset(Tabset, tearoff),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-textside", "textSide", "TextSide",
+ DEF_TABSET_TEXT_SIDE, Tk_Offset(Tabset, defTabStyle.textSide),
+ TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+ {TK_CONFIG_CUSTOM, "-tiers", "tiers", "Tiers",
+ DEF_TABSET_TIERS, Tk_Offset(Tabset, reqTiers),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Tabset, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+ DEF_TABSET_WIDTH, Tk_Offset(Tabset, reqWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static void DestroyTabset _ANSI_ARGS_((DestroyData dataPtr));
+static void DestroyTearoff _ANSI_ARGS_((DestroyData dataPtr));
+static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void TearoffEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void TabsetEventProc _ANSI_ARGS_((ClientData clientdata,
+ XEvent *eventPtr));
+static void DrawLabel _ANSI_ARGS_((Tabset *setPtr, Tab *tabPtr,
+ Drawable drawable));
+static void DrawFolder _ANSI_ARGS_((Tabset *setPtr, Tab *tabPtr,
+ Drawable drawable));
+static void DisplayTabset _ANSI_ARGS_((ClientData clientData));
+static void DisplayTearoff _ANSI_ARGS_((ClientData clientData));
+static void TabsetInstDeletedCmd _ANSI_ARGS_((ClientData clientdata));
+static int TabsetInstCmd _ANSI_ARGS_((ClientData clientdata,
+ Tcl_Interp *interp, int argc, char **argv));
+static void GetWindowRectangle _ANSI_ARGS_((Tab *tabPtr, Tk_Window parent,
+ int tearOff, XRectangle *rectPtr));
+static void ArrangeWindow _ANSI_ARGS_((Tk_Window tkwin, XRectangle *rectPtr,
+ int force));
+static void EventuallyRedraw _ANSI_ARGS_((Tabset *setPtr));
+static void EventuallyRedrawTearoff _ANSI_ARGS_((Tab *tabPtr));
+static void ComputeLayout _ANSI_ARGS_((Tabset *setPtr));
+static void DrawOuterBorders _ANSI_ARGS_((Tabset *setPtr, Drawable drawable));
+
+#ifdef __STDC__
+static Tk_ImageChangedProc ImageChangedProc;
+static Blt_TileChangedProc TileChangedProc;
+static Blt_BindTagProc GetTags;
+static Blt_BindPickProc PickTab;
+static Tcl_IdleProc AdoptWindow;
+static Tcl_CmdProc TabsetCmd;
+#endif /* __STDC__ */
+
+static ClientData
+MakeTag(setPtr, tagName)
+ Tabset *setPtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&(setPtr->tagTable), tagName, &isNew);
+ assert(hPtr);
+ return Blt_GetHashKey(&(setPtr->tagTable), hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WorldToScreen --
+ *
+ * Converts world coordinates to screen coordinates. Note that
+ * the world view is always tabs up.
+ *
+ * Results:
+ * The screen coordinates are returned via *xScreenPtr and *yScreenPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+WorldToScreen(setPtr, x, y, xScreenPtr, yScreenPtr)
+ Tabset *setPtr;
+ int x, y;
+ int *xScreenPtr, *yScreenPtr;
+{
+ int sx, sy;
+
+ sx = sy = 0; /* Suppress compiler warning. */
+
+ /* Translate world X-Y to screen coordinates */
+ /*
+ * Note that the world X-coordinate is translated by the selected label's
+ * X padding. This is done only to keep the scroll range is between
+ * 0.0 and 1.0, rather adding/subtracting the pad in various locations.
+ * It may be changed back in the future.
+ */
+ x += (setPtr->inset + setPtr->xSelectPad - setPtr->scrollOffset);
+ y += setPtr->inset + setPtr->yPad;
+
+ switch (setPtr->side) {
+ case SIDE_TOP:
+ sx = x, sy = y; /* Do nothing */
+ break;
+ case SIDE_RIGHT:
+ sx = Tk_Width(setPtr->tkwin) - y;
+ sy = x;
+ break;
+ case SIDE_LEFT:
+ sx = y, sy = x; /* Flip coordinates */
+ break;
+ case SIDE_BOTTOM:
+ sx = x;
+ sy = Tk_Height(setPtr->tkwin) - y;
+ break;
+ }
+ *xScreenPtr = sx;
+ *yScreenPtr = sy;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(setPtr)
+ Tabset *setPtr;
+{
+ if ((setPtr->tkwin != NULL) && !(setPtr->flags & TABSET_REDRAW)) {
+ setPtr->flags |= TABSET_REDRAW;
+ Tcl_DoWhenIdle(DisplayTabset, setPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawTearoff --
+ *
+ * Queues a request to redraw the tearoff at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawTearoff(tabPtr)
+ Tab *tabPtr;
+{
+ if ((tabPtr->tkwin != NULL) && !(tabPtr->flags & TAB_REDRAW)) {
+ tabPtr->flags |= TAB_REDRAW;
+ Tcl_DoWhenIdle(DisplayTearoff, tabPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ * This routine is called whenever an image displayed in a tab
+ * changes. In this case, we assume that everything will change
+ * and queue a request to re-layout and redraw the entire tabset.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+ Tabset *setPtr = clientData;
+
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetImage --
+ *
+ * This is a wrapper procedure for Tk_GetImage. The problem is
+ * that if the same image is used repeatedly in the same widget,
+ * the separate instances are saved in a linked list. This makes
+ * it especially slow to destroy the widget. As a workaround,
+ * this routine hashes the image and maintains a reference count
+ * for it.
+ *
+ * Results:
+ * Returns a pointer to the new image.
+ *
+ *----------------------------------------------------------------------
+ */
+static TabImage
+GetImage(setPtr, interp, tkwin, name)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *name;
+{
+ struct TabImageStruct *imagePtr;
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&(setPtr->imageTable), (char *)name, &isNew);
+ if (isNew) {
+ Tk_Image tkImage;
+ int width, height;
+
+ tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, setPtr);
+ if (tkImage == NULL) {
+ Blt_DeleteHashEntry(&(setPtr->imageTable), hPtr);
+ return NULL;
+ }
+ Tk_SizeOfImage(tkImage, &width, &height);
+ imagePtr = Blt_Malloc(sizeof(struct TabImageStruct));
+ imagePtr->tkImage = tkImage;
+ imagePtr->hashPtr = hPtr;
+ imagePtr->refCount = 1;
+ imagePtr->width = width;
+ imagePtr->height = height;
+ Blt_SetHashValue(hPtr, imagePtr);
+ } else {
+ imagePtr = Blt_GetHashValue(hPtr);
+ imagePtr->refCount++;
+ }
+ return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeImage --
+ *
+ * Releases the image if it's not being used anymore by this
+ * widget. Note there may be several uses of the same image
+ * by many tabs.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The reference count is decremented and the image is freed
+ * is it's not being used anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeImage(setPtr, imagePtr)
+ Tabset *setPtr;
+ struct TabImageStruct *imagePtr;
+{
+ imagePtr->refCount--;
+ if (imagePtr->refCount == 0) {
+ Blt_DeleteHashEntry(&(setPtr->imageTable), imagePtr->hashPtr);
+ Tk_FreeImage(imagePtr->tkImage);
+ Blt_Free(imagePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToImage --
+ *
+ * Converts an image name into a Tk image token.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToImage(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Contains a pointer to the tabset containing
+ * this image. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Window associated with the tabset. */
+ char *string; /* String representation */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset to field in structure */
+{
+ Tabset *setPtr = *(Tabset **)clientData;
+ TabImage *imagePtr = (TabImage *) (widgRec + offset);
+ TabImage image;
+
+ image = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ image = GetImage(setPtr, interp, tkwin, string);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*imagePtr != NULL) {
+ FreeImage(setPtr, *imagePtr);
+ }
+ *imagePtr = image;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageToString --
+ *
+ * Converts the Tk image back to its string representation (i.e.
+ * its name).
+ *
+ * Results:
+ * The name of the image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ImageToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Pointer to tabset containing image. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Tabset *setPtr = *(Tabset **)clientData;
+ TabImage *imagePtr = (TabImage *) (widgRec + offset);
+
+ if (*imagePtr == NULL) {
+ return "";
+ }
+ return Blt_GetHashKey(&(setPtr->imageTable), (*imagePtr)->hashPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToWindow --
+ *
+ * Converts a window name into Tk window.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToWindow(clientData, interp, parent, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window parent; /* Parent window */
+ char *string; /* String representation. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset to field in structure */
+{
+ Tab *tabPtr = (Tab *)widgRec;
+ Tk_Window *tkwinPtr = (Tk_Window *)(widgRec + offset);
+ Tk_Window old, tkwin;
+ Tabset *setPtr;
+
+ old = *tkwinPtr;
+ tkwin = NULL;
+ setPtr = tabPtr->setPtr;
+ if ((string != NULL) && (*string != '\0')) {
+ tkwin = Tk_NameToWindow(interp, string, parent);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (tkwin == old) {
+ return TCL_OK;
+ }
+ /*
+ * Allow only widgets that are children of the tabset to be
+ * embedded into the page. This way we can make assumptions about
+ * the window based upon its parent; either it's the tabset window
+ * or it has been torn off.
+ */
+ parent = Tk_Parent(tkwin);
+ if (parent != setPtr->tkwin) {
+ Tcl_AppendResult(interp, "can't manage \"", Tk_PathName(tkwin),
+ "\" in tabset \"", Tk_PathName(setPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_ManageGeometry(tkwin, &tabMgrInfo, tabPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+
+ /*
+ * We need to make the window to exist immediately. If the
+ * window is torn off (placed into another container window),
+ * the timing between the container and the its new child
+ * (this window) gets tricky. This should work for Tk 4.2.
+ */
+ Tk_MakeWindowExist(tkwin);
+ }
+ if (old != NULL) {
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ Tk_DeleteEventHandler(old, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ Tk_ManageGeometry(old, (Tk_GeomMgr *) NULL, tabPtr);
+ Tk_UnmapWindow(old);
+ }
+ *tkwinPtr = tkwin;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowToString --
+ *
+ * Converts the Tk window back to its string representation (i.e.
+ * its name).
+ *
+ * Results:
+ * The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+WindowToString(clientData, parent, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window parent; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Tk_Window tkwin = *(Tk_Window *)(widgRec + offset);
+
+ if (tkwin == NULL) {
+ return "";
+ }
+ return Tk_PathName(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSide --
+ *
+ * Converts "left", "right", "top", "bottom", into a numeric token
+ * designating the side of the tabset which to display tabs.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED */
+static int
+StringToSide(clientData, interp, parent, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window parent; /* Parent window */
+ char *string; /* Option value string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to field in structure */
+{
+ int *sidePtr = (int *)(widgRec + offset);
+ char c;
+ unsigned int length;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *sidePtr = SIDE_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *sidePtr = SIDE_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+ *sidePtr = SIDE_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+ *sidePtr = SIDE_BOTTOM;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", string,
+ "\": should be left, right, top, or bottom", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SideToString --
+ *
+ * Converts the window into its string representation (its name).
+ *
+ * Results:
+ * The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SideToString(clientData, parent, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window parent; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of windows array in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ int side = *(int *)(widgRec + offset);
+
+ switch (side) {
+ case SIDE_LEFT:
+ return "left";
+ case SIDE_RIGHT:
+ return "right";
+ case SIDE_BOTTOM:
+ return "bottom";
+ case SIDE_TOP:
+ return "top";
+ }
+ return "unknown side value";
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSlant --
+ *
+ * Converts the slant style string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none" Both sides are straight.
+ * "left" Left side is slanted.
+ * "right" Right side is slanted.
+ * "both" Both sides are slanted.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSlant(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representation of attribute. */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of field in widget record. */
+{
+ int *slantPtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *slantPtr = SLANT_NONE;
+ } else if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *slantPtr = SLANT_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *slantPtr = SLANT_RIGHT;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *slantPtr = SLANT_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", string,
+ "\": should be \"none\", \"left\", \"right\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SlantToString --
+ *
+ * Returns the slant style string based upon the slant flags.
+ *
+ * Results:
+ * The slant style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SlantToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget structure record. */
+ int offset; /* Offset of field in widget record. */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int slant = *(int *)(widgRec + offset);
+
+ switch (slant) {
+ case SLANT_LEFT:
+ return "left";
+ case SLANT_RIGHT:
+ return "right";
+ case SLANT_NONE:
+ return "none";
+ case SLANT_BOTH:
+ return "both";
+ default:
+ return "unknown value";
+ }
+}
+
+
+static int
+WorldY(tabPtr)
+ Tab *tabPtr;
+{
+ int tier;
+
+ tier = tabPtr->setPtr->nTiers - tabPtr->tier;
+ return tier * tabPtr->setPtr->tabHeight;
+}
+
+static int
+TabIndex(setPtr, tabPtr)
+ Tabset *setPtr;
+ Tab *tabPtr;
+{
+ Tab *t2Ptr;
+ int count;
+ Blt_ChainLink *linkPtr;
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ t2Ptr = Blt_ChainGetValue(linkPtr);
+ if (t2Ptr == tabPtr) {
+ return count;
+ }
+ count++;
+ }
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RenumberTiers --
+ *
+ * In multi-tier mode, we need to find the start of the tier
+ * containing the newly selected tab.
+ *
+ * Tiers are draw from the last tier to the first, so that
+ * the the lower-tiered tabs will partially cover the bottoms
+ * of tab directly above it. This simplifies the drawing of
+ * tabs because we don't worry how tabs are clipped by their
+ * neighbors.
+ *
+ * In addition, tabs are re-marked with the correct tier number.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Renumbering the tab's tier will change the vertical placement
+ * of the tab (i.e. shift tiers).
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+RenumberTiers(setPtr, tabPtr)
+ Tabset *setPtr;
+ Tab *tabPtr;
+{
+ int tier;
+ Tab *prevPtr;
+ Blt_ChainLink *linkPtr, *lastPtr;
+
+ setPtr->focusPtr = setPtr->selectPtr = tabPtr;
+ Blt_SetFocusItem(setPtr->bindTable, setPtr->focusPtr);
+
+ tier = tabPtr->tier;
+ for (linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); linkPtr != NULL;
+ linkPtr = lastPtr) {
+ lastPtr = Blt_ChainPrevLink(linkPtr);
+ prevPtr = Blt_ChainGetValue(linkPtr);
+ if ((prevPtr == NULL) || (prevPtr->tier != tier)) {
+ break;
+ }
+ tabPtr = prevPtr;
+ }
+ setPtr->startPtr = tabPtr;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = (tabPtr->tier - tier + 1);
+ if (tabPtr->tier < 1) {
+ tabPtr->tier += setPtr->nTiers;
+ }
+ tabPtr->worldY = WorldY(tabPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TabExists --
+ *
+ * Searches for a tab based upon its name.
+ *
+ * Results:
+ * Returns TRUE if the tab exists, FALSE otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+TabExists(setPtr, string)
+ Tabset *setPtr;
+ char *string;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(setPtr->tabTable), string);
+ return (hPtr != NULL);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetTabByName --
+ *
+ * Searches for a tab based upon its name.
+ *
+ * Results:
+ * A standard Tcl result. An error message is generated if
+ * the tab can't be found.
+ *
+ * Side Effects:
+ * If the tab is found, *tabPtrPtr will contain the pointer to the
+ * tab structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetTabByName(setPtr, string, tabPtrPtr)
+ Tabset *setPtr;
+ char *string;
+ Tab **tabPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+ *tabPtrPtr = NULL;
+
+ hPtr = Blt_FindHashEntry(&(setPtr->tabTable), string);
+ if (hPtr != NULL) {
+ *tabPtrPtr = (Tab *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendResult(setPtr->interp, "can't find tab named \"", string,
+ "\" in \"", Tk_PathName(setPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PickTab --
+ *
+ * Searches the tab located within the given screen X-Y coordinates
+ * in the viewport. Note that tabs overlap slightly, so that its
+ * important to search from the innermost tier out.
+ *
+ * Results:
+ * Returns the pointer to the tab. If the pointer isn't contained
+ * by any tab, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static ClientData
+PickTab(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates to test. */
+{
+ Tabset *setPtr = clientData; /* Tabset widget record. */
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ tabPtr = setPtr->selectPtr;
+ if ((setPtr->tearoff) && (tabPtr != NULL) &&
+ (tabPtr->container == NULL) && (tabPtr->tkwin != NULL)) {
+ int top, bottom, left, right;
+ int sx, sy;
+
+ /* Check first for perforation on the selected tab. */
+ WorldToScreen(setPtr, tabPtr->worldX + 2,
+ tabPtr->worldY + tabPtr->worldHeight + 4, &sx, &sy);
+ if (setPtr->side & SIDE_HORIZONTAL) {
+ left = sx - 2;
+ right = left + tabPtr->screenWidth;
+ top = sy - 4;
+ bottom = sy + 4;
+ } else {
+ left = sx - 4;
+ right = sx + 4;
+ top = sy - 2;
+ bottom = top + tabPtr->screenHeight;
+ }
+ if ((x >= left) && (y >= top) && (x < right) && (y < bottom)) {
+ return &setPtr->perforation;
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (!(tabPtr->flags & TAB_VISIBLE)) {
+ continue;
+ }
+ if ((x >= tabPtr->screenX) && (y >= tabPtr->screenY) &&
+ (x <= (tabPtr->screenX + tabPtr->screenWidth)) &&
+ (y < (tabPtr->screenY + tabPtr->screenHeight))) {
+ return tabPtr;
+ }
+ }
+ return NULL;
+}
+
+static Tab *
+TabLeft(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *newPtr;
+
+ newPtr = Blt_ChainGetValue(linkPtr);
+ /* Move only if the next tab is on another tier. */
+ if (newPtr->tier == tabPtr->tier) {
+ tabPtr = newPtr;
+ }
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabRight(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *newPtr;
+
+ newPtr = Blt_ChainGetValue(linkPtr);
+ /* Move only if the next tab is on another tier. */
+ if (newPtr->tier == tabPtr->tier) {
+ tabPtr = newPtr;
+ }
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabUp(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Tabset *setPtr;
+ int x, y;
+ int worldX, worldY;
+
+ setPtr = tabPtr->setPtr;
+ worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+ worldY = tabPtr->worldY - (setPtr->tabHeight / 2);
+ WorldToScreen(setPtr, worldX, worldY, &x, &y);
+
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ if (tabPtr == NULL) {
+ /*
+ * We might have inadvertly picked the gap between two tabs,
+ * so if the first pick fails, try again a little to the left.
+ */
+ WorldToScreen(setPtr, worldX + setPtr->gap, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ }
+ if ((tabPtr == NULL) &&
+ (setPtr->focusPtr->tier < (setPtr->nTiers - 1))) {
+ WorldToScreen(setPtr, worldX, worldY - setPtr->tabHeight, &x, &y);
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ }
+ if (tabPtr == NULL) {
+ tabPtr = setPtr->focusPtr;
+ }
+ }
+ return tabPtr;
+}
+
+static Tab *
+TabDown(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr != NULL) {
+ Tabset *setPtr;
+ int x, y;
+ int worldX, worldY;
+
+ setPtr = tabPtr->setPtr;
+ worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+ worldY = tabPtr->worldY + (3 * setPtr->tabHeight) / 2;
+ WorldToScreen(setPtr, worldX, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ if (tabPtr == NULL) {
+ /*
+ * We might have inadvertly picked the gap between two tabs,
+ * so if the first pick fails, try again a little to the left.
+ */
+ WorldToScreen(setPtr, worldX - setPtr->gap, worldY, &x, &y);
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ }
+ if ((tabPtr == NULL) && (setPtr->focusPtr->tier > 2)) {
+ WorldToScreen(setPtr, worldX, worldY + setPtr->tabHeight, &x, &y);
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ }
+ if (tabPtr == NULL) {
+ tabPtr = setPtr->focusPtr;
+ }
+ }
+ return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTabByIndex --
+ *
+ * Converts a string representing a tab index into a tab pointer.
+ * The index may be in one of the following forms:
+ *
+ * number Tab at position in the list of tabs.
+ * @x,y Tab closest to the specified X-Y screen coordinates.
+ * "active" Tab mouse is located over.
+ * "focus" Tab is the widget's focus.
+ * "select" Currently selected tab.
+ * "right" Next tab from the focus tab.
+ * "left" Previous tab from the focus tab.
+ * "up" Next tab from the focus tab.
+ * "down" Previous tab from the focus tab.
+ * "end" Last tab in list.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via tabPtrPtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetTabByIndex(setPtr, string, tabPtrPtr, allowNull)
+ Tabset *setPtr;
+ char *string;
+ Tab **tabPtrPtr;
+ int allowNull; /* Allow NULL tabPtr */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+ int position;
+ char c;
+
+ c = string[0];
+ linkPtr = NULL;
+ tabPtr = NULL;
+ if (setPtr->focusPtr == NULL) {
+ setPtr->focusPtr = setPtr->selectPtr;
+ Blt_SetFocusItem(setPtr->bindTable, setPtr->focusPtr);
+ }
+ if ((isdigit(UCHAR(c))) &&
+ (Tcl_GetInt(setPtr->interp, string, &position) == TCL_OK)) {
+ linkPtr = Blt_ChainGetNthLink(setPtr->chainPtr, position);
+ if (linkPtr == NULL) {
+ Tcl_AppendResult(setPtr->interp, "can't find tab \"", string,
+ "\" in \"", Tk_PathName(setPtr->tkwin), "\": no such index",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+ tabPtr = setPtr->activePtr;
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ tabPtr = (Tab *)Blt_GetCurrentItem(setPtr->bindTable);
+ if (tabPtr->name == NULL) {
+ /* Selected perforation. */
+ tabPtr = setPtr->selectPtr;
+ }
+ } else if ((c == 's') && (strcmp(string, "select") == 0)) {
+ tabPtr = setPtr->selectPtr;
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ tabPtr = setPtr->focusPtr;
+ } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+ switch (setPtr->side) {
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ tabPtr = TabLeft(setPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr = TabDown(setPtr->focusPtr);
+ break;
+
+ case SIDE_TOP:
+ tabPtr = TabUp(setPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+ switch (setPtr->side) {
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ tabPtr = TabRight(setPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr = TabUp(setPtr->focusPtr);
+ break;
+
+ case SIDE_TOP:
+ tabPtr = TabDown(setPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'l') && (strcmp(string, "left") == 0)) {
+ switch (setPtr->side) {
+ case SIDE_LEFT:
+ tabPtr = TabUp(setPtr->focusPtr);
+ break;
+
+ case SIDE_RIGHT:
+ tabPtr = TabDown(setPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ case SIDE_TOP:
+ tabPtr = TabLeft(setPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'r') && (strcmp(string, "right") == 0)) {
+ switch (setPtr->side) {
+ case SIDE_LEFT:
+ tabPtr = TabDown(setPtr->focusPtr);
+ break;
+
+ case SIDE_RIGHT:
+ tabPtr = TabUp(setPtr->focusPtr);
+ break;
+
+ case SIDE_BOTTOM:
+ case SIDE_TOP:
+ tabPtr = TabRight(setPtr->focusPtr);
+ break;
+ }
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ linkPtr = Blt_ChainLastLink(setPtr->chainPtr);
+ if (linkPtr != NULL) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ }
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(setPtr->interp, setPtr->tkwin, string, &x, &y)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ } else {
+ Tcl_AppendResult(setPtr->interp, "can't find tab \"", string,
+ "\" in \"", Tk_PathName(setPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *tabPtrPtr = tabPtr;
+ Tcl_ResetResult(setPtr->interp);
+
+ if ((!allowNull) && (tabPtr == NULL)) {
+ Tcl_AppendResult(setPtr->interp, "can't find tab \"", string,
+ "\" in \"", Tk_PathName(setPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+static Tab *
+NextOrLastTab(tabPtr)
+ Tab *tabPtr;
+{
+ if (tabPtr->linkPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr == NULL) {
+ linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+ }
+ if (linkPtr != NULL) {
+ return Blt_ChainGetValue(linkPtr);
+ }
+ }
+ return NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * EmbeddedWidgetEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on embedded widgets contained in the tabset.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When an embedded widget gets deleted, internal structures get
+ * cleaned up. When it gets resized, the tabset is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+EmbeddedWidgetEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ switch (eventPtr->type) {
+ case ConfigureNotify:
+ /*
+ * If the window's requested size changes, redraw the window.
+ * But only if it's currently the selected page.
+ */
+ if ((tabPtr->container == NULL) && (Tk_IsMapped(tabPtr->tkwin)) &&
+ (tabPtr->setPtr->selectPtr == tabPtr)) {
+ EventuallyRedraw(tabPtr->setPtr);
+ }
+ break;
+
+ case DestroyNotify:
+ /*
+ * Mark the tab as deleted by dereferencing the Tk window
+ * pointer. Redraw the window only if the tab is currently
+ * visible.
+ */
+ if ((Tk_IsMapped(tabPtr->tkwin)) &&
+ (tabPtr->setPtr->selectPtr == tabPtr)) {
+ EventuallyRedraw(tabPtr->setPtr);
+ }
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ tabPtr->tkwin = NULL;
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * EmbeddedWidgetCustodyProc --
+ *
+ * This procedure is invoked when a tab window has been
+ * stolen by another geometry manager. The information and
+ * memory associated with the tab window is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the widget formerly associated with the tab
+ * window to have its layout re-computed and arranged at the
+ * next idle point.
+ *
+ * ---------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetCustodyProc(clientData, tkwin)
+ ClientData clientData; /* Information about the former tab window. */
+ Tk_Window tkwin; /* Not used. */
+{
+ Tab *tabPtr = clientData;
+ Tabset *setPtr;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ setPtr = tabPtr->setPtr;
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ /*
+ * Mark the tab as deleted by dereferencing the Tk window
+ * pointer. Redraw the window only if the tab is currently
+ * visible.
+ */
+ if (tabPtr->tkwin != NULL) {
+ if (Tk_IsMapped(tabPtr->tkwin) && (setPtr->selectPtr == tabPtr)) {
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ }
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ tabPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * EmbeddedWidgetGeometryProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for tab
+ * windows managed by the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for tkwin, and all its managed siblings, to be
+ * repacked and drawn at the next idle point.
+ *
+ * ------------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetGeometryProc(clientData, tkwin)
+ ClientData clientData; /* Information about window that got new
+ * preferred geometry. */
+ Tk_Window tkwin; /* Other Tk-related information about the
+ * window. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+ fprintf(stderr, "%s: line %d \"tkwin is null\"", __FILE__, __LINE__);
+ return;
+ }
+ tabPtr->setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(tabPtr->setPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTab --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTab(setPtr, tabPtr)
+ Tabset *setPtr;
+ Tab *tabPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ if (tabPtr->flags & TAB_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+ }
+ if (tabPtr->container != NULL) {
+ Tk_DestroyWindow(tabPtr->container);
+ }
+ if (tabPtr->tkwin != NULL) {
+ Tk_ManageGeometry(tabPtr->tkwin, (Tk_GeomMgr *)NULL, tabPtr);
+ Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+ EmbeddedWidgetEventProc, tabPtr);
+ if (Tk_IsMapped(tabPtr->tkwin)) {
+ Tk_UnmapWindow(tabPtr->tkwin);
+ }
+ }
+ if (tabPtr == setPtr->activePtr) {
+ setPtr->activePtr = NULL;
+ }
+ if (tabPtr == setPtr->selectPtr) {
+ setPtr->selectPtr = NextOrLastTab(tabPtr);
+ }
+ if (tabPtr == setPtr->focusPtr) {
+ setPtr->focusPtr = setPtr->selectPtr;
+ Blt_SetFocusItem(setPtr->bindTable, setPtr->focusPtr);
+ }
+ if (tabPtr == setPtr->startPtr) {
+ setPtr->startPtr = NULL;
+ }
+ Tk_FreeOptions(tabConfigSpecs, (char *)tabPtr, setPtr->display, 0);
+ if (tabPtr->text != NULL) {
+ Blt_FreeUid(tabPtr->text);
+ }
+ hPtr = Blt_FindHashEntry(&(setPtr->tabTable), tabPtr->name);
+ assert(hPtr);
+ Blt_DeleteHashEntry(&(setPtr->tabTable), hPtr);
+
+ if (tabPtr->image != NULL) {
+ FreeImage(setPtr, tabPtr->image);
+ }
+ if (tabPtr->name != NULL) {
+ Blt_Free(tabPtr->name);
+ }
+ if (tabPtr->textGC != NULL) {
+ Tk_FreeGC(setPtr->display, tabPtr->textGC);
+ }
+ if (tabPtr->backGC != NULL) {
+ Tk_FreeGC(setPtr->display, tabPtr->backGC);
+ }
+ if (tabPtr->command != NULL) {
+ Blt_FreeUid(tabPtr->command);
+ }
+ if (tabPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(setPtr->chainPtr, tabPtr->linkPtr);
+ }
+ if (tabPtr->tags != NULL) {
+ Blt_FreeUid(tabPtr->tags);
+ }
+ Blt_DeleteBindings(setPtr->bindTable, tabPtr);
+ Blt_Free(tabPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTab --
+ *
+ * Creates a new tab structure. A tab contains information about
+ * the state of the tab and its embedded window.
+ *
+ * Results:
+ * Returns a pointer to the new tab structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Tab *
+CreateTab(setPtr, name)
+ Tabset *setPtr;
+ char *name;
+{
+ Tab *tabPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ tabPtr = Blt_Calloc(1, sizeof(Tab));
+ assert(tabPtr);
+ tabPtr->setPtr = setPtr;
+ tabPtr->name = Blt_Strdup(name);
+ tabPtr->text = Blt_GetUid(name);
+ tabPtr->fill = FILL_NONE;
+ tabPtr->anchor = TK_ANCHOR_CENTER;
+ tabPtr->container = NULL;
+ tabPtr->state = STATE_NORMAL;
+ hPtr = Blt_CreateHashEntry(&(setPtr->tabTable), name, &isNew);
+ Blt_SetHashValue(hPtr, tabPtr);
+ return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Stub for image change notifications. Since we immediately draw
+ * the image into a pixmap, we don't really care about image changes.
+ *
+ * It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Tabset *setPtr = clientData;
+
+ if (setPtr->tkwin != NULL) {
+ EventuallyRedraw(setPtr);
+ }
+}
+
+static int
+ConfigureTab(setPtr, tabPtr)
+ Tabset *setPtr;
+ Tab *tabPtr;
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ int labelWidth, labelHeight;
+ Tk_Font font;
+ Tk_3DBorder border;
+
+ font = GETATTR(tabPtr, font);
+ labelWidth = labelHeight = 0;
+ if (tabPtr->text != NULL) {
+ TextStyle ts;
+
+ Blt_InitTextStyle(&ts);
+ ts.font = font;
+ ts.shadow.offset = tabPtr->shadow.offset;
+ ts.padX.side1 = ts.padX.side2 = 2;
+ Blt_GetTextExtents(&ts, tabPtr->text, &labelWidth, &labelHeight);
+ Blt_GetBoundingBox(labelWidth, labelHeight, setPtr->defTabStyle.rotate,
+ &labelWidth, &labelHeight, (Point2D *)NULL);
+ }
+ tabPtr->textWidth = (short int)labelWidth;
+ tabPtr->textHeight = (short int)labelHeight;
+ if (tabPtr->image != NULL) {
+ int width, height;
+
+ width = ImageWidth(tabPtr->image) + 2 * IMAGE_PAD;
+ height = ImageHeight(tabPtr->image) + 2 * IMAGE_PAD;
+ if (setPtr->defTabStyle.textSide & SIDE_VERTICAL) {
+ labelWidth += width;
+ labelHeight = MAX(labelHeight, height);
+ } else {
+ labelHeight += height;
+ labelWidth = MAX(labelWidth, width);
+ }
+ }
+ labelWidth += PADDING(tabPtr->iPadX);
+ labelHeight += PADDING(tabPtr->iPadY);
+
+ tabPtr->labelWidth = ODD(labelWidth);
+ tabPtr->labelHeight = ODD(labelHeight);
+
+ newGC = NULL;
+ if (tabPtr->text != NULL) {
+ XColor *colorPtr;
+
+ gcMask = GCForeground | GCFont;
+ colorPtr = GETATTR(tabPtr, textColor);
+ gcValues.foreground = colorPtr->pixel;
+ gcValues.font = Tk_FontId(font);
+ newGC = Tk_GetGC(setPtr->tkwin, gcMask, &gcValues);
+ }
+ if (tabPtr->textGC != NULL) {
+ Tk_FreeGC(setPtr->display, tabPtr->textGC);
+ }
+ tabPtr->textGC = newGC;
+
+ gcMask = GCForeground | GCStipple | GCFillStyle;
+ gcValues.fill_style = FillStippled;
+ border = GETATTR(tabPtr, border);
+ gcValues.foreground = Tk_3DBorderColor(border)->pixel;
+ gcValues.stipple = tabPtr->stipple;
+ newGC = Tk_GetGC(setPtr->tkwin, gcMask, &gcValues);
+ if (tabPtr->backGC != NULL) {
+ Tk_FreeGC(setPtr->display, tabPtr->backGC);
+ }
+ tabPtr->backGC = newGC;
+ /*
+ * GC for tiled background.
+ */
+ if (tabPtr->tile != NULL) {
+ Blt_SetTileChangedProc(tabPtr->tile, TileChangedProc, setPtr);
+ }
+ if (tabPtr->flags & TAB_VISIBLE) {
+ EventuallyRedraw(setPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TearoffEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on the tearoff widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the tearoff gets deleted, internal structures get
+ * cleaned up. When it gets resized or exposed, it's redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TearoffEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about the tab window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Tab *tabPtr = clientData;
+
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+ (tabPtr->container == NULL)) {
+ return;
+ }
+ switch (eventPtr->type) {
+ case Expose:
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawTearoff(tabPtr);
+ }
+ break;
+
+ case ConfigureNotify:
+ EventuallyRedrawTearoff(tabPtr);
+ break;
+
+ case DestroyNotify:
+ if (tabPtr->flags & TAB_REDRAW) {
+ tabPtr->flags &= ~TAB_REDRAW;
+ Tcl_CancelIdleCall(DisplayTearoff, clientData);
+ }
+ Tk_DestroyWindow(tabPtr->container);
+ tabPtr->container = NULL;
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqWidth --
+ *
+ * Returns the width requested by the embedded tab window and
+ * any requested padding around it. This represents the requested
+ * width of the page.
+ *
+ * Results:
+ * Returns the requested width of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqWidth(tabPtr)
+ Tab *tabPtr;
+{
+ int width;
+
+ if (tabPtr->reqWidth > 0) {
+ width = tabPtr->reqWidth;
+ } else {
+ width = Tk_ReqWidth(tabPtr->tkwin);
+ }
+ width += PADDING(tabPtr->padX) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ if (width < 1) {
+ width = 1;
+ }
+ return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqHeight --
+ *
+ * Returns the height requested by the window and padding around
+ * the window. This represents the requested height of the page.
+ *
+ * Results:
+ * Returns the requested height of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqHeight(tabPtr)
+ Tab *tabPtr;
+{
+ int height;
+
+ if (tabPtr->reqHeight > 0) {
+ height = tabPtr->reqHeight;
+ } else {
+ height = Tk_ReqHeight(tabPtr->tkwin);
+ }
+ height += PADDING(tabPtr->padY) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ if (height < 1) {
+ height = 1;
+ }
+ return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based upon the
+ * anchor specified. The anchor indicates where the given xy position
+ * is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | | x,y ---+
+ * w center e | |
+ * | | +-----+
+ * sw --- s --- se
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
+ int dx, dy; /* Difference between outer and inner regions
+ */
+ Tk_Anchor anchor; /* Direction of the anchor */
+ int *xPtr, *yPtr;
+{
+ int x, y;
+
+ x = y = 0;
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y = dy;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x = (dx / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Centered */
+ x = (dx / 2);
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x = (dx / 2);
+ y = dy;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x = dx;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x = dx;
+ y = (dy / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x = dx;
+ y = dy;
+ break;
+ }
+ *xPtr = (*xPtr) + x;
+ *yPtr = (*yPtr) + y;
+}
+
+
+static void
+GetWindowRectangle(tabPtr, parent, tearoff, rectPtr)
+ Tab *tabPtr;
+ Tk_Window parent;
+ int tearoff;
+ XRectangle *rectPtr;
+{
+ int pad;
+ Tabset *setPtr;
+ int cavityWidth, cavityHeight;
+ int width, height;
+ int dx, dy;
+ int x, y;
+
+ setPtr = tabPtr->setPtr;
+ pad = setPtr->inset + setPtr->inset2;
+
+ if (!tearoff) {
+ switch (setPtr->side) {
+ case SIDE_RIGHT:
+ case SIDE_BOTTOM:
+ x = setPtr->inset + setPtr->inset2;
+ y = setPtr->inset + setPtr->inset2;
+ break;
+
+ case SIDE_LEFT:
+ x = setPtr->pageTop;
+ y = setPtr->inset + setPtr->inset2;
+ break;
+
+ case SIDE_TOP:
+ x = setPtr->inset + setPtr->inset2;
+ y = setPtr->pageTop;
+ break;
+ }
+
+ if (setPtr->side & SIDE_VERTICAL) {
+ cavityWidth = Tk_Width(setPtr->tkwin) - (setPtr->pageTop + pad);
+ cavityHeight = Tk_Height(setPtr->tkwin) - (2 * pad);
+ } else {
+ cavityWidth = Tk_Width(setPtr->tkwin) - (2 * pad);
+ cavityHeight = Tk_Height(setPtr->tkwin) - (setPtr->pageTop + pad);
+ }
+
+ } else {
+ x = setPtr->inset + setPtr->inset2;
+#define TEAR_OFF_TAB_SIZE 5
+ y = setPtr->inset + setPtr->inset2 + setPtr->yPad + setPtr->outerPad +
+ TEAR_OFF_TAB_SIZE;
+ cavityWidth = Tk_Width(parent) - (2 * pad);
+ cavityHeight = Tk_Height(parent) - (y + pad);
+ }
+ cavityWidth -= PADDING(tabPtr->padX);
+ cavityHeight -= PADDING(tabPtr->padY);
+ if (cavityWidth < 1) {
+ cavityWidth = 1;
+ }
+ if (cavityHeight < 1) {
+ cavityHeight = 1;
+ }
+ width = GetReqWidth(tabPtr);
+ height = GetReqHeight(tabPtr);
+
+ /*
+ * Resize the embedded window is of the following is true:
+ *
+ * 1) It's been torn off.
+ * 2) The -fill option (horizontal or vertical) is set.
+ * 3) the window is bigger than the cavity.
+ */
+ if ((tearoff) || (cavityWidth < width) || (tabPtr->fill & FILL_X)) {
+ width = cavityWidth;
+ }
+ if ((tearoff) || (cavityHeight < height) || (tabPtr->fill & FILL_Y)) {
+ height = cavityHeight;
+ }
+ dx = (cavityWidth - width);
+ dy = (cavityHeight - height);
+ if ((dx > 0) || (dy > 0)) {
+ TranslateAnchor(dx, dy, tabPtr->anchor, &x, &y);
+ }
+ /* Remember that X11 windows must be at least 1 pixel. */
+ if (width < 1) {
+ width = 1;
+ }
+ if (height < 1) {
+ height = 1;
+ }
+ rectPtr->x = (short)(x + tabPtr->padLeft);
+ rectPtr->y = (short)(y + tabPtr->padTop);
+ rectPtr->width = (short)width;
+ rectPtr->height = (short)height;
+}
+
+static void
+ArrangeWindow(tkwin, rectPtr, force)
+ Tk_Window tkwin;
+ XRectangle *rectPtr;
+ int force;
+{
+ if ((force) ||
+ (rectPtr->x != Tk_X(tkwin)) ||
+ (rectPtr->y != Tk_Y(tkwin)) ||
+ (rectPtr->width != Tk_Width(tkwin)) ||
+ (rectPtr->height != Tk_Height(tkwin))) {
+ Tk_MoveResizeWindow(tkwin, rectPtr->x, rectPtr->y, rectPtr->width,
+ rectPtr->height);
+ }
+ if (!Tk_IsMapped(tkwin)) {
+ Tk_MapWindow(tkwin);
+ }
+}
+
+
+/*ARGSUSED*/
+static void
+GetTags(table, object, list)
+ Blt_BindTable table;
+ ClientData object;
+ Blt_List list;
+{
+ Tab *tabPtr = (Tab *)object;
+ Tabset *setPtr;
+
+ setPtr = (Tabset *)table->clientData;
+ if (tabPtr->name == NULL) {
+ Blt_ListAppend(list, MakeTag(setPtr, "Perforation"), 0);
+ } else {
+ Blt_ListAppend(list, MakeTag(setPtr, tabPtr->name), 0);
+ if (tabPtr->tags != NULL) {
+ int nNames;
+ char **names;
+ register char **p;
+
+ /*
+ * This is a space/time trade-off in favor of space. The tags
+ * are stored as character strings in a hash table. That way,
+ * tabs can share the strings. It's likely that they will. The
+ * down side is that the same string is split over an over again.
+ */
+ if (Tcl_SplitList((Tcl_Interp *)NULL, tabPtr->tags, &nNames,
+ &names) == TCL_OK) {
+ for (p = names; *p != NULL; p++) {
+ Blt_ListAppend(list, MakeTag(setPtr, *p), 0);
+ }
+ Blt_Free(names);
+ }
+ }
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TabsetEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on tabset widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TabsetEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Tabset *setPtr = clientData;
+
+ switch (eventPtr->type) {
+ case Expose:
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedraw(setPtr);
+ }
+ break;
+
+ case ConfigureNotify:
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ break;
+
+ case FocusIn:
+ case FocusOut:
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ setPtr->flags |= TABSET_FOCUS;
+ } else {
+ setPtr->flags &= ~TABSET_FOCUS;
+ }
+ EventuallyRedraw(setPtr);
+ }
+ break;
+
+ case DestroyNotify:
+ if (setPtr->tkwin != NULL) {
+ setPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(setPtr->interp, setPtr->cmdToken);
+ }
+ if (setPtr->flags & TABSET_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTabset, setPtr);
+ }
+ Tcl_EventuallyFree(setPtr, DestroyTabset);
+ break;
+
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTabset --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of the widget at a safe
+ * time (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTabset(dataPtr)
+ DestroyData dataPtr; /* Pointer to the widget record. */
+{
+ Tabset *setPtr = (Tabset *)dataPtr;
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (setPtr->highlightGC != NULL) {
+ Tk_FreeGC(setPtr->display, setPtr->highlightGC);
+ }
+ if (setPtr->tile != NULL) {
+ Blt_FreeTile(setPtr->tile);
+ }
+ if (setPtr->defTabStyle.activeGC != NULL) {
+ Blt_FreePrivateGC(setPtr->display, setPtr->defTabStyle.activeGC);
+ }
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->linkPtr = NULL;
+ DestroyTab(setPtr, tabPtr);
+ }
+ Blt_ChainDestroy(setPtr->chainPtr);
+ Blt_DestroyBindingTable(setPtr->bindTable);
+ Blt_DeleteHashTable(&(setPtr->tabTable));
+ Blt_DeleteHashTable(&(setPtr->tagTable));
+ Tk_FreeOptions(configSpecs, (char *)setPtr, setPtr->display, 0);
+ Blt_Free(setPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTabset --
+ *
+ * ----------------------------------------------------------------------
+ */
+static Tabset *
+CreateTabset(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+{
+ Tabset *setPtr;
+
+ setPtr = Blt_Calloc(1, sizeof(Tabset));
+ assert(setPtr);
+
+ Tk_SetClass(tkwin, "Tabset");
+ setPtr->tkwin = tkwin;
+ setPtr->display = Tk_Display(tkwin);
+ setPtr->interp = interp;
+
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ setPtr->side = SIDE_TOP;
+ setPtr->borderWidth = setPtr->highlightWidth = 2;
+ setPtr->ySelectPad = SELECT_PADY;
+ setPtr->xSelectPad = SELECT_PADX;
+ setPtr->relief = TK_RELIEF_SUNKEN;
+ setPtr->defTabStyle.relief = TK_RELIEF_RAISED;
+ setPtr->defTabStyle.borderWidth = 1;
+ setPtr->defTabStyle.constWidth = TRUE;
+ setPtr->defTabStyle.textSide = SIDE_LEFT;
+ setPtr->scrollUnits = 2;
+ setPtr->corner = CORNER_OFFSET;
+ setPtr->gap = GAP;
+ setPtr->outerPad = OUTER_PAD;
+ setPtr->slant = SLANT_NONE;
+ setPtr->overlap = 0;
+ setPtr->tearoff = TRUE;
+ setPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, setPtr, PickTab,
+ GetTags);
+ setPtr->chainPtr = Blt_ChainCreate();
+ Blt_InitHashTable(&(setPtr->tabTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(setPtr->imageTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(setPtr->tagTable), BLT_STRING_KEYS);
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, setPtr);
+#endif
+ return setPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureTabset --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for setPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureTabset(interp, setPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors. */
+ Tabset *setPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+ int argc;
+ char **argv;
+ int flags;
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+
+ tabSet = setPtr;
+ if (Tk_ConfigureWidget(interp, setPtr->tkwin, configSpecs, argc, argv,
+ (char *)setPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ConfigModified(configSpecs, "-width", "-height", "-side", "-gap",
+ "-slant", (char *)NULL)) {
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ }
+ if ((setPtr->reqHeight > 0) && (setPtr->reqWidth > 0)) {
+ Tk_GeometryRequest(setPtr->tkwin, setPtr->reqWidth, setPtr->reqHeight);
+ }
+ /*
+ * GC for focus highlight.
+ */
+ gcMask = GCForeground;
+ gcValues.foreground = setPtr->highlightColor->pixel;
+ newGC = Tk_GetGC(setPtr->tkwin, gcMask, &gcValues);
+ if (setPtr->highlightGC != NULL) {
+ Tk_FreeGC(setPtr->display, setPtr->highlightGC);
+ }
+ setPtr->highlightGC = newGC;
+
+ /*
+ * GC for tiled background.
+ */
+ if (setPtr->tile != NULL) {
+ Blt_SetTileChangedProc(setPtr->tile, TileChangedProc, setPtr);
+ }
+ /*
+ * GC for active line.
+ */
+ gcMask = GCForeground | GCLineWidth | GCLineStyle | GCCapStyle;
+ gcValues.foreground = setPtr->defTabStyle.activeFgColor->pixel;
+ gcValues.line_width = 0;
+ gcValues.cap_style = CapProjecting;
+ gcValues.line_style = (LineIsDashed(setPtr->defTabStyle.dashes))
+ ? LineOnOffDash : LineSolid;
+
+ newGC = Blt_GetPrivateGC(setPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(setPtr->defTabStyle.dashes)) {
+ setPtr->defTabStyle.dashes.offset = 2;
+ Blt_SetDashes(setPtr->display, newGC, &(setPtr->defTabStyle.dashes));
+ }
+ if (setPtr->defTabStyle.activeGC != NULL) {
+ Blt_FreePrivateGC(setPtr->display, setPtr->defTabStyle.activeGC);
+ }
+ setPtr->defTabStyle.activeGC = newGC;
+
+ setPtr->defTabStyle.rotate = FMOD(setPtr->defTabStyle.rotate, 360.0);
+ if (setPtr->defTabStyle.rotate < 0.0) {
+ setPtr->defTabStyle.rotate += 360.0;
+ }
+ setPtr->inset = setPtr->highlightWidth + setPtr->borderWidth +
+ setPtr->outerPad;
+ if (Blt_ConfigModified(configSpecs, "-font", "-*foreground", "-rotate",
+ "-*background", "-side", (char *)NULL)) {
+ Blt_ChainLink *linkPtr;
+ Tab *tabPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ ConfigureTab(setPtr, tabPtr);
+ }
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ }
+ setPtr->inset2 = setPtr->defTabStyle.borderWidth + setPtr->corner;
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Tabset operations
+ *
+ * --------------------------------------------------------------
+ */
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Selects the tab to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ActivateOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr, *oldPtr, *selPtr;
+ Drawable drawable;
+
+ if (argv[2][0] == '\0') {
+ tabPtr = NULL;
+ } else if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr != NULL) && (tabPtr->state == STATE_DISABLED)) {
+ tabPtr = NULL;
+ }
+ oldPtr = setPtr->activePtr;
+
+ setPtr->activePtr = tabPtr;
+ drawable = Tk_WindowId(setPtr->tkwin);
+ if (tabPtr != oldPtr) {
+ int redraw;
+
+ selPtr = setPtr->selectPtr;
+ redraw = FALSE;
+ if (oldPtr != NULL) {
+ if ((selPtr != NULL) &&
+ ((oldPtr == TabLeft(selPtr)) || (oldPtr == TabRight(selPtr)))) {
+ redraw = TRUE;
+ }
+ if ((selPtr != NULL) && (oldPtr->tier == 2) &&
+ (oldPtr->worldX + oldPtr->worldWidth) >= (selPtr->worldX) &&
+ (oldPtr->worldX < (selPtr->worldX + selPtr->worldWidth))) {
+ redraw = TRUE;
+ } else {
+ DrawLabel(setPtr, oldPtr, drawable);
+ }
+ }
+ if ((tabPtr != NULL) && (!redraw)) {
+ if ((selPtr != NULL) &&
+ ((tabPtr == TabLeft(selPtr)) || (tabPtr == TabRight(selPtr)))) {
+ redraw = TRUE;
+ }
+ if ((selPtr != NULL) && (tabPtr->tier == 2) &&
+ (tabPtr->worldX + tabPtr->worldWidth) >= (selPtr->worldX) &&
+ (tabPtr->worldX < (selPtr->worldX + selPtr->worldWidth))) {
+ redraw = TRUE;
+ } else {
+ DrawLabel(setPtr, tabPtr, drawable);
+ }
+ }
+ DrawOuterBorders(setPtr, drawable);
+ if (redraw) {
+ EventuallyRedraw(setPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ if (argc == 2) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+
+ for (hPtr = Blt_FirstHashEntry(&(setPtr->tagTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&(setPtr->tagTable), hPtr);
+ Tcl_AppendElement(interp, tagName);
+ }
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindings(interp, setPtr->bindTable,
+ MakeTag(setPtr, argv[2]), argc - 3, argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ tabSet = setPtr;
+ return Tk_ConfigureValue(interp, setPtr->tkwin, configSpecs,
+ (char *)setPtr, argv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for setPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ tabSet = setPtr;
+ if (argc == 2) {
+ return Tk_ConfigureInfo(interp, setPtr->tkwin, configSpecs,
+ (char *)setPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ return Tk_ConfigureInfo(interp, setPtr->tkwin, configSpecs,
+ (char *)setPtr, argv[2], 0);
+ }
+ if (ConfigureTabset(interp, setPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes tab from the set. Deletes either a range of
+ * tabs or a single node.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *firstPtr, *lastPtr;
+
+ lastPtr = NULL;
+ if (GetTabByIndex(setPtr, argv[2], &firstPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((argc == 4) &&
+ (GetTabByIndex(setPtr, argv[3], &lastPtr, INVALID_FAIL) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (lastPtr == NULL) {
+ DestroyTab(setPtr, firstPtr);
+ } else {
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr, *nextLinkPtr;
+
+ tabPtr = NULL; /* Suppress compiler warning. */
+
+ /* Make sure that the first tab is before the last. */
+ for (linkPtr = firstPtr->linkPtr; linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr == lastPtr) {
+ break;
+ }
+ }
+ if (tabPtr != lastPtr) {
+ return TCL_OK;
+ }
+ linkPtr = firstPtr->linkPtr;
+ while (linkPtr != NULL) {
+ nextLinkPtr = Blt_ChainNextLink(linkPtr);
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ DestroyTab(setPtr, tabPtr);
+ linkPtr = nextLinkPtr;
+ if (tabPtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FocusOp --
+ *
+ * Selects the tab to get focus.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FocusOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr != NULL) {
+ setPtr->focusPtr = tabPtr;
+ Blt_SetFocusItem(setPtr->bindTable, setPtr->focusPtr);
+ EventuallyRedraw(setPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Converts a string representing a tab index.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each index found. If an index could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+ int search;
+
+#define SEARCH_NAMES 1
+#define SEARCH_INDICES 2
+ search = SEARCH_INDICES;
+ if (argc == 4) {
+ if (strcmp(argv[2], "-index") == 0) {
+ search = SEARCH_INDICES;
+ } else if (strcmp(argv[2], "-name") == 0) {
+ search = SEARCH_NAMES;
+ } else {
+ Tcl_AppendResult(interp, "bad switch \"", argv[2],
+ "\": should be \"-index\" or \"-name\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ argc--, argv++;
+ }
+ if (search == SEARCH_INDICES) {
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (GetTabByName(setPtr, argv[2], &tabPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (tabPtr == NULL) {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, Blt_Itoa(TabIndex(setPtr, tabPtr)),
+ TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Converts a tab index into the tab identifier.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each index found. If an index could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr == NULL) {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Add new entries into a tab set.
+ *
+ * .t insert end label option-value label option-value...
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+ register int i;
+ char **options;
+ Blt_ChainLink *linkPtr, *beforeLinkPtr;
+ int start, count;
+ char c;
+
+ c = argv[2][0];
+ if ((c == 'e') && (strcmp(argv[2], "end") == 0)) {
+ beforeLinkPtr = NULL;
+ } else if (isdigit(UCHAR(c))) {
+ int position;
+
+ if (Tcl_GetInt(interp, argv[2], &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position < 0) {
+ beforeLinkPtr = Blt_ChainFirstLink(setPtr->chainPtr);
+ } else if (position > Blt_ChainGetLength(setPtr->chainPtr)) {
+ beforeLinkPtr = NULL;
+ } else {
+ beforeLinkPtr = Blt_ChainGetNthLink(setPtr->chainPtr, position);
+ }
+ } else {
+ Tab *beforePtr;
+
+ if (GetTabByIndex(setPtr, argv[2], &beforePtr, INVALID_FAIL)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ beforeLinkPtr = beforePtr->linkPtr;
+ }
+ tabSet = setPtr;
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ for (i = 3; i < argc; /*empty*/ ) {
+ if (TabExists(setPtr, argv[i])) {
+ Tcl_AppendResult(setPtr->interp, "tab \"", argv[i],
+ "\" already exists in \"", Tk_PathName(setPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tabPtr = CreateTab(setPtr, argv[i]);
+ if (tabPtr == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * Count the option-value pairs that follow. Count until we
+ * spot one that doesn't look like a configuration option (i.e.
+ * doesn't start with a minus "-").
+ */
+ i++;
+ start = i;
+ for ( /*empty*/ ; i < argc; i += 2) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ }
+ count = i - start;
+ options = argv + start;
+ if (Blt_ConfigureWidgetComponent(interp, setPtr->tkwin, tabPtr->name,
+ "Tab", tabConfigSpecs, count, options, (char *)tabPtr, 0)
+ != TCL_OK) {
+ DestroyTab(setPtr, tabPtr);
+ return TCL_ERROR;
+ }
+ if (ConfigureTab(setPtr, tabPtr) != TCL_OK) {
+ DestroyTab(setPtr, tabPtr);
+ return TCL_ERROR;
+ }
+ linkPtr = Blt_ChainNewLink();
+ if (beforeLinkPtr == NULL) {
+ Blt_ChainAppendLink(setPtr->chainPtr, linkPtr);
+ } else {
+ Blt_ChainLinkBefore(setPtr->chainPtr, linkPtr, beforeLinkPtr);
+ }
+ tabPtr->linkPtr = linkPtr;
+ Blt_ChainSetValue(linkPtr, tabPtr);
+ }
+ return TCL_OK;
+
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+static void
+PercentSubst(setPtr, tabPtr, command, resultPtr)
+ Tabset *setPtr;
+ Tab *tabPtr;
+ char *command;
+ Tcl_DString *resultPtr;
+{
+ register char *last, *p;
+ /*
+ * Get the full path name of the node, in case we need to
+ * substitute for it.
+ */
+ Tcl_DStringInit(resultPtr);
+ for (last = p = command; *p != '\0'; p++) {
+ if (*p == '%') {
+ char *string;
+ char buf[3];
+
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ *p = '%';
+ }
+ switch (*(p + 1)) {
+ case '%': /* Percent sign */
+ string = "%";
+ break;
+ case 'W': /* Widget name */
+ string = Tk_PathName(setPtr->tkwin);
+ break;
+ case 'i': /* Tab Index */
+ string = Blt_Itoa(TabIndex(setPtr, tabPtr));
+ break;
+ case 'n': /* Tab name */
+ string = tabPtr->name;
+ break;
+ default:
+ if (*(p + 1) == '\0') {
+ p--;
+ }
+ buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+ string = buf;
+ break;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ p++;
+ last = p + 1;
+ }
+ }
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeOp --
+ *
+ * This procedure is called to invoke a selection command.
+ *
+ * .h invoke index
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvokeOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+ char *command;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ Tcl_Preserve(tabPtr);
+ command = GETATTR(tabPtr, command);
+ if (command != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ PercentSubst(setPtr, tabPtr, command, &dString);
+ result = Tcl_GlobalEval(setPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_Release(tabPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ * Moves a tab to a new location.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr, *linkPtr;
+ int before;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ if ((argv[3][0] == 'b') && (strcmp(argv[3], "before") == 0)) {
+ before = 1;
+ } else if ((argv[3][0] == 'a') && (strcmp(argv[3], "after") == 0)) {
+ before = 0;
+ } else {
+ Tcl_AppendResult(interp, "bad key word \"", argv[3],
+ "\": should be \"after\" or \"before\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetTabByIndex(setPtr, argv[4], &linkPtr, INVALID_FAIL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr == linkPtr) {
+ return TCL_OK;
+ }
+ Blt_ChainUnlinkLink(setPtr->chainPtr, tabPtr->linkPtr);
+ if (before) {
+ Blt_ChainLinkBefore(setPtr->chainPtr, tabPtr->linkPtr, linkPtr->linkPtr);
+ } else {
+ Blt_ChainLinkAfter(setPtr->chainPtr, tabPtr->linkPtr, linkPtr->linkPtr);
+ }
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y; /* Screen coordinates of the test point. */
+ Tab *tabPtr;
+
+ if ((Tk_GetPixels(interp, setPtr->tkwin, argv[2], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, setPtr->tkwin, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (setPtr->nVisible > 0) {
+ tabPtr = (Tab *)PickTab(setPtr, x, y);
+ if (tabPtr != NULL) {
+ Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * This procedure is called to selecta tab.
+ *
+ * .h select index
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK;
+ }
+ if ((setPtr->selectPtr != NULL) && (setPtr->selectPtr != tabPtr) &&
+ (setPtr->selectPtr->tkwin != NULL)) {
+ if (setPtr->selectPtr->container == NULL) {
+ if (Tk_IsMapped(setPtr->selectPtr->tkwin)) {
+ Tk_UnmapWindow(setPtr->selectPtr->tkwin);
+ }
+ } else {
+ /* Redraw now unselected container. */
+ EventuallyRedrawTearoff(setPtr->selectPtr);
+ }
+ }
+ setPtr->selectPtr = tabPtr;
+ if ((setPtr->nTiers > 1) && (tabPtr->tier != setPtr->startPtr->tier)) {
+ RenumberTiers(setPtr, tabPtr);
+ Blt_PickCurrentItem(setPtr->bindTable);
+ }
+ setPtr->flags |= (TABSET_SCROLL);
+ if (tabPtr->container != NULL) {
+ EventuallyRedrawTearoff(tabPtr);
+ }
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+static int
+ViewOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int width;
+
+ width = VPORTWIDTH(setPtr);
+ if (argc == 2) {
+ double fract;
+
+ /*
+ * Note: we are bounding the fractions between 0.0 and 1.0 to
+ * support the "canvas"-style of scrolling.
+ */
+
+ fract = (double)setPtr->scrollOffset / setPtr->worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ fract = (double)(setPtr->scrollOffset + width) / setPtr->worldWidth;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(setPtr->scrollOffset),
+ setPtr->worldWidth, width, setPtr->scrollUnits,
+ BLT_SCROLL_MODE_CANVAS) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ setPtr->flags |= TABSET_SCROLL;
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+
+static void
+AdoptWindow(clientData)
+ ClientData clientData;
+{
+ Tab *tabPtr = clientData;
+ int x, y;
+ Tabset *setPtr = tabPtr->setPtr;
+
+ x = setPtr->inset + setPtr->inset2 + tabPtr->padLeft;
+#define TEAR_OFF_TAB_SIZE 5
+ y = setPtr->inset + setPtr->inset2 + setPtr->yPad +
+ setPtr->outerPad + TEAR_OFF_TAB_SIZE + tabPtr->padTop;
+ Blt_RelinkWindow(tabPtr->tkwin, tabPtr->container, x, y);
+ Tk_MapWindow(tabPtr->tkwin);
+}
+
+static void
+DestroyTearoff(dataPtr)
+ DestroyData dataPtr;
+{
+ Tab *tabPtr = (Tab *)dataPtr;
+
+ if (tabPtr->container != NULL) {
+ Tabset *setPtr;
+ Tk_Window tkwin;
+ setPtr = tabPtr->setPtr;
+
+ tkwin = tabPtr->container;
+ if (tabPtr->flags & TAB_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+ }
+ Tk_DeleteEventHandler(tkwin, StructureNotifyMask, TearoffEventProc,
+ tabPtr);
+ if (tabPtr->tkwin != NULL) {
+ XRectangle rect;
+
+ GetWindowRectangle(tabPtr, setPtr->tkwin, FALSE, &rect);
+ Blt_RelinkWindow(tabPtr->tkwin, setPtr->tkwin, rect.x, rect.y);
+ if (tabPtr == setPtr->selectPtr) {
+ ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+ } else {
+ Tk_UnmapWindow(tabPtr->tkwin);
+ }
+ }
+ Tk_DestroyWindow(tkwin);
+ tabPtr->container = NULL;
+ }
+}
+
+static int
+CreateTearoff(setPtr, name, tabPtr)
+ Tabset *setPtr;
+ char *name;
+ Tab *tabPtr;
+{
+ Tk_Window tkwin;
+ int width, height;
+
+ tkwin = Tk_CreateWindowFromPath(setPtr->interp, setPtr->tkwin, name,
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ tabPtr->container = tkwin;
+ if (Tk_WindowId(tkwin) == None) {
+ Tk_MakeWindowExist(tkwin);
+ }
+ Tk_SetClass(tkwin, "Tearoff");
+ Tk_CreateEventHandler(tkwin, (ExposureMask | StructureNotifyMask),
+ TearoffEventProc, tabPtr);
+ if (Tk_WindowId(tabPtr->tkwin) == None) {
+ Tk_MakeWindowExist(tabPtr->tkwin);
+ }
+ width = Tk_Width(tabPtr->tkwin);
+ if (width < 2) {
+ width = (tabPtr->reqWidth > 0)
+ ? tabPtr->reqWidth : Tk_ReqWidth(tabPtr->tkwin);
+ }
+ width += PADDING(tabPtr->padX) + 2 *
+ Tk_Changes(tabPtr->tkwin)->border_width;
+ width += 2 * (setPtr->inset2 + setPtr->inset);
+#define TEAR_OFF_TAB_SIZE 5
+ height = Tk_Height(tabPtr->tkwin);
+ if (height < 2) {
+ height = (tabPtr->reqHeight > 0)
+ ? tabPtr->reqHeight : Tk_ReqHeight(tabPtr->tkwin);
+ }
+ height += PADDING(tabPtr->padY) +
+ 2 * Tk_Changes(tabPtr->tkwin)->border_width;
+ height += setPtr->inset + setPtr->inset2 + setPtr->yPad +
+ TEAR_OFF_TAB_SIZE + setPtr->outerPad;
+ Tk_GeometryRequest(tkwin, width, height);
+ Tk_UnmapWindow(tabPtr->tkwin);
+ /* Tk_MoveWindow(tabPtr->tkwin, 0, 0); */
+ Tcl_SetResult(setPtr->interp, Tk_PathName(tkwin), TCL_VOLATILE);
+#ifdef WIN32
+ AdoptWindow(tabPtr);
+#else
+ Tcl_DoWhenIdle(AdoptWindow, tabPtr);
+#endif
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabCgetOp --
+ *
+ * .h tab cget index option
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabCgetOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTabByName(setPtr, argv[3], &tabPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tabSet = setPtr;
+ return Tk_ConfigureValue(interp, setPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, argv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the options for
+ * one or more tabs in the widget.
+ *
+ * .h tab configure index ?index...? ?option value?...
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side Effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set; old resources get freed, if there were any.
+ * The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TabConfigureOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int nTabs, nOpts, result;
+ char **options;
+ register int i;
+ Tab *tabPtr;
+
+ /* Figure out where the option value pairs begin */
+ argc -= 3;
+ argv += 3;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ break;
+ }
+ if (GetTabByName(setPtr, argv[i], &tabPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find node. */
+ }
+ }
+ nTabs = i; /* Number of tab indices specified */
+ nOpts = argc - i; /* Number of options specified */
+ options = argv + i; /* Start of options in argv */
+
+ for (i = 0; i < nTabs; i++) {
+ GetTabByName(setPtr, argv[i], &tabPtr);
+ if (argc == 1) {
+ return Tk_ConfigureInfo(interp, setPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, (char *)NULL, 0);
+ } else if (argc == 2) {
+ return Tk_ConfigureInfo(interp, setPtr->tkwin, tabConfigSpecs,
+ (char *)tabPtr, argv[2], 0);
+ }
+ tabSet = setPtr;
+ Tcl_Preserve(tabPtr);
+ result = Tk_ConfigureWidget(interp, setPtr->tkwin, tabConfigSpecs,
+ nOpts, options, (char *)tabPtr, TK_CONFIG_ARGV_ONLY);
+ if (result == TCL_OK) {
+ result = ConfigureTab(setPtr, tabPtr);
+ }
+ Tcl_Release(tabPtr);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (tabPtr->flags & TAB_VISIBLE) {
+ setPtr->flags |= (TABSET_LAYOUT | TABSET_SCROLL);
+ EventuallyRedraw(setPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabDockallOp --
+ *
+ * .h tab dockall
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabDockallOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabPageHeight --
+ *
+ * .h tab pageheight
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabPageHeight(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tcl_SetResult(interp, Blt_Itoa(VPORTHEIGHT(setPtr)), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabPageWidth --
+ *
+ * .h tab pagewidth
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabPageWidth(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tcl_SetResult(interp, Blt_Itoa(VPORTWIDTH(setPtr)), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabNamesOp --
+ *
+ * .h tab names pattern
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabNamesOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ if (argc == 3) {
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_AppendElement(interp, tabPtr->name);
+ }
+ } else {
+ register int i;
+
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ for (i = 3; i < argc; i++) {
+ if (Tcl_StringMatch(tabPtr->name, argv[i])) {
+ Tcl_AppendElement(interp, tabPtr->name);
+ break;
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabTearoffOp --
+ *
+ * .h tab tearoff index ?title?
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabTearoffOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tab *tabPtr;
+ int result;
+ Tk_Window tkwin;
+
+ result = TCL_OK;
+
+ if (GetTabByIndex(setPtr, argv[3], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+ (tabPtr->state == STATE_DISABLED)) {
+ return TCL_OK; /* No-op */
+ }
+ if (argc == 4) {
+ Tk_Window parent;
+
+ parent = (tabPtr->container == NULL)
+ ? setPtr->tkwin : tabPtr->container;
+ Tcl_SetResult(setPtr->interp, Tk_PathName(parent), TCL_VOLATILE);
+ return TCL_OK;
+ }
+ Tcl_Preserve(tabPtr);
+ result = TCL_OK;
+
+ tkwin = Tk_NameToWindow(interp, argv[4], setPtr->tkwin);
+ Tcl_ResetResult(interp);
+
+ if (tabPtr->container != NULL) {
+ Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+ }
+ if ((tkwin != setPtr->tkwin) && (tabPtr->container == NULL)) {
+ result = CreateTearoff(setPtr, argv[4], tabPtr);
+ }
+ Tcl_Release(tabPtr);
+ EventuallyRedraw(setPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabOp --
+ *
+ * This procedure handles tab operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec tabOps[] =
+{
+ {"cget", 2, (Blt_Op)TabCgetOp, 5, 5, "nameOrIndex option",},
+ {"configure", 2, (Blt_Op)TabConfigureOp, 4, 0,
+ "nameOrIndex ?option value?...",},
+ {"dockall", 1, (Blt_Op)TabDockallOp, 3, 3, "" },
+ {"names", 1, (Blt_Op)TabNamesOp, 3, 0, "?pattern...?",},
+ {"pageheight", 5, (Blt_Op)TabPageHeight, 3, 3, "", },
+ {"pagewidth", 5, (Blt_Op)TabPageWidth, 3, 3, "", },
+ {"tearoff", 1, (Blt_Op)TabTearoffOp, 4, 5, "index ?parent?",},
+};
+
+static int nTabOps = sizeof(tabOps) / sizeof(Blt_OpSpec);
+
+static int
+TabOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nTabOps, tabOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (setPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationHighlightOp --
+ *
+ * This procedure is called to highlight the perforation.
+ *
+ * .h perforation highlight boolean
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationHighlightOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ int bool;
+
+ if (Tcl_GetBoolean(interp, argv[3], &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (bool) {
+ setPtr->flags |= PERFORATION_ACTIVE;
+ } else {
+ setPtr->flags &= ~PERFORATION_ACTIVE;
+ }
+ EventuallyRedraw(setPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationInvokeOp --
+ *
+ * This procedure is called to invoke a perforation command.
+ *
+ * .t perforation invoke
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationInvokeOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+
+ if (setPtr->selectPtr != NULL) {
+ char *cmd;
+
+ cmd = GETATTR(setPtr->selectPtr, perfCommand);
+ if (cmd != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ PercentSubst(setPtr, setPtr->selectPtr, cmd, &dString);
+ Tcl_Preserve(setPtr);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_Release(setPtr);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationOp --
+ *
+ * This procedure handles tab operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec perforationOps[] =
+{
+ {"highlight", 1, (Blt_Op)PerforationHighlightOp, 4, 4, "boolean" },
+ {"invoke", 1, (Blt_Op)PerforationInvokeOp, 3, 3, "",},
+};
+
+static int nPerforationOps = sizeof(perforationOps) / sizeof(Blt_OpSpec);
+
+static int
+PerforationOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nPerforationOps, perforationOps, BLT_OP_ARG2,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (setPtr, interp, argc, argv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ * Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int x, y;
+ char c;
+ unsigned int length;
+ int oper;
+
+#define SCAN_MARK 1
+#define SCAN_DRAGTO 2
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
+ oper = SCAN_MARK;
+ } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
+ oper = SCAN_DRAGTO;
+ } else {
+ Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
+ "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tk_GetPixels(interp, setPtr->tkwin, argv[3], &x) != TCL_OK) ||
+ (Tk_GetPixels(interp, setPtr->tkwin, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (oper == SCAN_MARK) {
+ if (setPtr->side & SIDE_VERTICAL) {
+ setPtr->scanAnchor = y;
+ } else {
+ setPtr->scanAnchor = x;
+ }
+ setPtr->scanOffset = setPtr->scrollOffset;
+ } else {
+ int offset, delta;
+
+ if (setPtr->side & SIDE_VERTICAL) {
+ delta = setPtr->scanAnchor - y;
+ } else {
+ delta = setPtr->scanAnchor - x;
+ }
+ offset = setPtr->scanOffset + (10 * delta);
+ offset = Blt_AdjustViewport(offset, setPtr->worldWidth,
+ VPORTWIDTH(setPtr), setPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS);
+ setPtr->scrollOffset = offset;
+ setPtr->flags |= TABSET_SCROLL;
+ EventuallyRedraw(setPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ Tab *tabPtr;
+
+ if (GetTabByIndex(setPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tabPtr != NULL) {
+ int left, right, width;
+
+ width = VPORTWIDTH(setPtr);
+ left = setPtr->scrollOffset + setPtr->xSelectPad;
+ right = setPtr->scrollOffset + width - setPtr->xSelectPad;
+
+ /* If the tab is partially obscured, scroll so that it's
+ * entirely in view. */
+ if (tabPtr->worldX < left) {
+ setPtr->scrollOffset = tabPtr->worldX - TAB_SCROLL_OFFSET;
+ } else if ((tabPtr->worldX + tabPtr->worldWidth) >= right) {
+ Blt_ChainLink *linkPtr;
+
+ setPtr->scrollOffset = tabPtr->worldX + tabPtr->worldWidth -
+ (width - 2 * setPtr->xSelectPad);
+ linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+ if (linkPtr != NULL) {
+ Tab *nextPtr;
+
+ nextPtr = Blt_ChainGetValue(linkPtr);
+ if (nextPtr->tier == tabPtr->tier) {
+ setPtr->scrollOffset += TAB_SCROLL_OFFSET;
+ }
+ }
+ }
+ setPtr->flags |= TABSET_SCROLL;
+ EventuallyRedraw(setPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SizeOp(setPtr, interp, argc, argv)
+ Tabset *setPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tcl_SetResult(interp, Blt_Itoa(Blt_ChainGetLength(setPtr->chainPtr)),
+ TCL_VOLATILE);
+ return TCL_OK;
+}
+
+
+static int
+CountTabs(setPtr)
+ Tabset *setPtr;
+{
+ int count;
+ int width, height;
+ Blt_ChainLink *linkPtr;
+ register Tab *tabPtr;
+ register int pageWidth, pageHeight;
+ int labelWidth, labelHeight;
+ int tabWidth, tabHeight;
+
+ pageWidth = pageHeight = 0;
+ count = 0;
+
+ labelWidth = labelHeight = 0;
+
+ /*
+ * Pass 1: Figure out the maximum area needed for a label and a
+ * page. Both the label and page dimensions are adjusted
+ * for orientation. In addition, reset the visibility
+ * flags and reorder the tabs.
+ */
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+
+ /* Reset visibility flag and order of tabs. */
+
+ tabPtr->flags &= ~TAB_VISIBLE;
+ count++;
+
+ if (tabPtr->tkwin != NULL) {
+ width = GetReqWidth(tabPtr);
+ if (pageWidth < width) {
+ pageWidth = width;
+ }
+ height = GetReqHeight(tabPtr);
+ if (pageHeight < height) {
+ pageHeight = height;
+ }
+ }
+ if (labelWidth < tabPtr->labelWidth) {
+ labelWidth = tabPtr->labelWidth;
+ }
+ if (labelHeight < tabPtr->labelHeight) {
+ labelHeight = tabPtr->labelHeight;
+ }
+ }
+
+ setPtr->overlap = 0;
+
+ /*
+ * Pass 2: Set the individual sizes of each tab. This is different
+ * for constant and variable width tabs. Add the extra space
+ * needed for slanted tabs, now that we know maximum tab
+ * height.
+ */
+ if (setPtr->defTabStyle.constWidth) {
+ int slant;
+
+ tabWidth = 2 * setPtr->inset2;
+ tabHeight = setPtr->inset2 /* + 4 */;
+
+ if (setPtr->side & SIDE_VERTICAL) {
+ tabWidth += labelHeight;
+ tabHeight += labelWidth;
+ slant = labelWidth;
+ } else {
+ tabWidth += labelWidth;
+ tabHeight += labelHeight;
+ slant = labelHeight;
+ }
+ if (setPtr->slant & SLANT_LEFT) {
+ tabWidth += slant;
+ setPtr->overlap += tabHeight / 2;
+ }
+ if (setPtr->slant & SLANT_RIGHT) {
+ tabWidth += slant;
+ setPtr->overlap += tabHeight / 2;
+ }
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->worldWidth = tabWidth;
+ tabPtr->worldHeight = tabHeight;
+ }
+ } else {
+ int slant;
+
+ tabWidth = tabHeight = 0;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+
+ width = 2 * setPtr->inset2;
+ height = setPtr->inset2 /* + 4 */;
+ if (setPtr->side & SIDE_VERTICAL) {
+ width += tabPtr->labelHeight;
+ height += labelWidth;
+ slant = labelWidth;
+ } else {
+ width += tabPtr->labelWidth;
+ height += labelHeight;
+ slant = labelHeight;
+ }
+ width += (setPtr->slant & SLANT_LEFT) ? slant : setPtr->corner;
+ width += (setPtr->slant & SLANT_RIGHT) ? slant : setPtr->corner;
+
+ tabPtr->worldWidth = width; /* + 2 * (setPtr->corner + setPtr->xSelectPad) */ ;
+ tabPtr->worldHeight = height;
+
+ if (tabWidth < width) {
+ tabWidth = width;
+ }
+ if (tabHeight < height) {
+ tabHeight = height;
+ }
+ }
+ if (setPtr->slant & SLANT_LEFT) {
+ setPtr->overlap += tabHeight / 2;
+ }
+ if (setPtr->slant & SLANT_RIGHT) {
+ setPtr->overlap += tabHeight / 2;
+ }
+ }
+
+ setPtr->tabWidth = tabWidth;
+ setPtr->tabHeight = tabHeight;
+
+ /*
+ * Let the user override any page dimension.
+ */
+ setPtr->pageWidth = pageWidth;
+ setPtr->pageHeight = pageHeight;
+ if (setPtr->reqPageWidth > 0) {
+ setPtr->pageWidth = setPtr->reqPageWidth;
+ }
+ if (setPtr->reqPageHeight > 0) {
+ setPtr->pageHeight = setPtr->reqPageHeight;
+ }
+ return count;
+}
+
+
+static void
+WidenTabs(setPtr, startPtr, nTabs, adjustment)
+ Tabset *setPtr;
+ Tab *startPtr;
+ int nTabs;
+ int adjustment;
+{
+ register Tab *tabPtr;
+ register int i;
+ int ration;
+ Blt_ChainLink *linkPtr;
+ int x;
+
+ x = startPtr->tier;
+ while (adjustment > 0) {
+ ration = adjustment / nTabs;
+ if (ration == 0) {
+ ration = 1;
+ }
+ linkPtr = startPtr->linkPtr;
+ for (i = 0; (linkPtr != NULL) && (i < nTabs) && (adjustment > 0); i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ adjustment -= ration;
+ tabPtr->worldWidth += ration;
+ assert(x == tabPtr->tier);
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+ }
+ /*
+ * Go back and reset the world X-coordinates of the tabs,
+ * now that their widths have changed.
+ */
+ x = 0;
+ linkPtr = startPtr->linkPtr;
+ for (i = 0; (i < nTabs) && (linkPtr != NULL); i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ }
+}
+
+
+static void
+AdjustTabSizes(setPtr, nTabs)
+ Tabset *setPtr;
+ int nTabs;
+{
+ int tabsPerTier;
+ int total, count, extra;
+ Tab *startPtr, *nextPtr;
+ Blt_ChainLink *linkPtr;
+ register Tab *tabPtr;
+ int x, maxWidth;
+
+ tabsPerTier = (nTabs + (setPtr->nTiers - 1)) / setPtr->nTiers;
+ x = 0;
+ maxWidth = 0;
+ if (setPtr->defTabStyle.constWidth) {
+ register int i;
+
+ linkPtr = Blt_ChainFirstLink(setPtr->chainPtr);
+ count = 1;
+ while (linkPtr != NULL) {
+ for (i = 0; i < tabsPerTier; i++) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = count;
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ if (x > maxWidth) {
+ maxWidth = x;
+ }
+ if (linkPtr == NULL) {
+ goto done;
+ }
+ }
+ count++;
+ x = 0;
+ }
+ }
+ done:
+ /* Add to tab widths to fill out row. */
+ if (((nTabs % tabsPerTier) != 0) && (setPtr->defTabStyle.constWidth)) {
+ return;
+ }
+ startPtr = NULL;
+ count = total = 0;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ /*empty*/ ) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if (startPtr == NULL) {
+ startPtr = tabPtr;
+ }
+ count++;
+ total += tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ linkPtr = Blt_ChainNextLink(linkPtr);
+ if (linkPtr != NULL) {
+ nextPtr = Blt_ChainGetValue(linkPtr);
+ if (tabPtr->tier == nextPtr->tier) {
+ continue;
+ }
+ }
+ total += setPtr->overlap;
+ extra = setPtr->worldWidth - total;
+ assert(count > 0);
+ if (extra > 0) {
+ WidenTabs(setPtr, startPtr, count, extra);
+ }
+ count = total = 0;
+ startPtr = NULL;
+ }
+}
+
+/*
+ *
+ * tabWidth = textWidth + gap + (2 * (pad + outerBW));
+ *
+ * tabHeight = textHeight + 2 * (pad + outerBW) + topMargin;
+ *
+ */
+static void
+ComputeLayout(setPtr)
+ Tabset *setPtr;
+{
+ int width, height;
+ Blt_ChainLink *linkPtr;
+ Tab *tabPtr;
+ int x, extra;
+ int nTiers, nTabs;
+
+ setPtr->nTiers = 0;
+ setPtr->pageTop = 0;
+ setPtr->worldWidth = 1;
+ setPtr->yPad = 0;
+
+ nTiers = 0;
+ nTabs = CountTabs(setPtr);
+ if (nTabs == 0) {
+ return;
+ }
+ /* Reset the pointers to the selected and starting tab. */
+ if (setPtr->selectPtr == NULL) {
+ linkPtr = Blt_ChainFirstLink(setPtr->chainPtr);
+ if (linkPtr != NULL) {
+ setPtr->selectPtr = Blt_ChainGetValue(linkPtr);
+ }
+ }
+ if (setPtr->startPtr == NULL) {
+ setPtr->startPtr = setPtr->selectPtr;
+ }
+ if (setPtr->focusPtr == NULL) {
+ setPtr->focusPtr = setPtr->selectPtr;
+ Blt_SetFocusItem(setPtr->bindTable, setPtr->focusPtr);
+ }
+ width = Tk_Width(setPtr->tkwin) - (2 * setPtr->inset) -
+ setPtr->xSelectPad - setPtr->corner;
+ height = Tk_Height(setPtr->tkwin) - 2 *
+ (setPtr->corner + setPtr->xSelectPad);
+
+ if (setPtr->side & SIDE_VERTICAL) {
+ int temp;
+
+ temp = width, width = height, height = temp;
+ }
+ setPtr->flags |= TABSET_STATIC;
+ if (setPtr->reqTiers > 1) {
+ int total, maxWidth;
+
+ /* Static multiple tier mode. */
+
+ /* Sum tab widths and determine the number of tiers needed. */
+ nTiers = 1;
+ total = x = 0;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((x + tabPtr->worldWidth) > width) {
+ nTiers++;
+ x = 0;
+ }
+ tabPtr->worldX = x;
+ tabPtr->tier = nTiers;
+ extra = tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ total += extra, x += extra;
+ }
+ maxWidth = width;
+
+ if (nTiers > setPtr->reqTiers) {
+ /*
+ * The tabs do not fit into the requested number of tiers.
+ * Go into scrolling mode.
+ */
+ width = ((total + setPtr->tabWidth) / setPtr->reqTiers);
+ x = 0;
+ nTiers = 1;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = nTiers;
+ /*
+ * Keep adding tabs to a tier until we overfill it.
+ */
+ tabPtr->worldX = x;
+ x += tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ if (x > width) {
+ nTiers++;
+ if (x > maxWidth) {
+ maxWidth = x;
+ }
+ x = 0;
+ }
+ }
+ setPtr->flags &= ~TABSET_STATIC;
+ }
+ setPtr->worldWidth = maxWidth;
+ setPtr->nTiers = nTiers;
+
+ if (nTiers > 1) {
+ AdjustTabSizes(setPtr, nTabs);
+ }
+ if (setPtr->flags & TABSET_STATIC) {
+ setPtr->worldWidth = VPORTWIDTH(setPtr);
+ } else {
+ /* Do you add an offset ? */
+ setPtr->worldWidth += (setPtr->xSelectPad + setPtr->corner);
+ }
+ setPtr->worldWidth += setPtr->overlap;
+ if (setPtr->selectPtr != NULL) {
+ RenumberTiers(setPtr, setPtr->selectPtr);
+ }
+ } else {
+ /*
+ * Scrollable single tier mode.
+ */
+ nTiers = 1;
+ x = 0;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->tier = nTiers;
+ tabPtr->worldX = x;
+ tabPtr->worldY = 0;
+ x += tabPtr->worldWidth + setPtr->gap - setPtr->overlap;
+ }
+ setPtr->worldWidth = x + setPtr->corner - setPtr->gap +
+ setPtr->xSelectPad + setPtr->overlap;
+ setPtr->flags &= ~TABSET_STATIC;
+ }
+ if (nTiers == 1) {
+ setPtr->yPad = setPtr->ySelectPad;
+ }
+ setPtr->nTiers = nTiers;
+ setPtr->pageTop = setPtr->inset + setPtr->yPad /* + 4 */ +
+ (setPtr->nTiers * setPtr->tabHeight) + setPtr->inset2;
+
+ if (setPtr->side & SIDE_VERTICAL) {
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenWidth = (short int)setPtr->tabHeight;
+ tabPtr->screenHeight = (short int)tabPtr->worldWidth;
+ }
+ } else {
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenWidth = (short int)tabPtr->worldWidth;
+ tabPtr->screenHeight = (short int)setPtr->tabHeight;
+ }
+ }
+}
+
+static void
+ComputeVisibleTabs(setPtr)
+ Tabset *setPtr;
+{
+ int nVisibleTabs;
+ register Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ setPtr->nVisible = 0;
+ if (Blt_ChainGetLength(setPtr->chainPtr) == 0) {
+ return;
+ }
+ nVisibleTabs = 0;
+ if (setPtr->flags & TABSET_STATIC) {
+
+ /* Static multiple tier mode. */
+
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->flags |= TAB_VISIBLE;
+ nVisibleTabs++;
+ }
+ } else {
+ int width, offset;
+ /*
+ * Scrollable (single or multiple) tier mode.
+ */
+ offset = setPtr->scrollOffset - (setPtr->outerPad + setPtr->xSelectPad);
+ width = VPORTWIDTH(setPtr) + setPtr->scrollOffset +
+ 2 * setPtr->outerPad;
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((tabPtr->worldX >= width) ||
+ ((tabPtr->worldX + tabPtr->worldWidth) < offset)) {
+ tabPtr->flags &= ~TAB_VISIBLE;
+ } else {
+ tabPtr->flags |= TAB_VISIBLE;
+ nVisibleTabs++;
+ }
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(setPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ tabPtr->screenX = tabPtr->screenY = -1000;
+ if (tabPtr->flags & TAB_VISIBLE) {
+ WorldToScreen(setPtr, tabPtr->worldX, tabPtr->worldY,
+ &(tabPtr->screenX), &(tabPtr->screenY));
+ switch (setPtr->side) {
+ case SIDE_RIGHT:
+ tabPtr->screenX -= setPtr->tabHeight;
+ break;
+
+ case SIDE_BOTTOM:
+ tabPtr->screenY -= setPtr->tabHeight;
+ break;
+ }
+ }
+ }
+ setPtr->nVisible = nVisibleTabs;
+ Blt_PickCurrentItem(setPtr->bindTable);
+}
+
+
+static void
+Draw3DFolder(setPtr, tabPtr, drawable, side, pointArr, nPoints)
+ Tabset *setPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+ int side;
+ XPoint pointArr[];
+ int nPoints;
+{
+ GC gc;
+ int relief, borderWidth;
+ Tk_3DBorder border;
+
+ if (tabPtr == setPtr->selectPtr) {
+ border = GETATTR(tabPtr, selBorder);
+ } else {
+ border = tabPtr->border;
+ if (border == NULL) {
+ border = setPtr->defTabStyle.border;
+ }
+ }
+ relief = setPtr->defTabStyle.relief;
+ if ((side == SIDE_RIGHT) || (side == SIDE_TOP)) {
+ borderWidth = -setPtr->defTabStyle.borderWidth;
+ if (relief == TK_RELIEF_SUNKEN) {
+ relief = TK_RELIEF_RAISED;
+ } else if (relief == TK_RELIEF_RAISED) {
+ relief = TK_RELIEF_SUNKEN;
+ }
+ } else {
+ borderWidth = setPtr->defTabStyle.borderWidth;
+ }
+ /* Draw the outline of the folder. */
+ gc = Tk_GCForColor(setPtr->shadowColor, drawable);
+ XDrawLines(setPtr->display, drawable, gc, pointArr, nPoints, CoordModeOrigin);
+ /* And the folder itself. */
+ if (tabPtr->tile != NULL) {
+ Blt_TilePolygon(setPtr->tkwin, drawable, tabPtr->tile, pointArr,
+ nPoints);
+ Tk_Draw3DPolygon(setPtr->tkwin, drawable, border, pointArr, nPoints,
+ borderWidth, relief);
+ } else {
+ Tk_Fill3DPolygon(setPtr->tkwin, drawable, border, pointArr, nPoints,
+ borderWidth, relief);
+ }
+}
+
+/*
+ * x,y
+ * |1|2|3| 4 |3|2|1|
+ *
+ * 1. tab border width
+ * 2. corner offset
+ * 3. label pad
+ * 4. label width
+ *
+ *
+ */
+static void
+DrawLabel(setPtr, tabPtr, drawable)
+ Tabset *setPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ int x, y, dx, dy;
+ int tx, ty, ix, iy;
+ int imgWidth, imgHeight;
+ int active, selected;
+ XColor *fgColor, *bgColor;
+ Tk_3DBorder border;
+ GC gc;
+
+ if (!(tabPtr->flags & TAB_VISIBLE)) {
+ return;
+ }
+ x = tabPtr->screenX;
+ y = tabPtr->screenY;
+
+ active = (setPtr->activePtr == tabPtr);
+ selected = (setPtr->selectPtr == tabPtr);
+
+ fgColor = GETATTR(tabPtr, textColor);
+ border = GETATTR(tabPtr, border);
+ if (selected) {
+ border = GETATTR(tabPtr, selBorder);
+ }
+ bgColor = Tk_3DBorderColor(border);
+ if (active) {
+ Tk_3DBorder activeBorder;
+
+ activeBorder = GETATTR(tabPtr, activeBorder);
+ bgColor = Tk_3DBorderColor(activeBorder);
+ }
+ dx = (tabPtr->screenWidth - tabPtr->labelWidth) / 2;
+ dy = (tabPtr->screenHeight - tabPtr->labelHeight) / 2;
+
+
+ /*
+ * The label position is computed with screen coordinates. This
+ * is because both text and image components are oriented in
+ * screen coordinate space, and not according to the orientation
+ * of the tabs themselves. That's why we have to consider the
+ * side when correcting for left/right slants.
+ */
+ switch (setPtr->side) {
+ case SIDE_TOP:
+ case SIDE_BOTTOM:
+ if (setPtr->slant == SLANT_LEFT) {
+ x += setPtr->overlap;
+ } else if (setPtr->slant == SLANT_RIGHT) {
+ x -= setPtr->overlap;
+ }
+ break;
+ case SIDE_LEFT:
+ case SIDE_RIGHT:
+ if (setPtr->slant == SLANT_LEFT) {
+ y += setPtr->overlap;
+ } else if (setPtr->slant == SLANT_RIGHT) {
+ y -= setPtr->overlap;
+ }
+ break;
+ }
+
+ /*
+ * Draw the active or normal background color over the entire
+ * label area. This includes both the tab's text and image.
+ * The rectangle should be 2 pixels wider/taller than this
+ * area. So if the label consists of just an image, we get an
+ * halo around the image when the tab is active.
+ */
+ gc = Tk_GCForColor(bgColor, drawable);
+ XFillRectangle(setPtr->display, drawable, gc, x + dx, y + dy,
+ tabPtr->labelWidth, tabPtr->labelHeight);
+ if ((setPtr->flags & TABSET_FOCUS) && (setPtr->focusPtr == tabPtr)) {
+ XDrawRectangle(setPtr->display, drawable, setPtr->defTabStyle.activeGC,
+ x + dx, y + dy, tabPtr->labelWidth - 1, tabPtr->labelHeight - 1);
+ }
+ tx = ty = ix = iy = 0; /* Suppress compiler warning. */
+
+ imgWidth = imgHeight = 0;
+ if (tabPtr->image != NULL) {
+ imgWidth = ImageWidth(tabPtr->image);
+ imgHeight = ImageHeight(tabPtr->image);
+ }
+ switch (setPtr->defTabStyle.textSide) {
+ case SIDE_LEFT:
+ tx = x + dx + tabPtr->iPadX.side1;
+ ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+ ix = tx + tabPtr->textWidth + IMAGE_PAD;
+ iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+ break;
+ case SIDE_RIGHT:
+ ix = x + dx + tabPtr->iPadX.side1 + IMAGE_PAD;
+ iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+ tx = ix + imgWidth;
+ ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+ break;
+ case SIDE_BOTTOM:
+ iy = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+ ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+ ty = iy + imgHeight;
+ tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+ break;
+ case SIDE_TOP:
+ tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+ ty = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+ ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+ iy = ty + tabPtr->textHeight;
+ break;
+ }
+ if (tabPtr->image != NULL) {
+ Tk_RedrawImage(ImageData(tabPtr->image), 0, 0, imgWidth, imgHeight,
+ drawable, ix, iy);
+ }
+ if (tabPtr->text != NULL) {
+ TextStyle ts;
+ XColor *activeColor;
+
+ activeColor = fgColor;
+ if (selected) {
+ activeColor = GETATTR(tabPtr, selColor);
+ } else if (active) {
+ activeColor = GETATTR(tabPtr, activeFgColor);
+ }
+ Blt_SetDrawTextStyle(&ts, GETATTR(tabPtr, font), tabPtr->textGC,
+ fgColor, activeColor, tabPtr->shadow.color,
+ setPtr->defTabStyle.rotate, TK_ANCHOR_NW, TK_JUSTIFY_LEFT,
+ 0, tabPtr->shadow.offset);
+ ts.state = tabPtr->state;
+ ts.border = border;
+ ts.padX.side1 = ts.padX.side2 = 2;
+ if ((selected) || (active)) {
+ ts.state |= STATE_ACTIVE;
+ }
+ Blt_DrawText(setPtr->tkwin, drawable, tabPtr->text, &ts, tx, ty);
+ }
+}
+
+
+static void
+DrawPerforation(setPtr, tabPtr, drawable)
+ Tabset *setPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ XPoint pointArr[2];
+ int x, y;
+ int segmentWidth, max;
+ Tk_3DBorder border, perfBorder;
+
+ if ((tabPtr->container != NULL) || (tabPtr->tkwin == NULL)) {
+ return;
+ }
+ WorldToScreen(setPtr, tabPtr->worldX + 2,
+ tabPtr->worldY + tabPtr->worldHeight + 2, &x, &y);
+ border = GETATTR(tabPtr, selBorder);
+ segmentWidth = 3;
+ if (setPtr->flags & PERFORATION_ACTIVE) {
+ perfBorder = GETATTR(tabPtr, activeBorder);
+ } else {
+ perfBorder = GETATTR(tabPtr, selBorder);
+ }
+ if (setPtr->side & SIDE_HORIZONTAL) {
+ pointArr[0].x = x;
+ pointArr[0].y = pointArr[1].y = y;
+ max = tabPtr->screenX + tabPtr->screenWidth - 2;
+ Tk_Fill3DRectangle(setPtr->tkwin, drawable, perfBorder,
+ x - 2, y - 4, tabPtr->screenWidth, 8, 0, TK_RELIEF_FLAT);
+ while (pointArr[0].x < max) {
+ pointArr[1].x = pointArr[0].x + segmentWidth;
+ if (pointArr[1].x > max) {
+ pointArr[1].x = max;
+ }
+ Tk_Draw3DPolygon(setPtr->tkwin, drawable, border, pointArr, 2, 1,
+ TK_RELIEF_RAISED);
+ pointArr[0].x += 2 * segmentWidth;
+ }
+ } else {
+ pointArr[0].x = pointArr[1].x = x;
+ pointArr[0].y = y;
+ max = tabPtr->screenY + tabPtr->screenHeight - 2;
+ Tk_Fill3DRectangle(setPtr->tkwin, drawable, perfBorder,
+ x - 4, y - 2, 8, tabPtr->screenHeight, 0, TK_RELIEF_FLAT);
+ while (pointArr[0].y < max) {
+ pointArr[1].y = pointArr[0].y + segmentWidth;
+ if (pointArr[1].y > max) {
+ pointArr[1].y = max;
+ }
+ Tk_Draw3DPolygon(setPtr->tkwin, drawable, border, pointArr, 2, 1,
+ TK_RELIEF_RAISED);
+ pointArr[0].y += 2 * segmentWidth;
+ }
+ }
+}
+
+#define NextPoint(px, py) \
+ pointPtr->x = (px), pointPtr->y = (py), pointPtr++, nPoints++
+
+#define BottomLeft(px, py) \
+ NextPoint((px) + setPtr->corner, (py)), \
+ NextPoint((px), (py) - setPtr->corner)
+
+#define TopLeft(px, py) \
+ NextPoint((px), (py) + setPtr->corner), \
+ NextPoint((px) + setPtr->corner, (py))
+
+#define TopRight(px, py) \
+ NextPoint((px) - setPtr->corner, (py)), \
+ NextPoint((px), (py) + setPtr->corner)
+
+#define BottomRight(px, py) \
+ NextPoint((px), (py) - setPtr->corner), \
+ NextPoint((px) - setPtr->corner, (py))
+
+
+/*
+ * From the left edge:
+ *
+ * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
+ *
+ * a. highlight ring
+ * b. tabset 3D border
+ * c. outer gap
+ * d. page border
+ * e. page corner
+ * f. gap + select pad
+ * g. label pad x (worldX)
+ * h. internal pad x
+ * i. label width
+ * j. rest of page width
+ *
+ * worldX, worldY
+ * |
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ *
+ */
+static void
+DrawFolder(setPtr, tabPtr, drawable)
+ Tabset *setPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+{
+ XPoint pointArr[16];
+ XPoint *pointPtr;
+ int width, height;
+ int left, bottom, right, top, yBot, yTop;
+ int x, y;
+ register int i;
+ int nPoints;
+
+ width = VPORTWIDTH(setPtr);
+ height = VPORTHEIGHT(setPtr);
+
+ x = tabPtr->worldX;
+ y = tabPtr->worldY;
+
+ nPoints = 0;
+ pointPtr = pointArr;
+
+ /* Remember these are all world coordinates. */
+ /*
+ * x Left side of tab.
+ * y Top of tab.
+ * yTop Top of folder.
+ * yBot Bottom of the tab.
+ * left Left side of the folder.
+ * right Right side of the folder.
+ * top Top of folder.
+ * bottom Bottom of folder.
+ */
+ left = setPtr->scrollOffset - setPtr->xSelectPad;
+ right = left + width;
+ yTop = y + tabPtr->worldHeight;
+ yBot = setPtr->pageTop - (setPtr->inset + setPtr->yPad);
+ top = yBot - setPtr->inset2 /* - 4 */;
+
+ if (setPtr->pageHeight == 0) {
+ bottom = yBot + 2 * setPtr->corner;
+ } else {
+ bottom = height - setPtr->yPad - 1;
+ }
+ if (tabPtr != setPtr->selectPtr) {
+
+ /*
+ * Case 1: Unselected tab
+ *
+ * * 3+ . . +4
+ * 2+ +5
+ * . .
+ * 1+ +6
+ * 0+ . . +7
+ *
+ */
+
+ if (setPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + setPtr->tabHeight, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+ x += tabPtr->worldWidth;
+ if (setPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - setPtr->tabHeight, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+
+ } else if (!(tabPtr->flags & TAB_VISIBLE)) {
+
+ /*
+ * Case 2: Selected tab not visible in viewport. Draw folder only.
+ *
+ * * 3+ . . +4
+ * 2+ +5
+ * . .
+ * 1+ +6
+ * 0+------+7
+ *
+ */
+
+ TopLeft(left, top);
+ TopRight(right, top);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ } else {
+ int flags;
+ int tabWidth;
+
+ x -= setPtr->xSelectPad;
+ y -= setPtr->yPad;
+ tabWidth = tabPtr->worldWidth + 2 * setPtr->xSelectPad;
+
+#define CLIP_NONE 0
+#define CLIP_LEFT (1<<0)
+#define CLIP_RIGHT (1<<1)
+ flags = 0;
+ if (x < left) {
+ flags |= CLIP_LEFT;
+ }
+ if ((x + tabWidth) > right) {
+ flags |= CLIP_RIGHT;
+ }
+ switch (flags) {
+ case CLIP_NONE:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ */
+
+ if (x < (left + setPtr->corner)) {
+ NextPoint(left, top);
+ } else {
+ TopLeft(left, top);
+ }
+ if (setPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yTop);
+ NextPoint(x + setPtr->tabHeight + setPtr->yPad, y);
+ } else {
+ NextPoint(x, top);
+ TopLeft(x, y);
+ }
+ x += tabWidth;
+ if (setPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - setPtr->tabHeight - setPtr->yPad, y);
+ NextPoint(x, yTop);
+ } else {
+ TopRight(x, y);
+ NextPoint(x, top);
+ }
+ if (x > (right - setPtr->corner)) {
+ NextPoint(right, top + setPtr->corner);
+ } else {
+ TopRight(right, top);
+ }
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+
+ case CLIP_LEFT:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 2+ +7 . . . .+8
+ * 1+ . . . +0 +9
+ * . .
+ * . .
+ * 13+ +10
+ * 12+--------+11
+ */
+
+ NextPoint(left, yBot);
+ if (setPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + setPtr->tabHeight + setPtr->yPad, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+
+ x += tabWidth;
+ if (setPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - setPtr->tabHeight - setPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, top);
+ } else {
+ TopRight(x, y);
+ NextPoint(x, top);
+ }
+ if (x > (right - setPtr->corner)) {
+ NextPoint(right, top + setPtr->corner);
+ } else {
+ TopRight(right, top);
+ }
+ /* TopRight(right, top); */
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+
+ case CLIP_RIGHT:
+
+ /*
+ * worldX, worldY
+ * |
+ * * 9+ . . +10
+ * 8+ +11
+ * . .
+ * . .
+ * 6+ . . . .7+ +12
+ * 5+ 0+ . . . +13
+ * . .
+ * . .
+ * 4+ +1
+ * 3+-------+2
+ */
+
+ NextPoint(right, yBot);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ /* TopLeft(left, top); */
+ if (x < (left + setPtr->corner)) {
+ NextPoint(left, top);
+ } else {
+ TopLeft(left, top);
+ }
+ NextPoint(x, top);
+
+ if (setPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yTop);
+ NextPoint(x + setPtr->tabHeight + setPtr->yPad, y);
+ } else {
+ TopLeft(x, y);
+ }
+ x += tabWidth;
+ if (setPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - setPtr->tabHeight - setPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+ break;
+
+ case (CLIP_LEFT | CLIP_RIGHT):
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . . . . . . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+ +7
+ * 2+ 0+ +9 .+8
+ * . .
+ * . .
+ * 13+ +10
+ * 12+-------+11
+ */
+
+ NextPoint(left, yBot);
+ if (setPtr->slant & SLANT_LEFT) {
+ NextPoint(x, yBot);
+ NextPoint(x, yTop);
+ NextPoint(x + setPtr->tabHeight + setPtr->yPad, y);
+ } else {
+ BottomLeft(x, yBot);
+ TopLeft(x, y);
+ }
+ x += tabPtr->worldWidth;
+ if (setPtr->slant & SLANT_RIGHT) {
+ NextPoint(x - setPtr->tabHeight - setPtr->yPad, y);
+ NextPoint(x, yTop);
+ NextPoint(x, yBot);
+ } else {
+ TopRight(x, y);
+ BottomRight(x, yBot);
+ }
+ NextPoint(right, yBot);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ break;
+ }
+ }
+ NextPoint(pointArr[0].x, pointArr[0].y);
+ for (i = 0; i < nPoints; i++) {
+ WorldToScreen(setPtr, pointArr[i].x, pointArr[i].y, &x, &y);
+ pointArr[i].x = x;
+ pointArr[i].y = y;
+ }
+ Draw3DFolder(setPtr, tabPtr, drawable, setPtr->side, pointArr, nPoints);
+ DrawLabel(setPtr, tabPtr, drawable);
+ if (tabPtr->container != NULL) {
+ XRectangle rect;
+
+ /* Draw a rectangle covering the spot representing the window */
+ GetWindowRectangle(tabPtr, setPtr->tkwin, FALSE, &rect);
+ XFillRectangles(setPtr->display, drawable, tabPtr->backGC,
+ &rect, 1);
+ }
+}
+
+static void
+DrawOuterBorders(setPtr, drawable)
+ Tabset *setPtr;
+ Drawable drawable;
+{
+ /*
+ * Draw 3D border just inside of the focus highlight ring. We
+ * draw the border even if the relief is flat so that any tabs
+ * that hang over the edge will be clipped.
+ */
+ if (setPtr->borderWidth > 0) {
+ Tk_Draw3DRectangle(setPtr->tkwin, drawable, setPtr->border,
+ setPtr->highlightWidth, setPtr->highlightWidth,
+ Tk_Width(setPtr->tkwin) - 2 * setPtr->highlightWidth,
+ Tk_Height(setPtr->tkwin) - 2 * setPtr->highlightWidth,
+ setPtr->borderWidth, setPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if (setPtr->highlightWidth > 0) {
+ XColor *color;
+ GC gc;
+
+ color = (setPtr->flags & TABSET_FOCUS)
+ ? setPtr->highlightColor : setPtr->highlightBgColor;
+ gc = Tk_GCForColor(color, drawable);
+ Tk_DrawFocusHighlight(setPtr->tkwin, gc, setPtr->highlightWidth,
+ drawable);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayTabset --
+ *
+ * This procedure is invoked to display the widget.
+ *
+ * Recomputes the layout of the widget if necessary. This is
+ * necessary if the world coordinate system has changed.
+ * Sets the vertical and horizontal scrollbars. This is done
+ * here since the window width and height are needed for the
+ * scrollbar calculations.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayTabset(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Tabset *setPtr = clientData;
+ Pixmap drawable;
+ int width, height;
+
+ setPtr->flags &= ~TABSET_REDRAW;
+ if (setPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (setPtr->flags & TABSET_LAYOUT) {
+ ComputeLayout(setPtr);
+ setPtr->flags &= ~TABSET_LAYOUT;
+ }
+ if ((setPtr->reqHeight == 0) || (setPtr->reqWidth == 0)) {
+ width = height = 0;
+ if (setPtr->side & SIDE_VERTICAL) {
+ height = setPtr->worldWidth;
+ } else {
+ width = setPtr->worldWidth;
+ }
+ if (setPtr->reqWidth > 0) {
+ width = setPtr->reqWidth;
+ } else if (setPtr->pageWidth > 0) {
+ width = setPtr->pageWidth;
+ }
+ if (setPtr->reqHeight > 0) {
+ height = setPtr->reqHeight;
+ } else if (setPtr->pageHeight > 0) {
+ height = setPtr->pageHeight;
+ }
+ if (setPtr->side & SIDE_VERTICAL) {
+ width += setPtr->pageTop + setPtr->inset + setPtr->inset2;
+ height += (setPtr->inset + setPtr->inset2);
+ } else {
+ height += setPtr->pageTop + setPtr->inset + setPtr->inset2;
+ width += (setPtr->inset + setPtr->inset2);
+ }
+ if ((Tk_ReqWidth(setPtr->tkwin) != width) ||
+ (Tk_ReqHeight(setPtr->tkwin) != height)) {
+ Tk_GeometryRequest(setPtr->tkwin, width, height);
+ }
+ }
+ if (setPtr->flags & TABSET_SCROLL) {
+ width = VPORTWIDTH(setPtr);
+ setPtr->scrollOffset = Blt_AdjustViewport(setPtr->scrollOffset,
+ setPtr->worldWidth, width, setPtr->scrollUnits,
+ BLT_SCROLL_MODE_CANVAS);
+ if (setPtr->scrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(setPtr->interp, setPtr->scrollCmdPrefix,
+ (double)setPtr->scrollOffset / setPtr->worldWidth,
+ (double)(setPtr->scrollOffset + width) / setPtr->worldWidth);
+ }
+ ComputeVisibleTabs(setPtr);
+ setPtr->flags &= ~TABSET_SCROLL;
+ }
+ if (!Tk_IsMapped(setPtr->tkwin)) {
+ return;
+ }
+ height = Tk_Height(setPtr->tkwin);
+ drawable = Tk_GetPixmap(setPtr->display, Tk_WindowId(setPtr->tkwin),
+ Tk_Width(setPtr->tkwin), Tk_Height(setPtr->tkwin),
+ Tk_Depth(setPtr->tkwin));
+
+ /*
+ * Clear the background either by tiling a pixmap or filling with
+ * a solid color. Tiling takes precedence.
+ */
+ if (setPtr->tile != NULL) {
+ Blt_SetTileOrigin(setPtr->tkwin, setPtr->tile, 0, 0);
+ Blt_TileRectangle(setPtr->tkwin, drawable, setPtr->tile, 0, 0,
+ Tk_Width(setPtr->tkwin), height);
+ } else {
+ Tk_Fill3DRectangle(setPtr->tkwin, drawable, setPtr->border, 0, 0,
+ Tk_Width(setPtr->tkwin), height, 0, TK_RELIEF_FLAT);
+ }
+
+ if (setPtr->nVisible > 0) {
+ register int i;
+ register Tab *tabPtr;
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = setPtr->startPtr->linkPtr;
+ for (i = 0; i < Blt_ChainGetLength(setPtr->chainPtr); i++) {
+ linkPtr = Blt_ChainPrevLink(linkPtr);
+ if (linkPtr == NULL) {
+ linkPtr = Blt_ChainLastLink(setPtr->chainPtr);
+ }
+ tabPtr = Blt_ChainGetValue(linkPtr);
+ if ((tabPtr != setPtr->selectPtr) &&
+ (tabPtr->flags & TAB_VISIBLE)) {
+ DrawFolder(setPtr, tabPtr, drawable);
+ }
+ }
+ DrawFolder(setPtr, setPtr->selectPtr, drawable);
+ if (setPtr->tearoff) {
+ DrawPerforation(setPtr, setPtr->selectPtr, drawable);
+ }
+
+ if ((setPtr->selectPtr->tkwin != NULL) &&
+ (setPtr->selectPtr->container == NULL)) {
+ XRectangle rect;
+
+ GetWindowRectangle(setPtr->selectPtr, setPtr->tkwin, FALSE, &rect);
+ ArrangeWindow(setPtr->selectPtr->tkwin, &rect, 0);
+ }
+ }
+ DrawOuterBorders(setPtr, drawable);
+ XCopyArea(setPtr->display, drawable, Tk_WindowId(setPtr->tkwin),
+ setPtr->highlightGC, 0, 0, Tk_Width(setPtr->tkwin), height, 0, 0);
+ Tk_FreePixmap(setPtr->display, drawable);
+}
+
+/*
+ * From the left edge:
+ *
+ * |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f| j |e|d|c|b|a|
+ *
+ * a. highlight ring
+ * b. tabset 3D border
+ * c. outer gap
+ * d. page border
+ * e. page corner
+ * f. gap + select pad
+ * g. label pad x (worldX)
+ * h. internal pad x
+ * i. label width
+ * j. rest of page width
+ *
+ * worldX, worldY
+ * |
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ *
+ */
+static void
+DisplayTearoff(clientData)
+ ClientData clientData;
+{
+ Tabset *setPtr;
+ Tab *tabPtr;
+ Drawable drawable;
+ XPoint pointArr[16];
+ XPoint *pointPtr;
+ int width, height;
+ int left, bottom, right, top;
+ int x, y;
+ int nPoints;
+ Tk_Window tkwin;
+ Tk_Window parent;
+ XRectangle rect;
+
+ tabPtr = clientData;
+ if (tabPtr == NULL) {
+ return;
+ }
+ tabPtr->flags &= ~TAB_REDRAW;
+ setPtr = tabPtr->setPtr;
+ if (setPtr->tkwin == NULL) {
+ return;
+ }
+ tkwin = tabPtr->container;
+ drawable = Tk_WindowId(tkwin);
+
+ /*
+ * Clear the background either by tiling a pixmap or filling with
+ * a solid color. Tiling takes precedence.
+ */
+ if (setPtr->tile != NULL) {
+ Blt_SetTileOrigin(tkwin, setPtr->tile, 0, 0);
+ Blt_TileRectangle(tkwin, drawable, setPtr->tile, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin));
+ } else {
+ Tk_Fill3DRectangle(tkwin, drawable, setPtr->border, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
+ }
+
+ width = Tk_Width(tkwin) - 2 * setPtr->inset;
+ height = Tk_Height(tkwin) - 2 * setPtr->inset;
+ x = setPtr->inset + setPtr->gap + setPtr->corner;
+ y = setPtr->inset;
+
+ left = setPtr->inset;
+ right = setPtr->inset + width;
+ top = setPtr->inset + setPtr->corner + setPtr->xSelectPad;
+ bottom = setPtr->inset + height;
+
+ /*
+ * worldX, worldY
+ * |
+ * * 4+ . . +5
+ * 3+ +6
+ * . .
+ * . .
+ * 1+. . .2+ +7 . . . .+8
+ * 0+ +9
+ * . .
+ * . .
+ *13+ +10
+ * 12+-------------------------+11
+ */
+
+ nPoints = 0;
+ pointPtr = pointArr;
+
+ TopLeft(left, top);
+ NextPoint(x, top);
+ TopLeft(x, y);
+ x += tabPtr->worldWidth;
+ TopRight(x, y);
+ NextPoint(x, top);
+ TopRight(right, top);
+ BottomRight(right, bottom);
+ BottomLeft(left, bottom);
+ NextPoint(pointArr[0].x, pointArr[0].y);
+ Draw3DFolder(setPtr, tabPtr, drawable, SIDE_TOP, pointArr, nPoints);
+
+ parent = (tabPtr->container == NULL) ? setPtr->tkwin : tabPtr->container;
+ GetWindowRectangle(tabPtr, parent, TRUE, &rect);
+ ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+
+ /* Draw 3D border. */
+ if ((setPtr->borderWidth > 0) && (setPtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(tkwin, drawable, setPtr->border, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), setPtr->borderWidth,
+ setPtr->relief);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TabsetCmd --
+ *
+ * This procedure is invoked to process the "tabset" command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec tabsetOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+ {"bind", 1, (Blt_Op)BindOp, 2, 5, "index ?sequence command?",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+ {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "first ?last?",},
+ {"focus", 1, (Blt_Op)FocusOp, 3, 3, "index",},
+ {"get", 1, (Blt_Op)GetOp, 3, 3, "index",},
+ {"highlight", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+ {"index", 3, (Blt_Op)IndexOp, 3, 5, "string",},
+ {"insert", 3, (Blt_Op)InsertOp, 3, 0,
+ "index name ?name...? ?option value?",},
+ {"invoke", 3, (Blt_Op)InvokeOp, 3, 3, "index",},
+ {"move", 1, (Blt_Op)MoveOp, 5, 5, "name after|before index",},
+ {"nearest", 1, (Blt_Op)NearestOp, 4, 4, "x y",},
+ {"perforation", 1, (Blt_Op)PerforationOp, 2, 0, "args",},
+ {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+ {"see", 3, (Blt_Op)SeeOp, 3, 3, "index",},
+ {"select", 3, (Blt_Op)SelectOp, 3, 3, "index",},
+ {"size", 2, (Blt_Op)SizeOp, 2, 2, "",},
+ {"tab", 1, (Blt_Op)TabOp, 2, 0, "oper args",},
+ {"view", 1, (Blt_Op)ViewOp, 2, 5,
+ "?moveto fract? ?scroll number what?",},
+};
+
+static int nTabsetOps = sizeof(tabsetOps) / sizeof(Blt_OpSpec);
+
+static int
+TabsetInstCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ Tabset *setPtr = clientData;
+ int result;
+
+ proc = Blt_GetOp(interp, nTabsetOps, tabsetOps, BLT_OP_ARG1,
+ argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(setPtr);
+ result = (*proc) (setPtr, interp, argc, argv);
+ Tcl_Release(setPtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabsetInstDeletedCmd --
+ *
+ * This procedure can be called if the window was destroyed
+ * (tkwin will be NULL) and the command was deleted
+ * automatically. In this case, we need to do nothing.
+ *
+ * Otherwise this routine was called because the command was
+ * deleted. Then we need to clean-up and destroy the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TabsetInstDeletedCmd(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Tabset *setPtr = clientData;
+
+ if (setPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = setPtr->tkwin;
+ setPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TabsetCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * See the user documentation.
+ *
+ * -----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+TabsetCmd(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. */
+{
+ Tabset *setPtr;
+ Tk_Window tkwin;
+ unsigned int mask;
+ Tcl_CmdInfo cmdInfo;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ setPtr = CreateTabset(interp, tkwin);
+ if (ConfigureTabset(interp, setPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ Tk_DestroyWindow(setPtr->tkwin);
+ return TCL_ERROR;
+ }
+ mask = (ExposureMask | StructureNotifyMask | FocusChangeMask);
+ Tk_CreateEventHandler(tkwin, mask, TabsetEventProc, setPtr);
+ setPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], TabsetInstCmd,
+ setPtr, TabsetInstDeletedCmd);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(setPtr->tkwin, setPtr->cmdToken);
+#endif
+
+ /*
+ * Try to invoke a procedure to initialize various bindings on
+ * tabs. Source the file containing the procedure now if the
+ * procedure isn't currently defined. We deferred this to now so
+ * that the user could set the variable "blt_library" within the
+ * script.
+ */
+ if (!Tcl_GetCommandInfo(interp, "blt::TabsetInit", &cmdInfo)) {
+ static char initCmd[] = "source [file join $blt_library tabset.tcl]";
+
+ if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) {
+ char info[200];
+
+ sprintf(info, "\n (while loading bindings for %s)", argv[0]);
+ Tcl_AddErrorInfo(interp, info);
+ Tk_DestroyWindow(setPtr->tkwin);
+ return TCL_ERROR;
+ }
+ }
+ if (Tcl_VarEval(interp, "blt::TabsetInit ", argv[1], (char *)NULL)
+ != TCL_OK) {
+ Tk_DestroyWindow(setPtr->tkwin);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(setPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+int
+Blt_TabsetInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "tabset", TabsetCmd,
+ };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_TABSET */
diff --git a/blt/src/bltTed.c b/blt/src/bltTed.c
new file mode 100644
index 00000000000..41470d351e2
--- /dev/null
+++ b/blt/src/bltTed.c
@@ -0,0 +1,1868 @@
+/*
+ * bltTed.c --
+ *
+ * This module implements an editor for the table geometry
+ * manager in the BLT toolkit.
+ *
+ * Copyright 1995 by AT&T Bell Laboratories.
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the
+ * names of AT&T Bell Laboratories any of their entities not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * AT&T disclaims all warranties with regard to this software, including
+ * all implied warranties of merchantability and fitness. In no event
+ * shall AT&T be liable for any special, indirect or consequential
+ * damages or any damages whatsoever resulting from loss of use, data
+ * or profits, whether in an action of contract, negligence or other
+ * tortuous action, arising out of or in connection with the use or
+ * performance of this software.
+ *
+ * Table editor was created by George Howlett.
+ */
+
+#include "bltInt.h"
+
+#include "bltTable.h"
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltDashesOption;
+
+typedef struct TedStruct Ted;
+
+#define TABLE_THREAD_KEY "BLT Table Data"
+
+typedef struct {
+ Blt_HashTable tableTable; /* Hash table of table structures keyed by
+ * the address of the reference Tk window */
+} TableData;
+
+
+typedef struct {
+ int flags;
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Entry window */
+ Entry *entryPtr; /* Entry it represents */
+ Table *tablePtr; /* Table where it can be found */
+ Ted *tedPtr; /* Table editor */
+ int mapped; /* Indicates if the debugging windows are
+ * mapped */
+} EntryRep;
+
+
+typedef struct {
+ Tk_Font font;
+ XColor *widgetColor;
+ XColor *cntlColor;
+ XColor *normalFg, *normalBg;
+ XColor *activeFg, *activeBg;
+
+ Tk_Cursor cursor; /* Cursor to display inside of this window */
+ Pixmap stipple;
+
+ GC drawGC; /* GC to draw grid, outlines */
+ GC fillGC; /* GC to fill entry area */
+ GC widgetFillGC; /* GC to fill widget area */
+ GC cntlGC; /* GC to fill rectangles */
+
+} EntryAttributes;
+
+typedef struct {
+ int count;
+ XRectangle *array;
+} Rectangles;
+
+struct TedStruct {
+ int gridLineWidth; /* Width of grid lines */
+ int buttonHeight; /* Height of row/column buttons */
+ int cavityPad; /* Extra padding to add to entry cavity */
+ int minSize; /* Minimum size for partitions */
+
+ EditorDrawProc *drawProc;
+ EditorDestroyProc *destroyProc;
+
+ Display *display;
+ Tk_Font font;
+ Table *tablePtr; /* Pointer to table being debugged */
+ Tcl_Interp *interp;
+ int flags;
+ Tk_Window tkwin; /* Grid window */
+ Tk_Window input; /* InputOnly window to receive events */
+ int inputIsSibling;
+
+ /* Form the grid */
+ XSegment *segArr;
+ int nSegs;
+ XRectangle *padRectArr;
+ int nPadRects;
+ XRectangle *widgetPadRectArr;
+ int nWidgetPadRects;
+
+ XRectangle *cntlRectArr;
+ int nCntlRects;
+
+ XRectangle *rectArr;
+ int nRects;
+
+ XRectangle activeRectArr[5];
+ int spanActive;
+
+ GC rectGC; /* GC to fill rectangles */
+ GC drawGC; /* GC to draw grid, outlines */
+ GC fillGC; /* GC to fill window */
+ GC spanGC; /* GC to fill spans */
+ GC padRectGC; /* GC to draw padding */
+
+ Tk_3DBorder border; /* Border to use with buttons */
+ int relief;
+ int borderWidth; /* Border width of buttons */
+ XColor *normalBg;
+ XColor *padColor;
+ XColor *gridColor;
+ XColor *buttonColor;
+ XColor *spanColor;
+
+ Pixmap padStipple;
+ Pixmap spanStipple;
+ Blt_Dashes dashes;
+ char *fileName; /* If non-NULL, indicates name of file
+ * to write final table output to */
+ int mapped; /* Indicates if the debugging windows are
+ * mapped */
+ int gripSize;
+ int doubleBuffer;
+ Tk_Cursor cursor;
+ Blt_Chain *chainPtr;
+ int nextWindowId;
+
+ EntryAttributes attributes; /* Entry attributes */
+};
+
+#define REDRAW_PENDING (1<<0) /* A DoWhenIdle handler has already
+ * been queued to redraw the window */
+#define LAYOUT_PENDING (1<<1)
+
+/*
+ *
+ *
+ * |Cavity|1|2|
+ *
+ *
+ */
+#define DEF_ENTRY_ACTIVE_BG_MONO RGB_BLACK
+#define DEF_ENTRY_ACTIVE_FG_MONO RGB_WHITE
+#define DEF_ENTRY_ACTIVE_BG_COLOR RGB_BLACK
+#define DEF_ENTRY_ACTIVE_FG_COLOR RGB_WHITE
+#define DEF_ENTRY_CURSOR (char *)NULL
+#define DEF_ENTRY_FONT "*-Courier-Bold-R-Normal-*-100-*"
+#define DEF_ENTRY_NORMAL_BG_COLOR RGB_BLUE
+#define DEF_ENTRY_NORMAL_BG_MONO RGB_BLACK
+#define DEF_ENTRY_NORMAL_FG_COLOR RGB_WHITE
+#define DEF_ENTRY_NORMAL_FG_MONO RGB_WHITE
+#define DEF_ENTRY_WIDGET_BG_COLOR RGB_GREEN
+#define DEF_ENTRY_CONTROL_BG_COLOR RGB_YELLOW
+#define DEF_ENTRY_WIDGET_BG_MONO RGB_BLACK
+#define DEF_ENTRY_STIPPLE "gray50"
+#define DEF_GRID_BG_COLOR RGB_WHITE
+#define DEF_GRID_BG_MONO RGB_WHITE
+#define DEF_GRID_CURSOR "crosshair"
+#define DEF_GRID_DASHES (char *)NULL
+#define DEF_GRID_FG_COLOR RGB_BLACK
+#define DEF_GRID_FG_MONO RGB_BLACK
+#define DEF_GRID_FONT "*-Courier-Bold-R-Normal-*-100-*"
+#define DEF_GRID_LINE_WIDTH "1"
+#define DEF_GRID_PAD_COLOR RGB_RED
+#define DEF_GRID_PAD_MONO RGB_BLACK
+#define DEF_GRID_PAD_STIPPLE "gray25"
+#define DEF_GRID_PAD_CAVITY "0"
+#define DEF_GRID_PAD_MIN "8"
+#define DEF_ROWCOL_BG_COLOR RGB_RED
+#define DEF_ROWCOL_BG_MONO RGB_BLACK
+#define DEF_ROWCOL_BORDER_COLOR RGB_RED
+#define DEF_ROWCOL_BORDER_MONO RGB_BLACK
+#define DEF_ROWCOL_BORDER_WIDTH "2"
+#define DEF_ROWCOL_HEIGHT "8"
+#define DEF_ROWCOL_RELIEF "raised"
+#define DEF_SPAN_STIPPLE "gray50"
+#define DEF_SPAN_COLOR RGB_BLACK
+#define DEF_SPAN_MONO RGB_BLACK
+#define DEF_SPAN_GRIP_SIZE "5"
+#define DEF_GRID_DOUBLE_BUFFER "1"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-bg", "tedBorder", (char *)NULL,
+ DEF_ROWCOL_BORDER_COLOR, Tk_Offset(Ted, border), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-bg", "tedBorder", (char *)NULL,
+ DEF_ROWCOL_BORDER_MONO, Tk_Offset(Ted, border), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-background", "tedBackground", (char *)NULL,
+ DEF_GRID_BG_COLOR, Tk_Offset(Ted, normalBg), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-background", "tedBackground", (char *)NULL,
+ DEF_GRID_BG_MONO, Tk_Offset(Ted, normalBg), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CURSOR, "-cursor", "cursor", (char *)NULL,
+ DEF_GRID_CURSOR, Tk_Offset(Ted, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-gridcolor", "gridColor", (char *)NULL,
+ DEF_GRID_FG_COLOR, Tk_Offset(Ted, gridColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-gridcolor", "gridColor", (char *)NULL,
+ DEF_GRID_FG_MONO, Tk_Offset(Ted, gridColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-buttoncolor", "buttonColor", (char *)NULL,
+ DEF_ROWCOL_BG_COLOR, Tk_Offset(Ted, buttonColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-buttoncolor", "buttonColor", (char *)NULL,
+ DEF_ROWCOL_BG_MONO, Tk_Offset(Ted, buttonColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-padcolor", "padColor", (char *)NULL,
+ DEF_GRID_PAD_COLOR, Tk_Offset(Ted, padColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-padcolor", "padColor", (char *)NULL,
+ DEF_GRID_PAD_MONO, Tk_Offset(Ted, padColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-padstipple", "padStipple", (char *)NULL,
+ DEF_GRID_PAD_STIPPLE, Tk_Offset(Ted, padStipple), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-font", "font", (char *)NULL,
+ DEF_GRID_FONT, Tk_Offset(Ted, font), 0},
+ {TK_CONFIG_CUSTOM, "-gridlinewidth", "gridLineWidth", (char *)NULL,
+ DEF_GRID_LINE_WIDTH, Tk_Offset(Ted, gridLineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-buttonheight", "buttonHeight", (char *)NULL,
+ DEF_ROWCOL_HEIGHT, Tk_Offset(Ted, buttonHeight),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-cavitypad", "cavityPad", (char *)NULL,
+ DEF_GRID_PAD_CAVITY, Tk_Offset(Ted, cavityPad),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-minsize", "minSize", (char *)NULL,
+ DEF_GRID_PAD_MIN, Tk_Offset(Ted, minSize),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", (char *)NULL,
+ DEF_GRID_DASHES, Tk_Offset(Ted, dashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_RELIEF, "-relief", "relief", (char *)NULL,
+ DEF_ROWCOL_RELIEF, Tk_Offset(Ted, relief), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", (char *)NULL,
+ DEF_ROWCOL_BORDER_WIDTH, Tk_Offset(Ted, borderWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CURSOR, "-entrycursor", "entryCursor", (char *)NULL,
+ DEF_ENTRY_CURSOR, Tk_Offset(Ted, attributes.cursor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_FONT, "-entryfont", "entryFont", (char *)NULL,
+ DEF_ENTRY_FONT, Tk_Offset(Ted, attributes.font), 0},
+ {TK_CONFIG_BITMAP, "-entrystipple", "entryStipple", (char *)NULL,
+ DEF_ENTRY_STIPPLE, Tk_Offset(Ted, attributes.stipple),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-widgetbackground", "widgetBackground", (char *)NULL,
+ DEF_ENTRY_WIDGET_BG_COLOR, Tk_Offset(Ted, attributes.widgetColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-widgetbackground", "widgetBackground", (char *)NULL,
+ DEF_ENTRY_WIDGET_BG_MONO, Tk_Offset(Ted, attributes.widgetColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-controlbackground", "controlBackground", (char *)NULL,
+ DEF_ENTRY_CONTROL_BG_COLOR, Tk_Offset(Ted, attributes.cntlColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-controlbackground", "controlBackground", (char *)NULL,
+ DEF_ENTRY_WIDGET_BG_MONO, Tk_Offset(Ted, attributes.cntlColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-entrybackground", "entryBackground", (char *)NULL,
+ DEF_ENTRY_NORMAL_BG_COLOR, Tk_Offset(Ted, attributes.normalBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-entrybackground", "entryBackground", (char *)NULL,
+ DEF_ENTRY_NORMAL_BG_MONO, Tk_Offset(Ted, attributes.normalBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-entryactivebackground", "entryActiveBackground", (char *)NULL,
+ DEF_ENTRY_ACTIVE_BG_COLOR, Tk_Offset(Ted, attributes.activeBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-entryactivebackground", "entryActiveBackground", (char *)NULL,
+ DEF_ENTRY_ACTIVE_BG_MONO, Tk_Offset(Ted, attributes.activeBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-entryactiveforeground", "entryActiveForeground", (char *)NULL,
+ DEF_ENTRY_ACTIVE_FG_COLOR, Tk_Offset(Ted, attributes.activeFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-entryactiveforeground", "entryActiveForeground", (char *)NULL,
+ DEF_ENTRY_ACTIVE_FG_MONO, Tk_Offset(Ted, attributes.activeFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-entryforeground", "entryForeground", (char *)NULL,
+ DEF_ENTRY_NORMAL_FG_COLOR, Tk_Offset(Ted, attributes.normalFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-entryforeground", "entryForeground", (char *)NULL,
+ DEF_ENTRY_NORMAL_FG_MONO, Tk_Offset(Ted, attributes.normalFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-spancolor", "spanColor", (char *)NULL,
+ DEF_SPAN_COLOR, Tk_Offset(Ted, spanColor), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-spancolor", "spanColor", (char *)NULL,
+ DEF_SPAN_MONO, Tk_Offset(Ted, spanColor), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-spanstipple", "spanStipple", (char *)NULL,
+ DEF_SPAN_STIPPLE, Tk_Offset(Ted, spanStipple), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-gripsize", "gripSize", (char *)NULL,
+ DEF_SPAN_GRIP_SIZE, Tk_Offset(Ted, gripSize),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-dbl", "doubleBuffer", (char *)NULL,
+ DEF_GRID_DOUBLE_BUFFER, Tk_Offset(Ted, doubleBuffer),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static void DrawEditor _ANSI_ARGS_((Editor *editor));
+static void DestroyEditor _ANSI_ARGS_((DestroyData destroyData));
+static void DisplayTed _ANSI_ARGS_((ClientData clientData));
+static void DestroyTed _ANSI_ARGS_((DestroyData destroyData));
+static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
+static void DestroyEntry _ANSI_ARGS_((DestroyData destoryData));
+
+
+#ifdef __STDC__
+static Tcl_CmdProc TedCmd;
+static Tk_EventProc EntryEventProc;
+static Tk_EventProc TedEventProc;
+#endif
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the text window at the next idle
+ * point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn. This doesn't
+ * seem to hurt performance noticeably, but if it does then this
+ * could be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(tedPtr)
+ Ted *tedPtr; /* Information about editor. */
+{
+ if ((tedPtr->tkwin != NULL) && !(tedPtr->flags & REDRAW_PENDING)) {
+ tedPtr->flags |= REDRAW_PENDING;
+ Tcl_DoWhenIdle(DisplayTed, tedPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Queues a request to redraw the text window at the next idle
+ * point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn. This doesn't
+ * seem to hurt performance noticeably, but if it does then this
+ * could be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawEntry(repPtr)
+ EntryRep *repPtr; /* Information about editor. */
+{
+ if ((repPtr->tkwin != NULL) && !(repPtr->flags & REDRAW_PENDING)) {
+ repPtr->flags |= REDRAW_PENDING;
+ Tcl_DoWhenIdle(DisplayEntry, repPtr);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * EntryEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on the editing grid for the table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+EntryEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ EntryRep *repPtr = (EntryRep *) clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ EventuallyRedrawEntry(repPtr);
+ } else if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawEntry(repPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ repPtr->tkwin = NULL;
+ if (repPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayEntry, repPtr);
+ }
+ Tcl_EventuallyFree(repPtr, DestroyEntry);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TedEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on the editing grid for the table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TedEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Ted *tedPtr = (Ted *) clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ EventuallyRedraw(tedPtr);
+ } else if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedraw(tedPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ tedPtr->tkwin = NULL;
+ if (tedPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayTed, tedPtr);
+ }
+ Tcl_EventuallyFree(tedPtr, DestroyTed);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateGrid --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+CreateGrid(tedPtr)
+ Ted *tedPtr;
+{
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Tk_Window master;
+ /*
+ * Create a sibling window to cover the master window. It will
+ * be stacked just above the master window.
+ */
+ interp = tedPtr->tablePtr->interp;
+ master = tedPtr->tablePtr->tkwin;
+ tkwin = Tk_CreateWindow(interp, master, "ted_%output%", (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Tk_SetClass(tkwin, "BltTed");
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ TedEventProc, tedPtr);
+ Tk_MoveResizeWindow(tkwin, 0, 0, Tk_Width(master), Tk_Height(master));
+ Tk_RestackWindow(tkwin, Below, (Tk_Window)NULL);
+ Tk_MapWindow(tkwin);
+ tedPtr->tkwin = tkwin;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateEventWindow --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+CreateEventWindow(tedPtr)
+ Ted *tedPtr;
+{
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Tk_Window master;
+ Tk_Window parent;
+
+ interp = tedPtr->tablePtr->interp;
+ master = tedPtr->tablePtr->tkwin;
+ /*
+ * Create an InputOnly window which sits above the table to
+ * collect and dispatch user events.
+ */
+ if (Tk_IsTopLevel(master)) {
+ /*
+ * If master is a top-level window, it's also the parent of
+ * the widgets (it can't have a sibling).
+ * In this case, the InputOnly window is a child of the
+ * master instead of a sibling.
+ */
+ parent = master;
+ tkwin = Tk_CreateWindow(interp, parent, "ted_%input%", (char *)NULL);
+ if (tkwin != NULL) {
+ Tk_ResizeWindow(tkwin, Tk_Width(parent), Tk_Height(parent));
+ }
+ tedPtr->inputIsSibling = 0;
+ } else {
+ char *namePtr; /* Name of InputOnly window. */
+
+ parent = Tk_Parent(master);
+ namePtr = Blt_Malloc(strlen(Tk_Name(master)) + 5);
+ sprintf(namePtr, "ted_%s", Tk_Name(master));
+ tkwin = Tk_CreateWindow(interp, parent, namePtr, (char *)NULL);
+ Blt_Free(namePtr);
+ if (tkwin != NULL) {
+ Tk_MoveResizeWindow(tkwin, Tk_X(master), Tk_Y(master),
+ Tk_Width(master), Tk_Height(master));
+ }
+ tedPtr->inputIsSibling = 1;
+ }
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_MakeTransparentWindowExist(tkwin, Tk_WindowId(parent), TRUE);
+ Tk_RestackWindow(tkwin, Above, (Tk_Window)NULL);
+ Tk_MapWindow(tkwin);
+ tedPtr->input = tkwin;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateEntry --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+CreateEntry(tedPtr, entryPtr)
+ Ted *tedPtr;
+ Entry *entryPtr;
+{
+ Tk_Window tkwin, master;
+ char string[200];
+ EntryRep *repPtr;
+ Blt_ChainLink *linkPtr;
+
+ repPtr = Blt_Calloc(1, sizeof(EntryRep));
+ assert(repPtr);
+ repPtr->tablePtr = tedPtr->tablePtr;
+ repPtr->tedPtr = tedPtr;
+ repPtr->interp = tedPtr->interp;
+ repPtr->entryPtr = entryPtr;
+ repPtr->mapped = 0;
+
+ /*
+ * Create a sibling window to cover the master window. It will
+ * be stacked just above the master window.
+ */
+
+ master = tedPtr->tablePtr->tkwin;
+ sprintf(string, "bltTed%d", tedPtr->nextWindowId);
+ tedPtr->nextWindowId++;
+ tkwin = Tk_CreateWindow(tedPtr->interp, master, string, (char *)NULL);
+ if (tkwin == NULL) {
+ Blt_Free(repPtr);
+ return TCL_ERROR;
+ }
+ Tk_SetClass(tkwin, "BltTed");
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ EntryEventProc, repPtr);
+ repPtr->tkwin = tkwin;
+ linkPtr = Blt_ChainNewLink();
+ Blt_ChainSetValue(linkPtr, repPtr);
+ Blt_ChainLinkAfter(tedPtr->chainPtr, linkPtr, (Blt_ChainLink *)NULL);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyEntry --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyEntry(data)
+ DestroyData data;
+{
+ EntryRep *repPtr = (EntryRep *)data;
+ Blt_ChainLink *linkPtr;
+ Entry *entryPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(repPtr->tedPtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (entryPtr == repPtr->entryPtr) {
+ Blt_ChainDeleteLink(repPtr->tedPtr->chainPtr, linkPtr);
+ Blt_Free(repPtr);
+ return;
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DisplayEntry --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DisplayEntry(clientData)
+ ClientData clientData;
+{
+ EntryRep *repPtr = (EntryRep *) clientData;
+ Ted *tedPtr;
+ Entry *entryPtr;
+ Tk_Window tkwin;
+ int x, y, width, height;
+
+ repPtr->flags &= ~REDRAW_PENDING;
+ if ((repPtr->tkwin == NULL) || (repPtr->entryPtr == NULL)) {
+ return;
+ }
+ if (!Tk_IsMapped(repPtr->tkwin)) {
+ return;
+ }
+ tedPtr = repPtr->tedPtr;
+ entryPtr = repPtr->entryPtr;
+ tkwin = repPtr->tkwin;
+
+ /*
+ * Check if the entry size and position.
+ * Move and resize the window accordingly.
+ */
+ x = Tk_X(entryPtr->tkwin) - (entryPtr->padLeft + tedPtr->cavityPad);
+ y = Tk_Y(entryPtr->tkwin) - (entryPtr->padTop + tedPtr->cavityPad);
+ width = Tk_Width(entryPtr->tkwin) + PADDING(entryPtr->padX) +
+ (2 * tedPtr->cavityPad);
+ height = Tk_Height(entryPtr->tkwin) + PADDING(entryPtr->padY) +
+ (2 * tedPtr->cavityPad);
+
+
+ if ((Tk_X(tkwin) != x) || (Tk_Y(tkwin) != y) ||
+ (Tk_Width(tkwin) != width) || (Tk_Height(tkwin) != height)) {
+ Tk_MoveResizeWindow(tkwin, x, y, width, height);
+ Tk_RestackWindow(tkwin, Above, (Tk_Window)NULL);
+ }
+ /* Clear the background of the entry */
+
+ XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ tedPtr->attributes.fillGC, 0, 0, width, height);
+
+ /* Draw the window */
+
+ x = entryPtr->padLeft + tedPtr->cavityPad;
+ y = entryPtr->padTop + tedPtr->cavityPad;
+
+ XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ tedPtr->attributes.widgetFillGC, x, y, Tk_Width(entryPtr->tkwin),
+ Tk_Height(entryPtr->tkwin));
+ XDrawRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ tedPtr->attributes.drawGC, x, y, Tk_Width(entryPtr->tkwin),
+ Tk_Height(entryPtr->tkwin));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * FindEditor --
+ *
+ * Searches for a table associated with the window given by its
+ * pathname. This window represents the master window of the table.
+ *
+ * Errors may occur because
+ * 1) pathName does not represent a valid Tk window or
+ * 2) the window is not associated with any table as its master.
+ *
+ * Results:
+ * If a table entry exists, a pointer to the Table structure is
+ * returned. Otherwise NULL is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Ted *
+FindEditor(clientData, interp, pathName)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to */
+ char *pathName; /* Path name of the master window */
+{
+ Table *tablePtr;
+
+ if (Blt_GetTable(clientData, interp, pathName, &tablePtr) != TCL_OK) {
+ return NULL;
+ }
+ if (tablePtr->editPtr == NULL) {
+ Tcl_AppendResult(interp, "no editor exists for table \"",
+ Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
+ return NULL;
+ }
+ return (Ted *) tablePtr->editPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateTed --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Ted *
+CreateTed(tablePtr, interp)
+ Table *tablePtr;
+ Tcl_Interp *interp;
+{
+ Ted *tedPtr;
+
+ tedPtr = Blt_Calloc(1, sizeof(Ted));
+ assert(tedPtr);
+ tedPtr->nextWindowId = 0;
+ tedPtr->interp = interp;
+ tedPtr->tablePtr = tablePtr;
+ tedPtr->gridLineWidth = 1;
+ tedPtr->buttonHeight = 0;
+ tedPtr->cavityPad = 0;
+ tedPtr->minSize = 3;
+ tedPtr->gripSize = 5;
+ tedPtr->drawProc = DrawEditor;
+ tedPtr->destroyProc = DestroyEditor;
+ tedPtr->display = Tk_Display(tablePtr->tkwin);
+ tedPtr->relief = TK_RELIEF_RAISED;
+ tedPtr->borderWidth = 2;
+ tedPtr->doubleBuffer = 1;
+ tedPtr->chainPtr = Blt_ChainCreate();
+ /* Create the grid window */
+
+ if (CreateGrid(tedPtr) != TCL_OK) {
+ return NULL;
+ }
+ /* Create an InputOnly window to collect user events */
+ if (CreateEventWindow(tedPtr) != TCL_OK) {
+ return NULL;
+ }
+ tablePtr->editPtr = (Editor *)tedPtr;
+ return tedPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyTed --
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyTed(freeProcData)
+ DestroyData freeProcData;
+{
+ Ted *tedPtr = (Ted *) freeProcData;
+
+ if (tedPtr->rectArr != NULL) {
+ Blt_Free(tedPtr->rectArr);
+ }
+ if (tedPtr->segArr != NULL) {
+ Blt_Free(tedPtr->segArr);
+ }
+ if (tedPtr->fillGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->fillGC);
+ }
+ if (tedPtr->drawGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->drawGC);
+ }
+ if (tedPtr->rectGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->rectGC);
+ }
+ if (tedPtr->padRectGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->padRectGC);
+ }
+ /* Is this save ? */
+ tedPtr->tablePtr->editPtr = NULL;
+ Blt_Free(tedPtr);
+
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureTed --
+ *
+ * This procedure is called to process an argv/argc list in order to
+ * configure the table geometry manager.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Table configuration options (padx, pady, rows, columns, etc) get
+ * set. The table is recalculated and arranged at the next idle
+ * point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureTed(tedPtr, argc, argv, flags)
+ Ted *tedPtr;
+ int argc;
+ char **argv; /* Option-value pairs */
+ int flags;
+{
+ XGCValues gcValues;
+ GC newGC;
+ unsigned long gcMask;
+
+ if (Tk_ConfigureWidget(tedPtr->interp, tedPtr->tkwin, configSpecs,
+ argc, argv, (char *)tedPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* GC for filling background of edit window */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->normalBg->pixel;
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->fillGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->fillGC);
+ }
+ tedPtr->fillGC = newGC;
+
+ /* GC for drawing grid lines */
+
+ gcMask = (GCForeground | GCBackground | GCLineWidth | GCLineStyle |
+ GCCapStyle | GCJoinStyle | GCFont);
+ gcValues.font = Tk_FontId(tedPtr->font);
+ gcValues.foreground = tedPtr->gridColor->pixel;
+ gcValues.background = tedPtr->normalBg->pixel;
+ gcValues.line_width = LineWidth(tedPtr->gridLineWidth);
+ gcValues.cap_style = CapRound;
+ gcValues.join_style = JoinRound;
+ gcValues.line_style = LineSolid;
+ if (LineIsDashed(tedPtr->dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ }
+ newGC = Blt_GetPrivateGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->drawGC != NULL) {
+ Blt_FreePrivateGC(tedPtr->display, tedPtr->drawGC);
+ }
+ if (LineIsDashed(tedPtr->dashes)) {
+ XSetDashes(tedPtr->display, newGC, 0,
+ (CONST char *)tedPtr->dashes.values,
+ strlen((char *)tedPtr->dashes.values));
+ }
+ tedPtr->drawGC = newGC;
+
+ /* GC for button rectangles */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->buttonColor->pixel;
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->rectGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->rectGC);
+ }
+ tedPtr->rectGC = newGC;
+
+ /* GC for button rectangles */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->padColor->pixel;
+ if (tedPtr->padStipple != None) {
+ gcMask |= GCStipple | GCFillStyle;
+ gcValues.stipple = tedPtr->padStipple;
+ gcValues.fill_style = FillStippled;
+ }
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->padRectGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->padRectGC);
+ }
+ tedPtr->padRectGC = newGC;
+
+ /* GC for filling entrys */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->attributes.normalBg->pixel;
+ if (tedPtr->attributes.stipple != None) {
+ gcMask |= GCStipple | GCFillStyle;
+ gcValues.stipple = tedPtr->attributes.stipple;
+ gcValues.fill_style = FillStippled;
+ }
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->attributes.fillGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->attributes.fillGC);
+ }
+ tedPtr->attributes.fillGC = newGC;
+
+ /* GC for drawing entrys */
+
+ gcMask = GCForeground | GCBackground | GCFont;
+ gcValues.foreground = tedPtr->attributes.normalFg->pixel;
+ gcValues.background = tedPtr->attributes.normalBg->pixel;
+ gcValues.font = Tk_FontId(tedPtr->attributes.font);
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->attributes.drawGC != NULL) {
+ Blt_FreePrivateGC(tedPtr->display, tedPtr->attributes.drawGC);
+ }
+ tedPtr->attributes.drawGC = newGC;
+
+ /* GC for filling widget rectangles */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->attributes.widgetColor->pixel;
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->attributes.widgetFillGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->attributes.widgetFillGC);
+ }
+ tedPtr->attributes.widgetFillGC = newGC;
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->attributes.cntlColor->pixel;
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->attributes.cntlGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->attributes.cntlGC);
+ }
+ tedPtr->attributes.cntlGC = newGC;
+
+ /* GC for filling span rectangle */
+
+ gcMask = GCForeground;
+ gcValues.foreground = tedPtr->spanColor->pixel;
+ if (tedPtr->spanStipple != None) {
+ gcMask |= GCStipple | GCFillStyle;
+ gcValues.stipple = tedPtr->spanStipple;
+ gcValues.fill_style = FillStippled;
+ }
+ newGC = Tk_GetGC(tedPtr->tkwin, gcMask, &gcValues);
+ if (tedPtr->spanGC != NULL) {
+ Tk_FreeGC(tedPtr->display, tedPtr->spanGC);
+ }
+ tedPtr->spanGC = newGC;
+
+ /* Define cursor for grid events */
+ if (tedPtr->cursor != None) {
+ Tk_DefineCursor(tedPtr->input, tedPtr->cursor);
+ } else {
+ Tk_UndefineCursor(tedPtr->input);
+ }
+ return TCL_OK;
+}
+
+
+static void
+LayoutGrid(tedPtr)
+ Ted *tedPtr;
+{
+ int needed;
+ XSegment *segArr;
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+ int startX, endX;
+ int startY, endY;
+ int count;
+
+ tablePtr = tedPtr->tablePtr;
+ if (tedPtr->segArr != NULL) {
+ Blt_Free(tedPtr->segArr);
+ tedPtr->segArr = NULL;
+ }
+ tedPtr->nSegs = 0;
+ if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
+ return;
+ }
+ needed = tablePtr->nRows + tablePtr->nColumns + 2;
+ segArr = Blt_Calloc(needed, sizeof(XSegment));
+ if (segArr == NULL) {
+ return;
+ }
+ linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ startX = rcPtr->offset - tedPtr->gridLineWidth;
+
+ linkPtr = Blt_ChainLastLink(tablePtr->columnInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ endX = rcPtr->offset + rcPtr->size - 1;
+
+ linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ startY = rcPtr->offset - tedPtr->gridLineWidth;
+
+ linkPtr = Blt_ChainLastLink(tablePtr->rowInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ endY = rcPtr->offset + rcPtr->size - 1;
+
+ count = 0; /* Reset segment counter */
+
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ segArr[count].x1 = startX;
+ segArr[count].x2 = endX;
+ segArr[count].y1 = segArr[count].y2 = rcPtr->offset -
+ tedPtr->gridLineWidth;
+ count++;
+ }
+ segArr[count].x1 = startX;
+ segArr[count].x2 = endX;
+ segArr[count].y1 = segArr[count].y2 = endY;
+ count++;
+
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ segArr[count].y1 = startY;
+ segArr[count].y2 = endY;
+ segArr[count].x1 = segArr[count].x2 = rcPtr->offset -
+ tedPtr->gridLineWidth;
+ count++;
+ }
+ segArr[count].x1 = segArr[count].x2 = endX;
+ segArr[count].y1 = startY;
+ segArr[count].y2 = endY;
+ count++;
+ assert(count == needed);
+ if (tedPtr->segArr != NULL) {
+ Blt_Free(tedPtr->segArr);
+ }
+ tedPtr->segArr = segArr;
+ tedPtr->nSegs = count;
+}
+
+
+static void
+LayoutPads(tedPtr)
+ Ted *tedPtr;
+{
+ int needed;
+ XRectangle *rectArr, *rectPtr;
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+ int startX, endX;
+ int startY, endY;
+ int count;
+
+ tablePtr = tedPtr->tablePtr;
+ if (tedPtr->padRectArr != NULL) {
+ Blt_Free(tedPtr->padRectArr);
+ tedPtr->padRectArr = NULL;
+ }
+ tedPtr->nPadRects = 0;
+ if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
+ return;
+ }
+ needed = 2 * (tablePtr->nRows + tablePtr->nColumns);
+ rectArr = Blt_Calloc(needed, sizeof(XRectangle));
+ if (rectArr == NULL) {
+ return;
+ }
+ linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ startX = rcPtr->offset;
+
+ linkPtr = Blt_ChainLastLink(tablePtr->columnInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ endX = (rcPtr->offset + rcPtr->size);
+
+ linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ startY = rcPtr->offset;
+
+ linkPtr = Blt_ChainLastLink(tablePtr->rowInfo.chainPtr);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ endY = (rcPtr->offset + rcPtr->size);
+
+ count = 0; /* Reset segment counter */
+ rectPtr = rectArr;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->pad.side1 > 0) {
+ rectPtr->x = startX;
+ rectPtr->y = rcPtr->offset;
+ rectPtr->height = rcPtr->pad.side1;
+ rectPtr->width = endX - startX - 1;
+ rectPtr++, count++;
+ }
+ if (rcPtr->pad.side2 > 0) {
+ rectPtr->x = startX;
+ rectPtr->y = rcPtr->offset + rcPtr->size - rcPtr->pad.side2 - 1;
+ rectPtr->height = rcPtr->pad.side2;
+ rectPtr->width = endX - startX - 1;
+ rectPtr++, count++;
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ if (rcPtr->pad.side1 > 0) {
+ rectPtr->x = rcPtr->offset;
+ rectPtr->y = startY;
+ rectPtr->height = endY - startY - 1;
+ rectPtr->width = rcPtr->pad.side1;
+ rectPtr++, count++;
+ }
+ if (rcPtr->pad.side2 > 0) {
+ rectPtr->x = rcPtr->offset + rcPtr->size - rcPtr->pad.side2;
+ rectPtr->y = startY;
+ rectPtr->height = endY - startY - 1;
+ rectPtr->width = rcPtr->pad.side2;
+ rectPtr++, count++;
+ }
+ }
+ if (count == 0) {
+ Blt_Free(rectArr);
+ return;
+ }
+ tedPtr->padRectArr = rectArr;
+ tedPtr->nPadRects = count;
+}
+
+static void
+LayoutEntries(tedPtr)
+ Ted *tedPtr;
+{
+ Entry *entryPtr;
+ XRectangle *rectArr;
+ int needed;
+ int count;
+ Blt_ChainLink *linkPtr;
+
+ if (tedPtr->widgetPadRectArr != NULL) {
+ Blt_Free(tedPtr->widgetPadRectArr);
+ tedPtr->widgetPadRectArr = NULL;
+ }
+ tedPtr->nWidgetPadRects = 0;
+
+ needed = Blt_ChainGetLength(tedPtr->tablePtr->chainPtr);
+ rectArr = Blt_Calloc(needed, sizeof(XRectangle));
+ if (rectArr == NULL) {
+ return;
+ }
+ /* Draw any entry windows */
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(tedPtr->tablePtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if ((PADDING(entryPtr->padX) + PADDING(entryPtr->padY)) == 0) {
+ continue;
+ }
+ rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
+ rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
+ rectArr[count].width = Tk_Width(entryPtr->tkwin) +
+ PADDING(entryPtr->padX);
+ rectArr[count].height = Tk_Height(entryPtr->tkwin) +
+ PADDING(entryPtr->padY);
+ count++;
+ }
+ if (count == 0) {
+ Blt_Free(rectArr);
+ return;
+ }
+ tedPtr->widgetPadRectArr = rectArr;
+ tedPtr->nWidgetPadRects = count;
+}
+
+static void
+LayoutControlEntries(tedPtr)
+ Ted *tedPtr;
+{
+ Entry *entryPtr;
+ XRectangle *rectArr;
+ int needed;
+ int count;
+ Table *tablePtr = tedPtr->tablePtr;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+
+ if (tedPtr->cntlRectArr != NULL) {
+ Blt_Free(tedPtr->cntlRectArr);
+ tedPtr->cntlRectArr = NULL;
+ }
+ tedPtr->nCntlRects = 0;
+
+ needed = (tablePtr->nRows + tablePtr->nColumns);
+ rectArr = Blt_Calloc(needed, sizeof(XRectangle));
+ if (rectArr == NULL) {
+ return;
+ }
+ /* Draw any entry windows */
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ entryPtr = rcPtr->control;
+ if (entryPtr != NULL) {
+ rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
+ rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
+ rectArr[count].width = Tk_Width(entryPtr->tkwin) +
+ PADDING(entryPtr->padX);
+ rectArr[count].height = Tk_Height(entryPtr->tkwin) +
+ PADDING(entryPtr->padY);
+ count++;
+ }
+ }
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ entryPtr = rcPtr->control;
+ if (entryPtr != NULL) {
+ rectArr[count].x = Tk_X(entryPtr->tkwin) - entryPtr->padLeft;
+ rectArr[count].y = Tk_Y(entryPtr->tkwin) - entryPtr->padTop;
+ rectArr[count].width = Tk_Width(entryPtr->tkwin) +
+ PADDING(entryPtr->padX);
+ rectArr[count].height = Tk_Height(entryPtr->tkwin) +
+ PADDING(entryPtr->padY);
+ count++;
+ }
+ }
+ if (count == 0) {
+ Blt_Free(rectArr);
+ return;
+ }
+ tedPtr->cntlRectArr = rectArr;
+ tedPtr->nCntlRects = count;
+}
+
+static void
+LayoutButtons(tedPtr)
+ Ted *tedPtr;
+{
+ int needed;
+ XRectangle *rectArr;
+ Table *tablePtr;
+ Blt_ChainLink *linkPtr;
+ RowColumn *rcPtr;
+ int count;
+
+ tablePtr = tedPtr->tablePtr;
+ if ((tablePtr->nRows == 0) || (tablePtr->nColumns == 0)) {
+ if (tedPtr->rectArr != NULL) {
+ Blt_Free(tedPtr->rectArr);
+ }
+ tedPtr->rectArr = NULL;
+ tedPtr->nRects = 0;
+ return; /* Nothing to display, empty table */
+ }
+ needed = 2 * (tablePtr->nRows + tablePtr->nColumns);
+ rectArr = Blt_Calloc(needed, sizeof(XRectangle));
+ if (rectArr == NULL) {
+ return; /* Can't allocate rectangles */
+ }
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rectArr[count].x = 0;
+ rectArr[count].y = rcPtr->offset - rcPtr->pad.side1;
+ rectArr[count].width = tedPtr->buttonHeight;
+ rectArr[count].height = rcPtr->size - 2;
+ count++;
+ rectArr[count].x = Tk_Width(tedPtr->tkwin) - tedPtr->buttonHeight;
+ rectArr[count].y = rcPtr->offset - rcPtr->pad.side1;
+ rectArr[count].width = tedPtr->buttonHeight;
+ rectArr[count].height = rcPtr->size - 2;
+ count++;
+ }
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ rectArr[count].x = rcPtr->offset - rcPtr->pad.side1;
+ rectArr[count].y = 0;
+ rectArr[count].width = rcPtr->size - 2;
+ rectArr[count].height = tedPtr->buttonHeight;
+ count++;
+ rectArr[count].x = rcPtr->offset - rcPtr->pad.side1;
+ rectArr[count].y = Tk_Height(tedPtr->tkwin) - tedPtr->buttonHeight;
+ rectArr[count].width = rcPtr->size - 2;
+ rectArr[count].height = tedPtr->buttonHeight;
+ count++;
+ }
+ assert(count == needed);
+ if (tedPtr->rectArr != NULL) {
+ Blt_Free(tedPtr->rectArr);
+ }
+ tedPtr->rectArr = rectArr;
+ tedPtr->nRects = count;
+}
+
+
+static void
+DisplayTed(clientData)
+ ClientData clientData;
+{
+ Ted *tedPtr = (Ted *) clientData;
+ Tk_Window master;
+ Tk_Window tkwin;
+ Blt_ChainLink *linkPtr;
+ EntryRep *repPtr;
+ Drawable drawable;
+ Pixmap pixmap;
+
+#ifdef notdef
+ fprintf(stderr, "display grid\n");
+#endif
+ tedPtr->flags &= ~REDRAW_PENDING;
+ if (!Tk_IsMapped(tedPtr->tkwin)) {
+ return;
+ }
+ /*
+ * Check if the master window has changed size and resize the
+ * grid and input windows accordingly.
+ */
+ master = tedPtr->tablePtr->tkwin;
+ if ((Tk_Width(master) != Tk_Width(tedPtr->tkwin)) ||
+ (Tk_Height(master) != Tk_Height(tedPtr->tkwin))) {
+#ifdef notdef
+ fprintf(stderr, "resizing windows\n");
+#endif
+ Tk_ResizeWindow(tedPtr->tkwin, Tk_Width(master), Tk_Height(master));
+ Tk_ResizeWindow(tedPtr->input, Tk_Width(master), Tk_Height(master));
+ if (tedPtr->inputIsSibling) {
+ Tk_MoveWindow(tedPtr->input, Tk_X(master), Tk_X(master));
+ }
+ tedPtr->flags |= LAYOUT_PENDING;
+ }
+ if (tedPtr->flags & LAYOUT_PENDING) {
+#ifdef notdef
+ fprintf(stderr, "layout of grid\n");
+#endif
+ LayoutPads(tedPtr);
+ LayoutEntries(tedPtr);
+ LayoutControlEntries(tedPtr);
+ LayoutGrid(tedPtr);
+ LayoutButtons(tedPtr);
+ tedPtr->flags &= ~LAYOUT_PENDING;
+ }
+ tkwin = tedPtr->tkwin;
+
+ pixmap = None; /* Suppress compiler warning. */
+ drawable = Tk_WindowId(tkwin);
+ if (tedPtr->doubleBuffer) {
+ /* Create an off-screen pixmap for semi-smooth scrolling. */
+ pixmap = Tk_GetPixmap(tedPtr->display, Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+ drawable = pixmap;
+ }
+ /* Clear the background of the grid */
+
+ XFillRectangle(Tk_Display(tkwin), drawable, tedPtr->fillGC, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin));
+
+ /* Draw the row and column buttons */
+
+ if (tedPtr->nRects > 0) {
+ int i;
+
+ for (i = 0; i < tedPtr->nRects; i++) {
+ Tk_Fill3DRectangle(tkwin, drawable, tedPtr->border,
+ tedPtr->rectArr[i].x, tedPtr->rectArr[i].y,
+ tedPtr->rectArr[i].width, tedPtr->rectArr[i].height,
+ tedPtr->borderWidth, tedPtr->relief);
+ }
+#ifdef notdef
+ XFillRectangles(tedPtr->display, drawable, tedPtr->rectGC,
+ tedPtr->rectArr, tedPtr->nRects);
+ XDrawRectangles(tedPtr->display, drawable, tedPtr->drawGC,
+ tedPtr->rectArr, tedPtr->nRects);
+#endif
+ }
+ if (tedPtr->nPadRects > 0) {
+ XFillRectangles(tedPtr->display, drawable, tedPtr->padRectGC,
+ tedPtr->padRectArr, tedPtr->nPadRects);
+ }
+ if (tedPtr->spanActive) {
+ XFillRectangles(tedPtr->display, drawable, tedPtr->spanGC,
+ tedPtr->activeRectArr, 1);
+ XFillRectangles(tedPtr->display, drawable, tedPtr->drawGC,
+ tedPtr->activeRectArr + 1, 4);
+ }
+ if (tedPtr->nWidgetPadRects > 0) {
+ XFillRectangles(tedPtr->display, drawable, tedPtr->attributes.fillGC,
+ tedPtr->widgetPadRectArr, tedPtr->nWidgetPadRects);
+ }
+ if (tedPtr->nCntlRects > 0) {
+ XFillRectangles(tedPtr->display, drawable, tedPtr->attributes.cntlGC,
+ tedPtr->cntlRectArr, tedPtr->nCntlRects);
+ }
+ /* Draw the grid lines */
+ if (tedPtr->nSegs > 0) {
+ XDrawSegments(tedPtr->display, drawable, tedPtr->drawGC,
+ tedPtr->segArr, tedPtr->nSegs);
+ }
+#ifndef notdef
+ /* Draw any entry windows */
+ for (linkPtr = Blt_ChainFirstLink(tedPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ repPtr = Blt_ChainGetValue(linkPtr);
+ if (repPtr->mapped) {
+ DisplayEntry(repPtr);
+ }
+ }
+#endif
+ if (tedPtr->doubleBuffer) {
+ XCopyArea(tedPtr->display, drawable, Tk_WindowId(tkwin), tedPtr->fillGC,
+ 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(tedPtr->display, pixmap);
+ }
+}
+
+
+static void
+DrawEditor(editPtr)
+ Editor *editPtr;
+{
+ Ted *tedPtr = (Ted *) editPtr;
+
+ tedPtr->flags |= LAYOUT_PENDING;
+ if ((tedPtr->tkwin != NULL) && !(tedPtr->flags & REDRAW_PENDING)) {
+ tedPtr->flags |= REDRAW_PENDING;
+#ifdef notdef
+ fprintf(stderr, "from draw editor\n");
+#endif
+ Tcl_DoWhenIdle(DisplayTed, tedPtr);
+ }
+}
+
+static void
+DestroyEditor(destroyData)
+ DestroyData destroyData;
+{
+ Ted *tedPtr = (Ted *) destroyData;
+
+ tedPtr->tkwin = NULL;
+ if (tedPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayTed, tedPtr);
+ }
+ Tcl_EventuallyFree(tedPtr, DestroyTed);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * EditOp --
+ *
+ * Processes an argv/argc list of table entries to add and configure
+ * new widgets into the table. A table entry consists of the
+ * window path name, table index, and optional configuration options.
+ * The first argument in the argv list is the name of the table. If
+ * no table exists for the given window, a new one is created.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred, TCL_ERROR is
+ * returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is allocated, a new master table is possibly created, etc.
+ * The table is re-computed and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+EditOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return list of names to */
+ int argc; /* Number of arguments */
+ char **argv;
+{
+ Table *tablePtr;
+ Ted *tedPtr;
+
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tablePtr->editPtr != NULL) { /* Already editing this table */
+ tedPtr = (Ted *) tablePtr->editPtr;
+ } else {
+ tedPtr = CreateTed(tablePtr, interp);
+ if (tedPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (ConfigureTed(tedPtr, argc - 3, argv + 3, 0) != TCL_OK) {
+ tedPtr->tkwin = NULL;
+ if (tedPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayTed, tedPtr);
+ }
+ Tcl_EventuallyFree(tedPtr, DestroyTed);
+ return TCL_ERROR;
+ }
+ /* Rearrange the table */
+ if (!(tablePtr->flags & ARRANGE_PENDING)) {
+ tablePtr->flags |= ARRANGE_PENDING;
+ Tcl_DoWhenIdle(tablePtr->arrangeProc, tablePtr);
+ }
+ interp->result = Tk_PathName(tedPtr->tkwin);
+ tedPtr->flags |= LAYOUT_PENDING;
+ EventuallyRedraw(tedPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CgetCmd --
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report results back to */
+ int argc; /* Not used. */
+ char **argv; /* Option-value pairs */
+{
+ Ted *tedPtr;
+
+ tedPtr = FindEditor(dataPtr, interp, argv[2]);
+ if (tedPtr == NULL) {
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, tedPtr->tkwin, configSpecs,
+ (char *)tedPtr, argv[3], 0);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureCmd --
+ *
+ * This procedure is called to process an argv/argc list in order to
+ * configure the table geometry manager.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Table configuration options (padx, pady, rows, columns, etc) get
+ * set. The table is recalculated and arranged at the next idle
+ * point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to report results back to */
+ int argc;
+ char **argv; /* Option-value pairs */
+{
+ Ted *tedPtr;
+
+ tedPtr = FindEditor(dataPtr, interp, argv[2]);
+ if (tedPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, tedPtr->tkwin, configSpecs,
+ (char *)tedPtr, (char *)NULL, 0);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, tedPtr->tkwin, configSpecs,
+ (char *)tedPtr, argv[3], 0);
+ }
+ if (ConfigureTed(tedPtr, argc - 3, argv + 3,
+ TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EventuallyRedraw(tedPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return list of names to */
+ int argc; /* Not used. */
+ char **argv;
+{
+ Table *tablePtr;
+ Ted *tedPtr;
+ Entry *entryPtr;
+ int active;
+ int x, y, width, height;
+ int ix, iy;
+ Blt_ChainLink *linkPtr;
+ Tk_Window tkwin;
+
+ /* ted select master @x,y */
+ tkwin = Tk_MainWindow(interp);
+ tedPtr = FindEditor(dataPtr, interp, argv[2]);
+ if (tedPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetXY(interp, tkwin, argv[3], &ix, &iy) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tablePtr = tedPtr->tablePtr;
+ active = 0;
+ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ x = entryPtr->x - entryPtr->padX.side1;
+ y = entryPtr->y - entryPtr->padY.side1;
+ width = Tk_Width(entryPtr->tkwin) + PADDING(entryPtr->padX);
+ height = Tk_Height(entryPtr->tkwin) + PADDING(entryPtr->padY);
+ if ((ix >= x) && (ix <= (x + width)) &&
+ (iy >= y) && (iy <= (y + height))) {
+ int left, right, top, bottom;
+ int last;
+ int grip;
+ RowColumn *rcPtr;
+
+ last = entryPtr->column.rcPtr->index + entryPtr->column.span - 1;
+ linkPtr = Blt_ChainGetNthLink(tablePtr->columnInfo.chainPtr, last);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+
+ /* Calculate the span rectangle */
+ left = (entryPtr->column.rcPtr->offset -
+ entryPtr->column.rcPtr->pad.side1);
+ right = (rcPtr->offset - rcPtr->pad.side1) + rcPtr->size;
+
+ top = (entryPtr->row.rcPtr->offset -
+ entryPtr->row.rcPtr->pad.side1);
+
+ last = entryPtr->row.rcPtr->index + entryPtr->row.span - 1;
+ linkPtr = Blt_ChainGetNthLink(tablePtr->rowInfo.chainPtr, last);
+ rcPtr = Blt_ChainGetValue(linkPtr);
+ bottom = (rcPtr->offset - rcPtr->pad.side1) + rcPtr->size;
+
+ tedPtr->activeRectArr[0].x = left;
+ tedPtr->activeRectArr[0].y = top;
+ tedPtr->activeRectArr[0].width = (right - left);
+ tedPtr->activeRectArr[0].height = (bottom - top);
+
+ grip = tedPtr->gripSize;
+ tedPtr->activeRectArr[1].x = (left + right - grip) / 2;
+ tedPtr->activeRectArr[1].y = top;
+ tedPtr->activeRectArr[1].width = grip - 1;
+ tedPtr->activeRectArr[1].height = grip - 1;
+
+ tedPtr->activeRectArr[2].x = left;
+ tedPtr->activeRectArr[2].y = (top + bottom - grip) / 2;
+ tedPtr->activeRectArr[2].width = grip - 1;
+ tedPtr->activeRectArr[2].height = grip - 1;
+
+ tedPtr->activeRectArr[3].x = (left + right - grip) / 2;
+ tedPtr->activeRectArr[3].y = bottom - grip;
+ tedPtr->activeRectArr[3].width = grip - 1;
+ tedPtr->activeRectArr[3].height = grip - 1;
+
+ tedPtr->activeRectArr[4].x = right - grip;
+ tedPtr->activeRectArr[4].y = (top + bottom - grip) / 2;
+ tedPtr->activeRectArr[4].width = grip - 1;
+ tedPtr->activeRectArr[4].height = grip - 1;
+
+ interp->result = Tk_PathName(entryPtr->tkwin);
+ active = 1;
+ break;
+ }
+ }
+ if ((active) || (active != tedPtr->spanActive)) {
+ tedPtr->spanActive = active;
+ EventuallyRedraw(tedPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * EditOp --
+ *
+ * Processes an argv/argc list of table entries to add and configure
+ * new widgets into the table. A table entry consists of the
+ * window path name, table index, and optional configuration options.
+ * The first argument in the argv list is the name of the table. If
+ * no table exists for the given window, a new one is created.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred, TCL_ERROR is
+ * returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ * Memory is allocated, a new master table is possibly created, etc.
+ * The table is re-computed and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+RepOp(dataPtr, interp, argc, argv)
+ TableInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Interp *interp; /* Interpreter to return list of names to */
+ int argc; /* Number of arguments */
+ char **argv;
+{
+ Tk_Window tkwin;
+ Table *tablePtr;
+ Ted *tedPtr;
+
+ /* ted rep master index */
+ tkwin = Tk_NameToWindow(interp, argv[3], Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tablePtr->editPtr != NULL) { /* Already editing this table */
+ tedPtr = (Ted *) tablePtr->editPtr;
+ } else {
+ tedPtr = CreateTed(tablePtr, interp);
+ if (tedPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (ConfigureTed(tedPtr, argc - 3, argv + 3, 0) != TCL_OK) {
+ tedPtr->tkwin = NULL;
+ if (tedPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayTed, tedPtr);
+ }
+ Tcl_EventuallyFree(tedPtr, DestroyTed);
+ return TCL_ERROR;
+ }
+ /* Rearrange the table */
+ if (!(tablePtr->flags & ARRANGE_PENDING)) {
+ tablePtr->flags |= ARRANGE_PENDING;
+ Tcl_DoWhenIdle(tablePtr->arrangeProc, tablePtr);
+ }
+ interp->result = Tk_PathName(tedPtr->tkwin);
+ tedPtr->flags |= LAYOUT_PENDING;
+ EventuallyRedraw(tedPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Command options for the table editor.
+ *
+ * The fields for Blt_OperSpec are as follows:
+ *
+ * - option name
+ * - minimum number of characters required to disambiguate the option name.
+ * - function associated with command option.
+ * - minimum number of arguments required.
+ * - maximum number of arguments allowed (0 indicates no limit).
+ * - usage string
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Blt_OpSpec opSpecs[] =
+{
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "master option",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0,
+ "master ?option...?",},
+ {"edit", 1, (Blt_Op)EditOp, 3, 0, "master ?options...?",},
+ {"rep", 1, (Blt_Op)RepOp, 2, 0, "master index ?options...?",},
+ {"select", 1, (Blt_Op)SelectOp, 4, 0, "master @x,y",},
+ /* {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "master ?master...?",},
+ {"index", 1, (Blt_Op)IndexOp, 3, 0, "master ?item...?",}, */
+};
+static int nSpecs = sizeof(opSpecs) / sizeof(Blt_OpSpec);
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TedCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to the table geometry manager. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+TedCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nSpecs, opSpecs, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+static TableData *
+GetTableInterpData(interp)
+ Tcl_Interp *interp;
+{
+ TableData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (TableData *)Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc);
+ assert(dataPtr);
+ return dataPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Blt_TedInit --
+ *
+ * This procedure is invoked to initialize the Tcl command that
+ * corresponds to the table geometry manager.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds an entry into a global Tcl
+ * associative array.
+ *
+ * ---------------------------------------------------------------------------
+ */
+int
+Blt_TedInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec = {"ted", TedCmd, };
+ TableData *dataPtr;
+
+ dataPtr = GetTableInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
diff --git a/blt/src/bltText.c b/blt/src/bltText.c
new file mode 100644
index 00000000000..af1e2e2c7ba
--- /dev/null
+++ b/blt/src/bltText.c
@@ -0,0 +1,1500 @@
+
+/*
+ * bltText.c --
+ *
+ * This module implements multi-line, rotate-able text for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include <X11/Xutil.h>
+#include "bltImage.h"
+
+#define WINDEBUG 0
+
+#define ROTATE_0 0
+#define ROTATE_90 1
+#define ROTATE_180 2
+#define ROTATE_270 3
+
+static Blt_HashTable bitmapGCTable;
+static int initialized;
+
+static void
+DrawTextLayout(display, drawable, gc, font, x, y, textPtr)
+ Display *display;
+ Drawable drawable;
+ GC gc;
+ Tk_Font font;
+ register int x, y; /* Origin of text */
+ TextLayout *textPtr;
+{
+ register TextFragment *fragPtr;
+ register int i;
+
+ fragPtr = textPtr->fragArr;
+ for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
+#if HAVE_UTF
+ Tk_DrawChars(display, drawable, gc, font, fragPtr->text,
+ fragPtr->count, x + fragPtr->x, y + fragPtr->y);
+#else
+ XDrawString(display, drawable, gc, x + fragPtr->x, y + fragPtr->y,
+ fragPtr->text, fragPtr->count);
+#endif /*HAVE_UTF*/
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetTextLayout --
+ *
+ * Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ * Returns via *widthPtr* and *heightPtr* the dimensions of
+ * the text string.
+ *
+ * -----------------------------------------------------------------
+ */
+TextLayout *
+Blt_GetTextLayout(string, tsPtr)
+ char string[];
+ TextStyle *tsPtr;
+{
+ int maxHeight, maxWidth;
+ int count; /* Count # of characters on each line */
+ int nFrags;
+ int width; /* Running dimensions of the text */
+ TextFragment *fragPtr;
+ TextLayout *textPtr;
+ int lineHeight;
+ int size;
+ register char *p;
+ register int i;
+ Tk_FontMetrics fontMetrics;
+
+ Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
+ lineHeight = fontMetrics.linespace +
+ tsPtr->leader + tsPtr->shadow.offset;
+ nFrags = 0;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '\n') {
+ nFrags++;
+ }
+ }
+ if ((p != string) && (*(p - 1) != '\n')) {
+ nFrags++;
+ }
+ size = sizeof(TextLayout) + (sizeof(TextFragment) * (nFrags - 1));
+ textPtr = Blt_Calloc(1, size);
+ textPtr->nFrags = nFrags;
+ nFrags = count = 0;
+ width = maxWidth = 0;
+ maxHeight = tsPtr->padTop;
+ fragPtr = textPtr->fragArr;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '\n') {
+ if (count > 0) {
+ width = Tk_TextWidth(tsPtr->font, string, count) +
+ tsPtr->shadow.offset;
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ }
+ fragPtr->width = width;
+ fragPtr->count = count;
+ fragPtr->y = maxHeight + fontMetrics.ascent;
+ fragPtr->text = string;
+ fragPtr++;
+ nFrags++;
+ maxHeight += lineHeight;
+ string = p + 1; /* Start the string on the next line */
+ count = 0; /* Reset to indicate the start of a new line */
+ continue;
+ }
+ count++;
+ }
+ if (nFrags < textPtr->nFrags) {
+ width = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ fragPtr->width = width;
+ fragPtr->count = count;
+ fragPtr->y = maxHeight + fontMetrics.ascent;
+ fragPtr->text = string;
+ maxHeight += lineHeight;
+ nFrags++;
+ }
+ maxHeight += tsPtr->padBottom;
+ maxWidth += PADDING(tsPtr->padX);
+ fragPtr = textPtr->fragArr;
+ for (i = 0; i < nFrags; i++, fragPtr++) {
+ switch (tsPtr->justify) {
+ default:
+ case TK_JUSTIFY_LEFT:
+ /* No offset for left justified text strings */
+ fragPtr->x = tsPtr->padLeft;
+ break;
+ case TK_JUSTIFY_RIGHT:
+ fragPtr->x = (maxWidth - fragPtr->width) - tsPtr->padRight;
+ break;
+ case TK_JUSTIFY_CENTER:
+ fragPtr->x = (maxWidth - fragPtr->width) / 2;
+ break;
+ }
+ }
+ textPtr->width = maxWidth;
+ textPtr->height = maxHeight - tsPtr->leader;
+ return textPtr;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetTextExtents --
+ *
+ * Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ * Returns via *widthPtr* and *heightPtr* the dimensions of
+ * the text string.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_GetTextExtents(tsPtr, string, widthPtr, heightPtr)
+ TextStyle *tsPtr;
+ char *string;
+ int *widthPtr, *heightPtr;
+{
+ int count; /* Count # of characters on each line */
+ int width, height;
+ int w, lineHeight;
+ register char *p;
+ Tk_FontMetrics fontMetrics;
+
+ if (string == NULL) {
+ return; /* NULL string? */
+ }
+ Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
+ lineHeight = fontMetrics.linespace + tsPtr->leader + tsPtr->shadow.offset;
+ count = 0;
+ width = height = 0;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '\n') {
+ if (count > 0) {
+ w = Tk_TextWidth(tsPtr->font, string, count) +
+ tsPtr->shadow.offset;
+ if (w > width) {
+ width = w;
+ }
+ }
+ height += lineHeight;
+ string = p + 1; /* Start the string on the next line */
+ count = 0; /* Reset to indicate the start of a new line */
+ continue;
+ }
+ count++;
+ }
+ if ((count > 0) && (*(p - 1) != '\n')) {
+ height += lineHeight;
+ w = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
+ if (w > width) {
+ width = w;
+ }
+ }
+ *widthPtr = width + PADDING(tsPtr->padX);
+ *heightPtr = height + PADDING(tsPtr->padY);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetBoundingBox
+ *
+ * Computes the dimensions of the bounding box surrounding a
+ * rectangle rotated about its center. If pointArr isn't NULL,
+ * the coordinates of the rotated rectangle are also returned.
+ *
+ * The dimensions are determined by rotating the rectangle, and
+ * doubling the maximum x-coordinate and y-coordinate.
+ *
+ * w = 2 * maxX, h = 2 * maxY
+ *
+ * Since the rectangle is centered at 0,0, the coordinates of
+ * the bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2).
+ *
+ * 0 ------- 1
+ * | |
+ * | x |
+ * | |
+ * 3 ------- 2
+ *
+ * Results:
+ * The width and height of the bounding box containing the
+ * rotated rectangle are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_GetBoundingBox(width, height, theta, rotWidthPtr, rotHeightPtr, bbox)
+ int width, height; /* Unrotated region */
+ double theta; /* Rotation of box */
+ int *rotWidthPtr, *rotHeightPtr; /* (out) Bounding box region */
+ Point2D *bbox; /* (out) Points of the rotated box */
+{
+ register int i;
+ double sinTheta, cosTheta;
+ double xMax, yMax;
+ register double x, y;
+ Point2D corner[4];
+
+ theta = FMOD(theta, 360.0);
+ if (FMOD(theta, (double)90.0) == 0.0) {
+ int ll, ur, ul, lr;
+ int rotWidth, rotHeight;
+ int quadrant;
+
+ /* Handle right-angle rotations specifically */
+
+ quadrant = (int)(theta / 90.0);
+ switch (quadrant) {
+ case ROTATE_270: /* 270 degrees */
+ ul = 3, ur = 0, lr = 1, ll = 2;
+ rotWidth = height;
+ rotHeight = width;
+ break;
+ case ROTATE_90: /* 90 degrees */
+ ul = 1, ur = 2, lr = 3, ll = 0;
+ rotWidth = height;
+ rotHeight = width;
+ break;
+ case ROTATE_180: /* 180 degrees */
+ ul = 2, ur = 3, lr = 0, ll = 1;
+ rotWidth = width;
+ rotHeight = height;
+ break;
+ default:
+ case ROTATE_0: /* 0 degrees */
+ ul = 0, ur = 1, lr = 2, ll = 3;
+ rotWidth = width;
+ rotHeight = height;
+ break;
+ }
+ if (bbox != NULL) {
+ x = (double)rotWidth *0.5;
+ y = (double)rotHeight *0.5;
+ bbox[ll].x = bbox[ul].x = -x;
+ bbox[ur].y = bbox[ul].y = -y;
+ bbox[lr].x = bbox[ur].x = x;
+ bbox[ll].y = bbox[lr].y = y;
+ }
+ *rotWidthPtr = rotWidth;
+ *rotHeightPtr = rotHeight;
+ return;
+ }
+ /* Set the four corners of the rectangle whose center is the origin */
+
+ corner[1].x = corner[2].x = (double)width *0.5;
+ corner[0].x = corner[3].x = -corner[1].x;
+ corner[2].y = corner[3].y = (double)height *0.5;
+ corner[0].y = corner[1].y = -corner[2].y;
+
+ theta = (-theta / 180.0) * M_PI;
+ sinTheta = sin(theta), cosTheta = cos(theta);
+ xMax = yMax = 0.0;
+
+ /* Rotate the four corners and find the maximum X and Y coordinates */
+
+ for (i = 0; i < 4; i++) {
+ x = (corner[i].x * cosTheta) - (corner[i].y * sinTheta);
+ y = (corner[i].x * sinTheta) + (corner[i].y * cosTheta);
+ if (x > xMax) {
+ xMax = x;
+ }
+ if (y > yMax) {
+ yMax = y;
+ }
+ if (bbox != NULL) {
+ bbox[i].x = x;
+ bbox[i].y = y;
+ }
+ }
+
+ /*
+ * By symmetry, the width and height of the bounding box are
+ * twice the maximum x and y coordinates.
+ */
+ *rotWidthPtr = (int)((xMax + xMax) + 0.5);
+ *rotHeightPtr = (int)((yMax + yMax) + 0.5);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based
+ * upon the anchor specified. The anchor indicates where
+ * the given xy position is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | |
+ * w center e
+ * | |
+ * sw --- s --- se
+ *
+ * The coordinates returned are translated to the origin of the
+ * bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_TranslateAnchor(x, y, width, height, anchor, transXPtr, transYPtr)
+ int x, y; /* Window coordinates of anchor */
+ int width, height; /* Extents of the bounding box */
+ Tk_Anchor anchor; /* Direction of the anchor */
+ int *transXPtr, *transYPtr;
+{
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y -= (height / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y -= height;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x -= (width / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ x -= (width / 2);
+ y -= (height / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x -= (width / 2);
+ y -= height;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x -= width;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x -= width;
+ y -= (height / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x -= width;
+ y -= height;
+ break;
+ }
+ *transXPtr = x;
+ *transYPtr = y;
+}
+
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TranslatePoint --
+ *
+ * Translate the coordinates of a given bounding box based
+ * upon the anchor specified. The anchor indicates where
+ * the given xy position is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | |
+ * w center e
+ * | |
+ * sw --- s --- se
+ *
+ * The coordinates returned are translated to the origin of the
+ * bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+Point2D
+Blt_TranslatePoint(pointPtr, width, height, anchor)
+ Point2D *pointPtr; /* Window coordinates of anchor */
+ int width, height; /* Extents of the bounding box */
+ Tk_Anchor anchor; /* Direction of the anchor */
+{
+ Point2D trans;
+
+ trans = *pointPtr;
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ trans.y -= (height * 0.5);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ trans.y -= height;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ trans.x -= (width * 0.5);
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ trans.x -= (width * 0.5);
+ trans.y -= (height * 0.5);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ trans.x -= (width * 0.5);
+ trans.y -= height;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ trans.x -= width;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ trans.x -= width;
+ trans.y -= (height * 0.5);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ trans.x -= width;
+ trans.y -= height;
+ break;
+ }
+ return trans;
+}
+
+#ifdef WIN32
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_RotateBitmap --
+ *
+ * Creates a new bitmap containing the rotated image of the given
+ * bitmap. We also need a special GC of depth 1, so that we do
+ * not need to rotate more than one plane of the bitmap.
+ *
+ * Results:
+ * Returns a new bitmap containing the rotated image.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_RotateBitmap(
+ Tk_Window tkwin,
+ Pixmap srcBitmap, /* Source bitmap to be rotated */
+ int srcWidth,
+ int srcHeight, /* Width and height of the source bitmap */
+ double theta, /* Right angle rotation to perform */
+ int *destWidthPtr,
+ int *destHeightPtr)
+{
+ Display *display; /* X display */
+ Window root; /* Root window drawable */
+ Pixmap destBitmap;
+ int destWidth, destHeight;
+ HDC hDC;
+ TkWinDCState state;
+ register int x, y; /* Destination bitmap coordinates */
+ register int sx, sy; /* Source bitmap coordinates */
+ unsigned long pixel;
+ HBITMAP hBitmap;
+ int result;
+ struct MonoBitmap {
+ BITMAPINFOHEADER bi;
+ RGBQUAD colors[2];
+ } mb;
+ unsigned char *srcBits, *destBits;
+ int srcBytesPerRow, destBytesPerRow;
+
+ display = Tk_Display(tkwin);
+ root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+ Blt_GetBoundingBox(srcWidth, srcHeight, theta, &destWidth, &destHeight,
+ (Point2D *)NULL);
+ destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+ if (destBitmap == None) {
+ return None; /* Can't allocate pixmap. */
+ }
+ destBits = srcBits = NULL;
+
+ srcBits = Blt_GetBitmapData(display, srcBitmap, srcWidth, srcHeight,
+ &srcBytesPerRow);
+ if (srcBits == NULL) {
+ OutputDebugString("Blt_GetBitmapData failed");
+ return None;
+ }
+ destBytesPerRow = ((destWidth + 31) & ~31) / 8;
+ destBits = Blt_Calloc(destHeight, destBytesPerRow);
+#define GetBit(x, y) \
+ srcBits[(srcBytesPerRow * y) + (x / 8)] & (0x80 >> (x % 8))
+#define SetBit(x, y) \
+ destBits[(destBytesPerRow * y) + (x / 8)] |= (0x80 >> (x % 8))
+
+ theta = FMOD(theta, 360.0);
+ if (FMOD(theta, (double)90.0) == 0.0) {
+ int quadrant;
+
+ /* Handle right-angle rotations specifically */
+
+ quadrant = (int)(theta / 90.0);
+ switch (quadrant) {
+ case ROTATE_90: /* 90 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = y, sy = destWidth - x - 1;
+ pixel = GetBit(sx, sy);
+ if (pixel) {
+ SetBit(x, y);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_0: /* 0 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = destWidth - x - 1, sy = destHeight - y - 1;
+ pixel = GetBit(sx, sy);
+ if (pixel) {
+ SetBit(x, y);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_270: /* 270 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = destHeight - y - 1, sy = x;
+ pixel = GetBit(sx, sy);
+ if (pixel) {
+ SetBit(x, y);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_180: /* 180 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ pixel = GetBit(x, y);
+ if (pixel) {
+ SetBit(x, y);
+ }
+ }
+ }
+ break;
+
+ default:
+ /* The calling routine should never let this happen. */
+ break;
+ }
+ } else {
+ double radians, sinTheta, cosTheta;
+ double srcCX, srcCY; /* Center of source rectangle */
+ double destCX, destCY; /* Center of destination rectangle */
+ double tx, ty;
+ double rx, ry; /* Angle of rotation for x and y coordinates */
+
+ radians = (theta / 180.0) * M_PI;
+ sinTheta = sin(-radians), cosTheta = cos(-radians);
+
+ /*
+ * Coordinates of the centers of the source and destination rectangles
+ */
+ srcCX = srcWidth * 0.5;
+ srcCY = srcHeight * 0.5;
+ destCX = destWidth * 0.5;
+ destCY = destHeight * 0.5;
+
+ /* Rotate each pixel of dest image, placing results in source image */
+
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+
+ /* Translate origin to center of destination image */
+ tx = x - destCX;
+ ty = y - destCY;
+
+ /* Rotate the coordinates about the origin */
+ rx = (tx * cosTheta) - (ty * sinTheta);
+ ry = (tx * sinTheta) + (ty * cosTheta);
+
+ /* Translate back to the center of the source image */
+ rx += srcCX;
+ ry += srcCY;
+
+ sx = ROUND(rx);
+ sy = ROUND(ry);
+
+ /*
+ * Verify the coordinates, since the destination image can be
+ * bigger than the source
+ */
+
+ if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) ||
+ (sy < 0)) {
+ continue;
+ }
+ pixel = GetBit(sx, sy);
+ if (pixel) {
+ SetBit(x, y);
+ }
+ }
+ }
+ }
+ hBitmap = ((TkWinDrawable *)destBitmap)->bitmap.handle;
+ ZeroMemory(&mb, sizeof(mb));
+ mb.bi.biSize = sizeof(BITMAPINFOHEADER);
+ mb.bi.biPlanes = 1;
+ mb.bi.biBitCount = 1;
+ mb.bi.biCompression = BI_RGB;
+ mb.bi.biWidth = destWidth;
+ mb.bi.biHeight = destHeight;
+ mb.bi.biSizeImage = destBytesPerRow * destHeight;
+ mb.colors[0].rgbBlue = mb.colors[0].rgbRed = mb.colors[0].rgbGreen = 0x0;
+ mb.colors[1].rgbBlue = mb.colors[1].rgbRed = mb.colors[1].rgbGreen = 0xFF;
+ hDC = TkWinGetDrawableDC(display, destBitmap, &state);
+ result = SetDIBits(hDC, hBitmap, 0, destHeight, (LPVOID)destBits,
+ (BITMAPINFO *)&mb, DIB_RGB_COLORS);
+ TkWinReleaseDrawableDC(destBitmap, hDC, &state);
+ if (!result) {
+#if WINDEBUG
+ PurifyPrintf("can't setDIBits: %s\n", Blt_LastError());
+#endif
+ destBitmap = None;
+ }
+ if (destBits != NULL) {
+ Blt_Free(destBits);
+ }
+ if (srcBits != NULL) {
+ Blt_Free(srcBits);
+ }
+
+ *destWidthPtr = destWidth;
+ *destHeightPtr = destHeight;
+ return destBitmap;
+}
+
+#else
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_RotateBitmap --
+ *
+ * Creates a new bitmap containing the rotated image of the given
+ * bitmap. We also need a special GC of depth 1, so that we do
+ * not need to rotate more than one plane of the bitmap.
+ *
+ * Results:
+ * Returns a new bitmap containing the rotated image.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_RotateBitmap(tkwin, srcBitmap, srcWidth, srcHeight, theta,
+ destWidthPtr, destHeightPtr)
+ Tk_Window tkwin;
+ Pixmap srcBitmap; /* Source bitmap to be rotated */
+ int srcWidth, srcHeight; /* Width and height of the source bitmap */
+ double theta; /* Right angle rotation to perform */
+ int *destWidthPtr, *destHeightPtr;
+{
+ Display *display; /* X display */
+ Window root; /* Root window drawable */
+ Pixmap destBitmap;
+ int destWidth, destHeight;
+ XImage *src, *dest;
+ register int x, y; /* Destination bitmap coordinates */
+ register int sx, sy; /* Source bitmap coordinates */
+ unsigned long pixel;
+ GC bitmapGC;
+
+ display = Tk_Display(tkwin);
+ root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+
+ /* Create a bitmap and image big enough to contain the rotated text */
+ Blt_GetBoundingBox(srcWidth, srcHeight, theta, &destWidth, &destHeight,
+ (Point2D *)NULL);
+ destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+ bitmapGC = Blt_GetBitmapGC(tkwin);
+ XSetForeground(display, bitmapGC, 0x0);
+ XFillRectangle(display, destBitmap, bitmapGC, 0, 0, destWidth, destHeight);
+
+ src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap);
+ dest = XGetImage(display, destBitmap, 0, 0, destWidth, destHeight, 1,
+ ZPixmap);
+ theta = FMOD(theta, 360.0);
+ if (FMOD(theta, (double)90.0) == 0.0) {
+ int quadrant;
+
+ /* Handle right-angle rotations specifically */
+
+ quadrant = (int)(theta / 90.0);
+ switch (quadrant) {
+ case ROTATE_270: /* 270 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = y, sy = destWidth - x - 1;
+ pixel = XGetPixel(src, sx, sy);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_180: /* 180 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = destWidth - x - 1, sy = destHeight - y - 1;
+ pixel = XGetPixel(src, sx, sy);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_90: /* 90 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ sx = destHeight - y - 1, sy = x;
+ pixel = XGetPixel(src, sx, sy);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+ break;
+
+ case ROTATE_0: /* 0 degrees */
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+ pixel = XGetPixel(src, x, y);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+ break;
+
+ default:
+ /* The calling routine should never let this happen. */
+ break;
+ }
+ } else {
+ double radians, sinTheta, cosTheta;
+ double srcCX, srcCY; /* Offset from the center of
+ * the source rectangle. */
+ double destCX, destCY; /* Offset to the center of the destination
+ * rectangle. */
+ double tx, ty; /* Translated coordinates from center */
+ double rx, ry; /* Angle of rotation for x and y coordinates */
+
+ radians = (theta / 180.0) * M_PI;
+ sinTheta = sin(radians), cosTheta = cos(radians);
+
+ /*
+ * Coordinates of the centers of the source and destination rectangles
+ */
+ srcCX = srcWidth * 0.5;
+ srcCY = srcHeight * 0.5;
+ destCX = destWidth * 0.5;
+ destCY = destHeight * 0.5;
+
+ /* Rotate each pixel of dest image, placing results in source image */
+
+ for (x = 0; x < destWidth; x++) {
+ for (y = 0; y < destHeight; y++) {
+
+ /* Translate origin to center of destination image */
+ tx = x - destCX;
+ ty = y - destCY;
+
+ /* Rotate the coordinates about the origin */
+ rx = (tx * cosTheta) - (ty * sinTheta);
+ ry = (tx * sinTheta) + (ty * cosTheta);
+
+ /* Translate back to the center of the source image */
+ rx += srcCX;
+ ry += srcCY;
+
+ sx = ROUND(rx);
+ sy = ROUND(ry);
+
+ /*
+ * Verify the coordinates, since the destination image can be
+ * bigger than the source
+ */
+
+ if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) ||
+ (sy < 0)) {
+ continue;
+ }
+ pixel = XGetPixel(src, sx, sy);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+ }
+ /* Write the rotated image into the destination bitmap */
+ XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, destWidth,
+ destHeight);
+
+ /* Clean up temporary resources used */
+ XDestroyImage(src), XDestroyImage(dest);
+ *destWidthPtr = destWidth;
+ *destHeightPtr = destHeight;
+ return destBitmap;
+}
+
+#endif /* WIN32 */
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_CreateTextBitmap --
+ *
+ * Draw a bitmap, using the the given window coordinates
+ * as an anchor for the text bounding box.
+ *
+ * Results:
+ * Returns the bitmap representing the text string.
+ *
+ * Side Effects:
+ * Bitmap is drawn using the given font and GC in the
+ * drawable at the given coordinates, anchor, and rotation.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, bmWidthPtr, bmHeightPtr)
+ Tk_Window tkwin;
+ TextLayout *textPtr; /* Text string to draw */
+ TextStyle *tsPtr; /* Text attributes: rotation, color, font,
+ * linespacing, justification, etc. */
+ int *bmWidthPtr;
+ int *bmHeightPtr; /* Extents of rotated text string */
+{
+ int width, height;
+ Pixmap bitmap;
+ Display *display;
+ Window root;
+ GC gc;
+#ifdef WIN32
+ HDC hDC;
+ TkWinDCState state;
+#endif
+ display = Tk_Display(tkwin);
+
+ width = textPtr->width;
+ height = textPtr->height;
+
+ /* Create a temporary bitmap to contain the text string */
+ root = RootWindow(display, Tk_ScreenNumber(tkwin));
+ bitmap = Tk_GetPixmap(display, root, width, height, 1);
+ assert(bitmap != None);
+ if (bitmap == None) {
+ return None; /* Can't allocate pixmap. */
+ }
+ /* Clear the pixmap and draw the text string into it */
+ gc = Blt_GetBitmapGC(tkwin);
+#ifdef WIN32
+ hDC = TkWinGetDrawableDC(display, bitmap, &state);
+ PatBlt(hDC, 0, 0, width, height, WHITENESS);
+ TkWinReleaseDrawableDC(bitmap, hDC, &state);
+#else
+ XSetForeground(display, gc, 0);
+ XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+#endif /* WIN32 */
+
+ XSetFont(display, gc, Tk_FontId(tsPtr->font));
+ XSetForeground(display, gc, 1);
+ DrawTextLayout(display, bitmap, gc, tsPtr->font, 0, 0, textPtr);
+
+#ifdef WIN32
+ /*
+ * Under Win32 when drawing into a bitmap, the bits are
+ * reversed. Which is why we are inverting the bitmap here.
+ */
+ hDC = TkWinGetDrawableDC(display, bitmap, &state);
+ PatBlt(hDC, 0, 0, width, height, DSTINVERT);
+ TkWinReleaseDrawableDC(bitmap, hDC, &state);
+#endif
+ if (tsPtr->theta != 0.0) {
+ Pixmap rotBitmap;
+
+ /* Replace the text pixmap with a rotated one */
+
+ rotBitmap = Blt_RotateBitmap(tkwin, bitmap, width, height,
+ tsPtr->theta, bmWidthPtr, bmHeightPtr);
+ assert(rotBitmap);
+ if (rotBitmap != None) {
+ Tk_FreePixmap(display, bitmap);
+ return rotBitmap;
+ }
+ }
+ *bmWidthPtr = textPtr->width, *bmHeightPtr = textPtr->height;
+ return bitmap;
+}
+
+#ifdef WIN32
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleBitmapRegion --
+ *
+ * Creates a new scaled bitmap from another bitmap. The new bitmap
+ * is bounded by a specified region. Only this portion of the bitmap
+ * is scaled from the original bitmap.
+ *
+ * By bounding scaling to a region we can generate a new bitmap
+ * which is no bigger than the specified viewport.
+ *
+ * Results:
+ * The new scaled bitmap is returned.
+ *
+ * Side Effects:
+ * A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleBitmapRegion(
+ Tk_Window tkwin,
+ Pixmap srcBitmap,
+ int srcWidth,
+ int srcHeight,
+ int destWidth,
+ int destHeight,
+ Region2D *regionPtr)
+{
+ TkWinDCState srcState, destState;
+ HDC src, dest;
+ Pixmap destBitmap;
+ double xScale, yScale;
+ register int sx, sy; /* Source bitmap coordinates */
+ Window root;
+ Display *display;
+ int width, height;
+
+ /* Create a new bitmap the size of the region and clear it */
+
+ display = Tk_Display(tkwin);
+ root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+ width = RegionWidth(regionPtr);
+ height = RegionHeight(regionPtr);
+ destBitmap = Tk_GetPixmap(display, root, width, height, 1);
+ if (destBitmap == None) {
+ return None;
+ }
+ /* Compute scaling factors from destination to source bitmaps */
+ xScale = (double)srcWidth / (double)destWidth;
+ yScale = (double)srcHeight / (double)destHeight;
+
+ src = TkWinGetDrawableDC(display, srcBitmap, &srcState);
+ dest = TkWinGetDrawableDC(display, destBitmap, &destState);
+
+ sx = (int)(regionPtr->left * xScale + 0.5);
+ sy = (int)(regionPtr->top * yScale + 0.5);
+ srcWidth = (int)(width * xScale + 0.5);
+ srcHeight = (int)(height * yScale + 0.5);
+
+ StretchBlt(dest, 0, 0, destWidth, destHeight, src, sx, sy,
+ srcWidth, srcHeight, SRCCOPY);
+
+ TkWinReleaseDrawableDC(srcBitmap, src, &srcState);
+ TkWinReleaseDrawableDC(destBitmap, dest, &destState);
+ return destBitmap;
+}
+
+#else
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleBitmapRegion --
+ *
+ * Creates a new scaled bitmap from another bitmap. The new bitmap
+ * is bounded by a specified region. Only this portion of the bitmap
+ * is scaled from the original bitmap.
+ *
+ * By bounding scaling to a region we can generate a new bitmap
+ * which is no bigger than the specified viewport.
+ *
+ * Results:
+ * The new scaled bitmap is returned.
+ *
+ * Side Effects:
+ * A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight,
+ destWidth, destHeight, regionPtr)
+ Tk_Window tkwin;
+ Pixmap srcBitmap;
+ int srcWidth, srcHeight, destWidth, destHeight;
+ Region2D *regionPtr;
+{
+ Display *display;
+ Window root;
+ XImage *src, *dest;
+ Pixmap destBitmap;
+ GC bitmapGC;
+ double xScale, yScale;
+ register int x, y; /* Destination bitmap coordinates */
+ register int sx, sy; /* Source bitmap coordinates */
+ unsigned long pixel;
+ double tmp;
+ int width, height;
+
+ /* Create a new bitmap the size of the region and clear it */
+
+ display = Tk_Display(tkwin);
+
+ width = RegionWidth(regionPtr);
+ height = RegionHeight(regionPtr);
+ root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+ destBitmap = Tk_GetPixmap(display, root, width, height, 1);
+ bitmapGC = Blt_GetBitmapGC(tkwin);
+ XSetForeground(display, bitmapGC, 0x0);
+ XFillRectangle(display, destBitmap, bitmapGC, 0, 0, width, height);
+
+ src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap);
+ dest = XGetImage(display, destBitmap, 0, 0, width, height, 1, ZPixmap);
+
+ /*
+ * Scale each pixel of destination image from results of source
+ * image. Verify the coordinates, since the destination image can
+ * be bigger than the source
+ */
+ xScale = (double)srcWidth / (double)destWidth;
+ yScale = (double)srcHeight / (double)destHeight;
+
+ for (y = 0; y < height; y++) {
+ tmp = (double)(y + regionPtr->top) * yScale;
+ sy = ROUND(tmp);
+ if (sy >= srcHeight) {
+ continue;
+ }
+ for (x = 0; x < width; x++) {
+ tmp = (double)(x + regionPtr->left) * xScale;
+ sx = ROUND(tmp);
+ if (sx >= srcWidth) {
+ continue;
+ }
+ pixel = XGetPixel(src, sx, sy);
+ if (pixel) {
+ XPutPixel(dest, x, y, pixel);
+ }
+ }
+ }
+
+ /* Write the rotated image into the destination bitmap */
+
+ XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, width, height);
+ XDestroyImage(src), XDestroyImage(dest);
+ return destBitmap;
+}
+
+#endif /* WIN32 */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleBitmap --
+ *
+ * Same as Blt_ScaleBitmapRegion, except that the region is unbounded.
+ * The scaled bitmap will be a fully scaled version of the original,
+ * not a portion of it.
+ *
+ * Results:
+ * The new scaled bitmap is returned.
+ *
+ * Side Effects:
+ * A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleBitmap(tkwin, srcBitmap, srcWidth, srcHeight, scaledWidth,
+ scaledHeight)
+ Tk_Window tkwin;
+ Pixmap srcBitmap;
+ int srcWidth, srcHeight, scaledWidth, scaledHeight;
+{
+ Region2D region;
+
+ Blt_SetRegion(0, 0, scaledWidth, scaledHeight, &region);
+ return Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight,
+ scaledWidth, scaledHeight, &region);
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InitTextStyle(tsPtr)
+ TextStyle *tsPtr;
+{
+ /* Initialize these attributes to zero */
+ tsPtr->activeColor = (XColor *)NULL;
+ tsPtr->anchor = TK_ANCHOR_CENTER;
+ tsPtr->color = (XColor *)NULL;
+ tsPtr->font = NULL;
+ tsPtr->justify = TK_JUSTIFY_CENTER;
+ tsPtr->leader = 0;
+ tsPtr->padLeft = tsPtr->padRight = 0;
+ tsPtr->padTop = tsPtr->padBottom = 0;
+ tsPtr->shadow.color = (XColor *)NULL;
+ tsPtr->shadow.offset = 0;
+ tsPtr->state = 0;
+ tsPtr->theta = 0.0;
+}
+
+void
+Blt_SetDrawTextStyle(tsPtr, font, gc, normalColor, activeColor, shadowColor,
+ theta, anchor, justify, leader, shadowOffset)
+ TextStyle *tsPtr;
+ Tk_Font font;
+ GC gc;
+ XColor *normalColor, *activeColor, *shadowColor;
+ double theta;
+ Tk_Anchor anchor;
+ Tk_Justify justify;
+ int leader, shadowOffset;
+{
+ Blt_InitTextStyle(tsPtr);
+ tsPtr->activeColor = activeColor;
+ tsPtr->anchor = anchor;
+ tsPtr->color = normalColor;
+ tsPtr->font = font;
+ tsPtr->gc = gc;
+ tsPtr->justify = justify;
+ tsPtr->leader = leader;
+ tsPtr->shadow.color = shadowColor;
+ tsPtr->shadow.offset = shadowOffset;
+ tsPtr->theta = theta;
+}
+
+void
+Blt_SetPrintTextStyle(tsPtr, font, fgColor, activeColor, shadowColor, theta,
+ anchor, justify, leader, shadowOffset)
+ TextStyle *tsPtr;
+ Tk_Font font;
+ XColor *fgColor, *activeColor, *shadowColor;
+ double theta;
+ Tk_Anchor anchor;
+ Tk_Justify justify;
+ int leader, shadowOffset;
+{
+ Blt_InitTextStyle(tsPtr);
+ tsPtr->color = fgColor;
+ tsPtr->activeColor = activeColor;
+ tsPtr->shadow.color = shadowColor;
+ tsPtr->font = font;
+ tsPtr->theta = theta;
+ tsPtr->anchor = anchor;
+ tsPtr->justify = justify;
+ tsPtr->leader = leader;
+ tsPtr->shadow.offset = shadowOffset;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawTextLayout --
+ *
+ * Draw a text string, possibly rotated, using the the given
+ * window coordinates as an anchor for the text bounding box.
+ * If the text is not rotated, simply use the X text drawing
+ * routines. Otherwise, generate a bitmap of the rotated text.
+ *
+ * Results:
+ * Returns the x-coordinate to the right of the text.
+ *
+ * Side Effects:
+ * Text string is drawn using the given font and GC at the
+ * the given window coordinates.
+ *
+ * The Stipple, FillStyle, and TSOrigin fields of the GC are
+ * modified for rotated text. This assumes the GC is private,
+ * *not* shared (via Tk_GetGC)
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y)
+ Tk_Window tkwin;
+ Drawable drawable;
+ TextLayout *textPtr;
+ TextStyle *tsPtr; /* Text attribute information */
+ int x, y; /* Window coordinates to draw text */
+{
+ int width, height;
+ double theta;
+ Display *display;
+ Pixmap bitmap;
+ int active;
+
+ display = Tk_Display(tkwin);
+ theta = FMOD(tsPtr->theta, (double)360.0);
+ if (theta < 0.0) {
+ theta += 360.0;
+ }
+ active = tsPtr->state & STATE_ACTIVE;
+ if (theta == 0.0) {
+
+ /*
+ * This is the easy case of no rotation. Simply draw the text
+ * using the standard drawing routines. Handle offset printing
+ * for engraved (disabled) and shadowed text.
+ */
+ width = textPtr->width, height = textPtr->height;
+ Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
+ if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (tsPtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ XSetForeground(display, tsPtr->gc, color1->pixel);
+ }
+ DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x + 1,
+ y + 1, textPtr);
+ if (color2 != NULL) {
+ XSetForeground(display, tsPtr->gc, color2->pixel);
+ }
+ DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y,
+ textPtr);
+
+ /* Reset the foreground color back to its original setting,
+ * so not to invalidate the GC cache. */
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+
+ return; /* Done */
+ }
+ if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+ XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
+ DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font,
+ x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, textPtr);
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+ }
+ if (active) {
+ XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
+ }
+ DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y,
+ textPtr);
+ if (active) {
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+ }
+ return; /* Done */
+ }
+#ifdef WIN32
+ if (Blt_DrawRotatedText(display, drawable, x, y, theta, tsPtr, textPtr)) {
+ return;
+ }
+#endif
+ /*
+ * Rotate the text by writing the text into a bitmap and rotating
+ * the bitmap. Set the clip mask and origin in the GC first. And
+ * make sure we restore the GC because it may be shared.
+ */
+ tsPtr->theta = theta;
+ bitmap = Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, &width, &height);
+ if (bitmap == None) {
+ return;
+ }
+ Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
+ theta = FMOD(theta, (double)90.0);
+ XSetClipMask(display, tsPtr->gc, bitmap);
+
+ if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (tsPtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ XSetForeground(display, tsPtr->gc, color1->pixel);
+ }
+ XSetClipOrigin(display, tsPtr->gc, x + 1, y + 1);
+ XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
+ height, x + 1, y + 1, 1);
+ if (color2 != NULL) {
+ XSetForeground(display, tsPtr->gc, color2->pixel);
+ }
+ XSetClipOrigin(display, tsPtr->gc, x, y);
+ XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
+ height, x, y, 1);
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+ } else {
+ if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+ XSetClipOrigin(display, tsPtr->gc, x + tsPtr->shadow.offset,
+ y + tsPtr->shadow.offset);
+ XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
+ XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width,
+ height, x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, 1);
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+ }
+ if (active) {
+ XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
+ }
+ XSetClipOrigin(display, tsPtr->gc, x, y);
+ XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, height,
+ x, y, 1);
+ if (active) {
+ XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+ }
+ }
+ XSetClipMask(display, tsPtr->gc, None);
+ Tk_FreePixmap(display, bitmap);
+}
+
+void
+Blt_DrawText2(tkwin, drawable, string, tsPtr, x, y, areaPtr)
+ Tk_Window tkwin;
+ Drawable drawable;
+ char string[];
+ TextStyle *tsPtr; /* Text attribute information */
+ int x, y; /* Window coordinates to draw text */
+ Dim2D *areaPtr;
+{
+ TextLayout *textPtr;
+ int width, height;
+ double theta;
+
+ if ((string == NULL) || (*string == '\0')) {
+ return; /* Empty string, do nothing */
+ }
+ textPtr = Blt_GetTextLayout(string, tsPtr);
+ Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
+ theta = FMOD(tsPtr->theta, (double)360.0);
+ if (theta < 0.0) {
+ theta += 360.0;
+ }
+ width = textPtr->width;
+ height = textPtr->height;
+ if (theta != 0.0) {
+ Blt_GetBoundingBox(width, height, theta, &width, &height,
+ (Point2D *)NULL);
+ }
+ Blt_Free(textPtr);
+ areaPtr->width = width;
+ areaPtr->height = height;
+}
+
+void
+Blt_DrawText(tkwin, drawable, string, tsPtr, x, y)
+ Tk_Window tkwin;
+ Drawable drawable;
+ char string[];
+ TextStyle *tsPtr; /* Text attribute information */
+ int x, y; /* Window coordinates to draw text */
+{
+ TextLayout *textPtr;
+
+ if ((string == NULL) || (*string == '\0')) {
+ return; /* Empty string, do nothing */
+ }
+ textPtr = Blt_GetTextLayout(string, tsPtr);
+ Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
+ Blt_Free(textPtr);
+}
+
+GC
+Blt_GetBitmapGC(tkwin)
+ Tk_Window tkwin;
+{
+ int isNew;
+ GC gc;
+ Display *display;
+ Blt_HashEntry *hPtr;
+
+ if (!initialized) {
+ Blt_InitHashTable(&bitmapGCTable, BLT_ONE_WORD_KEYS);
+ initialized = TRUE;
+ }
+ display = Tk_Display(tkwin);
+ hPtr = Blt_CreateHashEntry(&bitmapGCTable, (char *)display, &isNew);
+ if (isNew) {
+ Pixmap bitmap;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Window root;
+
+ root = RootWindow(display, Tk_ScreenNumber(tkwin));
+ bitmap = Tk_GetPixmap(display, root, 1, 1, 1);
+ gcValues.foreground = gcValues.background = 0;
+ gcMask = (GCForeground | GCBackground);
+ gc = Blt_GetPrivateGCFromDrawable(display, bitmap, gcMask, &gcValues);
+ Tk_FreePixmap(display, bitmap);
+ Blt_SetHashValue(hPtr, gc);
+ } else {
+ gc = (GC)Blt_GetHashValue(hPtr);
+ }
+ return gc;
+}
+
+void
+Blt_ResetTextStyle(tkwin, tsPtr)
+ Tk_Window tkwin;
+ TextStyle *tsPtr;
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ gcMask = GCFont;
+ gcValues.font = Tk_FontId(tsPtr->font);
+ if (tsPtr->color != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = tsPtr->color->pixel;
+ }
+ newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+ if (tsPtr->gc != NULL) {
+ Tk_FreeGC(Tk_Display(tkwin), tsPtr->gc);
+ }
+ tsPtr->gc = newGC;
+}
+
+
+void
+Blt_FreeTextStyle(display, tsPtr)
+ Display *display;
+ TextStyle *tsPtr;
+{
+ if (tsPtr->gc != NULL) {
+ Tk_FreeGC(display, tsPtr->gc);
+ }
+}
diff --git a/blt/src/bltText.h b/blt/src/bltText.h
new file mode 100644
index 00000000000..decd624a75e
--- /dev/null
+++ b/blt/src/bltText.h
@@ -0,0 +1,212 @@
+/*
+ * bltText.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_TEXT_H
+#define _BLT_TEXT_H
+
+#if (TK_MAJOR_VERSION == 4)
+
+/*
+ * The following structure is used by Tk_GetFontMetrics() to return
+ * information about the properties of a Tk_Font.
+ */
+typedef struct {
+ int ascent; /* The amount in pixels that the tallest
+ * letter sticks up above the baseline, plus
+ * any extra blank space added by the designer
+ * of the font. */
+ int descent; /* The largest amount in pixels that any
+ * letter sticks below the baseline, plus any
+ * extra blank space added by the designer of
+ * the font. */
+ int linespace; /* The sum of the ascent and descent. How
+ * far apart two lines of text in the same
+ * font should be placed so that none of the
+ * characters in one line overlap any of the
+ * characters in the other line. */
+} Tk_FontMetrics;
+
+typedef XFontStruct *Tk_Font;
+
+#define Tk_FontId(font) ((font)->fid)
+#define Tk_TextWidth(font, str, len) (XTextWidth((font),(str),(len)))
+#define Tk_GetFontMetrics(font, fmPtr) \
+ ((fmPtr)->ascent = (font)->ascent, \
+ (fmPtr)->descent = (font)->descent, \
+ (fmPtr)->linespace = (font)->ascent + (font)->descent)
+
+#define Tk_NameOfFont(font) (Tk_NameOfFontStruct(font))
+#define Tk_DrawChars(dpy, draw, gc, font, str, len, x, y) \
+ TkDisplayChars((dpy),(draw),(gc),(font),(str),(len),(x),(y), 0, DEF_TEXT_FLAGS)
+
+#define Tk_MeasureChars(font, text, len, maxPixels, flags, lenPtr) \
+ TkMeasureChars((font),(text), (len), 0, maxPixels, 0,(flags), (lenPtr))
+
+extern int TkMeasureChars _ANSI_ARGS_((Tk_Font font, char *source,
+ int maxChars, int startX, int maxX, int tabOrigin, int flags,
+ int *nextXPtr));
+extern void TkDisplayChars _ANSI_ARGS_((Display *display, Drawable drawable,
+ GC gc, Tk_Font font, char *string, int length, int x, int y,
+ int tabOrigin, int flags));
+/*
+ * FLAGS passed to TkMeasureChars:
+ */
+#define TK_WHOLE_WORDS (1<<0)
+#define TK_AT_LEAST_ONE (1<<1)
+#define TK_PARTIAL_OK (1<<2)
+#define TK_IGNORE_NEWLINES (1<<3)
+#define TK_IGNORE_TABS (1<<4)
+#define NO_FLAGS 0
+
+#endif /* TK_MAJOR_VERSION == 4 */
+
+#define DEF_TEXT_FLAGS (TK_PARTIAL_OK | TK_IGNORE_NEWLINES)
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextFragment --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ char *text; /* Text to be displayed */
+
+ short int x, y; /* X-Y offset of the baseline from the
+ * upper-left corner of the bbox. */
+
+ short int sx, sy; /* See bltWinUtil.c */
+
+ short int count; /* Number of bytes in text. The actual
+ * character count may differ because of
+ * multi-byte UTF encodings. */
+
+ short int width; /* Width of segment in pixels. This
+ * information is used to draw
+ * PostScript strings the same width
+ * as X. */
+} TextFragment;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextLayout --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ int nFrags; /* # fragments of text */
+ short int width, height; /* Dimensions of text bounding box */
+ TextFragment fragArr[1]; /* Information about each fragment of text */
+} TextLayout;
+
+typedef struct {
+ XColor *color;
+ int offset;
+} Shadow;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextStyle --
+ *
+ * Represents a convenient structure to hold text attributes
+ * which determine how a text string is to be displayed on the
+ * window, or drawn with PostScript commands. The alternative
+ * is to pass lots of parameters to the drawing and printing
+ * routines. This seems like a more efficient and less cumbersome
+ * way of passing parameters.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+ unsigned int state; /* If non-zero, indicates to draw text
+ * in the active color */
+ short int width, height; /* Extents of text */
+
+ XColor *color; /* Normal color */
+ XColor *activeColor; /* Active color */
+ Tk_Font font; /* Font to use to draw text */
+ Tk_3DBorder border; /* Background color of text. This is also
+ * used for drawing disabled text. */
+ Shadow shadow; /* Drop shadow color and offset */
+ Tk_Justify justify; /* Justification of the text string. This
+ * only matters if the text is composed
+ * of multiple lines. */
+ GC gc; /* GC used to draw the text */
+ double theta; /* Rotation of text in degrees. */
+ Tk_Anchor anchor; /* Indicates how the text is anchored around
+ * its x and y coordinates. */
+ Blt_Pad padX, padY; /* # pixels padding of around text region */
+ short int leader; /* # pixels spacing between lines of text */
+
+} TextStyle;
+
+
+extern TextLayout *Blt_GetTextLayout _ANSI_ARGS_((char *string,
+ TextStyle *stylePtr));
+
+extern void Blt_GetTextExtents _ANSI_ARGS_((TextStyle *stylePtr,
+ char *text, int *widthPtr, int *heightPtr));
+
+extern void Blt_InitTextStyle _ANSI_ARGS_((TextStyle *stylePtr));
+
+extern void Blt_ResetTextStyle _ANSI_ARGS_((Tk_Window tkwin,
+ TextStyle *stylePtr));
+
+extern void Blt_FreeTextStyle _ANSI_ARGS_((Display *display,
+ TextStyle *stylePtr));
+
+extern void Blt_SetDrawTextStyle _ANSI_ARGS_((TextStyle *stylePtr,
+ Tk_Font font, GC gc, XColor *normalColor, XColor *activeColor,
+ XColor *shadowColor, double theta, Tk_Anchor anchor, Tk_Justify justify,
+ int leader, int shadowOffset));
+
+extern void Blt_SetPrintTextStyle _ANSI_ARGS_((TextStyle *stylePtr,
+ Tk_Font font, XColor *fgColor, XColor *bgColor, XColor *shadowColor,
+ double theta, Tk_Anchor anchor, Tk_Justify justify, int leader,
+ int shadowOffset));
+
+extern void Blt_DrawText _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+ char *string, TextStyle *stylePtr, int x, int y));
+
+extern void Blt_DrawTextLayout _ANSI_ARGS_((Tk_Window tkwin,
+ Drawable drawable, TextLayout *textPtr, TextStyle *stylePtr,
+ int x, int y));
+
+extern void Blt_DrawText2 _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+ char *string, TextStyle *stylePtr, int x, int y,
+ Dim2D * dimPtr));
+
+extern Pixmap Blt_CreateTextBitmap _ANSI_ARGS_((Tk_Window tkwin,
+ TextLayout *textPtr, TextStyle *stylePtr, int *widthPtr,
+ int *heightPtr));
+
+extern int Blt_DrawRotatedText _ANSI_ARGS_((Display *display,
+ Drawable drawable, int x, int y, double theta,
+ TextStyle *stylePtr, TextLayout *textPtr));
+
+#endif /* _BLT_TEXT_H */
diff --git a/blt/src/bltTile.c b/blt/src/bltTile.c
new file mode 100644
index 00000000000..16fae765771
--- /dev/null
+++ b/blt/src/bltTile.c
@@ -0,0 +1,1274 @@
+/*
+ * bltTile.c --
+ *
+ * This module manages images for tiled backgrounds for the BLT toolkit.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltImage.h"
+#include <X11/Xutil.h>
+
+#include "bltTile.h"
+
+#define TILE_THREAD_KEY "BLT Tile Data"
+#define TILE_MAGIC ((unsigned int) 0x46170277)
+
+typedef struct {
+ Blt_HashTable tileTable; /* Hash table of tile structures keyed by
+ * the name of the image. */
+ Tcl_Interp *interp;
+} TileInterpData;
+
+typedef struct {
+ char *name; /* Name of image used to generate the pixmap.*/
+ Display *display; /* Display where pixmap was created. */
+ int flags; /* See definitions below. */
+ Tcl_Interp *interp;
+ Blt_HashEntry *hashPtr; /* Pointer to hash table location */
+ Blt_HashTable *tablePtr;
+
+ Pixmap pixmap; /* Pixmap generated from image. */
+ Pixmap mask; /* Monochrome pixmap used as
+ * transparency mask. */
+ GC gc; /* GC */
+ Tk_Image tkImage; /* Tk image token. */
+ Blt_Chain *clients; /* Chain of clients sharing this tile. */
+ int width, height;
+} Server;
+
+#define NOTIFY_PENDING 1 /* If set, indicates that the image
+ * associated with the tile has been
+ * updated or deleted. The tile pixmap
+ * will be changed and the clients of the
+ * tile will be notified (if they supplied
+ * a TileChangedProc routine. */
+typedef struct {
+ unsigned int magic;
+ Tk_Window tkwin; /* Client window. */
+ int xOrigin, yOrigin; /* Tiling origin in relation to the
+ * client window. */
+ Blt_TileChangedProc *notifyProc; /* If non-NULL, routine to
+ * call to when tile image changes. */
+ ClientData clientData; /* Data to pass to when calling the above
+ * routine. */
+ Server *serverPtr; /* Pointer to actual tile information */
+ Blt_ChainLink *linkPtr; /* Pointer to client entry in the server's
+ * client list. Used to delete the client */
+} Client;
+
+typedef struct {
+ Display *display;
+ Tk_Uid nameId;
+ int depth;
+} TileKey;
+
+static TileInterpData *GetTileInterpData _ANSI_ARGS_((Tcl_Interp *interp));
+
+#ifdef __STDC__
+static Tcl_IdleProc UpdateTile;
+static Tk_ImageChangedProc ImageChangedProc;
+static Tcl_InterpDeleteProc TileInterpDeleteProc;
+#endif
+
+static void DestroyClient _ANSI_ARGS_((Client *clientPtr));
+static void DestroyServer _ANSI_ARGS_((Server *serverPtr));
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RedrawTile --
+ *
+ * Generates a pixmap and draws the tile image into it. Also
+ * a tranparency mask is possibly generated from the image.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+RedrawTile(tkwin, serverPtr)
+ Tk_Window tkwin;
+ Server *serverPtr;
+{
+ GC newGC;
+ Tk_PhotoHandle photo;
+ XGCValues gcValues;
+ int width, height;
+ unsigned int gcMask;
+
+ Tk_SizeOfImage(serverPtr->tkImage, &width, &height);
+
+ Tk_MakeWindowExist(tkwin);
+ if ((width != serverPtr->width) || (height != serverPtr->height)) {
+ Pixmap pixmap;
+
+ /*
+ * Create the new pixmap *before* destroying the old one. I don't
+ * why this happens, but if you delete the old pixmap first, the
+ * old pixmap sometimes gets used in the client's GCs. I suspect
+ * it has something to do with the way Tk reallocates X resource
+ * identifiers.
+ */
+ pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), width,
+ height, Tk_Depth(tkwin));
+ if (serverPtr->pixmap != None) {
+ Tk_FreePixmap(Tk_Display(tkwin), serverPtr->pixmap);
+ }
+ serverPtr->pixmap = pixmap;
+ }
+ Tk_RedrawImage(serverPtr->tkImage, 0, 0, width, height, serverPtr->pixmap,
+ 0, 0);
+
+ gcMask = (GCTile | GCFillStyle);
+ gcValues.fill_style = FillTiled;
+ gcValues.tile = serverPtr->pixmap;
+ newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+ if (serverPtr->gc != NULL) {
+ Tk_FreeGC(Tk_Display(tkwin), serverPtr->gc);
+ }
+ serverPtr->gc = newGC;
+ serverPtr->width = width;
+ serverPtr->height = height;
+
+ if (serverPtr->mask != None) {
+#ifdef WIN32
+ Tk_FreePixmap(Tk_Display(tkwin), serverPtr->mask);
+#else
+ XFreePixmap(Tk_Display(tkwin), serverPtr->mask);
+#endif /* WIN32 */
+ serverPtr->mask = None;
+ }
+ photo = Blt_FindPhoto(serverPtr->interp,
+ Blt_NameOfImage(serverPtr->tkImage));
+ if (photo != NULL) {
+ Tk_PhotoImageBlock src;
+
+ Tk_PhotoGetImage(photo, &src);
+ if ((src.offset[3] < src.pixelSize) && (src.offset[3] >= 0)) {
+ serverPtr->mask = Blt_PhotoImageMask(tkwin, src);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateTile --
+ *
+ * It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UpdateTile(clientData)
+ ClientData clientData;
+{
+ Server *serverPtr = (Server *) clientData;
+ Client *clientPtr;
+ Blt_ChainLink *linkPtr;
+
+ serverPtr->flags &= ~NOTIFY_PENDING;
+ if (Tk_ImageIsDeleted(serverPtr->tkImage)) {
+ if (serverPtr->pixmap != None) {
+ Tk_FreePixmap(serverPtr->display, serverPtr->pixmap);
+ }
+ serverPtr->pixmap = None;
+ } else {
+ /* Pick any client window to generate the new pixmap. */
+ linkPtr = Blt_ChainFirstLink(serverPtr->clients);
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ RedrawTile(clientPtr->tkwin, serverPtr);
+ }
+
+ /* Notify each of the tile's clients that the pixmap has changed. */
+
+ for (linkPtr = Blt_ChainFirstLink(serverPtr->clients); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ if (clientPtr->notifyProc != NULL) {
+ (*clientPtr->notifyProc) (clientPtr->clientData,
+ (Blt_Tile)clientPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ * The Tk image has changed or been deleted, redraw the pixmap
+ * tile.
+ *
+ * Note: As of Tk 4.2 (rechecked in 8.3), if you redraw Tk
+ * images from a Tk_ImageChangedProc you'll get a
+ * coredump. As a workaround, we have to simulate
+ * how the Tk widgets use images and redraw within
+ * an idle event.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight; /* Not used. */
+{
+ Server *serverPtr = (Server *) clientData;
+
+ if (!(serverPtr->flags & NOTIFY_PENDING)) {
+ Tcl_DoWhenIdle(UpdateTile, serverPtr);
+ serverPtr->flags |= NOTIFY_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyServer --
+ *
+ * Deletes the tile server structure, including the pixmap
+ * representing the tile.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyServer(serverPtr)
+ Server *serverPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Client *clientPtr;
+
+ if (serverPtr->flags & NOTIFY_PENDING) {
+ Tcl_CancelIdleCall(UpdateTile, serverPtr);
+ }
+ for (linkPtr = Blt_ChainFirstLink(serverPtr->clients); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(clientPtr);
+ }
+ Blt_ChainDestroy(serverPtr->clients);
+
+ if (serverPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(serverPtr->tablePtr, serverPtr->hashPtr);
+ }
+ if (serverPtr->pixmap != None) {
+ Tk_FreePixmap(serverPtr->display, serverPtr->pixmap);
+ }
+ Tk_FreeImage(serverPtr->tkImage);
+
+ if (serverPtr->gc != NULL) {
+ Tk_FreeGC(serverPtr->display, serverPtr->gc);
+ }
+ if (serverPtr->name != NULL) {
+ Blt_Free(serverPtr->name);
+ }
+ Blt_Free(serverPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateServer --
+ *
+ * Creates a tile server. A tile server manages a single image,
+ * possibly shared by several clients. Clients will be updated
+ * (if requested) by the server if the image changes, so they
+ * know to redraw themselves. For X11 the image is drawn into a
+ * pixmap that is used in a new GC as its tile. For Windows we
+ * have to do the tiling ourselves by redrawing the image across
+ * the drawing area (see Blt_TileRectangle and Blt_TilePolygon).
+ *
+ * Results:
+ * Returns a pointer to the new tile server. If the image name
+ * does not represent a Tk image, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Server *
+CreateServer(interp, tkwin, imageName)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *imageName;
+{
+ Server *serverPtr;
+ Tk_Image tkImage;
+
+ serverPtr = Blt_Calloc(1, sizeof(Server));
+ assert(serverPtr);
+ /*
+ * Get the image. Funnel all change notifications to a single routine.
+ */
+ tkImage = Tk_GetImage(interp, tkwin, imageName, ImageChangedProc,
+ serverPtr);
+ if (tkImage == NULL) {
+ Blt_Free(serverPtr);
+ return NULL;
+ }
+
+ /*
+ * Initialize the tile server.
+ */
+ serverPtr->display = Tk_Display(tkwin);
+ serverPtr->interp = interp;
+ serverPtr->name = Blt_Strdup(imageName);
+ serverPtr->clients = Blt_ChainCreate();
+ serverPtr->tkImage = tkImage;
+ RedrawTile(tkwin, serverPtr);
+ return serverPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyClient --
+ *
+ * Removes the client from the servers's list of clients and
+ * memory used by the client token is released. When the last
+ * client is deleted, the server is also removed.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyClient(clientPtr)
+ Client *clientPtr;
+{
+ Server *serverPtr;
+ serverPtr = clientPtr->serverPtr;
+
+ /* Remove the client from the server's list */
+ if (clientPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(serverPtr->clients, clientPtr->linkPtr);
+ }
+ if (Blt_ChainGetLength(serverPtr->clients) == 0) {
+ /*
+ * If there are no more clients of the tile, then remove the
+ * pixmap, image, and the server record.
+ */
+ DestroyServer(serverPtr);
+ }
+ Blt_Free(clientPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateClient --
+ *
+ * Returns a token to a tile (possibly shared by many clients).
+ * A client uses the token to query or display the tile. Clients
+ * request tiles by their image names. Each tile is known by its
+ * display, screen depth, and image name. The tile server tracks
+ * what clients are using the tile and notifies them (via a
+ * callback) whenever the tile changes. If no server exists
+ * already, one is created on-the-fly.
+ *
+ * Results:
+ * A pointer to the newly created client (i.e. tile).
+ *
+ *----------------------------------------------------------------------
+ */
+static Client *
+CreateClient(interp, tkwin, name)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *name;
+{
+ Client *clientPtr;
+ Server *serverPtr;
+ TileInterpData *dataPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ TileKey key;
+
+ dataPtr = GetTileInterpData(interp);
+
+ key.nameId = Tk_GetUid(name);
+ key.display = Tk_Display(tkwin);
+ key.depth = Tk_Depth(tkwin);
+ hPtr = Blt_CreateHashEntry(&(dataPtr->tileTable), (char *)&key, &isNew);
+ if (isNew) {
+ serverPtr = CreateServer(interp, tkwin, name);
+ if (serverPtr == NULL) {
+ Blt_DeleteHashEntry(&(dataPtr->tileTable), hPtr);
+ return NULL;
+ }
+ serverPtr->hashPtr = hPtr;
+ serverPtr->tablePtr = &(dataPtr->tileTable);
+ Blt_SetHashValue(hPtr, serverPtr);
+ } else {
+ serverPtr = Blt_GetHashValue(hPtr);
+ }
+ clientPtr = Blt_Calloc(1, sizeof(Client));
+ assert(clientPtr);
+
+ /* Initialize client information. */
+ clientPtr->magic = TILE_MAGIC;
+ clientPtr->tkwin = tkwin;
+ clientPtr->linkPtr = Blt_ChainAppend(serverPtr->clients, clientPtr);
+ clientPtr->serverPtr = serverPtr;
+ return clientPtr;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TileInterpDeleteProc --
+ *
+ * This is called when the interpreter is deleted. All the tiles
+ * are specific to that interpreter are destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys the tile table.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TileInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+{
+ TileInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Server *serverPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->tileTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ serverPtr = Blt_GetHashValue(hPtr);
+ serverPtr->hashPtr = NULL;
+ DestroyServer(serverPtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->tileTable));
+ Tcl_DeleteAssocData(interp, TILE_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+static TileInterpData *
+GetTileInterpData(interp)
+ Tcl_Interp *interp;
+{
+ TileInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (TileInterpData *)
+ Tcl_GetAssocData(interp, TILE_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(TileInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ Tcl_SetAssocData(interp, TILE_THREAD_KEY, TileInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->tileTable), sizeof(TileKey)/sizeof(int));
+ }
+ return dataPtr;
+}
+
+
+/* Public API for tiles. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetTile
+ *
+ * Convert the named image into a tile.
+ *
+ * Results:
+ * If the image is valid, a new tile is returned. If the name
+ * does not represent a proper image, an error message is left in
+ * interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_GetTile(interp, tkwin, imageName, tilePtr)
+ Tcl_Interp *interp; /* Interpreter to report results back to */
+ Tk_Window tkwin; /* Window on the same display as tile */
+ char *imageName; /* Name of image */
+ Blt_Tile *tilePtr; /* (out) Returns the allocated tile. */
+{
+ Client *clientPtr;
+
+ clientPtr = CreateClient(interp, tkwin, imageName);
+ if (clientPtr == NULL) {
+ return TCL_ERROR;
+ }
+ *tilePtr = (Blt_Tile)clientPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeTile
+ *
+ * Release the resources associated with the tile.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Memory and X resources are freed. Bookkeeping information
+ * about the tile (i.e. width, height, and name) is discarded.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_FreeTile(tile)
+ Blt_Tile tile; /* Tile to be deleted */
+{
+ Client *clientPtr = (Client *)tile;
+
+ if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+ return; /* No tile */
+ }
+ DestroyClient(clientPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfTile
+ *
+ * Returns the name of the image from which the tile was
+ * generated.
+ *
+ * Results:
+ * The name of the image is returned. The name is not unique.
+ * Many tiles may use the same image.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+char *
+Blt_NameOfTile(tile)
+ Blt_Tile tile; /* Tile to query */
+{
+ Client *clientPtr = (Client *)tile;
+
+ if (clientPtr == NULL) {
+ return "";
+ }
+ if (clientPtr->magic != TILE_MAGIC) {
+ return "not a tile";
+ }
+ return clientPtr->serverPtr->name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PixmapOfTile
+ *
+ * Returns the pixmap of the tile.
+ *
+ * Results:
+ * The X pixmap used as the tile is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Pixmap
+Blt_PixmapOfTile(tile)
+ Blt_Tile tile; /* Tile to query */
+{
+ Client *clientPtr = (Client *)tile;
+
+ if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+ return None;
+ }
+ return clientPtr->serverPtr->pixmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SizeOfTile
+ *
+ * Returns the width and height of the tile.
+ *
+ * Results:
+ * The width and height of the tile are returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_SizeOfTile(tile, widthPtr, heightPtr)
+ Blt_Tile tile; /* Tile to query */
+ int *widthPtr, *heightPtr; /* Returned dimensions of the tile (out) */
+{
+ Client *clientPtr = (Client *)tile;
+
+ if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+ *widthPtr = *heightPtr = 0;
+ return; /* No tile given. */
+ }
+ *widthPtr = clientPtr->serverPtr->width;
+ *heightPtr = clientPtr->serverPtr->height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SetTileChangedProc
+ *
+ * Sets the routine to called when an image changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The designated routine will be called the next time the
+ * image associated with the tile changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_SetTileChangedProc(tile, notifyProc, clientData)
+ Blt_Tile tile; /* Tile to query */
+ Blt_TileChangedProc *notifyProc;
+ ClientData clientData;
+{
+ Client *clientPtr = (Client *)tile;
+
+ if ((clientPtr != NULL) && (clientPtr->magic == TILE_MAGIC)) {
+ clientPtr->notifyProc = notifyProc;
+ clientPtr->clientData = clientData;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SetTileOrigin --
+ *
+ * Set the pattern origin of the tile to a common point (i.e. the
+ * origin (0,0) of the top level window) so that tiles from two
+ * different widgets will match up. This done by setting the
+ * GCTileStipOrigin field is set to the translated origin of the
+ * toplevel window in the hierarchy.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The GCTileStipOrigin is reset in the GC. This will cause the
+ * tile origin to change when the GC is used for drawing.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+void
+Blt_SetTileOrigin(tkwin, tile, x, y)
+ Tk_Window tkwin;
+ Blt_Tile tile;
+ int x, y;
+{
+ Client *clientPtr = (Client *)tile;
+
+ while (!Tk_IsTopLevel(tkwin)) {
+ x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+ y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+ tkwin = Tk_Parent(tkwin);
+ }
+ XSetTSOrigin(Tk_Display(tkwin), clientPtr->serverPtr->gc, -x, -y);
+ clientPtr->xOrigin = -x;
+ clientPtr->yOrigin = -y;
+}
+
+void
+Blt_SetTSOrigin(tkwin, tile, x, y)
+ Tk_Window tkwin;
+ Blt_Tile tile;
+ int x, y;
+{
+ Client *clientPtr = (Client *)tile;
+
+ XSetTSOrigin(Tk_Display(tkwin), clientPtr->serverPtr->gc, x, y);
+ clientPtr->xOrigin = x;
+ clientPtr->yOrigin = y;
+}
+
+#ifdef WIN32
+static 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 */
+};
+#define MASKPAT 0x00E20746 /* dest = (src & pat) | (!src & dst) */
+#define COPYFG 0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG 0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+
+static void
+TileRegion(
+ HDC destDC,
+ HDC srcDC,
+ Client *clientPtr,
+ int x, int y,
+ int width, int height)
+{
+ Server *serverPtr = clientPtr->serverPtr;
+ int destX, destY;
+ int destWidth, destHeight;
+ int srcX, srcY;
+ int startX, startY; /* Starting upper left corner of region. */
+ int delta;
+ int left, top, right, bottom;
+
+ startX = x;
+ if (x < clientPtr->xOrigin) {
+ delta = (clientPtr->xOrigin - x) % serverPtr->width;
+ if (delta > 0) {
+ startX -= (serverPtr->width - delta);
+ }
+ } else if (x > clientPtr->xOrigin) {
+ delta = (x - clientPtr->xOrigin) % serverPtr->width;
+ if (delta > 0) {
+ startX -= delta;
+ }
+ }
+ startY = y;
+ if (y < clientPtr->yOrigin) {
+ delta = (clientPtr->yOrigin - y) % serverPtr->height;
+ if (delta > 0) {
+ startY -= (serverPtr->height - delta);
+ }
+ } else if (y >= clientPtr->yOrigin) {
+ delta = (y - clientPtr->yOrigin) % serverPtr->height;
+ if (delta > 0) {
+ startY -= delta;
+ }
+ }
+#ifdef notdef
+ PurifyPrintf("tile is (%d,%d,%d,%d)\n",
+ clientPtr->xOrigin, clientPtr->yOrigin,
+ serverPtr->width, serverPtr->height);
+ PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+ PurifyPrintf("starting at %d,%d\n", startX, startY);
+#endif
+ left = x;
+ right = x + width;
+ top = y;
+ bottom = y + height;
+ for (y = startY; y < bottom; y += serverPtr->height) {
+ srcY = 0;
+ destY = y;
+ destHeight = serverPtr->height;
+ if (y < top) {
+ srcY = (top - y);
+ destHeight = serverPtr->height - srcY;
+ destY = top;
+ }
+ if ((destY + destHeight) > bottom) {
+ destHeight = (bottom - destY);
+ }
+ for (x = startX; x < right; x += serverPtr->width) {
+ srcX = 0;
+ destX = x;
+ destWidth = serverPtr->width;
+ if (x < left) {
+ srcX = (left - x);
+ destWidth = serverPtr->width - srcX;
+ destX = left;
+ }
+ if ((destX + destWidth) > right) {
+ destWidth = (right - destX);
+ }
+#ifdef notdef
+ PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+ srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+ if (serverPtr->mask != None) { /* With transparency. */
+ HDC maskDC;
+ TkWinDCState maskState;
+
+ maskDC = TkWinGetDrawableDC(serverPtr->display,
+ serverPtr->mask, &maskState);
+ SetBkColor(destDC, RGB(255, 255, 255));
+ SetTextColor(destDC, RGB(0, 0, 0));
+ BitBlt(destDC, destX, destY, destWidth, destHeight, maskDC,
+ 0, 0, SRCAND);
+ BitBlt(destDC, destX, destY, destWidth, destHeight, srcDC,
+ srcX, srcY, SRCPAINT);
+ TkWinReleaseDrawableDC(serverPtr->mask, maskDC, &maskState);
+ } else { /* Opaque tile. */
+ BitBlt(destDC, destX, destY, destWidth, destHeight,
+ srcDC, srcX, srcY, SRCCOPY);
+ }
+ }
+ }
+}
+
+void
+Blt_TilePolygon(
+ Tk_Window tkwin,
+ Drawable drawable,
+ Blt_Tile tile,
+ XPoint pointArr[],
+ int nPoints)
+{
+ Client *clientPtr = (Client *)tile;
+ HBITMAP oldBitmap;
+ HDC hDC;
+ HDC memDC;
+ HRGN hRgn;
+ Region2D bbox;
+ Display *display;
+ Server *serverPtr;
+ TkWinDCState state;
+ TkWinDrawable *twdPtr;
+ XPoint *endPtr, *pointPtr;
+ int width, height;
+ POINT *p, *winPts;
+ int fillMode;
+
+ if (drawable == None) {
+ return;
+ }
+ display = Tk_Display(tkwin);
+ serverPtr = clientPtr->serverPtr;
+
+ /* Determine the bounding box of the polygon. */
+ bbox.left = bbox.right = pointArr[0].x;
+ bbox.top = bbox.bottom = pointArr[0].y;
+
+ endPtr = pointArr + nPoints;
+ for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+ if (pointPtr->x < bbox.left) {
+ bbox.left = pointPtr->x;
+ }
+ if (pointPtr->x > bbox.right) {
+ bbox.right = pointPtr->x;
+ }
+ if (pointPtr->y < bbox.top) {
+ bbox.top = pointPtr->y;
+ }
+ if (pointPtr->y > bbox.bottom) {
+ bbox.bottom = pointPtr->y;
+ }
+ }
+ width = bbox.right - bbox.left + 1;
+ height = bbox.bottom - bbox.top + 1;
+
+ /* Allocate and fill an array of POINTS to create the polygon path. */
+ p = winPts = Blt_Malloc(sizeof(POINT) * nPoints);
+ for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+ p->x = pointPtr->x - bbox.left;
+ p->y = pointPtr->y - bbox.top;
+ p++;
+ }
+
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[serverPtr->gc->function]);
+ fillMode = (serverPtr->gc->fill_rule == EvenOddRule) ? ALTERNATE : WINDING;
+ /* Use the polygon as a clip path. */
+ hRgn = CreatePolygonRgn(winPts, nPoints, fillMode);
+ SelectClipRgn(hDC, hRgn);
+ OffsetClipRgn(hDC, bbox.left, bbox.top);
+ Blt_Free(winPts);
+
+ twdPtr = (TkWinDrawable *)serverPtr->pixmap;
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+
+ /* Tile the bounding box. */
+ TileRegion(hDC, memDC, clientPtr, bbox.left, bbox.top, width, height);
+
+ SelectBitmap(memDC, oldBitmap);
+ DeleteDC(memDC);
+ SelectClipRgn(hDC, NULL);
+ DeleteRgn(hRgn);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_TileRectangle(tkwin, drawable, tile, x, y, width, height)
+ Tk_Window tkwin;
+ Drawable drawable;
+ Blt_Tile tile;
+ int x, y;
+ unsigned int width, height;
+{
+ Client *clientPtr = (Client *)tile;
+ HBITMAP oldBitmap;
+ HDC hDC, memDC;
+ Server *serverPtr;
+ TkWinDCState state;
+ TkWinDrawable *twdPtr;
+
+ if (drawable == None) {
+ return;
+ }
+ serverPtr = clientPtr->serverPtr;
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[serverPtr->gc->function]);
+
+ twdPtr = (TkWinDrawable *)serverPtr->pixmap;
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+
+ TileRegion(hDC, memDC, clientPtr, x, y, width, height);
+
+ SelectBitmap(memDC, oldBitmap);
+ DeleteDC(memDC);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_TileRectangles(
+ Tk_Window tkwin,
+ Drawable drawable,
+ Blt_Tile tile,
+ XRectangle rectArr[],
+ int nRectangles)
+{
+ Client *clientPtr = (Client *)tile;
+ HBITMAP oldBitmap;
+ HDC hDC, memDC;
+ Server *serverPtr;
+ TkWinDCState state;
+ TkWinDrawable *twdPtr;
+ XRectangle *rectPtr, *endPtr;
+
+ if (drawable == None) {
+ return;
+ }
+ serverPtr = clientPtr->serverPtr;
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[serverPtr->gc->function]);
+
+ twdPtr = (TkWinDrawable *)serverPtr->pixmap;
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+
+ endPtr = rectArr + nRectangles;
+ for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+ TileRegion(hDC, memDC, clientPtr, (int)rectPtr->x, (int)rectPtr->y,
+ (int)rectPtr->width, (int)rectPtr->height);
+ }
+ SelectBitmap(memDC, oldBitmap);
+ DeleteDC(memDC);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+#else
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RectangleMask --
+ *
+ * Creates a rectangular mask also stippled by the mask of the
+ * tile. This is used to draw the tiled polygon images with
+ * transparent areas.
+ *
+ * Results:
+ * A bitmap mask is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pixmap
+RectangleMask(display, drawable, x, y, width, height, mask, xOrigin, yOrigin)
+ Display *display;
+ Drawable drawable;
+ int x, y;
+ unsigned int width, height;
+ Pixmap mask;
+ int xOrigin, yOrigin;
+{
+ GC gc;
+ Pixmap bitmap;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ bitmap = Tk_GetPixmap(display, drawable, width, height, 1);
+ gcMask = (GCForeground | GCBackground | GCFillStyle |
+ GCTileStipXOrigin | GCTileStipYOrigin | GCStipple);
+ gcValues.foreground = 0x1;
+ gcValues.background = 0x0;
+ gcValues.fill_style = FillOpaqueStippled;
+ gcValues.ts_x_origin = xOrigin - x;
+ gcValues.ts_y_origin = yOrigin - y;
+ gcValues.stipple = mask;
+ gc = XCreateGC(display, bitmap, gcMask, &gcValues);
+ XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+ Blt_FreePrivateGC(display, gc);
+ return bitmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TileRectangle --
+ *
+ * Draws a rectangle filled by a tiled image. This differs from
+ * the normal XFillRectangle call in that we also try to handle
+ * a transparency mask.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws the rectangle.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TileRectangle(tkwin, drawable, tile, x, y, width, height)
+ Tk_Window tkwin;
+ Drawable drawable;
+ Blt_Tile tile;
+ int x, y;
+ unsigned int width, height;
+{
+ Client *clientPtr = (Client *)tile;
+ Server *serverPtr;
+ Display *display;
+
+ display = Tk_Display(tkwin);
+ serverPtr = clientPtr->serverPtr;
+ if (clientPtr->serverPtr->mask != None) {
+ Pixmap mask;
+
+ mask = RectangleMask(display, drawable, x, y, width, height,
+ serverPtr->mask, clientPtr->xOrigin, clientPtr->yOrigin);
+ XSetClipMask(display, serverPtr->gc, mask);
+ XSetClipOrigin(display, serverPtr->gc, x, y);
+ XFillRectangle(display, drawable, serverPtr->gc, x, y, width, height);
+ XSetClipMask(display, serverPtr->gc, None);
+ XSetClipOrigin(display, serverPtr->gc, 0, 0);
+ Tk_FreePixmap(display, mask);
+ } else {
+ XFillRectangle(display, drawable, serverPtr->gc, x, y, width, height);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TileRectangles --
+ *
+ * Draws rectangles filled by a tiled image. This differs from
+ * the normal XFillRectangles call in that we also try to handle
+ * a transparency mask.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws the given rectangles.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TileRectangles(tkwin, drawable, tile, rectArr, nRectangles)
+ Tk_Window tkwin;
+ Drawable drawable;
+ Blt_Tile tile;
+ XRectangle rectArr[];
+ int nRectangles;
+{
+ Client *clientPtr = (Client *)tile;
+ Server *serverPtr;
+
+ serverPtr = clientPtr->serverPtr;
+ if (serverPtr->mask != None) {
+ XRectangle *rectPtr, *endPtr;
+
+ endPtr = rectArr + nRectangles;
+ for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+ Blt_TileRectangle(tkwin, drawable, tile, rectPtr->x, rectPtr->y,
+ rectPtr->width, rectPtr->height);
+ }
+ } else {
+ XFillRectangles(Tk_Display(tkwin), drawable, serverPtr->gc, rectArr,
+ nRectangles);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PolygonMask --
+ *
+ * Creates a polygon shaped mask also stippled by the mask
+ * of the tile. This is used to draw the tiled polygon images
+ * with transparent areas.
+ *
+ * Results:
+ * A bitmap mask is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pixmap
+PolygonMask(display, pointArr, nPoints, regionPtr, mask, xOrigin, yOrigin)
+ Display *display;
+ XPoint *pointArr;
+ int nPoints;
+ Region2D *regionPtr;
+ Pixmap mask;
+ int xOrigin, yOrigin;
+{
+ unsigned int width, height;
+ Pixmap bitmap;
+ GC gc;
+ XPoint *destArr;
+ register XPoint *srcPtr, *destPtr, *endPtr;
+
+ width = regionPtr->right - regionPtr->left + 1;
+ height = regionPtr->bottom - regionPtr->top + 1;
+ bitmap =
+ Tk_GetPixmap(display, DefaultRootWindow(display), width, height, 1);
+
+ destArr = Blt_Malloc(sizeof(XPoint) * nPoints);
+ endPtr = destArr + nPoints;
+ srcPtr = pointArr;
+ for (destPtr = destArr; destPtr < endPtr; destPtr++) {
+ destPtr->x = srcPtr->x - regionPtr->left;
+ destPtr->y = srcPtr->y - regionPtr->top;
+ srcPtr++;
+ }
+ gc = XCreateGC(display, bitmap, 0, NULL);
+ XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+ XSetForeground(display, gc, 0x01);
+ XSetFillStyle(display, gc, FillStippled);
+ XSetTSOrigin(display, gc, xOrigin - regionPtr->left,
+ yOrigin - regionPtr->top);
+ XSetStipple(display, gc, mask);
+ XFillPolygon(display, bitmap, gc, destArr, nPoints, Complex,
+ CoordModeOrigin);
+ XFreeGC(display, gc);
+ Blt_Free(destArr);
+ return bitmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TilePolygon --
+ *
+ * Draws a polygon filled by a tiled image. This differs from
+ * the normal XFillPolygon call in that we also try to handle
+ * a transparency mask.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws the polygon.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TilePolygon(tkwin, drawable, tile, pointArr, nPoints)
+ Tk_Window tkwin;
+ Drawable drawable;
+ Blt_Tile tile;
+ XPoint pointArr[];
+ int nPoints;
+{
+ Client *clientPtr = (Client *)tile;
+ Server *serverPtr;
+ Display *display;
+
+ display = Tk_Display(tkwin);
+ serverPtr = clientPtr->serverPtr;
+ if (serverPtr->mask != None) {
+ XPoint *pointPtr, *endPtr;
+ Region2D region;
+ Pixmap mask;
+
+ /* Determine the bounding box of the polygon. */
+ pointPtr = pointArr;
+ region.left = region.right = pointPtr->x;
+ region.top = region.bottom = pointPtr->y;
+
+ endPtr = pointArr + nPoints;
+ for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+ if (region.left > pointPtr->x) {
+ region.left = pointPtr->x;
+ } else if (region.right < pointPtr->x) {
+ region.right = pointPtr->x;
+ }
+ if (region.top > pointPtr->y) {
+ region.top = pointPtr->y;
+ } else if (region.bottom < pointPtr->y) {
+ region.bottom = pointPtr->y;
+ }
+ }
+ mask = PolygonMask(display, pointArr, nPoints, &region,
+ serverPtr->mask, clientPtr->xOrigin, clientPtr->yOrigin);
+ XSetClipMask(display, serverPtr->gc, mask);
+ XSetClipOrigin(display, serverPtr->gc, region.left, region.top);
+ XFillPolygon(display, drawable, serverPtr->gc, pointArr,
+ nPoints, Complex, CoordModeOrigin);
+ XSetClipMask(display, serverPtr->gc, None);
+ XSetClipOrigin(display, serverPtr->gc, 0, 0);
+ Tk_FreePixmap(display, mask);
+ } else {
+ XFillPolygon(display, drawable, serverPtr->gc, pointArr,
+ nPoints, Complex, CoordModeOrigin);
+ }
+}
+#endif
diff --git a/blt/src/bltTile.h b/blt/src/bltTile.h
new file mode 100644
index 00000000000..0710a28df02
--- /dev/null
+++ b/blt/src/bltTile.h
@@ -0,0 +1,63 @@
+/*
+ * bltTile.h --
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef BLT_TILE_H
+#define BLT_TILE_H
+
+#define TILE_THREAD_KEY "BLT Tile Data"
+#define TILE_MAGIC ((unsigned int) 0x46170277)
+
+typedef int *Blt_Tile; /* Opaque type for tiles */
+
+typedef void (Blt_TileChangedProc) _ANSI_ARGS_((ClientData clientData,
+ Blt_Tile tile));
+
+extern int Blt_GetTile _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ char *imageName, Blt_Tile *tilePtr));
+
+extern void Blt_FreeTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern char *Blt_NameOfTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern void Blt_SetTileChangedProc _ANSI_ARGS_((Blt_Tile tile,
+ Blt_TileChangedProc *changeProc, ClientData clientData));
+
+extern void Blt_TileRectangle _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+ Blt_Tile tile, int x, int y, unsigned int width, unsigned int height));
+extern void Blt_TileRectangles _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+ Blt_Tile tile, XRectangle *rectArr, int nRects));
+extern void Blt_TilePolygon _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+ Blt_Tile tile, XPoint *pointArr, int nPoints));
+extern Pixmap Blt_PixmapOfTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern void Blt_SizeOfTile _ANSI_ARGS_((Blt_Tile tile, int *widthPtr,
+ int *heightPtr));
+
+extern void Blt_SetTileOrigin _ANSI_ARGS_((Tk_Window tkwin, Blt_Tile tile,
+ int x, int y));
+
+extern void Blt_SetTSOrigin _ANSI_ARGS_((Tk_Window tkwin, Blt_Tile tile,
+ int x, int y));
+
+#endif /* BLT_TILE_H */
diff --git a/blt/src/bltTkInt.h b/blt/src/bltTkInt.h
new file mode 100644
index 00000000000..0609e4e5108
--- /dev/null
+++ b/blt/src/bltTkInt.h
@@ -0,0 +1,240 @@
+/*
+ * bltTkInt.h --
+ *
+ * Contains copies of internal Tk structures.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_TKINT_H
+#define _BLT_TKINT_H
+
+typedef struct {
+ Tk_Uid family; /* Font family. The most important field. */
+ int pointsize; /* Pointsize of font, 0 for default size, or
+ * negative number meaning pixel size. */
+ int weight; /* Weight flag; see below for def'n. */
+ int slant; /* Slant flag; see below for def'n. */
+ int underline; /* Non-zero for underline font. */
+ int overstrike; /* Non-zero for overstrike font. */
+} TkFontAttributes;
+
+typedef struct {
+ int ascent; /* From baseline to top of font. */
+ int descent; /* From baseline to bottom of font. */
+ int maxWidth; /* Width of widest character in font. */
+ int fixed; /* Non-zero if this is a fixed-width font,
+ * 0 otherwise. */
+} TkFontMetrics;
+
+
+typedef struct TkFontStruct {
+ /*
+ * Fields used and maintained exclusively by generic code.
+ */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+ int resourceRefCount; /* Number of active uses of this font (each
+ * active use corresponds to a call to
+ * Tk_AllocFontFromTable or Tk_GetFont).
+ * If this count is 0, then this TkFont
+ * structure is no longer valid and it isn't
+ * present in a hash table: it is being
+ * kept around only because there are objects
+ * referring to it. The structure is freed
+ * when resourceRefCount and objRefCount
+ * are both 0. */
+ int objRefCount; /* The number of Tcl objects that reference
+ * this structure. */
+#else
+ int refCount; /* Number of users of the TkFont. */
+#endif
+ Tcl_HashEntry *cacheHashPtr;/* Entry in font cache for this structure,
+ * used when deleting it. */
+ Tcl_HashEntry *namedHashPtr;/* Pointer to hash table entry that
+ * corresponds to the named font that the
+ * tkfont was based on, or NULL if the tkfont
+ * was not based on a named font. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+ Screen *screen; /* The screen where this font is valid. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+ int tabWidth; /* Width of tabs in this font (pixels). */
+ int underlinePos; /* Offset from baseline to origin of
+ * underline bar (used for drawing underlines
+ * on a non-underlined font). */
+ int underlineHeight; /* Height of underline bar (used for drawing
+ * underlines on a non-underlined font). */
+
+ /*
+ * Fields in the generic font structure that are filled in by
+ * platform-specific code.
+ */
+
+ Font fid; /* For backwards compatibility with XGCValues
+ * structures. Remove when TkGCValues is
+ * implemented. */
+ TkFontAttributes fa; /* Actual font attributes obtained when the
+ * the font was created, as opposed to the
+ * desired attributes passed in to
+ * TkpGetFontFromAttributes(). The desired
+ * metrics can be determined from the string
+ * that was used to create this font. */
+ TkFontMetrics fm; /* Font metrics determined when font was
+ * created. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+ struct TkFontStruct *nextPtr; /* Points to the next TkFont structure with
+ * the same name. All fonts with the
+ * same name (but different displays) are
+ * chained together off a single entry in
+ * a hash table. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+} TkFont;
+
+/*
+ * This structure is used by the Mac and Window porting layers as
+ * the internal representation of a clip_mask in a GC.
+ */
+typedef struct TkRegionStruct *TkRegion;
+
+typedef struct {
+ int type; /* One of TKP_CLIP_PIXMAP or TKP_CLIP_REGION */
+ union {
+ Pixmap pixmap;
+ TkRegion region;
+ } value;
+} TkpClipMask;
+
+#define TKP_CLIP_PIXMAP 0
+#define TKP_CLIP_REGION 1
+
+#ifdef WIN32
+/*
+ * 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 TkWindowStruct TkWindow;
+
+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 TkWinDCState is used to save the state of a device context
+ * so that it can be restored later.
+ */
+
+typedef struct {
+ HPALETTE palette;
+ int bkmode; /* This field was added in Tk
+ * 8.3.1. Be careful that you don't
+ * use this structure in a context
+ * where its size is important. */
+} TkWinDCState;
+
+extern HDC TkWinGetDrawableDC(Display *display, Drawable drawable,
+ TkWinDCState * state);
+extern HDC TkWinReleaseDrawableDC(Drawable drawable, HDC dc,
+ TkWinDCState * state);
+
+extern HWND Tk_GetHWND _ANSI_ARGS_((Window window));
+
+extern HINSTANCE Tk_GetHINSTANCE _ANSI_ARGS_((void));
+
+extern Window Tk_AttachHWND _ANSI_ARGS_((Tk_Window tkwin, HWND hWnd));
+
+#endif /* WIN32 */
+
+/*
+ * The Border structure used internally by the Tk_3D* routines.
+ * The following is a copy of it from tk3d.c.
+ */
+
+typedef struct TkBorderStruct {
+ Screen *screen; /* Screen on which the border will be used. */
+ Visual *visual; /* Visual for all windows and pixmaps using
+ * the border. */
+ int depth; /* Number of bits per pixel of drawables where
+ * the border will be used. */
+ Colormap colormap; /* Colormap out of which pixels are
+ * allocated. */
+ int refCount; /* Number of different users of
+ * this border. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+ int objRefCount; /* The number of Tcl objects that reference
+ * this structure. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+ XColor *bgColor; /* Background color (intensity between
+ * lightColorPtr and darkColorPtr). */
+ XColor *darkColor; /* Color for darker areas (must free when
+ * deleting structure). NULL means shadows
+ * haven't been allocated yet.*/
+ XColor *lightColor; /* Color used for lighter areas of border
+ * (must free this when deleting structure).
+ * NULL means shadows haven't been allocated
+ * yet. */
+ Pixmap shadow; /* Stipple pattern to use for drawing
+ * shadows areas. Used for displays with
+ * <= 64 colors or where colormap has filled
+ * up. */
+ GC bgGC; /* Used (if necessary) to draw areas in
+ * the background color. */
+ GC darkGC; /* Used to draw darker parts of the
+ * border. None means the shadow colors
+ * haven't been allocated yet.*/
+ GC lightGC; /* Used to draw lighter parts of
+ * the border. None means the shadow colors
+ * haven't been allocated yet. */
+ Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in
+ * order to delete structure). */
+ struct TkBorderStruct *nextPtr; /* Points to the next TkBorder structure with
+ * the same color name. Borders with the
+ * same name but different screens or
+ * colormaps are chained together off a
+ * single entry in borderTable. */
+} TkBorder;
+
+#endif /* BLT_TKINT_H */
diff --git a/blt/src/bltTree.c b/blt/src/bltTree.c
new file mode 100644
index 00000000000..f1812038f61
--- /dev/null
+++ b/blt/src/bltTree.c
@@ -0,0 +1,2511 @@
+/*
+ * bltTree.c --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "tree" data object was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREE
+
+#include "bltTree.h"
+
+static Tcl_InterpDeleteProc TreeInterpDeleteProc;
+static Blt_TreeApplyProc SizeApplyProc;
+static Tcl_IdleProc NotifyIdleProc;
+
+#define TREE_THREAD_KEY "BLT Tree Data"
+#define TREE_MAGIC ((unsigned int) 0x46170277)
+#define TREE_DESTROYED (1<<0)
+
+typedef struct Blt_TreeNodeStruct Node;
+typedef struct Blt_TreeClientStruct TreeClient;
+typedef struct Blt_TreeObjectStruct TreeObject;
+typedef struct Blt_TreeValueStruct Value;
+
+/*
+ * The hash table below is used to keep track of all the Blt_TreeKeys
+ * created so far.
+ */
+static Blt_HashTable keyTable;
+static int keyTableInitialized = 0;
+
+typedef struct {
+ Blt_HashTable treeTable; /* Table of trees. */
+ unsigned int nextId;
+ Tcl_Interp *interp;
+} TreeInterpData;
+
+typedef struct {
+ Tcl_Interp *interp;
+ ClientData clientData;
+ Blt_TreeKey key;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ Blt_TreeNotifyEvent event;
+ int notifyPending;
+} EventHandler;
+
+typedef struct {
+ ClientData clientData;
+ char *keyPattern;
+ Node *nodePtr;
+ unsigned int mask;
+ Blt_TreeTraceProc *proc;
+ TreeClient *clientPtr;
+ Blt_ChainLink *linkPtr;
+} TraceHandler;
+
+/*
+ * --------------------------------------------------------------
+ *
+ * GetTreeInterpData --
+ *
+ * Creates or retrieves data associated with tree data objects
+ * for a particular thread. We're using Tcl_GetAssocData rather
+ * than the Tcl thread routines so BLT can work with pre-8.0
+ * Tcl versions that don't have thread support.
+ *
+ * Results:
+ * Returns a pointer to the tree interpreter data.
+ *
+ * --------------------------------------------------------------
+ */
+static TreeInterpData *
+GetTreeInterpData(interp)
+ Tcl_Interp *interp;
+{
+ Tcl_InterpDeleteProc *proc;
+ TreeInterpData *dataPtr;
+
+ dataPtr = (TreeInterpData *)
+ Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(TreeInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->treeTable), BLT_STRING_KEYS);
+ }
+ return dataPtr;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewNode --
+ *
+ * Creates a new node in the tree without installing it. The
+ * number of nodes in the tree is incremented and a unique serial
+ * number is generated for the node.
+ *
+ * Also, all nodes have a label. If no label was provided (name
+ * is NULL) then automatically generate one in the form "nodeN"
+ * where N is the serial number of the node.
+ *
+ * Results:
+ * Returns a pointer to the new node.
+ *
+ * --------------------------------------------------------------
+ */
+static Node *
+NewNode(treeObjPtr, name, inode)
+ TreeObject *treeObjPtr;
+ CONST char *name;
+ int inode;
+{
+ Node *nodePtr;
+
+ /* Create the node structure */
+ nodePtr = Blt_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
+ nodePtr->inode = inode;
+ nodePtr->treeObject = treeObjPtr;
+ nodePtr->parent = NULL;
+ nodePtr->depth = 0;
+ nodePtr->flags = 0;
+ nodePtr->next = nodePtr->prev = NULL;
+ nodePtr->first = nodePtr->last = NULL;
+ nodePtr->nChildren = 0;
+ nodePtr->values = NULL;
+ if (name == NULL) {
+#ifndef notdef /* FIXME: The automatic naming of
+ * nodes should be a "tree" command
+ * feature, not a tree object
+ * feature. Push this into "tree"
+ * command. */
+ char string[200];
+
+ sprintf(string, "node%d", inode);
+ nodePtr->label = Blt_TreeGetKey(string);
+#else
+ nodePtr->label = NULL;
+#endif
+ } else {
+ nodePtr->label = Blt_TreeGetKey(name);
+ }
+ treeObjPtr->nNodes++;
+ return nodePtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetDepths --
+ *
+ * Called after moving a node, resets the depths of each node
+ * for the entire branch (node and it's decendants).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetDepths(nodePtr, depth)
+ Node *nodePtr; /* Root node. */
+ int depth; /* Depth of the node. */
+{
+ nodePtr->depth = depth;
+ /* Also reset the depth for each descendant node. */
+ for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+ ResetDepths(nodePtr, depth + 1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinkBefore --
+ *
+ * Inserts a link preceding a given link.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LinkBefore(parentPtr, nodePtr, beforePtr)
+ Node *parentPtr; /* Parent to hold the new entry. */
+ Node *nodePtr; /* New node to be inserted. */
+ Node *beforePtr; /* Node to link before. */
+{
+ int depth;
+
+ if (parentPtr->first == NULL) {
+ parentPtr->last = parentPtr->first = nodePtr;
+ } else if (beforePtr == NULL) { /* Append onto the end of the chain */
+ nodePtr->next = NULL;
+ nodePtr->prev = parentPtr->last;
+ parentPtr->last->next = nodePtr;
+ parentPtr->last = nodePtr;
+ } else {
+ nodePtr->prev = beforePtr->prev;
+ nodePtr->next = beforePtr;
+ if (beforePtr == parentPtr->first) {
+ parentPtr->first = nodePtr;
+ } else {
+ beforePtr->prev->next = nodePtr;
+ }
+ beforePtr->prev = nodePtr;
+ }
+ parentPtr->nChildren++;
+ nodePtr->parent = parentPtr;
+ depth = parentPtr->depth + 1;
+ if (nodePtr->depth != depth) {
+ /* Reset the depths of all descendant nodes. */
+ ResetDepths(nodePtr, depth);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkNode --
+ *
+ * Unlinks a link from the chain. The link is not deallocated,
+ * but only removed from the chain.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UnlinkNode(nodePtr)
+ Node *nodePtr;
+{
+ Node *parentPtr;
+ int unlinked; /* Indicates if the link is actually
+ * removed from the chain. */
+ parentPtr = nodePtr->parent;
+ unlinked = FALSE;
+ if (parentPtr->first == nodePtr) {
+ parentPtr->first = nodePtr->next;
+ unlinked = TRUE;
+ }
+ if (parentPtr->last == nodePtr) {
+ parentPtr->last = nodePtr->prev;
+ unlinked = TRUE;
+ }
+ if (nodePtr->next != NULL) {
+ nodePtr->next->prev = nodePtr->prev;
+ unlinked = TRUE;
+ }
+ if (nodePtr->prev != NULL) {
+ nodePtr->prev->next = nodePtr->next;
+ unlinked = TRUE;
+ }
+ if (unlinked) {
+ parentPtr->nChildren--;
+ }
+ nodePtr->prev = nodePtr->next = NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * FreeNode --
+ *
+ * Unlinks a given node from the tree, removes its data, and
+ * frees memory allocated to the node.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+FreeNode(treeObjPtr, nodePtr)
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+{
+ Blt_HashEntry *hPtr;
+ register Value *valuePtr, *nextPtr;
+
+ /*
+ * Destroy any data fields associated with this node.
+ */
+ for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+ nextPtr = valuePtr->next;
+ if (valuePtr->objPtr != NULL) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ }
+ /* Unlink the node from parent's list of siblings. */
+ UnlinkNode(nodePtr);
+ treeObjPtr->nNodes--;
+#ifdef notdef
+ if (nodePtr->inode == (treeObjPtr->nextNode - 1)) {
+ treeObjPtr->nextNode--;
+ }
+#endif
+ hPtr = Blt_FindHashEntry(&(treeObjPtr->nodeTable), (char *)nodePtr->inode);
+ assert(hPtr);
+ Blt_DeleteHashEntry(&(treeObjPtr->nodeTable), hPtr);
+ Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewTreeObject --
+ *
+ * Creates and initializes a new tree object. Trees always
+ * contain a root node, so one is allocated here.
+ *
+ * Results:
+ * Returns a pointer to the new tree object is successful, NULL
+ * otherwise. If a tree can't be generated, interp->result will
+ * contain an error message.
+ *
+ * -------------------------------------------------------------- */
+static TreeObject *
+NewTreeObject(dataPtr, interp, treeName)
+ TreeInterpData *dataPtr;
+ Tcl_Interp *interp;
+ CONST char *treeName;
+{
+ TreeObject *treeObjPtr;
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ treeObjPtr = Blt_Calloc(1, sizeof(TreeObject));
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree", (char *)NULL);
+ return NULL;
+ }
+ treeObjPtr->name = Blt_Strdup(treeName);
+ treeObjPtr->interp = interp;
+ treeObjPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ treeObjPtr->nodePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ treeObjPtr->clients = Blt_ChainCreate();
+ treeObjPtr->depth = 1;
+ treeObjPtr->notifyFlags = 0;
+ Blt_InitHashTableWithPool(&treeObjPtr->nodeTable, BLT_ONE_WORD_KEYS);
+
+ hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
+ treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
+ Blt_SetHashValue(hPtr, treeObjPtr->root);
+
+ treeObjPtr->tablePtr = &dataPtr->treeTable;
+ treeObjPtr->hashPtr = Blt_CreateHashEntry(treeObjPtr->tablePtr, treeName,
+ &isNew);
+ Blt_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
+
+ return treeObjPtr;
+}
+
+static TreeObject *
+FindTreeInNamespace(dataPtr, nsPtr, treeName)
+ TreeInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Namespace *nsPtr;
+ char *treeName;
+{
+ Tcl_DString dString;
+ char *name;
+ Blt_HashEntry *hPtr;
+
+ name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+ hPtr = Blt_FindHashEntry(&(dataPtr->treeTable), name);
+ Tcl_DStringFree(&dString);
+ if (hPtr != NULL) {
+ return Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetTreeObject --
+ *
+ * Searches for the tree object associated by the name given.
+ *
+ * Results:
+ * Returns a pointer to the tree if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static TreeObject *
+GetTreeObject(interp, name, flags)
+ Tcl_Interp *interp;
+ CONST char *name;
+ int flags;
+{
+ char *treeName;
+ Tcl_Namespace *nsPtr; /* Namespace associated with the tree object.
+ * If NULL, indicates to look in first the
+ * current namespace and then the global
+ * for the tree. */
+ TreeInterpData *dataPtr; /* Interpreter-specific data. */
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = NULL;
+ if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ dataPtr = GetTreeInterpData(interp);
+ if (nsPtr != NULL) {
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ } else {
+ if (flags & NS_SEARCH_CURRENT) {
+ /* Look first in the current namespace. */
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ }
+ if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+ nsPtr = Tcl_GetGlobalNamespace(interp);
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ }
+ }
+ return treeObjPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TeardownTree --
+ *
+ * Destroys an entire branch. This is a special purpose routine
+ * used to speed up the final clean up of the tree.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TeardownTree(treeObjPtr, nodePtr)
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+{
+ if (nodePtr->first != NULL) {
+ Node *childPtr, *nextPtr;
+
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+ nextPtr = childPtr->next;
+ TeardownTree(treeObjPtr, childPtr);
+ }
+ }
+ if (nodePtr->values != NULL) {
+ register Value *valuePtr, *nextPtr;
+ /*
+ * Run through the list of data fields, decrementing the
+ * reference counts on all the Tcl objects associated with
+ * this node.
+ */
+ for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+ nextPtr = valuePtr->next;
+ if (valuePtr->objPtr != NULL) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ }
+ nodePtr->values = NULL;
+ }
+ Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+static void
+DestroyTreeObject(treeObjPtr)
+ TreeObject *treeObjPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeClient *clientPtr;
+
+ treeObjPtr->flags |= TREE_DESTROYED;
+ treeObjPtr->nNodes = 0;
+
+ /* Remove the remaining clients. */
+ for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ Blt_ChainDestroy(clientPtr->events);
+ Blt_ChainDestroy(clientPtr->traces);
+ Blt_Free(clientPtr);
+ }
+ Blt_ChainDestroy(treeObjPtr->clients);
+
+ TeardownTree(treeObjPtr, treeObjPtr->root);
+ Blt_PoolDestroy(treeObjPtr->nodePool);
+ Blt_PoolDestroy(treeObjPtr->valuePool);
+ Blt_DeleteHashTable(&(treeObjPtr->nodeTable));
+
+ if (treeObjPtr->hashPtr != NULL) {
+ /* Remove the entry from the global tree table. */
+ Blt_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr);
+ if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
+ keyTableInitialized = FALSE;
+ Blt_DeleteHashTable(&keyTable);
+ }
+ }
+ if (treeObjPtr->name != NULL) {
+ Blt_Free(treeObjPtr->name);
+ }
+ Blt_Free(treeObjPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TreeInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the tree object
+ * is deleted from the interpreter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys all remaining trees and removes the hash table
+ * used to register tree names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TreeInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ TreeInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ TreeObject *treeObjPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->treeTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ treeObjPtr = (TreeObject *)Blt_GetHashValue(hPtr);
+ treeObjPtr->hashPtr = NULL;
+ DestroyTreeObject(treeObjPtr);
+ }
+ if (keyTableInitialized) {
+ keyTableInitialized = FALSE;
+ Blt_DeleteHashTable(&keyTable);
+ }
+ Blt_DeleteHashTable(&(dataPtr->treeTable));
+ Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyIdleProc --
+ *
+ * Used to invoke event handler routines at some idle point.
+ * This routine is called from the Tcl event loop. Errors
+ * generated by the event handler routines are backgrounded.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotifyIdleProc(clientData)
+ ClientData clientData;
+{
+ EventHandler *handlerPtr = clientData;
+ int result;
+
+ handlerPtr->notifyPending = FALSE;
+ handlerPtr->mask |= TREE_NOTIFY_ACTIVE;
+ result = (*handlerPtr->proc)(handlerPtr->clientData, &(handlerPtr->event));
+ handlerPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(handlerPtr->interp);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckEventHandlers --
+ *
+ * Traverses the list of client event callbacks and checks
+ * if one matches the given event. A client may trigger an
+ * action that causes the tree to notify it. The can be
+ * prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
+ * the event handler.
+ *
+ * If a matching handler is found, a callback may be called either
+ * immediately or at the next idle time depending upon the
+ * TREE_NOTIFY_WHENIDLE bit.
+ *
+ * Since a handler routine may trigger yet another call to
+ * itself, callbacks are ignored while the event handler is
+ * executing.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CheckEventHandlers(clientPtr, isSource, eventPtr)
+ TreeClient *clientPtr;
+ int isSource; /* Indicates if the client is the source
+ * of the event. */
+ Blt_TreeNotifyEvent *eventPtr;
+{
+ Blt_ChainLink *linkPtr, *nextPtr;
+ EventHandler *handlerPtr;
+
+ eventPtr->tree = clientPtr;
+ for (linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->mask & TREE_NOTIFY_ACTIVE) ||
+ (handlerPtr->mask & eventPtr->type) == 0) {
+ continue; /* Ignore callbacks that are generated
+ * inside of a notify handler routine. */
+ }
+ if ((isSource) && (handlerPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
+ continue; /* Don't notify yourself. */
+ }
+ if (handlerPtr->mask & TREE_NOTIFY_WHENIDLE) {
+ if (!handlerPtr->notifyPending) {
+ handlerPtr->notifyPending = TRUE;
+ handlerPtr->event = *eventPtr;
+ Tcl_DoWhenIdle(NotifyIdleProc, handlerPtr);
+ }
+ } else {
+ int result;
+
+ handlerPtr->mask |= TREE_NOTIFY_ACTIVE;
+ result = (*handlerPtr->proc) (handlerPtr->clientData, eventPtr);
+ handlerPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(handlerPtr->interp);
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyClients --
+ *
+ * Traverses the list of clients for a particular tree and
+ * notifies each client that an event occurred. Clients
+ * indicate interest in a particular event through a bit
+ * flag.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotifyClients(sourcePtr, treeObjPtr, nodePtr, eventFlag)
+ TreeClient *sourcePtr;
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+ int eventFlag;
+{
+ Blt_ChainLink *linkPtr;
+ Blt_TreeNotifyEvent event;
+ TreeClient *clientPtr;
+ int isSource;
+
+ event.type = eventFlag;
+ event.inode = nodePtr->inode;
+
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ isSource = (clientPtr == sourcePtr);
+ CheckEventHandlers(clientPtr, isSource, &event);
+ }
+}
+
+
+/* Public Routines */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeGetKey --
+ *
+ * Given a string, returns a unique identifier for the string.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeKey
+Blt_TreeGetKey(string)
+ CONST char *string; /* String to convert. */
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (!keyTableInitialized) {
+ Blt_InitHashTable(&keyTable, BLT_STRING_KEYS);
+ keyTableInitialized = 1;
+ }
+ hPtr = Blt_CreateHashEntry(&keyTable, string, &isNew);
+ return (Blt_TreeKey)Blt_GetHashKey(&keyTable, hPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNode --
+ *
+ * Creates a new node in the given parent node. The name and
+ * position in the parent are also provided.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNode(clientPtr, parentPtr, name, pos)
+ TreeClient *clientPtr; /* The tree client that is creating
+ * this node. If NULL, indicates to
+ * trigger notify events on behalf of
+ * the initiating client also. */
+ Node *parentPtr; /* Parent node where the new node will
+ * be inserted. */
+ CONST char *name; /* Name of node. */
+ int pos; /* Position in the parent's list of children
+ * where to insert the new node. */
+{
+ Blt_HashEntry *hPtr;
+ Node *beforePtr;
+ Node *nodePtr; /* Node to be inserted. */
+ TreeObject *treeObjPtr;
+ int inode;
+ int isNew;
+
+ treeObjPtr = parentPtr->treeObject;
+
+ /* Generate an unique serial number for this node. */
+ do {
+ inode = treeObjPtr->nextNode++;
+ hPtr = Blt_CreateHashEntry(&(treeObjPtr->nodeTable),(char *)inode,
+ &isNew);
+ } while (!isNew);
+ nodePtr = NewNode(treeObjPtr, name, inode);
+ Blt_SetHashValue(hPtr, nodePtr);
+
+ if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = parentPtr->first;
+ while ((pos > 0) && (beforePtr != NULL)) {
+ pos--;
+ beforePtr = beforePtr->next;
+ }
+ }
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNodeWithId --
+ *
+ * Like Blt_TreeCreateNode, but provides a specific id to use
+ * for the node. If the tree already contains a node by that
+ * id, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNodeWithId(clientPtr, parentPtr, name, inode, position)
+ TreeClient *clientPtr;
+ Node *parentPtr; /* Parent node where the new node will
+ * be inserted. */
+ CONST char *name; /* Name of node. */
+ int inode; /* Requested id of the new node. If a
+ * node by this id already exists in the
+ * tree, no node is created. */
+ int position; /* Position in the parent's list of children
+ * where to insert the new node. */
+{
+ Blt_HashEntry *hPtr;
+ Node *beforePtr;
+ Node *nodePtr; /* Node to be inserted. */
+ TreeObject *treeObjPtr;
+ int isNew;
+
+ treeObjPtr = parentPtr->treeObject;
+ hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
+ if (!isNew) {
+ return NULL;
+ }
+ nodePtr = NewNode(treeObjPtr, name, inode);
+ Blt_SetHashValue(hPtr, nodePtr);
+
+ if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = parentPtr->first;
+ while ((position > 0) && (beforePtr != NULL)) {
+ position--;
+ beforePtr = beforePtr->next;
+ }
+ }
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeMoveNode --
+ *
+ * Move an entry into a new location in the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_TreeMoveNode(clientPtr, nodePtr, parentPtr, beforePtr)
+ TreeClient *clientPtr;
+ Node *nodePtr, *parentPtr, *beforePtr;
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+
+ if (nodePtr == beforePtr) {
+ return TCL_ERROR;
+ }
+ if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
+ return TCL_ERROR;
+ }
+ if (nodePtr->parent == NULL) {
+ return TCL_ERROR; /* Can't move root. */
+ }
+ /* Verify that the node isn't an ancestor of the new parent. */
+ if (Blt_TreeIsAncestor(nodePtr, parentPtr)) {
+ return TCL_ERROR;
+ }
+ UnlinkNode(nodePtr);
+ /*
+ * Relink the node as a child of the new parent.
+ */
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+
+ /*
+ * Issue callbacks to each client indicating that a node has
+ * been moved.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE);
+ return TCL_OK;
+}
+
+int
+Blt_TreeDeleteNode(clientPtr, nodePtr)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Node *childPtr, *nextPtr;
+
+ /* In depth-first order, delete each descendant node. */
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+ nextPtr = childPtr->next;
+ Blt_TreeDeleteNode(clientPtr, childPtr);
+ }
+ /*
+ * Issue callbacks to each client indicating that the node can
+ * no longer be used.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE);
+
+ /* Now remove the actual node. */
+ FreeNode(treeObjPtr, nodePtr);
+ return TCL_OK;
+}
+
+Blt_TreeNode
+Blt_TreeGetNode(clientPtr, inode)
+ TreeClient *clientPtr;
+ unsigned int inode;
+{
+ TreeObject *treeObjPtr = clientPtr->treeObject;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(treeObjPtr->nodeTable), (char *)inode);
+ if (hPtr != NULL) {
+ return (Blt_TreeNode)Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+Blt_TreeTrace
+Blt_TreeCreateTrace(clientPtr, nodePtr, keyPattern, mask, proc, clientData)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *keyPattern;
+ unsigned int mask;
+ Blt_TreeTraceProc *proc;
+ ClientData clientData;
+{
+ TraceHandler *handlerPtr;
+
+ handlerPtr = Blt_Malloc(sizeof (TraceHandler));
+ assert(handlerPtr);
+ handlerPtr->linkPtr = Blt_ChainAppend(clientPtr->traces, handlerPtr);
+ handlerPtr->keyPattern = Blt_Strdup(keyPattern);
+ handlerPtr->clientPtr = clientPtr;
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->mask = mask;
+ handlerPtr->nodePtr = nodePtr;
+ return (Blt_TreeTrace)handlerPtr;
+}
+
+void
+Blt_TreeDeleteTrace(trace)
+ Blt_TreeTrace trace;
+{
+ TraceHandler *handlerPtr = (TraceHandler *)trace;
+
+ Blt_ChainDeleteLink(handlerPtr->clientPtr->traces, handlerPtr->linkPtr);
+ if (handlerPtr->keyPattern != NULL) {
+ Blt_Free(handlerPtr->keyPattern);
+ }
+ Blt_Free(handlerPtr);
+}
+
+void
+Blt_TreeRelabelNode(clientPtr, nodePtr, string)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string;
+{
+ nodePtr->label = Blt_TreeGetKey(string);
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
+ TREE_NOTIFY_RELABEL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeFindChild --
+ *
+ * Searches for the named node in a parent's chain of siblings.
+ *
+ *
+ * Results:
+ * If found, the child node is returned, otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeFindChild(parentPtr, string)
+ Node *parentPtr;
+ CONST char *string;
+{
+ Blt_TreeKey label;
+ register Node *nodePtr;
+
+ label = Blt_TreeGetKey(string);
+ for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+ if (label == nodePtr->label) {
+ return nodePtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePosition --
+ *
+ * Returns the position of the node in its parent's list of
+ * children. The root's position is 0.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeNodePosition(nodePtr)
+ Node *nodePtr;
+{
+ Node *parentPtr;
+ int count;
+
+ count = 0;
+ parentPtr = nodePtr->parent;
+ if (parentPtr != NULL) {
+ Node *childPtr;
+
+ for (childPtr = parentPtr->first; childPtr != NULL;
+ childPtr = childPtr->next) {
+ if (nodePtr == childPtr) {
+ break;
+ }
+ count++;
+ }
+ }
+ return count;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreePrevNode --
+ *
+ * Returns the "previous" node in the tree. This node (in
+ * depth-first order) is its parent, if the node has no siblings
+ * that are previous to it. Otherwise it is the last descendant
+ * of the last sibling. In this case, descend the sibling's
+ * hierarchy, using the last child at any ancestor, with we
+ * we find a leaf.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreePrevNode(rootPtr, nodePtr)
+ Node *rootPtr, *nodePtr;
+{
+ Node *prevPtr;
+
+ if (nodePtr == rootPtr) {
+ return NULL; /* The root is the first node. */
+ }
+ prevPtr = nodePtr->prev;
+ if (prevPtr == NULL) {
+ /* There are no siblings previous to this one, so pick the parent. */
+ return nodePtr->parent;
+ }
+ /*
+ * Traverse down the right-most thread, in order to select the
+ * next entry. Stop when we reach a leaf.
+ */
+ nodePtr = prevPtr;
+ while ((prevPtr = nodePtr->last) != NULL) {
+ nodePtr = prevPtr;
+ }
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNextNode --
+ *
+ * Returns the "next" node in relation to the given node.
+ * The next node (in depth-first order) is either the first
+ * child of the given node the next sibling if the node has
+ * no children (the node is a leaf). If the given node is the
+ * last sibling, then try it's parent next sibling. Continue
+ * until we either find a next sibling for some ancestor or
+ * we reach the root node. In this case the current node is
+ * the last node in the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeNextNode(rootPtr, nodePtr)
+ Node *rootPtr, *nodePtr;
+{
+ Node *nextPtr;
+
+ /* Pick the first sub-node. */
+ nextPtr = nodePtr->first;
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ /*
+ * Back up until we can find a level where we can pick a
+ * "next sibling". For the last entry we'll thread our
+ * way back to the root.
+ */
+ while (nodePtr != rootPtr) {
+ nextPtr = nodePtr->next;
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ nodePtr = nodePtr->parent;
+ }
+ return NULL; /* At root, no next node. */
+}
+
+
+int
+Blt_TreeIsBefore(n1Ptr, n2Ptr)
+ Node *n1Ptr, *n2Ptr;
+{
+ int depth;
+ register int i;
+ Node *nodePtr;
+
+ if (n1Ptr == n2Ptr) {
+ return FALSE;
+ }
+ depth = MIN(n1Ptr->depth, n2Ptr->depth);
+ if (depth == 0) { /* One of the nodes is root. */
+ return (n1Ptr->parent == NULL);
+ }
+ /*
+ * Traverse back from the deepest node, until both nodes are at
+ * the same depth. Check if this ancestor node is the same for
+ * both nodes.
+ */
+ for (i = n1Ptr->depth; i > depth; i--) {
+ n1Ptr = n1Ptr->parent;
+ }
+ if (n1Ptr == n2Ptr) {
+ return FALSE;
+ }
+ for (i = n2Ptr->depth; i > depth; i--) {
+ n2Ptr = n2Ptr->parent;
+ }
+ if (n2Ptr == n1Ptr) {
+ return TRUE;
+ }
+
+ /*
+ * First find the mutual ancestor of both nodes. Look at each
+ * preceding ancestor level-by-level for both nodes. Eventually
+ * we'll find a node that's the parent of both ancestors. Then
+ * find the first ancestor in the parent's list of subnodes.
+ */
+ for (i = depth; i > 0; i--) {
+ if (n1Ptr->parent == n2Ptr->parent) {
+ break;
+ }
+ n1Ptr = n1Ptr->parent;
+ n2Ptr = n2Ptr->parent;
+ }
+ for (nodePtr = n1Ptr->parent->first; nodePtr != NULL;
+ nodePtr = nodePtr->next) {
+ if (nodePtr == n1Ptr) {
+ return TRUE;
+ } else if (nodePtr == n2Ptr) {
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+CallTraces(interp, sourcePtr, treeObjPtr, nodePtr, key, flags)
+ Tcl_Interp *interp;
+ TreeClient *sourcePtr; /* Client holding a reference to the
+ * tree. If NULL, indicates to
+ * execute all handlers, including
+ * those of the caller. */
+ TreeObject *treeObjPtr; /* Tree that was changed. */
+ Node *nodePtr;
+ Blt_TreeKey key;
+ unsigned int flags;
+{
+ Blt_ChainLink *l1Ptr, *l2Ptr;
+ TreeClient *clientPtr;
+ TraceHandler *handlerPtr;
+
+ for(l1Ptr = Blt_ChainFirstLink(treeObjPtr->clients);
+ l1Ptr != NULL; l1Ptr = Blt_ChainNextLink(l1Ptr)) {
+ clientPtr = Blt_ChainGetValue(l1Ptr);
+ for(l2Ptr = Blt_ChainFirstLink(clientPtr->traces);
+ l2Ptr != NULL; l2Ptr = Blt_ChainNextLink(l2Ptr)) {
+ handlerPtr = Blt_ChainGetValue(l2Ptr);
+ if (!Tcl_StringMatch(key, handlerPtr->keyPattern)) {
+ continue;
+ }
+ if ((clientPtr == sourcePtr) &&
+ (handlerPtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
+ continue;
+ }
+ if (((handlerPtr->nodePtr == NULL) ||
+ (handlerPtr->nodePtr == nodePtr)) &&
+ (handlerPtr->mask & flags)) {
+ nodePtr->flags |= TREE_TRACE_ACTIVE;
+ if ((*handlerPtr->proc) (handlerPtr->clientData,
+ treeObjPtr->interp, nodePtr, key, flags) != TCL_OK) {
+ if (interp != NULL) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ nodePtr->flags &= ~TREE_TRACE_ACTIVE;
+ }
+ }
+ }
+}
+
+static Value *
+GetTreeValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ register Value *valuePtr;
+
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't access private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return NULL;
+ }
+ return valuePtr;
+ }
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find field \"", key, "\"",
+ (char *)NULL);
+ }
+ return NULL;
+}
+
+int
+Blt_TreePrivateValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ Value *valuePtr;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ valuePtr->owner = clientPtr;
+ return TCL_OK;
+}
+
+int
+Blt_TreePublicValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ Value *valuePtr;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ valuePtr->owner = NULL;
+ return TCL_OK;
+}
+
+int
+Blt_TreeValueExistsByKey(clientPtr, nodePtr, key)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ register Value *valuePtr;
+
+ valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+Blt_TreeGetValueByKey(interp, clientPtr, nodePtr, key, objPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+ Tcl_Obj **objPtrPtr;
+{
+ register Value *valuePtr;
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ *objPtrPtr = valuePtr->objPtr;
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
+ TREE_TRACE_READ);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeSetValueByKey(interp, clientPtr, nodePtr, key, objPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ Blt_TreeKey key; /* Identifies the field key. */
+ Tcl_Obj *objPtr; /* New value of field. */
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Value *valuePtr;
+ unsigned int flags;
+
+ assert(objPtr != NULL);
+
+ valuePtr = NULL;
+ /* See if a data field by the given name exists. */
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't set private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ flags = 0;
+ if (valuePtr == NULL) {
+ /* Create a new one and append it to the node's chain. */
+ valuePtr = Blt_PoolAllocItem(treeObjPtr->valuePool, sizeof(Value));
+ valuePtr->key = key;
+ valuePtr->owner = NULL;
+ Tcl_IncrRefCount(objPtr);
+ valuePtr->next = nodePtr->values;
+ nodePtr->values = valuePtr;
+ flags |= TREE_TRACE_CREATE;
+ } else {
+ if (objPtr != valuePtr->objPtr) {
+ Tcl_IncrRefCount(objPtr);
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ }
+ valuePtr->objPtr = objPtr;
+ flags |= TREE_TRACE_WRITE;
+
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key,
+ flags);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ Blt_TreeKey key; /* Name of field in node. */
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Value *valuePtr, *prevPtr;
+
+ prevPtr = valuePtr = NULL;
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't unset private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ prevPtr = valuePtr;
+ }
+ if (valuePtr == NULL) {
+ return TCL_OK; /* No value was already set. */
+ }
+ if (prevPtr == NULL) {
+ nodePtr->values = valuePtr->next; /* Remove the head */
+ } else {
+ prevPtr->next = valuePtr->next;
+ }
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET);
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ return TCL_OK;
+}
+
+static int
+ParseParentheses(interp, string, leftPtr, rightPtr)
+ Tcl_Interp *interp;
+ char *string;
+ char **leftPtr, **rightPtr;
+{
+ register char *p;
+ char *left, *right;
+
+ left = right = NULL;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '(') {
+ left = p;
+ } else if (*p == ')') {
+ right = p;
+ }
+ }
+ if (left != right) {
+ if (((left != NULL) && (right == NULL)) ||
+ ((left == NULL) && (right != NULL)) ||
+ (left > right) || (right != (p - 1))) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad array specification \"", string,
+ "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ }
+ *leftPtr = left;
+ *rightPtr = right;
+ return TCL_OK;
+}
+
+int
+Blt_TreeGetValue(interp, clientPtr, nodePtr, string, objPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string; /* String identifying the field in node. */
+ Tcl_Obj **objPtrPtr;
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeGetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1, objPtrPtr);
+ } else {
+ result = Blt_TreeGetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy), objPtrPtr);
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeSetValue(interp, clientPtr, nodePtr, string, valueObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ CONST char *string; /* String identifying the field in node. */
+ Tcl_Obj *valueObjPtr; /* New value of field. If NULL, field
+ * is deleted. */
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeSetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1, valueObjPtr);
+ } else {
+ result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy), valueObjPtr);
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeUnsetValue(interp, clientPtr, nodePtr, string)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ CONST char *string; /* String identifying the field in node. */
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1);
+ } else {
+ result = Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy));
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeValueExists(clientPtr, nodePtr, string)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string;
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses((Tcl_Interp *)NULL, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return FALSE;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeArrayValueExists(clientPtr, nodePtr, copy, left + 1);
+ } else {
+ result = Blt_TreeValueExistsByKey(clientPtr, nodePtr,
+ Blt_TreeGetKey(string));
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+Blt_TreeKey
+Blt_TreeFirstKey(clientPtr, nodePtr, iterPtr)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKeySearch *iterPtr;
+{
+ Value *valuePtr;
+
+ valuePtr = nodePtr->values;
+ if (valuePtr == NULL) {
+ return NULL;
+ }
+ *iterPtr = (Blt_TreeKeySearch)valuePtr->next;
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ return Blt_TreeNextKey(clientPtr, iterPtr);
+ }
+ return valuePtr->key;
+}
+
+Blt_TreeKey
+Blt_TreeNextKey(clientPtr, iterPtr)
+ TreeClient *clientPtr;
+ Blt_TreeKeySearch *iterPtr;
+{
+ Value *valuePtr;
+
+ for (valuePtr = *(Value **)iterPtr; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if ((valuePtr->owner == NULL) || (valuePtr->owner == clientPtr)) {
+ *iterPtr = (Blt_TreeKeySearch)valuePtr->next;
+ return valuePtr->key;
+ }
+ }
+ return NULL;
+}
+
+int
+Blt_TreeIsAncestor(n1Ptr, n2Ptr)
+ Node *n1Ptr, *n2Ptr;
+{
+ if (n2Ptr != NULL) {
+ n2Ptr = n2Ptr->parent;
+ while (n2Ptr != NULL) {
+ if (n2Ptr == n1Ptr) {
+ return TRUE;
+ }
+ n2Ptr = n2Ptr->parent;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeSortNode --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeSortNode(clientPtr, nodePtr, proc)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeCompareNodesProc *proc;
+{
+ Node **nodeArr;
+ Node *childPtr;
+ int nNodes;
+ register Node **p;
+
+ nNodes = nodePtr->nChildren;
+ if (nNodes < 2) {
+ return TCL_OK;
+ }
+ nodeArr = Blt_Malloc((nNodes + 1) * sizeof(Node *));
+ if (nodeArr == NULL) {
+ return TCL_ERROR; /* Out of memory. */
+ }
+ for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL;
+ childPtr = childPtr->next, p++) {
+ *p = childPtr;
+ }
+ *p = NULL;
+
+ qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
+ for (p = nodeArr; *p != NULL; p++) {
+ UnlinkNode(*p);
+ LinkBefore(nodePtr, *p, (Blt_TreeNode)NULL);
+ }
+ Blt_Free(nodeArr);
+ NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
+ return TCL_OK;
+}
+
+#define TEST_RESULT(result) \
+ switch (result) { \
+ case TCL_CONTINUE: \
+ return TCL_OK; \
+ case TCL_OK: \
+ break; \
+ default: \
+ return (result); \
+ }
+
+int
+Blt_TreeApply(nodePtr, proc, clientData)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+{
+ Node *childPtr, *nextPtr;
+ int result;
+
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+
+ /*
+ * Get the next link in the chain before calling Blt_TreeApply
+ * recursively. This is because the apply callback may delete
+ * the node and its link.
+ */
+
+ nextPtr = childPtr->next;
+ result = Blt_TreeApply(childPtr, proc, clientData);
+ TEST_RESULT(result);
+ }
+ return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+}
+
+int
+Blt_TreeApplyDFS(nodePtr, proc, clientData, order)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+ int order; /* Order of traversal. */
+{
+ Node *childPtr, *nextPtr;
+ int result;
+
+ if (order & TREE_PREORDER) {
+ result = (*proc) (nodePtr, clientData, TREE_PREORDER);
+ TEST_RESULT(result);
+ }
+ childPtr = nodePtr->first;
+ if (order & TREE_INORDER) {
+ if (childPtr != NULL) {
+ result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+ TEST_RESULT(result);
+ childPtr = childPtr->next;
+ }
+ result = (*proc) (nodePtr, clientData, TREE_INORDER);
+ TEST_RESULT(result);
+ }
+ for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
+ /*
+ * Get the next link in the chain before calling
+ * Blt_TreeApply recursively. This is because the
+ * apply callback may delete the node and its link.
+ */
+ nextPtr = childPtr->next;
+ result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+ TEST_RESULT(result);
+ }
+ if (order & TREE_POSTORDER) {
+ return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeApplyBFS(nodePtr, proc, clientData)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+{
+ Blt_Chain *queuePtr;
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Node *childPtr;
+ int result;
+
+ queuePtr = Blt_ChainCreate();
+ linkPtr = Blt_ChainAppend(queuePtr, nodePtr);
+ while (linkPtr != NULL) {
+ nodePtr = Blt_ChainGetValue(linkPtr);
+ /* Add the children to the queue. */
+ for (childPtr = nodePtr->first; childPtr != NULL;
+ childPtr = childPtr->next) {
+ Blt_ChainAppend(queuePtr, childPtr);
+ }
+ /* Process the node. */
+ result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
+ switch (result) {
+ case TCL_CONTINUE:
+ Blt_ChainDestroy(queuePtr);
+ return TCL_OK;
+ case TCL_OK:
+ break;
+ default:
+ Blt_ChainDestroy(queuePtr);
+ return result;
+ }
+ /* Remove the node from the queue. */
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ Blt_ChainDeleteLink(queuePtr, linkPtr);
+ linkPtr = nextPtr;
+ }
+ Blt_ChainDestroy(queuePtr);
+ return TCL_OK;
+}
+
+static TreeClient *
+NewTreeClient(treeObjPtr)
+ TreeObject *treeObjPtr;
+{
+ TreeClient *clientPtr;
+
+ clientPtr = Blt_Calloc(1, sizeof(TreeClient));
+ if (clientPtr != NULL) {
+ clientPtr->magic = TREE_MAGIC;
+ clientPtr->linkPtr = Blt_ChainAppend(treeObjPtr->clients, clientPtr);
+ clientPtr->events = Blt_ChainCreate();
+ clientPtr->traces = Blt_ChainCreate();
+ clientPtr->treeObject = treeObjPtr;
+ clientPtr->root = treeObjPtr->root;
+ }
+ return clientPtr;
+}
+
+int
+Blt_TreeCreate(interp, name, clientPtrPtr)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in namespace. Tree
+ * must not already exist. */
+ TreeClient **clientPtrPtr; /* (out) Client token of newly created
+ * tree. Releasing the token will
+ * free the tree. If NULL, no token
+ * is generated. */
+{
+ Tcl_DString dString;
+ Tcl_Namespace *nsPtr;
+ TreeInterpData *dataPtr;
+ TreeObject *treeObjPtr;
+ char *treeName;
+ char string[200];
+
+ dataPtr = GetTreeInterpData(interp);
+ if (name != NULL) {
+ /* Check if this tree already exists the current namespace. */
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
+ if (treeObjPtr != NULL) {
+ Tcl_AppendResult(interp, "a tree object \"", name,
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ /* Generate a unique tree name in the current namespace. */
+ do {
+ sprintf(string, "tree%d", dataPtr->nextId++);
+ } while (GetTreeObject(interp, name, NS_SEARCH_CURRENT) != NULL);
+ name = string;
+ }
+ /*
+ * Tear apart and put back together the namespace-qualified name
+ * of the tree. This is to ensure that naming is consistent.
+ */
+ treeName = (char *)name;
+ if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nsPtr == NULL) {
+ /*
+ * Note: Unlike Tcl_CreateCommand, an unqualified name
+ * doesn't imply the global namespace, but the current one.
+ */
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+ treeObjPtr = NewTreeObject(dataPtr, interp, name);
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree \"", name, "\"",
+ (char *)NULL);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ Tcl_DStringFree(&dString);
+ if (clientPtrPtr != NULL) {
+ TreeClient *clientPtr;
+
+ clientPtr = NewTreeClient(treeObjPtr);
+ if (clientPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree token",(char *)NULL);
+ return TCL_ERROR;
+ }
+ *clientPtrPtr = clientPtr;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeGetToken(interp, name, clientPtrPtr)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in namespace. */
+ TreeClient **clientPtrPtr;
+{
+ TreeClient *clientPtr;
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ clientPtr = NewTreeClient(treeObjPtr);
+ if (clientPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate token for tree \"",
+ name, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *clientPtrPtr = clientPtr;
+ return TCL_OK;
+}
+
+void
+Blt_TreeReleaseToken(clientPtr)
+ TreeClient *clientPtr;
+{
+ TreeObject *treeObjPtr;
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+ TraceHandler *tracePtr;
+
+ if (clientPtr->magic != TREE_MAGIC) {
+ fprintf(stderr, "invalid tree object token 0x%lx\n",
+ (unsigned long)clientPtr);
+ return;
+ }
+ /* Remove any traces that may be set. */
+ for (linkPtr = Blt_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ if (tracePtr->keyPattern != NULL) {
+ Blt_Free(tracePtr->keyPattern);
+ }
+ Blt_Free(tracePtr);
+ }
+ Blt_ChainDestroy(clientPtr->traces);
+ /* And any event handlers. */
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if (handlerPtr->notifyPending) {
+ Tcl_CancelIdleCall(NotifyIdleProc, handlerPtr);
+ }
+ Blt_Free(handlerPtr);
+ }
+ Blt_ChainDestroy(clientPtr->events);
+ treeObjPtr = clientPtr->treeObject;
+ if (treeObjPtr != NULL) {
+ /* Remove the client from the server's list */
+ Blt_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
+ if (Blt_ChainGetLength(treeObjPtr->clients) == 0) {
+ DestroyTreeObject(treeObjPtr);
+ }
+ }
+ clientPtr->magic = 0;
+ Blt_Free(clientPtr);
+}
+
+int
+Blt_TreeExists(interp, name)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in designated namespace. */
+{
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+ if (treeObjPtr == NULL) {
+ Tcl_ResetResult(interp);
+ return 0;
+ }
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+SizeApplyProc(nodePtr, clientData, order)
+ Node *nodePtr; /* Not used. */
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ int *sumPtr = clientData;
+ *sumPtr = *sumPtr + 1;
+ return TCL_OK;
+}
+
+int
+Blt_TreeSize(nodePtr)
+ Node *nodePtr;
+{
+ int sum;
+
+ sum = 0;
+ Blt_TreeApply(nodePtr, SizeApplyProc, &sum);
+ return sum;
+}
+
+
+void
+Blt_TreeCreateEventHandler(clientPtr, mask, proc, clientData)
+ TreeClient *clientPtr;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+
+ handlerPtr = NULL; /* Suppress compiler warning. */
+
+ /* Check if the event is already handled. */
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->proc == proc) &&
+ (handlerPtr->clientData == clientData)) {
+ break;
+ }
+ }
+ if (linkPtr == NULL) {
+ handlerPtr = Blt_Malloc(sizeof (EventHandler));
+ assert(handlerPtr);
+ linkPtr = Blt_ChainAppend(clientPtr->events, handlerPtr);
+ }
+ if (proc == NULL) {
+ Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+ Blt_Free(handlerPtr);
+ } else {
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->mask = mask;
+ handlerPtr->notifyPending = FALSE;
+ handlerPtr->interp = clientPtr->treeObject->interp;
+ }
+}
+
+void
+Blt_TreeDeleteEventHandler(clientPtr, mask, proc, clientData)
+ TreeClient *clientPtr;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->proc == proc) && (handlerPtr->mask == mask) &&
+ (handlerPtr->clientData == clientData)) {
+ if (handlerPtr->notifyPending) {
+ Tcl_CancelIdleCall(NotifyIdleProc, handlerPtr);
+ }
+ Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+ Blt_Free(handlerPtr);
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePath --
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_TreeNodePath(nodePtr, resultPtr)
+ Node *nodePtr;
+ Tcl_DString *resultPtr;
+{
+ char **nameArr; /* Used to stack the component names. */
+ char *staticSpace[64];
+ int nLevels;
+ register int i;
+
+ nLevels = nodePtr->depth;
+ if (nLevels > 64) {
+ nameArr = Blt_Malloc(nLevels * sizeof(char *));
+ assert(nameArr);
+ } else {
+ nameArr = staticSpace;
+ }
+ for (i = nLevels - 1; i >= 0; i--) {
+ /* Save the name of each ancestor in the name array.
+ * Note that we ignore the root. */
+ nameArr[i] = nodePtr->label;
+ nodePtr = nodePtr->parent;
+ }
+ /* Append each the names in the array. */
+ Tcl_DStringInit(resultPtr);
+ for (i = 0; i < nLevels; i++) {
+ Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+ }
+ if (nameArr != staticSpace) {
+ Blt_Free(nameArr);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+int
+Blt_TreeArrayValueExists(clientPtr, nodePtr, arrayName, elemName)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName, *elemName;
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return FALSE;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj((Tcl_Interp *)NULL, valuePtr->objPtr, &tablePtr)
+ != TCL_OK) {
+ return FALSE;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ return (hPtr != NULL);
+}
+
+int
+Blt_TreeGetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName,
+ valueObjPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName;
+ char *elemName;
+ Tcl_Obj **valueObjPtrPtr;
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ if (hPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
+ elemName, ")\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *valueObjPtrPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+
+ /* Reading any element of the array can cause a trace to fire. */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key,
+ TREE_TRACE_READ);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName,
+ valueObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ char *arrayName;
+ char *elemName;
+ Tcl_Obj *valueObjPtr; /* New value of element. */
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+ unsigned int flags;
+ int isNew;
+
+ assert(valueObjPtr != NULL);
+
+ /*
+ * Search for the array in the list of data fields. If one
+ * doesn't exist, create it.
+ */
+ key = Blt_TreeGetKey(arrayName);
+
+ valuePtr = NULL;
+ /* See if a data field by the given name exists. */
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't set private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ flags = 0;
+ if (valuePtr == NULL) {
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = (TreeObject *)nodePtr->treeObject;
+ /* Create a new one and append it to the node's chain. */
+ valuePtr = Blt_PoolAllocItem(treeObjPtr->valuePool, sizeof(Value));
+ valuePtr->key = key;
+ valuePtr->owner = NULL;
+ valuePtr->objPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL);
+ valuePtr->next = nodePtr->values;
+ nodePtr->values = valuePtr;
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ flags |= TREE_TRACE_CREATE;
+ }
+ flags |= TREE_TRACE_WRITE;
+
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_InvalidateStringRep(valuePtr->objPtr);
+
+ hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew);
+ assert(hPtr);
+
+ if (!isNew) {
+ Tcl_Obj *oldValueObjPtr;
+
+ /* An element by the same name already exists. Decrement the
+ * reference count of the old value. */
+
+ oldValueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ if (oldValueObjPtr != NULL) {
+ Tcl_DecrRefCount(oldValueObjPtr);
+ }
+ }
+ Tcl_IncrRefCount(valueObjPtr);
+ Blt_SetHashValue(hPtr, valueObjPtr);
+
+ /*
+ * We don't handle traces on a per array element basis. Setting
+ * any element can fire traces for the value.
+ */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
+ valuePtr->key, flags);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ char *arrayName;
+ char *elemName;
+{
+ Blt_TreeKey key; /* Name of field in node. */
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *valueObjPtr;
+ Value *valuePtr;
+
+ valuePtr = NULL; /* Suppress compiler warning. */
+
+ key = Blt_TreeGetKey(arrayName);
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't unset private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ if (valuePtr == NULL) {
+ return TCL_OK; /* Array doesn't exist. */
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ if (hPtr == NULL) {
+ return TCL_OK; /* Element doesn't exist, Ok. */
+ }
+ valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ Tcl_DecrRefCount(valueObjPtr);
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+
+ /*
+ * Un-setting any element in the array can cause the trace on the value
+ * to fire.
+ */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
+ valuePtr->key, TREE_TRACE_WRITE);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeArrayNames(interp, clientPtr, nodePtr, arrayName, listObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName;
+ Tcl_Obj *listObjPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *objPtr;
+ Value *valuePtr;
+ char *key;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tablePtr = (Blt_HashTable *)valuePtr->objPtr;
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ objPtr = Tcl_NewStringObj(Blt_GetHashKey(tablePtr, hPtr), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNewTagTable --
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeTagTable *
+Blt_TreeNewTagTable()
+{
+ Blt_TreeTagTable *tablePtr;
+
+ tablePtr = Blt_Malloc(sizeof(Blt_TreeTagTable));
+ Blt_InitHashTable(&tablePtr->table, BLT_STRING_KEYS);
+ tablePtr->refCount = 1;
+ return tablePtr;
+}
+
+void
+Blt_TreeReleaseTagTable(tablePtr)
+ Blt_TreeTagTable *tablePtr;
+{
+ tablePtr->refCount--;
+ if (tablePtr->refCount <= 0) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+
+ for(hPtr = Blt_FirstHashEntry(&tablePtr->table, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashTable(&tagPtr->nodeTable);
+ Blt_Free(tagPtr);
+ }
+ Blt_DeleteHashTable(&tablePtr->table);
+ Blt_Free(tablePtr);
+ }
+}
+
+void
+Blt_TreeClearTags(tablePtr, node)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+{
+ Blt_HashEntry *hPtr, *h2Ptr;
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&tablePtr->table, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ h2Ptr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node);
+ if (h2Ptr != NULL) {
+ Blt_DeleteHashEntry(&tagPtr->nodeTable, h2Ptr);
+ }
+ }
+}
+
+int
+Blt_TreeHasTag(tablePtr, node, tagName)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ Blt_TreeTag *tagPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr == NULL) {
+ return FALSE;
+ }
+ tagPtr = Blt_GetHashValue(hPtr);
+ hPtr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node);
+ if (hPtr == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+Blt_TreeAddTag(tablePtr, node, tagName)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+ Blt_TreeTag *tagPtr;
+
+ hPtr = Blt_CreateHashEntry(&tablePtr->table, tagName, &isNew);
+ assert(hPtr);
+ if (isNew) {
+ tagPtr = Blt_Malloc(sizeof(Blt_TreeTag));
+ Blt_InitHashTable(&tagPtr->nodeTable, BLT_ONE_WORD_KEYS);
+ Blt_SetHashValue(hPtr, tagPtr);
+ tagPtr->hashPtr = hPtr;
+ tagPtr->tagName = Blt_GetHashKey(&tablePtr->table, hPtr);
+ } else {
+ tagPtr = Blt_GetHashValue(hPtr);
+ }
+ hPtr = Blt_CreateHashEntry(&tagPtr->nodeTable, (char *)node, &isNew);
+ assert(hPtr);
+ if (isNew) {
+ Blt_SetHashValue(hPtr, node);
+ }
+ return TCL_OK;
+}
+
+void
+Blt_TreeForgetTag(tablePtr, tagName)
+ Blt_TreeTagTable *tablePtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr != NULL) {
+ Blt_TreeTag *tagPtr;
+
+ tagPtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashEntry(&tablePtr->table, hPtr);
+ Blt_DeleteHashTable(&tagPtr->nodeTable);
+ Blt_Free(tagPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeTagHashTable --
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashTable *
+Blt_TreeTagHashTable(tablePtr, tagName)
+ Blt_TreeTagTable *tablePtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr != NULL) {
+ Blt_TreeTag *tagPtr;
+
+ tagPtr = Blt_GetHashValue(hPtr);
+ return &tagPtr->nodeTable;
+ }
+ return NULL;
+}
+
+#endif /* NO_TREE */
diff --git a/blt/src/bltTree.h b/blt/src/bltTree.h
new file mode 100644
index 00000000000..671cb9c4d0f
--- /dev/null
+++ b/blt/src/bltTree.h
@@ -0,0 +1,435 @@
+/*
+ * bltTree.h --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "tree" data object was created by George A. Howlett.
+ */
+
+#ifndef _BLT_TREE_H
+#define _BLT_TREE_H
+
+#include <bltChain.h>
+#include <bltHash.h>
+#include <bltPool.h>
+
+typedef struct Blt_TreeNodeStruct *Blt_TreeNode;
+typedef struct Blt_TreeObjectStruct *Blt_TreeObject;
+typedef struct Blt_TreeClientStruct *Blt_Tree;
+typedef struct Blt_TreeTraceStruct *Blt_TreeTrace;
+typedef struct Blt_TreeKeySearchStruct *Blt_TreeKeySearch;
+typedef struct Blt_TreeValueStruct *Blt_TreeValue;
+typedef struct Blt_TreeTagStruct Blt_TreeTag;
+typedef struct Blt_TreeTagTableStruct Blt_TreeTagTable;
+
+typedef char *Blt_TreeKey;
+
+#define TREE_PREORDER (1<<0)
+#define TREE_POSTORDER (1<<1)
+#define TREE_INORDER (1<<2)
+#define TREE_BREADTHFIRST (1<<3)
+
+#define TREE_TRACE_UNSET (1<<3)
+#define TREE_TRACE_WRITE (1<<4)
+#define TREE_TRACE_READ (1<<5)
+#define TREE_TRACE_CREATE (1<<6)
+#define TREE_TRACE_ALL \
+ (TREE_TRACE_UNSET | TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_CREATE)
+#define TREE_TRACE_MASK (TREE_TRACE_ALL)
+
+#define TREE_TRACE_FOREIGN_ONLY (1<<8)
+#define TREE_TRACE_ACTIVE (1<<9)
+
+#define TREE_NOTIFY_CREATE (1<<0)
+#define TREE_NOTIFY_DELETE (1<<1)
+#define TREE_NOTIFY_MOVE (1<<2)
+#define TREE_NOTIFY_SORT (1<<3)
+#define TREE_NOTIFY_RELABEL (1<<4)
+#define TREE_NOTIFY_ALL \
+ (TREE_NOTIFY_CREATE | TREE_NOTIFY_DELETE | TREE_NOTIFY_MOVE | \
+ TREE_NOTIFY_SORT | TREE_NOTIFY_RELABEL)
+#define TREE_NOTIFY_MASK (TREE_NOTIFY_ALL)
+
+#define TREE_NOTIFY_WHENIDLE (1<<8)
+#define TREE_NOTIFY_FOREIGN_ONLY (1<<9)
+#define TREE_NOTIFY_ACTIVE (1<<10)
+
+typedef struct {
+ int type;
+ Blt_Tree tree;
+ int inode; /* Node of event */
+ Tcl_Interp *interp;
+} Blt_TreeNotifyEvent;
+
+/*
+ * Blt_TreeValue --
+ *
+ * Tree nodes contain heterogeneous data fields, represented as a
+ * chain of these structures. Each field contains the key of the
+ * field (Blt_TreeKey) and the value (Tcl_Obj) containing the
+ * actual data representations.
+ *
+ */
+
+struct Blt_TreeValueStruct {
+ Blt_TreeKey key; /* String identifying the data field */
+ Tcl_Obj *objPtr; /* Data representation. */
+ Blt_Tree owner; /* Non-NULL if privately owned. */
+ Blt_TreeValue next; /* Next value in the chain. */
+};
+
+
+/*
+ * Blt_TreeObject --
+ *
+ * Structure providing the internal representation of the tree
+ * object. A tree is uniquely identified by a combination of
+ * its name and originating namespace. Two trees in the same
+ * interpreter can have the same names but reside in different
+ * namespaces.
+ *
+ * The tree object represents a general-ordered tree of nodes.
+ * Each node may contain a heterogeneous collection of data
+ * values. Each value is identified by a field name and nodes
+ * do not need to contain the same data fields. Data field
+ * names are saved as reference counted strings and can be
+ * shared among nodes.
+ *
+ * The tree is threaded. A node contains both a pointer to
+ * back its parents and another to its siblings. Therefore
+ * the tree maybe traversed non-recursively.
+ *
+ * A tree object can be shared by several clients. When a
+ * client wants to use a tree object, it is given a token
+ * that represents the tree. The tree object uses the tokens
+ * to keep track of its clients. When all clients have
+ * released their tokens the tree is automatically destroyed.
+ */
+struct Blt_TreeObjectStruct {
+ Tcl_Interp *interp; /* Interpreter associated with this tree. */
+
+ char *name;
+
+ Tcl_Namespace *nsPtr;
+
+ Blt_HashEntry *hashPtr; /* Pointer to this tree object in tree
+ * object hash table. */
+ Blt_HashTable *tablePtr;
+
+ Blt_TreeNode root; /* Root of the entire tree. */
+
+ char *sortNodesCmd; /* Tcl command to invoke to sort entries */
+
+ Blt_Chain *clients; /* List of clients using this tree */
+
+ Blt_Pool nodePool;
+ Blt_Pool valuePool;
+
+ Blt_HashTable nodeTable; /* Table of node identifiers. Used to
+ * search for a node pointer given an inode.*/
+ unsigned int nextNode;
+
+ unsigned int nNodes; /* Always counts root node. */
+
+ unsigned int depth; /* Maximum depth of the tree. */
+
+ unsigned int flags; /* Internal flags. See definitions
+ * below. */
+ unsigned int notifyFlags; /* Notification flags. See definitions
+ * below. */
+
+};
+
+/*
+ * Blt_TreeNodeStruct --
+ *
+ * Structure representing a node in a general ordered tree.
+ * Nodes are identified by their index, or inode. Nodes also
+ * have names, but nodes names are not unique and can be
+ * changed. Inodes are valid even if the node is moved.
+ *
+ * Each node can contain a list of data fields. Fields are
+ * name-value pairs. The values are represented by Tcl_Objs.
+ *
+ */
+struct Blt_TreeNodeStruct {
+ Blt_TreeNode parent; /* Parent node. If NULL, then this is
+ the root node. */
+ Blt_TreeNode next; /* Next sibling node. */
+ Blt_TreeNode prev; /* Previous sibling node. */
+ Blt_TreeNode first; /* First child node. */
+ Blt_TreeNode last; /* Last child node. */
+
+ Blt_TreeKey label; /* Node label (doesn't have to be
+ * unique). */
+
+ Blt_TreeObject treeObject;
+
+ Blt_TreeValue values; /* Chain of Blt_TreeValue structures.
+ * Holds key/value data pairs. Can be
+ * NULL, if no data is currently
+ * held. */
+ unsigned int nChildren; /* # of children for this node. */
+ unsigned int inode; /* Serial number of the node. */
+
+ unsigned short depth; /* The depth of this node in the tree. */
+
+ unsigned short flags;
+};
+
+/*
+ * Blt_TreeClientStruct --
+ *
+ * A tree can be shared by several clients. Each client allocates
+ * this structure which acts as a ticket for using the tree. Clients
+ * can designate notifier routines that are automatically invoked
+ * by the tree object whenever the tree is changed is specific
+ * ways by other clients.
+ */
+
+struct Blt_TreeClientStruct {
+ unsigned int magic; /* Magic value indicating whether a
+ * generic pointer is really a tree
+ * token or not. */
+ Blt_ChainLink *linkPtr; /* Pointer into the server's chain of
+ * clients. */
+ Blt_TreeObject treeObject; /* Pointer to the structure containing
+ * the master information about the
+ * tree used by the client. If NULL,
+ * this indicates that the tree has
+ * been destroyed (but as of yet, this
+ * client hasn't recognized it). */
+
+ Blt_Chain *events; /* Chain of node event handlers. */
+ Blt_Chain *traces; /* Chain of data field callbacks. */
+ Blt_TreeNode root; /* Designated root for this client */
+};
+
+struct Blt_TreeTagStruct {
+ char *tagName;
+ Blt_HashEntry *hashPtr;
+ Blt_HashTable nodeTable;
+};
+
+struct Blt_TreeTagTableStruct {
+ Blt_HashTable table;
+ int refCount;
+};
+
+
+typedef int (Blt_TreeNotifyEventProc) _ANSI_ARGS_((ClientData clientData,
+ Blt_TreeNotifyEvent *eventPtr));
+
+typedef int (Blt_TreeTraceProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key,
+ unsigned int flags));
+
+typedef int (Blt_TreeEnumProc) _ANSI_ARGS_((Blt_TreeNode node, Blt_TreeKey key,
+ Tcl_Obj *valuePtr));
+
+typedef int (Blt_TreeCompareNodesProc) _ANSI_ARGS_((Blt_TreeNode *n1Ptr,
+ Blt_TreeNode *n2Ptr));
+
+typedef int (Blt_TreeApplyProc) _ANSI_ARGS_((Blt_TreeNode node,
+ ClientData clientData, int order));
+
+struct Blt_TreeTraceStruct {
+ ClientData clientData;
+ Blt_TreeKey key;
+ Blt_TreeNode node;
+ unsigned int mask;
+ Blt_TreeTraceProc *proc;
+};
+
+EXTERN Blt_TreeKey Blt_TreeGetKey _ANSI_ARGS_((CONST char *string));
+
+EXTERN Blt_TreeNode Blt_TreeCreateNode _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode parent, CONST char *name, int position));
+EXTERN Blt_TreeNode Blt_TreeCreateNodeWithId _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode parent, CONST char *name, int position, int inode));
+
+EXTERN int Blt_TreeDeleteNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node));
+
+EXTERN int Blt_TreeMoveNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node,
+ Blt_TreeNode parent, Blt_TreeNode before));
+
+EXTERN Blt_TreeNode Blt_TreeGetNode _ANSI_ARGS_((Blt_Tree tree,
+ unsigned int inode));
+
+EXTERN Blt_TreeNode Blt_TreeFindChild _ANSI_ARGS_((Blt_TreeNode parent,
+ CONST char *name));
+
+EXTERN Blt_TreeNode Blt_TreeFirstChild _ANSI_ARGS_((Blt_TreeNode parent));
+
+EXTERN Blt_TreeNode Blt_TreeNextSibling _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeLastChild _ANSI_ARGS_((Blt_TreeNode parent));
+
+EXTERN Blt_TreeNode Blt_TreePrevSibling _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeNextNode _ANSI_ARGS_((Blt_TreeNode root,
+ Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreePrevNode _ANSI_ARGS_((Blt_TreeNode root,
+ Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeChangeRoot _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeEndNode _ANSI_ARGS_((Blt_TreeNode node,
+ unsigned int nodeFlags));
+
+EXTERN int Blt_TreeIsBefore _ANSI_ARGS_((Blt_TreeNode node1,
+ Blt_TreeNode node2));
+
+EXTERN int Blt_TreeIsAncestor _ANSI_ARGS_((Blt_TreeNode node1,
+ Blt_TreeNode node2));
+
+EXTERN int Blt_TreePrivateValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreePublicValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreeGetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, CONST char *string, Tcl_Obj **valuePtr));
+
+EXTERN int Blt_TreeValueExists _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node,
+ CONST char *string));
+
+EXTERN int Blt_TreeSetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, CONST char *string, Tcl_Obj *valuePtr));
+
+EXTERN int Blt_TreeUnsetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, CONST char *string));
+
+EXTERN int Blt_TreeGetArrayValue _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, char *arrayName, char *elemName,
+ Tcl_Obj **valueObjPtrPtr));
+
+EXTERN int Blt_TreeSetArrayValue _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, char *arrayName, char *elemName,
+ Tcl_Obj *valueObjPtr));
+
+EXTERN int Blt_TreeUnsetArrayValue _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, char *arrayName, char *elemName));
+
+EXTERN int Blt_TreeArrayValueExists _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode node, char *arrayName, char *elemName));
+
+EXTERN int Blt_TreeArrayNames _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+ Blt_TreeNode node, char *arrayName, Tcl_Obj *listObjPtr));
+
+EXTERN int Blt_TreeGetValueByKey _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key,
+ Tcl_Obj **valuePtr));
+
+EXTERN int Blt_TreeSetValueByKey _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key, Tcl_Obj *valuePtr));
+
+EXTERN int Blt_TreeUnsetValueByKey _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreeValueExistsByKey _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN Blt_TreeKey Blt_TreeFirstKey _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode node, Blt_TreeKeySearch *cursorPtr));
+
+EXTERN Blt_TreeKey Blt_TreeNextKey _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeKeySearch *cursorPtr));
+
+EXTERN int Blt_TreeApply _ANSI_ARGS_((Blt_TreeNode root,
+ Blt_TreeApplyProc *proc, ClientData clientData));
+
+EXTERN int Blt_TreeApplyDFS _ANSI_ARGS_((Blt_TreeNode root,
+ Blt_TreeApplyProc *proc, ClientData clientData, int order));
+
+EXTERN int Blt_TreeApplyBFS _ANSI_ARGS_((Blt_TreeNode root,
+ Blt_TreeApplyProc *proc, ClientData clientData));
+
+EXTERN int Blt_TreeSortNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node,
+ Blt_TreeCompareNodesProc *proc));
+
+EXTERN int Blt_TreeCreate _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name,
+ Blt_Tree *treePtr));
+
+EXTERN int Blt_TreeExists _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name));
+
+EXTERN int Blt_TreeGetToken _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name,
+ Blt_Tree *treePtr));
+
+EXTERN void Blt_TreeReleaseToken _ANSI_ARGS_((Blt_Tree tree));
+
+EXTERN int Blt_TreeSize _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeTrace Blt_TreeCreateTrace _ANSI_ARGS_((Blt_Tree tree,
+ Blt_TreeNode node, CONST char *pattern, unsigned int mask,
+ Blt_TreeTraceProc *proc, ClientData clientData));
+
+EXTERN void Blt_TreeDeleteTrace _ANSI_ARGS_((Blt_TreeTrace token));
+
+EXTERN void Blt_TreeCreateEventHandler _ANSI_ARGS_((Blt_Tree tree,
+ unsigned int mask, Blt_TreeNotifyEventProc *proc,
+ ClientData clientData));
+
+EXTERN void Blt_TreeDeleteEventHandler _ANSI_ARGS_((Blt_Tree tree,
+ unsigned int mask, Blt_TreeNotifyEventProc *proc,
+ ClientData clientData));
+
+EXTERN void Blt_TreeRelabelNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node,
+ CONST char *string));
+EXTERN char *Blt_TreeNodePath _ANSI_ARGS_((Blt_TreeNode node,
+ Tcl_DString *resultPtr));
+EXTERN int Blt_TreeNodePosition _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeTagTable *Blt_TreeNewTagTable _ANSI_ARGS_((void));
+EXTERN void Blt_TreeReleaseTagTable _ANSI_ARGS_((Blt_TreeTagTable *tablePtr));
+EXTERN void Blt_TreeClearTags _ANSI_ARGS_((Blt_TreeTagTable *tablePtr,
+ Blt_TreeNode node));
+EXTERN int Blt_TreeHasTag _ANSI_ARGS_((Blt_TreeTagTable *tablePtr,
+ Blt_TreeNode node, char *tagName));
+EXTERN int Blt_TreeAddTag _ANSI_ARGS_((Blt_TreeTagTable *tablePtr,
+ Blt_TreeNode node, char *tagName));
+EXTERN void Blt_TreeForgetTag _ANSI_ARGS_((Blt_TreeTagTable *tablePtr,
+ char *tagName));
+EXTERN Blt_HashTable *Blt_TreeTagHashTable _ANSI_ARGS_((
+ Blt_TreeTagTable *tablePtr, char *tagName));
+
+#define Blt_TreeName(token) ((token)->treeObject->name)
+#define Blt_TreeRootNode(token) ((token)->root)
+#define Blt_TreeChangeRoot(token, node) ((token)->root = (node))
+
+#define Blt_TreeNodeDepth(token, node) ((node)->depth - (token)->root->depth)
+#define Blt_TreeNodeLabel(node) ((node)->label)
+#define Blt_TreeNodeId(node) ((node)->inode)
+#define Blt_TreeNodeParent(node) ((node)->parent)
+#define Blt_TreeNodeDegree(node) ((node)->nChildren)
+
+#define Blt_TreeIsLeaf(node) ((node)->nChildren == 0)
+
+#define Blt_TreeFirstChild(node) ((node)->first)
+#define Blt_TreeLastChild(node) ((node)->last)
+#define Blt_TreeNextSibling(node) (((node) == NULL) ? NULL : (node)->next)
+#define Blt_TreePrevSibling(node) (((node) == NULL) ? NULL : (node)->prev)
+
+#endif /* _BLT_TREE_H */
+
diff --git a/blt/src/bltTreeCmd.c b/blt/src/bltTreeCmd.c
new file mode 100644
index 00000000000..3ba34538394
--- /dev/null
+++ b/blt/src/bltTreeCmd.c
@@ -0,0 +1,5793 @@
+/*
+ *
+ * bltTreeCmd.c --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "tree" data object was created by George A. Howlett.
+ */
+
+/*
+ tree create t0 t1 t2
+ tree names
+ t0 destroy
+ -or-
+ tree destroy t0
+ tree copy tree@node tree@node -recurse -tags
+
+ tree move node after|before|into t2@node
+
+ $t apply -recurse $root command arg arg
+
+ $t attach treename
+
+ $t children $n
+ t0 copy node1 node2 node3 node4 node5 destName
+ $t delete $n...
+ $t depth $n
+ $t dump $root
+ $t dumpfile $root fileName
+ $t dup $t2
+ $t find $root -name pat -name pattern
+ $t firstchild $n
+ $t get $n $key
+ $t get $n $key(abc)
+ $t index $n
+ $t insert $parent $switches?
+ $t isancestor $n1 $n2
+ $t isbefore $n1 $n2
+ $t isleaf $n
+ $t lastchild $n
+ $t move $n1 after|before|into $n2
+ $t next $n
+ $t nextsibling $n
+ $t path $n1 $n2 $n3...
+ $t parent $n
+ $t previous $n
+ $t prevsibling $n
+ $t restore $root data -overwrite
+ $t root ?$n?
+
+ $t set $n $key $value ?$key $value?
+ $t size $n
+ $t slink $n $t2@$node ???
+ $t sort -recurse $root
+
+ $t tag delete tag1 tag2 tag3...
+ $t tag names
+ $t tag nodes $tag
+ $t tag set $n tag1 tag2 tag3...
+ $t tag unset $n tag1 tag2 tag3...
+
+ $t trace create $n $key how command
+ $t trace delete id1 id2 id3...
+ $t trace names
+ $t trace info $id
+
+ $t unset $n key1 key2 key3...
+
+ $t notify create -oncreate -ondelete -onmove command
+ $t notify create -oncreate -ondelete -onmove -onsort command arg arg arg
+ $t notify delete id1 id2 id3
+ $t notify names
+ $t notify info id
+
+ for { set n [$t firstchild $node] } { $n >= 0 } {
+ set n [$t nextsibling $n] } {
+ }
+ foreach n [$t children $node] {
+
+ }
+ set n [$t next $node]
+ set n [$t previous $node]
+
+*/
+
+#include <bltInt.h>
+
+#ifndef NO_TREE
+
+#include <bltTree.h>
+#include <bltHash.h>
+#include "bltSwitch.h"
+#include <ctype.h>
+
+#define TREE_THREAD_KEY "BLT Tree Command Data"
+#define TREE_MAGIC ((unsigned int) 0x46170277)
+
+enum TagTypes { TAG_TYPE_NONE, TAG_TYPE_ALL, TAG_TYPE_TAG };
+
+typedef struct {
+ Blt_HashTable treeTable; /* Hash table of trees keyed by address. */
+ Tcl_Interp *interp;
+} TreeCmdInterpData;
+
+typedef struct {
+ Tcl_Interp *interp;
+ Tcl_Command cmdToken; /* Token for tree's Tcl command. */
+ Blt_Tree tree; /* Token holding internal tree. */
+
+ Blt_HashEntry *hashPtr;
+ Blt_HashTable *tablePtr;
+
+ Blt_TreeTagTable *tagTablePtr;
+
+ TreeCmdInterpData *dataPtr; /* */
+
+ int traceCounter; /* Used to generate trace id strings. */
+ Blt_HashTable traceTable; /* Table of active traces. Maps trace ids
+ * back to their TraceInfo records. */
+
+ int notifyCounter; /* Used to generate notify id strings. */
+ Blt_HashTable notifyTable; /* Table of event handlers. Maps notify ids
+ * back to their NotifyInfo records. */
+} TreeCmd;
+
+typedef struct {
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+
+ Blt_TreeTrace traceToken;
+
+ char *withTag; /* If non-NULL, the event or trace was
+ * specified with this tag. In this
+ * case, the standard handler will
+ * check if the particular node has
+ * this tag. */
+
+ char command[1]; /* Command prefix for the trace or notify
+ * Tcl callback. Extra arguments will be
+ * appended to the end. Extra space will
+ * be allocated for the length of the string
+ */
+} TraceInfo;
+
+typedef struct {
+ TreeCmd *cmdPtr;
+ int mask;
+ Tcl_Obj **objv;
+ int objc;
+ Blt_TreeNode node; /* Node affected by event. */
+ Blt_TreeTrace notifyToken;
+} NotifyInfo;
+
+
+typedef struct {
+ int mask;
+} NotifyData;
+
+static Blt_SwitchSpec notifySwitches[] =
+{
+ {BLT_SWITCH_FLAG, "-create", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_CREATE},
+ {BLT_SWITCH_FLAG, "-delete", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_DELETE},
+ {BLT_SWITCH_FLAG, "-move", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_MOVE},
+ {BLT_SWITCH_FLAG, "-sort", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_SORT},
+ {BLT_SWITCH_FLAG, "-relabel", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_RELABEL},
+ {BLT_SWITCH_FLAG, "-allevents", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_ALL},
+ {BLT_SWITCH_FLAG, "-whenidle", Blt_Offset(NotifyData, mask), 0, 0,
+ TREE_NOTIFY_WHENIDLE},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToChild;
+#define INSERT_BEFORE (ClientData)0
+#define INSERT_AFTER (ClientData)1
+static Blt_SwitchCustom beforeSwitch =
+{
+ StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
+};
+static Blt_SwitchCustom afterSwitch =
+{
+ StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
+};
+
+typedef struct {
+ char *label;
+ int insertPos;
+ int inode;
+ char **tags;
+ char **dataPairs;
+ Blt_TreeNode parent;
+} InsertData;
+
+static Blt_SwitchSpec insertSwitches[] =
+{
+ {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0,
+ &afterSwitch},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
+ {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
+ &beforeSwitch},
+ {BLT_SWITCH_LIST, "-data", Blt_Offset(InsertData, dataPairs), 0},
+ {BLT_SWITCH_STRING, "-label", Blt_Offset(InsertData, label), 0},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-node", Blt_Offset(InsertData, inode), 0},
+ {BLT_SWITCH_LIST, "-tags", Blt_Offset(InsertData, tags), 0},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+#define PATTERN_EXACT (1)
+#define PATTERN_GLOB (2)
+#define PATTERN_MASK (0x3)
+#define PATTERN_NONE (0)
+#define PATTERN_REGEXP (3)
+#define MATCH_INVERT (1<<8)
+#define MATCH_LEAFONLY (1<<4)
+#define MATCH_NOCASE (1<<5)
+#define MATCH_PATHNAME (1<<6)
+
+typedef struct {
+ TreeCmd *cmdPtr; /* Tree to examine. */
+ Tcl_Obj *listObjPtr; /* List to accumulate the indices of
+ * matching nodes. */
+ Tcl_Obj **objv; /* Command converted into an array of
+ * Tcl_Obj's. */
+ int objc; /* Number of Tcl_Objs in above array. */
+
+ int nMatches; /* Current number of matches. */
+
+ unsigned int flags; /* See flags definitions above. */
+
+ /* Integer options. */
+ int maxMatches; /* If > 0, stop after this many matches. */
+ int maxDepth; /* If > 0, don't descend more than this
+ * many levels. */
+ int order; /* Order of search: Can be either
+ * TREE_PREORDER, TREE_POSTORDER,
+ * TREE_INORDER, TREE_BREADTHFIRST. */
+ /* String options. */
+ char *pattern; /* If non-NULL, pattern to use when
+ * comparing node names. */
+ char *addTag; /* If non-NULL, tag added to selected nodes. */
+
+ char **command; /* Command split into a Tcl list. */
+
+ char *key; /* */
+ char *withTag;
+
+} FindData;
+
+static Blt_SwitchParseProc StringToOrder;
+static Blt_SwitchCustom orderSwitch =
+{
+ StringToOrder, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+
+static Blt_SwitchSpec findSwitches[] =
+{
+ {BLT_SWITCH_STRING, "-addtag", Blt_Offset(FindData, addTag), 0},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-count", Blt_Offset(FindData, maxMatches), 0},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(FindData, maxDepth), 0},
+ {BLT_SWITCH_STRING, "-exact", Blt_Offset(FindData, pattern), 0},
+ {BLT_SWITCH_LIST, "-exec", Blt_Offset(FindData, command), 0},
+ {BLT_SWITCH_STRING, "-glob", Blt_Offset(FindData, pattern), 0},
+ {BLT_SWITCH_FLAG, "-invert", Blt_Offset(FindData, flags), 0, 0,
+ MATCH_INVERT},
+ {BLT_SWITCH_STRING, "-key", Blt_Offset(FindData, key), 0},
+ {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(FindData, flags), 0, 0,
+ MATCH_LEAFONLY},
+ {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(FindData, flags), 0, 0,
+ MATCH_NOCASE},
+ {BLT_SWITCH_CUSTOM, "-order", Blt_Offset(FindData, order), 0, &orderSwitch},
+ {BLT_SWITCH_FLAG, "-path", Blt_Offset(FindData, flags), 0, 0,
+ MATCH_PATHNAME},
+ {BLT_SWITCH_STRING, "-regexp", Blt_Offset(FindData, pattern), 0},
+ {BLT_SWITCH_STRING, "-tag", Blt_Offset(FindData, withTag), 0},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToNode;
+static Blt_SwitchCustom nodeSwitch =
+{
+ StringToNode, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+ TreeCmd *cmdPtr; /* Tree to move nodes. */
+ Blt_TreeNode node;
+ int insertPos;
+} MoveData;
+
+static Blt_SwitchSpec moveSwitches[] =
+{
+ {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(MoveData, node), 0, &nodeSwitch},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(MoveData, insertPos), 0},
+ {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(MoveData, node), 0, &nodeSwitch},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+ Blt_TreeNode srcNode, destNode;
+ Blt_Tree srcTree, destTree;
+ TreeCmd *srcPtr, *destPtr;
+ unsigned int flags;
+ char *label;
+} CopyData;
+
+#define COPY_RECURSE (1<<0)
+#define COPY_TAGS (1<<1)
+#define COPY_OVERWRITE (1<<2)
+
+static Blt_SwitchSpec copySwitches[] =
+{
+ {BLT_SWITCH_STRING, "-label", Blt_Offset(CopyData, label), 0},
+ {BLT_SWITCH_FLAG, "-recurse", Blt_Offset(CopyData, flags), 0, 0,
+ COPY_RECURSE},
+ {BLT_SWITCH_FLAG, "-tags", Blt_Offset(CopyData, flags), 0, 0,
+ COPY_TAGS},
+ {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(CopyData, flags), 0, 0,
+ COPY_OVERWRITE},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+ TreeCmd *cmdPtr; /* Tree to examine. */
+ Tcl_Obj **preObjv; /* Command converted into an array of
+ * Tcl_Obj's. */
+ int preObjc; /* Number of Tcl_Objs in above array. */
+
+ Tcl_Obj **postObjv; /* Command converted into an array of
+ * Tcl_Obj's. */
+ int postObjc; /* Number of Tcl_Objs in above array. */
+
+ unsigned int flags; /* See flags definitions above. */
+
+ int maxDepth; /* If > 0, don't descend more than this
+ * many levels. */
+ /* String options. */
+ char *pattern; /* If non-NULL, pattern to use when
+ * comparing node names. */
+ char **preCmd; /* Pre-command split into a Tcl list. */
+ char **postCmd; /* Post-command split into a Tcl list. */
+
+ char *key; /* */
+ char *withTag;
+} ApplyData;
+
+static Blt_SwitchSpec applySwitches[] =
+{
+ {BLT_SWITCH_LIST, "-precommand", Blt_Offset(ApplyData, preCmd), 0},
+ {BLT_SWITCH_LIST, "-postcommand", Blt_Offset(ApplyData, postCmd), 0},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(ApplyData, maxDepth), 0},
+ {BLT_SWITCH_STRING, "-exact", Blt_Offset(ApplyData, pattern), 0},
+ {BLT_SWITCH_STRING, "-glob", Blt_Offset(ApplyData, pattern), 0},
+ {BLT_SWITCH_FLAG, "-invert", Blt_Offset(ApplyData, flags), 0, 0,
+ MATCH_INVERT},
+ {BLT_SWITCH_STRING, "-key", Blt_Offset(ApplyData, key), 0},
+ {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(ApplyData, flags), 0, 0,
+ MATCH_LEAFONLY},
+ {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(ApplyData, flags), 0, 0,
+ MATCH_NOCASE},
+ {BLT_SWITCH_FLAG, "-path", Blt_Offset(ApplyData, flags), 0, 0,
+ MATCH_PATHNAME},
+ {BLT_SWITCH_STRING, "-regexp", Blt_Offset(ApplyData, pattern), 0},
+ {BLT_SWITCH_STRING, "-tag", Blt_Offset(ApplyData, withTag), 0},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+ unsigned int flags;
+ Blt_HashTable idTable;
+ Blt_TreeNode root;
+} RestoreData;
+
+#define RESTORE_NO_TAGS (1<<0)
+#define RESTORE_OVERWRITE (1<<1)
+
+static Blt_SwitchSpec restoreSwitches[] =
+{
+ {BLT_SWITCH_FLAG, "-notags", Blt_Offset(RestoreData, flags), 0, 0,
+ RESTORE_NO_TAGS},
+ {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(RestoreData, flags), 0, 0,
+ RESTORE_OVERWRITE},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToFormat;
+static Blt_SwitchCustom formatSwitch =
+{
+ StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+ int sort; /* If non-zero, sort the nodes. */
+ int withParent; /* If non-zero, add the parent node id
+ * to the output of the command.*/
+ int withId; /* If non-zero, echo the node id in the
+ * output of the command. */
+} PositionData;
+
+#define POSITION_SORTED (1<<0)
+
+static Blt_SwitchSpec positionSwitches[] =
+{
+ {BLT_SWITCH_FLAG, "-sort", Blt_Offset(PositionData, sort), 0, 0,
+ POSITION_SORTED},
+ {BLT_SWITCH_CUSTOM, "-format", 0, 0, &formatSwitch},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+
+static Tcl_InterpDeleteProc TreeInterpDeleteProc;
+static Blt_TreeApplyProc MatchNodeProc, SortApplyProc;
+static Blt_TreeApplyProc ApplyNodeProc;
+static Blt_TreeTraceProc TreeTraceProc;
+static Tcl_CmdDeleteProc TreeInstDeleteProc;
+static Blt_TreeCompareNodesProc CompareNodes;
+
+static Tcl_ObjCmdProc TreeObjCmd;
+static Tcl_ObjCmdProc CompareDictionaryCmd;
+static Tcl_ObjCmdProc ExitCmd;
+static Blt_TreeNotifyEventProc TreeEventProc;
+
+static int GetNode _ANSI_ARGS_((TreeCmd *cmdPtr, Tcl_Obj *objPtr,
+ Blt_TreeNode *nodePtr));
+
+static int nLines;
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToChild --
+ *
+ * Convert a string represent a node number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToChild(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Flag indicating if the node is
+ * considered before or after the
+ * insertion position. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ InsertData *dataPtr = (InsertData *)record;
+ Blt_TreeNode node;
+
+ node = Blt_TreeFindChild(dataPtr->parent, string);
+ if (node == NULL) {
+ Tcl_AppendResult(interp, "can't find a child named \"", string,
+ "\" in \"", Blt_TreeNodeLabel(dataPtr->parent), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ dataPtr->insertPos = Blt_TreeNodeDegree(node);
+ if (clientData == INSERT_AFTER) {
+ dataPtr->insertPos++;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToNode --
+ *
+ * Convert a string represent a node number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToNode(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ MoveData *dataPtr = (MoveData *)record;
+ Blt_TreeNode node;
+ Tcl_Obj *objPtr;
+ TreeCmd *cmdPtr = dataPtr->cmdPtr;
+
+ objPtr = Tcl_NewStringObj(string, -1);
+ if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ dataPtr->node = node;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToOrder --
+ *
+ * Convert a string represent a node number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToOrder(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ int *orderPtr = (int *)(record + offset);
+ char c;
+
+ c = string[0];
+ if ((c == 'b') && (strcmp(string, "breadthfirst") == 0)) {
+ *orderPtr = TREE_BREADTHFIRST;
+ } else if ((c == 'i') && (strcmp(string, "inorder") == 0)) {
+ *orderPtr = TREE_INORDER;
+ } else if ((c == 'p') && (strcmp(string, "preorder") == 0)) {
+ *orderPtr = TREE_PREORDER;
+ } else if ((c == 'p') && (strcmp(string, "postorder") == 0)) {
+ *orderPtr = TREE_POSTORDER;
+ } else {
+ Tcl_AppendResult(interp, "bad order \"", string,
+ "\": should be breadthfirst, inorder, preorder, or postorder",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToOrder --
+ *
+ * Convert a string represent a node number into its integer
+ * value.
+ *
+ * Results:
+ * The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, switchName, string, record, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ char *switchName; /* Not used. */
+ char *string; /* String representation */
+ char *record; /* Structure record */
+ int offset; /* Offset to field in structure */
+{
+ PositionData *dataPtr = (PositionData *)record;
+
+ if (strcmp(string, "position") == 0) {
+ dataPtr->withParent = FALSE;
+ dataPtr->withId = FALSE;
+ } else if (strcmp(string, "id+position") == 0) {
+ dataPtr->withParent = FALSE;
+ dataPtr->withId = TRUE;
+ } else if (strcmp(string, "parent-at-position") == 0) {
+ dataPtr->withParent = TRUE;
+ dataPtr->withId = FALSE;
+ } else if (strcmp(string, "id+parent-at-position") == 0) {
+ dataPtr->withParent = TRUE;
+ dataPtr->withId = TRUE;
+ } else {
+ Tcl_AppendResult(interp, "bad format \"", string,
+ "\": should be position, parent-at-position, id+position, or id+parent-at-position",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTreeCmdInterpData --
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeCmdInterpData *
+GetTreeCmdInterpData(interp)
+ Tcl_Interp *interp;
+{
+ TreeCmdInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (TreeCmdInterpData *)
+ Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(TreeCmdInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&dataPtr->treeTable, BLT_ONE_WORD_KEYS);
+ }
+ return dataPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTreeCmd --
+ *
+ * Find the tree command associated with the Tcl command "string".
+ *
+ * We have to do multiple lookups to get this right.
+ *
+ * The first step is to generate a canonical command name. If an
+ * unqualified command name (i.e. no namespace qualifier) is
+ * given, we should search first the current namespace and then
+ * the global one. Most Tcl commands (like Tcl_GetCmdInfo) look
+ * only at the global namespace.
+ *
+ * Next check if the string is
+ * a) a Tcl command and
+ * b) really is a command for a tree object.
+ * Tcl_GetCommandInfo will get us the objClientData field that
+ * should be a cmdPtr. We can verify that by searching our hashtable
+ * of cmdPtr addresses.
+ *
+ * Results:
+ * A pointer to the tree command. If no associated tree command
+ * can be found, NULL is returned. It's up to the calling routines
+ * to generate an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeCmd *
+GetTreeCmd(dataPtr, interp, string)
+ TreeCmdInterpData *dataPtr;
+ Tcl_Interp *interp;
+ char *string;
+{
+ char *name;
+ Tcl_Namespace *nsPtr;
+ Tcl_CmdInfo cmdInfo;
+ Blt_HashEntry *hPtr;
+ Tcl_DString dString;
+ char *treeName;
+ int result;
+
+ /* Put apart the tree name and put is back together in a standard
+ * format. */
+ if (Blt_ParseQualifiedName(interp, string, &nsPtr, &name) != TCL_OK) {
+ return NULL; /* No such parent namespace. */
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ /* Rebuild the fully qualified name. */
+ treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+ result = Tcl_GetCommandInfo(interp, treeName, &cmdInfo);
+ Tcl_DStringFree(&dString);
+
+ if (!result) {
+ return NULL;
+ }
+ hPtr = Blt_FindHashEntry(&dataPtr->treeTable,
+ (char *)(cmdInfo.objClientData));
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return Blt_GetHashValue(hPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ForgetTag --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ForgetTag(cmdPtr, tagName)
+ TreeCmd *cmdPtr;
+ char *tagName;
+{
+ if ((strcmp(tagName, "all") != 0) || (strcmp(tagName, "root") != 0)) {
+ Blt_TreeForgetTag(cmdPtr->tagTablePtr, tagName);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddTag --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+AddTag(cmdPtr, node, tagName)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)) {
+ Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
+ tagName, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_TreeAddTag(cmdPtr->tagTablePtr, node, tagName);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HasTag --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+HasTag(cmdPtr, node, tagName)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ if ((strcmp(tagName, "root") == 0) &&
+ (node == Blt_TreeRootNode(cmdPtr->tree))) {
+ return TRUE;
+ }
+ if (strcmp(tagName, "all") == 0) {
+ return TRUE;
+ }
+ return Blt_TreeHasTag(cmdPtr->tagTablePtr, node, tagName);
+}
+
+
+static Blt_TreeNode
+ParseModifiers(interp, tree, node, modifiers)
+ Tcl_Interp *interp;
+ Blt_Tree tree;
+ Blt_TreeNode node;
+ char *modifiers;
+{
+ char *p, *np;
+
+ p = modifiers;
+ do {
+ p += 2; /* Skip the initial "->" */
+ np = strstr(p, "->");
+ if (np != NULL) {
+ *np = '\0';
+ }
+ if ((*p == 'p') && (strcmp(p, "parent") == 0)) {
+ node = Blt_TreeNodeParent(node);
+ } else if ((*p == 'f') && (strcmp(p, "firstchild") == 0)) {
+ node = Blt_TreeFirstChild(node);
+ } else if ((*p == 'l') && (strcmp(p, "lastchild") == 0)) {
+ node = Blt_TreeLastChild(node);
+ } else if ((*p == 'n') && (strcmp(p, "next") == 0)) {
+ node = Blt_TreeNextNode(Blt_TreeRootNode(tree), node);
+ } else if ((*p == 'n') && (strcmp(p, "nextsibling") == 0)) {
+ node = Blt_TreeNextSibling(node);
+ } else if ((*p == 'p') && (strcmp(p, "previous") == 0)) {
+ node = Blt_TreePrevNode(Blt_TreeRootNode(tree), node);
+ } else if ((*p == 'p') && (strcmp(p, "prevsibling") == 0)) {
+ node = Blt_TreePrevSibling(node);
+ } else if (isdigit(UCHAR(*p))) {
+ int inode;
+
+ if (Tcl_GetInt(interp, p, &inode) != TCL_OK) {
+ node = NULL;
+ } else {
+ node = Blt_TreeGetNode(tree, inode);
+ }
+ } else {
+ char *endp;
+
+ if (np != NULL) {
+ endp = np - 1;
+ } else {
+ endp = p + strlen(p) - 1;
+ }
+ if ((*p == '"') && (*endp == '"')) {
+ *endp = '\0';
+ node = Blt_TreeFindChild(node, p + 1);
+ *endp = '"';
+ } else {
+ node = Blt_TreeFindChild(node, p);
+ }
+ }
+ if (node == NULL) {
+ goto error;
+ }
+ if (np != NULL) {
+ *np = '-'; /* Repair the string */
+ }
+ p = np;
+ } while (np != NULL);
+ return node;
+ error:
+ if (np != NULL) {
+ *np = '-'; /* Repair the string */
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetForeignNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetForeignNode(interp, tree, objPtr, nodePtr)
+ Tcl_Interp *interp;
+ Blt_Tree tree;
+ Tcl_Obj *objPtr;
+ Blt_TreeNode *nodePtr;
+{
+ char c;
+ Blt_TreeNode node;
+ char *string;
+ char *p;
+
+ node = NULL;
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+
+ /*
+ * Check if modifiers are present.
+ */
+ p = strstr(string, "->");
+ if (isdigit(UCHAR(c))) {
+ int inode;
+
+ if (p != NULL) {
+ char save;
+ int result;
+
+ save = *p;
+ *p = '\0';
+ result = Tcl_GetInt(interp, string, &inode);
+ *p = save;
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ node = Blt_TreeGetNode(tree, inode);
+ if (p != NULL) {
+ node = ParseModifiers(interp, tree, node, p);
+ }
+ if (node != NULL) {
+ *nodePtr = node;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+ Blt_TreeName(tree), (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetNode(cmdPtr, objPtr, nodePtr)
+ TreeCmd *cmdPtr;
+ Tcl_Obj *objPtr;
+ Blt_TreeNode *nodePtr;
+{
+ Tcl_Interp *interp = cmdPtr->interp;
+ Blt_Tree tree = cmdPtr->tree;
+ char c;
+ Blt_TreeNode node;
+ char *string;
+ char *p;
+
+ node = NULL;
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+
+ /*
+ * Check if modifiers are present.
+ */
+ p = strstr(string, "->");
+ if (isdigit(UCHAR(c))) {
+ int inode;
+
+ if (p != NULL) {
+ char save;
+ int result;
+
+ save = *p;
+ *p = '\0';
+ result = Tcl_GetInt(interp, string, &inode);
+ *p = save;
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ node = Blt_TreeGetNode(tree, inode);
+ if (p != NULL) {
+ node = ParseModifiers(interp, tree, node, p);
+ }
+ if (node != NULL) {
+ *nodePtr = node;
+ return TCL_OK;
+ }
+ } else if (cmdPtr != NULL) {
+ char save;
+
+ save = '\0'; /* Suppress compiler warning. */
+ if (p != NULL) {
+ save = *p;
+ *p = '\0';
+ }
+ if (strcmp(string, "all") == 0) {
+ if (Blt_TreeSize(Blt_TreeRootNode(tree)) > 1) {
+ Tcl_AppendResult(interp, "more than one node tagged as \"",
+ string, "\"", (char *)NULL);
+ if (p != NULL) {
+ *p = save;
+ }
+ return TCL_ERROR;
+ }
+ node = Blt_TreeRootNode(tree);
+ } else if (strcmp(string, "root") == 0) {
+ node = Blt_TreeRootNode(tree);
+ } else {
+ Blt_HashTable *tablePtr;
+ Blt_HashSearch cursor;
+ Blt_HashEntry *hPtr;
+ int result;
+
+ node = NULL;
+ result = TCL_ERROR;
+ tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string);
+ if (tablePtr == NULL) {
+ Tcl_AppendResult(interp, "can't find tag or id \"", string,
+ "\" in ", Blt_TreeName(cmdPtr->tree), (char *)NULL);
+ } else if (tablePtr->numEntries > 1) {
+ Tcl_AppendResult(interp, "more than one node tagged as \"",
+ string, "\"", (char *)NULL);
+ } else {
+ hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
+ node = Blt_GetHashValue(hPtr);
+ result = TCL_OK;
+ }
+ if (result == TCL_ERROR) {
+ if (p != NULL) {
+ *p = save;
+ }
+ return TCL_ERROR;
+ }
+ }
+ if (p != NULL) {
+ *p = save;
+ node = ParseModifiers(interp, tree, node, p);
+ }
+ if (node != NULL) {
+ *nodePtr = node;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+ Blt_TreeName(tree), (char *)NULL);
+ return TCL_ERROR;
+}
+
+typedef struct {
+ int tagType;
+ Blt_TreeNode root;
+ Blt_HashSearch cursor;
+} TagSearch;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FirstTaggedNode --
+ *
+ * Returns the id of the first node tagged by the given tag in
+ * objPtr. It basically hides much of the cumbersome special
+ * case details. For example, the special tags "root" and "all"
+ * always exist, so they don't have entries in the tag hashtable.
+ * If it's a hashed tag (not "root" or "all"), we have to save
+ * the place of where we are in the table for the next call to
+ * NextTaggedNode.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeNode
+FirstTaggedNode(interp, cmdPtr, objPtr, cursorPtr)
+ Tcl_Interp *interp;
+ TreeCmd *cmdPtr;
+ Tcl_Obj *objPtr;
+ TagSearch *cursorPtr;
+{
+ Blt_TreeNode node, root;
+ char *string;
+
+ node = NULL;
+ string = Tcl_GetString(objPtr);
+
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ string = Tcl_GetString(objPtr);
+ cursorPtr->tagType = TAG_TYPE_NONE;
+ cursorPtr->root = root;
+
+ /* Process strings with modifiers or digits as simple ids, not
+ * tags. */
+ if ((strstr(string, "->") != NULL) || (isdigit(UCHAR(*string)))) {
+ if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) {
+ return NULL;
+ }
+ return node;
+ }
+ if (strcmp(string, "all") == 0) {
+ cursorPtr->tagType = TAG_TYPE_ALL;
+ return root;
+ } else if (strcmp(string, "root") == 0) {
+ return root;
+ } else {
+ Blt_HashTable *tablePtr;
+
+ tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string);
+ if (tablePtr != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FirstHashEntry(tablePtr, &(cursorPtr->cursor));
+ node = Blt_GetHashValue(hPtr);
+ cursorPtr->tagType = TAG_TYPE_TAG;
+ return node;
+ }
+ }
+ Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+ Blt_TreeName(cmdPtr->tree), (char *)NULL);
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextTaggedNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeNode
+NextTaggedNode(node, cursorPtr)
+ Blt_TreeNode node;
+ TagSearch *cursorPtr;
+{
+ if (cursorPtr->tagType == TAG_TYPE_ALL) {
+ return Blt_TreeNextNode(cursorPtr->root, node);
+ }
+ if (cursorPtr->tagType == TAG_TYPE_TAG) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_NextHashEntry(&(cursorPtr->cursor));
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DeleteNode(cmdPtr, node)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+{
+ Blt_TreeNode root;
+
+ Blt_TreeClearTags(cmdPtr->tagTablePtr, node);
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ if (node == root) {
+ Blt_TreeNode next;
+ /* Don't delete the root node. Simply clean out the tree. */
+ for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
+ next = Blt_TreeNextSibling(node);
+ Blt_TreeDeleteNode(cmdPtr->tree, node);
+ }
+ } else if (Blt_TreeIsAncestor(root, node)) {
+ Blt_TreeDeleteNode(cmdPtr->tree, node);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNodePath --
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+GetNodePath(cmdPtr, root, node, rootFlag, resultPtr)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode root, node;
+ int rootFlag; /* If non-zero, indicates to include
+ * the root name in the path */
+ Tcl_DString *resultPtr;
+{
+ char **nameArr; /* Used to stack the component names. */
+ char *staticSpace[64];
+ register int i;
+ int nLevels;
+
+ nLevels = Blt_TreeNodeDepth(cmdPtr->tree, node) -
+ Blt_TreeNodeDepth(cmdPtr->tree, root);
+ if (rootFlag) {
+ nLevels++;
+ }
+ if (nLevels > 64) {
+ nameArr = Blt_Malloc(nLevels * sizeof(char *));
+ assert(nameArr);
+ } else {
+ nameArr = staticSpace;
+ }
+ for (i = nLevels; i > 0; i--) {
+ /* Save the name of each ancestor in the name array.
+ * Note that we ignore the root. */
+ nameArr[i - 1] = Blt_TreeNodeLabel(node);
+ node = Blt_TreeNodeParent(node);
+ }
+ /* Append each the names in the array. */
+ Tcl_DStringInit(resultPtr);
+ for (i = 0; i < nLevels; i++) {
+ Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+ }
+ if (nameArr != staticSpace) {
+ Blt_Free(nameArr);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetKeys --
+ *
+ * Tree values are stored in a list last-to-first. Create an
+ * array to hold the pointer so that we can return them in
+ * reverse order.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeKey *
+GetKeys(tree, node)
+ Blt_Tree tree;
+ Blt_TreeNode node;
+{
+ int nValues;
+ Blt_TreeKeySearch keyIter;
+ register Blt_TreeKey key, *keys;
+
+ /* Find out how may values there are. Note that we have to iterate
+ * through the list because some values may be private. */
+ nValues = 0;
+ for (key = Blt_TreeFirstKey(tree, node, &keyIter); key != NULL;
+ key = Blt_TreeNextKey(tree, &keyIter)) {
+ nValues++;
+ }
+ /* Create a temporary array and store the keys in reverse order. */
+ keys = Blt_Malloc(sizeof(Blt_TreeKey) * (nValues + 1));
+ assert(keys);
+ keys[nValues] = NULL;
+ for (key = Blt_TreeFirstKey(tree, node, &keyIter); key != NULL;
+ key = Blt_TreeNextKey(tree, &keyIter)) {
+ nValues--;
+ keys[nValues] = key;
+ }
+ return keys;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseNode5 --
+ *
+ * Parses and creates a node based upon the first 3 fields of
+ * a five field entry. This is the new restore file format.
+ *
+ * parentId nodeId pathList dataList tagList
+ *
+ * The purpose is to attempt to save and restore the node ids
+ * embedded in the restore file information. The old format
+ * could not distinquish between two sibling nodes with the same
+ * label unless they were both leaves. I'm trying to avoid
+ * dependencies upon labels.
+ *
+ * If you're starting from an empty tree, this obviously should
+ * work without a hitch. We only need to map the file's root id
+ * to 0. It's a little more complicated when adding node to an
+ * already full tree.
+ *
+ * First see if the node id isn't already in use. Otherwise, map
+ * the node id (via a hashtable) to the real node. We'll need it
+ * later when subsequent entries refer to their parent id.
+ *
+ * If a parent id is unknown (the restore file may be out of
+ * order), then follow plan B and use its path.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeNode
+ParseNode5(cmdPtr, argc, argv, dataPtr)
+ TreeCmd *cmdPtr;
+ int argc;
+ char **argv;
+ RestoreData *dataPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_TreeNode node, parent;
+ char **names;
+ int nNames, isNew;
+ int parentId, nodeId;
+
+ if ((Tcl_GetInt(cmdPtr->interp, argv[0], &parentId) != TCL_OK) ||
+ (Tcl_GetInt(cmdPtr->interp, argv[1], &nodeId) != TCL_OK) ||
+ (Tcl_SplitList(cmdPtr->interp, argv[2], &nNames, &names) != TCL_OK)) {
+ return NULL;
+ }
+
+ node = parent = dataPtr->root;
+ if (parentId == -1) { /* Dump marks root's parent as -1. */
+ node = dataPtr->root;
+ /* Create a mapping between the old id and the new node */
+ hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
+ &isNew);
+ Blt_SetHashValue(hPtr, node);
+ Blt_TreeRelabelNode(cmdPtr->tree, node, names[0]);
+ } else {
+ /*
+ * Check if the parent has been translated to another id.
+ * This can happen when there's a id collision with an
+ * existing node.
+ */
+ hPtr = Blt_FindHashEntry(&dataPtr->idTable, (char *)parentId);
+ if (hPtr != NULL) {
+ parent = Blt_GetHashValue(hPtr);
+ } else {
+ /* Check if the id already exists in the tree. */
+ parent = Blt_TreeGetNode(cmdPtr->tree, parentId);
+ if (parent == NULL) {
+ /* Parent id doesn't exist (partial restore?).
+ * Plan B: Use the path to create/find the parent with
+ * the requested parent id. */
+ if (nNames > 1) {
+ int i;
+
+ for (i = 1; i < (nNames - 2); i++) {
+ node = Blt_TreeFindChild(parent, names[i]);
+ if (node == NULL) {
+ node = Blt_TreeCreateNode(cmdPtr->tree, parent,
+ names[i], -1);
+ }
+ parent = node;
+ }
+ node = Blt_TreeFindChild(parent, names[nNames - 2]);
+ if (node == NULL) {
+ node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent,
+ names[nNames - 2], parentId, -1);
+ }
+ parent = node;
+ } else {
+ parent = dataPtr->root;
+ }
+ }
+ }
+ /* Check if old node id already in use. */
+ node = NULL;
+ if (dataPtr->flags & RESTORE_OVERWRITE) {
+ node = Blt_TreeFindChild(parent, names[nNames - 1]);
+ /* Create a mapping between the old id and the new node */
+ hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
+ &isNew);
+ Blt_SetHashValue(hPtr, node);
+ }
+ if (node == NULL) {
+ node = Blt_TreeGetNode(cmdPtr->tree, nodeId);
+ if (node != NULL) {
+ node = Blt_TreeCreateNode(cmdPtr->tree, parent,
+ names[nNames - 1], -1);
+ /* Create a mapping between the old id and the new node */
+ hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
+ &isNew);
+ Blt_SetHashValue(hPtr, node);
+ } else {
+ /* Otherwise create a new node with the requested id. */
+ node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent,
+ names[nNames - 1], nodeId, -1);
+ }
+ }
+ }
+ Blt_Free(names);
+ return node;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseNode3 --
+ *
+ * Parses and creates a node based upon the first field of
+ * a three field entry. This is the old restore file format.
+ *
+ * pathList dataList tagList
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeNode
+ParseNode3(cmdPtr, argc, argv, dataPtr)
+ TreeCmd *cmdPtr;
+ int argc;
+ char **argv;
+ RestoreData *dataPtr;
+{
+ Blt_TreeNode node, parent;
+ char **names;
+ int i;
+ int nNames;
+
+ if (Tcl_SplitList(cmdPtr->interp, argv[0], &nNames, &names) != TCL_OK) {
+ return NULL;
+ }
+ node = parent = dataPtr->root;
+ /* Automatically create nodes as needed except for the last node. */
+ for (i = 0; i < (nNames - 1); i++) {
+ node = Blt_TreeFindChild(parent, names[i]);
+ if (node == NULL) {
+ node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
+ }
+ parent = node;
+ }
+ if (nNames > 0) {
+ /*
+ * By default, create duplicate nodes (two sibling nodes with
+ * the same label), unless the -overwrite flag was set.
+ */
+ node = NULL;
+ if (dataPtr->flags & RESTORE_OVERWRITE) {
+ node = Blt_TreeFindChild(parent, names[i]);
+ }
+ if (node == NULL) {
+ node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
+ }
+ }
+ Blt_Free(names);
+ return node;
+}
+
+static int
+RestoreNode(cmdPtr, argc, argv, dataPtr)
+ TreeCmd *cmdPtr;
+ int argc;
+ char **argv;
+ RestoreData *dataPtr;
+{
+ Blt_TreeNode node;
+ Tcl_Obj *valueObjPtr, *emptyStringObjPtr;
+ char **elemArr;
+ int nElem;
+ register int i;
+
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines),
+ ": wrong # elements in restore entry", (char *)NULL);
+ return TCL_ERROR;
+ }
+ emptyStringObjPtr = Tcl_NewStringObj("", -1);
+ /* Parse the path name. */
+ if (argc == 3) {
+ node = ParseNode3(cmdPtr, argc, argv, dataPtr);
+ argc--, argv++;
+ } else if (argc == 5) {
+ node = ParseNode5(cmdPtr, argc, argv, dataPtr);
+ argc -= 3, argv += 3;
+ } else {
+ Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines),
+ ": wrong # elements in restore entry", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ /* Parse the key-value list. */
+ if (Tcl_SplitList(cmdPtr->interp, argv[0], &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nElem; i += 2) {
+ if ((i + 1) < nElem) {
+ valueObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
+ } else {
+ valueObjPtr = emptyStringObjPtr;
+ }
+ if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, elemArr[i],
+ valueObjPtr) != TCL_OK) {
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+ }
+ }
+ Blt_Free(elemArr);
+ if (!(dataPtr->flags & RESTORE_NO_TAGS)) {
+ /* Parse the tag list. */
+ if (Tcl_SplitList(cmdPtr->interp, argv[1], &nElem, &elemArr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nElem; i++) {
+ AddTag(cmdPtr, node, elemArr[i]);
+ }
+ Blt_Free(elemArr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PrintNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+PrintNode(cmdPtr, root, node, resultPtr)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode root, node;
+ Tcl_DString *resultPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *pathName;
+ Tcl_DString dString;
+ Tcl_Obj *valueObjPtr;
+ register Blt_TreeKey *key;
+ Blt_TreeKey *keys;
+ Blt_TreeTag *tagPtr;
+
+ if (node == root) {
+ Tcl_DStringAppendElement(resultPtr, "-1");
+ } else {
+ Blt_TreeNode parent;
+
+ parent = Blt_TreeNodeParent(node);
+ Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(parent)));
+ }
+ Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(node)));
+
+ pathName = GetNodePath(cmdPtr, root, node, TRUE, &dString);
+ Tcl_DStringAppendElement(resultPtr, pathName);
+ Tcl_DStringStartSublist(resultPtr);
+ keys = GetKeys(cmdPtr->tree, node);
+ for (key = keys; *key != NULL; key++) {
+ if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, cmdPtr->tree, node,
+ *key, &valueObjPtr) == TCL_OK) {
+ Tcl_DStringAppendElement(resultPtr, *key);
+ Tcl_DStringAppendElement(resultPtr, Tcl_GetString(valueObjPtr));
+ }
+ }
+ Blt_Free(keys);
+ Tcl_DStringEndSublist(resultPtr);
+ Tcl_DStringStartSublist(resultPtr);
+ for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ if (Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node) != NULL) {
+ Tcl_DStringAppendElement(resultPtr, tagPtr->tagName);
+ }
+ }
+ Tcl_DStringEndSublist(resultPtr);
+ Tcl_DStringAppend(resultPtr, "\n", -1);
+ Tcl_DStringFree(&dString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PrintTraceFlags --
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+PrintTraceFlags(flags, string)
+ unsigned int flags;
+ char *string;
+{
+ register char *p;
+
+ p = string;
+ if (flags & TREE_TRACE_READ) {
+ *p++ = 'r';
+ }
+ if (flags & TREE_TRACE_WRITE) {
+ *p++ = 'w';
+ }
+ if (flags & TREE_TRACE_UNSET) {
+ *p++ = 'u';
+ }
+ if (flags & TREE_TRACE_CREATE) {
+ *p++ = 'c';
+ }
+ *p = '\0';
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTraceFlags --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetTraceFlags(string)
+ char *string;
+{
+ register char *p;
+ unsigned int flags;
+
+ flags = 0;
+ for (p = string; *p != '\0'; p++) {
+ switch (toupper(*p)) {
+ case 'R':
+ flags |= TREE_TRACE_READ;
+ break;
+ case 'W':
+ flags |= TREE_TRACE_WRITE;
+ break;
+ case 'U':
+ flags |= TREE_TRACE_UNSET;
+ break;
+ case 'C':
+ flags |= TREE_TRACE_CREATE;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return flags;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetValues --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SetValues(cmdPtr, node, objc, objv)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ char *string;
+
+ for (i = 0; i < objc; i += 2) {
+ string = Tcl_GetString(objv[i]);
+ if ((i + 1) == objc) {
+ Tcl_AppendResult(cmdPtr->interp, "missing value for field \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, string,
+ objv[i + 1]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnsetValues --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+UnsetValues(cmdPtr, node, objc, objv)
+ TreeCmd *cmdPtr;
+ Blt_TreeNode node;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 0) {
+ Blt_TreeKey key;
+ Blt_TreeKeySearch keyIter;
+
+ for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL;
+ key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
+ if (Blt_TreeUnsetValueByKey(cmdPtr->interp, NULL, node, key)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ } else {
+ register int i;
+
+ for (i = 0; i < objc; i ++) {
+ if (Blt_TreeUnsetValue(cmdPtr->interp, NULL, node,
+ Tcl_GetString(objv[i])) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MatchNodeProc --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MatchNodeProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order;
+{
+ FindData *dataPtr = clientData;
+ Tcl_DString dString;
+ TreeCmd *cmdPtr = dataPtr->cmdPtr;
+ Tcl_Interp *interp = dataPtr->cmdPtr->interp;
+ int result, invert, patternFlags;
+ char *string;
+
+ if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
+ return TCL_OK;
+ }
+ if ((dataPtr->maxDepth >= 0) &&
+ (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
+ return TCL_OK;
+ }
+ result = TRUE;
+ patternFlags = (dataPtr->flags & PATTERN_MASK);
+ Tcl_DStringInit(&dString);
+ string = NULL;
+ if (dataPtr->key != NULL) {
+ Tcl_Obj *objPtr;
+
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node,
+ dataPtr->key, &objPtr) == TCL_OK) {
+ string = Tcl_GetString(objPtr);
+ } else {
+ result = FALSE;
+ }
+ } else {
+ if (dataPtr->flags & MATCH_PATHNAME) {
+ string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
+ node, FALSE, &dString);
+ } else {
+ string = Blt_TreeNodeLabel(node);
+ }
+ }
+ if ((string != NULL) && (patternFlags != PATTERN_NONE)) {
+ if (dataPtr->flags & MATCH_NOCASE) {
+ string = Blt_Strdup(string);
+ strtolower(string);
+ }
+ switch (patternFlags) {
+ case PATTERN_EXACT:
+ result = (strcmp(string, dataPtr->pattern) == 0);
+ break;
+
+ case PATTERN_GLOB:
+ result = Tcl_StringMatch(string, dataPtr->pattern);
+ break;
+
+ case PATTERN_REGEXP:
+ result = Tcl_RegExpMatch(interp, string, dataPtr->pattern);
+ break;
+ }
+ if (dataPtr->flags & MATCH_NOCASE) {
+ Blt_Free(string);
+ }
+ }
+ if ((dataPtr->withTag != NULL) &&
+ (!HasTag(cmdPtr, node, dataPtr->withTag))) {
+ result = FALSE;
+ }
+ Tcl_DStringFree(&dString);
+ invert = (dataPtr->flags & MATCH_INVERT) ? TRUE : FALSE;
+ if (result != invert) {
+ Tcl_Obj *objPtr;
+
+ if (dataPtr->addTag != NULL) {
+ if (AddTag(cmdPtr, node, dataPtr->addTag) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ Tcl_ListObjAppendElement(interp, dataPtr->listObjPtr, objPtr);
+ if (dataPtr->objv != NULL) {
+ dataPtr->objv[dataPtr->objc - 1] = objPtr;
+ result = Tcl_EvalObjv(interp, dataPtr->objc, dataPtr->objv, 0);
+ if (result != TCL_OK) {
+ return result;
+ }
+ }
+ dataPtr->nMatches++;
+ if ((dataPtr->maxMatches > 0) &&
+ (dataPtr->nMatches >= dataPtr->maxMatches)) {
+ return TCL_BREAK;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ApplyNodeProc --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ApplyNodeProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order;
+{
+ ApplyData *dataPtr = clientData;
+ TreeCmd *cmdPtr = dataPtr->cmdPtr;
+ Tcl_Interp *interp = cmdPtr->interp;
+ char *string;
+ int invert, result;
+ unsigned int patternFlags;
+ Tcl_DString dString;
+
+ if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
+ return TCL_OK;
+ }
+ if ((dataPtr->maxDepth >= 0) &&
+ (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
+ return TCL_OK;
+ }
+ Tcl_DStringInit(&dString);
+ result = TRUE;
+ patternFlags = (dataPtr->flags & PATTERN_MASK);
+ string = NULL;
+ if (dataPtr->key != NULL) {
+ Tcl_Obj *valueObjPtr;
+
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node,
+ dataPtr->key, &valueObjPtr) == TCL_OK) {
+ string = Tcl_GetString(valueObjPtr);
+ } else {
+ result = FALSE;
+ }
+ } else {
+ if (dataPtr->flags & MATCH_PATHNAME) {
+ string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node,
+ FALSE, &dString);
+ } else {
+ string = Blt_TreeNodeLabel(node);
+ }
+ }
+ if (patternFlags != PATTERN_NONE) {
+ if (dataPtr->flags & MATCH_NOCASE) {
+ string = Blt_Strdup(string);
+ strtolower(string);
+ }
+ switch (patternFlags) {
+ case PATTERN_EXACT:
+ result = (strcmp(string, dataPtr->pattern) == 0);
+ break;
+
+ case PATTERN_GLOB:
+ result = Tcl_StringMatch(string, dataPtr->pattern);
+ break;
+
+ case PATTERN_REGEXP:
+ result = Tcl_RegExpMatch(interp, string, dataPtr->pattern);
+ break;
+ }
+ if (dataPtr->flags & MATCH_NOCASE) {
+ Blt_Free(string);
+ }
+ }
+ Tcl_DStringFree(&dString);
+ if ((dataPtr->withTag != NULL) &&
+ (!HasTag(cmdPtr, node, dataPtr->withTag))) {
+ result = FALSE;
+ }
+ invert = (dataPtr->flags & MATCH_INVERT) ? 1 : 0;
+ if (result != invert) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ if (order == TREE_PREORDER) {
+ dataPtr->preObjv[dataPtr->preObjc - 1] = objPtr;
+ return Tcl_EvalObjv(interp, dataPtr->preObjc, dataPtr->preObjv, 0);
+ } else if (order == TREE_POSTORDER) {
+ dataPtr->postObjv[dataPtr->postObjc - 1] = objPtr;
+ return Tcl_EvalObjv(interp, dataPtr->postObjc, dataPtr->postObjv,0);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReleaseTreeObject --
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReleaseTreeObject(cmdPtr)
+ TreeCmd *cmdPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ TraceInfo *tracePtr;
+ NotifyInfo *notifyPtr;
+ int i;
+
+ Blt_TreeReleaseToken(cmdPtr->tree);
+ /*
+ * When the tree token is released, all the traces and
+ * notification events are automatically removed. But we still
+ * need to clean up the bookkeeping kept for traces. Clear all
+ * the tags and trace information.
+ */
+ for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tracePtr = Blt_GetHashValue(hPtr);
+ Blt_Free(tracePtr);
+ }
+ for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ notifyPtr = Blt_GetHashValue(hPtr);
+ for (i = 0; i < notifyPtr->objc - 2; i++) {
+ Tcl_DecrRefCount(notifyPtr->objv[i]);
+ }
+ Blt_Free(notifyPtr->objv);
+ Blt_Free(notifyPtr);
+ }
+ if (cmdPtr->tagTablePtr != NULL) {
+ Blt_TreeReleaseTagTable(cmdPtr->tagTablePtr);
+ cmdPtr->tagTablePtr = NULL;
+ }
+ cmdPtr->tree = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeTraceProc --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TreeTraceProc(clientData, interp, node, key, flags)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ Blt_TreeNode node; /* Node that has just been updated. */
+ Blt_TreeKey key; /* Field that's updated. */
+ unsigned int flags;
+{
+ TraceInfo *tracePtr = clientData;
+ Tcl_DString dsCmd, dsName;
+ char string[5];
+ char *qualName;
+ int result;
+
+ /*
+ * If the trace was set on a tag, check that this node has the tag.
+ * There's a special check for the "all" tag. We don't want to
+ * actually tag all nodes in the table, so we do a separate test.
+ */
+ if ((tracePtr->withTag != NULL) &&
+ (!HasTag(tracePtr->cmdPtr, node, tracePtr->withTag))) {
+ return TCL_OK;
+ }
+ Tcl_DStringInit(&dsCmd);
+ Tcl_DStringAppend(&dsCmd, tracePtr->command, -1);
+ Tcl_DStringInit(&dsName);
+ qualName = Blt_GetQualifiedName(
+ Blt_GetCommandNamespace(interp, tracePtr->cmdPtr->cmdToken),
+ Tcl_GetCommandName(interp, tracePtr->cmdPtr->cmdToken), &dsName);
+ Tcl_DStringAppendElement(&dsCmd, qualName);
+ Tcl_DStringFree(&dsName);
+ if (node != NULL) {
+ Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(node)));
+ } else {
+ Tcl_DStringAppendElement(&dsCmd, "");
+ }
+ Tcl_DStringAppendElement(&dsCmd, key);
+ PrintTraceFlags(flags, string);
+ Tcl_DStringAppendElement(&dsCmd, string);
+ result = Tcl_Eval(interp, Tcl_DStringValue(&dsCmd));
+ Tcl_DStringFree(&dsCmd);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeEventProc --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TreeEventProc(clientData, eventPtr)
+ ClientData clientData;
+ Blt_TreeNotifyEvent *eventPtr;
+{
+ TreeCmd *cmdPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ NotifyInfo *notifyPtr;
+ Blt_TreeNode node;
+ char *string;
+
+ switch (eventPtr->type) {
+ case TREE_NOTIFY_CREATE:
+ string = "-create";
+ break;
+
+ case TREE_NOTIFY_DELETE:
+ node = Blt_TreeGetNode(cmdPtr->tree, eventPtr->inode);
+ if (node != NULL) {
+ Blt_TreeClearTags(cmdPtr->tagTablePtr, node);
+ }
+ string = "-delete";
+ break;
+
+ case TREE_NOTIFY_MOVE:
+ string = "-move";
+ break;
+
+ case TREE_NOTIFY_SORT:
+ string = "-sort";
+ break;
+
+ case TREE_NOTIFY_RELABEL:
+ string = "-relabel";
+ break;
+
+ default:
+ /* empty */
+ string = "???";
+ break;
+ }
+
+ for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ notifyPtr = Blt_GetHashValue(hPtr);
+ if (notifyPtr->mask & eventPtr->type) {
+ int result;
+ Tcl_Obj *flagObjPtr, *nodeObjPtr;
+
+ flagObjPtr = Tcl_NewStringObj(string, -1);
+ nodeObjPtr = Tcl_NewIntObj(eventPtr->inode);
+ Tcl_IncrRefCount(flagObjPtr);
+ notifyPtr->objv[notifyPtr->objc - 2] = flagObjPtr;
+ notifyPtr->objv[notifyPtr->objc - 1] = nodeObjPtr;
+ Tcl_IncrRefCount(nodeObjPtr);
+ result = Tcl_EvalObjv(cmdPtr->interp, notifyPtr->objc,
+ notifyPtr->objv, 0);
+ Tcl_DecrRefCount(nodeObjPtr);
+ Tcl_DecrRefCount(flagObjPtr);
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(cmdPtr->interp);
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(cmdPtr->interp);
+ }
+ }
+ return TCL_OK;
+}
+
+
+/* Tree command operations. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ApplyOp --
+ *
+ * t0 apply root -precommand {command} -postcommand {command}
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ApplyOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int result;
+ Blt_TreeNode node;
+ int i;
+ Tcl_Obj **objArr;
+ int count;
+ ApplyData data;
+ int order;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ memset(&data, 0, sizeof(data));
+ data.maxDepth = -1;
+ data.cmdPtr = cmdPtr;
+
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, applySwitches, objc - 3, objv + 3,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ order = 0;
+ if (data.preCmd != NULL) {
+ char **p;
+
+ count = 0;
+ for (p = data.preCmd; *p != NULL; p++) {
+ count++;
+ }
+ objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+ for (i = 0; i < count; i++) {
+ objArr[i] = Tcl_NewStringObj(data.preCmd[i], -1);
+ Tcl_IncrRefCount(objArr[i]);
+ }
+ data.preObjv = objArr;
+ data.preObjc = count + 1;
+ order |= TREE_PREORDER;
+ }
+ if (data.postCmd != NULL) {
+ char **p;
+
+ count = 0;
+ for (p = data.postCmd; *p != NULL; p++) {
+ count++;
+ }
+ objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+ for (i = 0; i < count; i++) {
+ objArr[i] = Tcl_NewStringObj(data.postCmd[i], -1);
+ Tcl_IncrRefCount(objArr[i]);
+ }
+ data.postObjv = objArr;
+ data.postObjc = count + 1;
+ order |= TREE_POSTORDER;
+ }
+ result = Blt_TreeApplyDFS(node, ApplyNodeProc, &data, order);
+ if (data.preObjv != NULL) {
+ for (i = 0; i < (data.preObjc - 1); i++) {
+ Tcl_DecrRefCount(data.preObjv[i]);
+ }
+ Blt_Free(data.preObjv);
+ }
+ if (data.postObjv != NULL) {
+ for (i = 0; i < (data.postObjc - 1); i++) {
+ Tcl_DecrRefCount(data.postObjv[i]);
+ }
+ Blt_Free(data.postObjv);
+ }
+ Blt_FreeSwitches(applySwitches, (char *)&data, 0);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+AncestorOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int d1, d2, minDepth;
+ register int i;
+ Blt_TreeNode ancestor, node1, node2;
+
+ if ((GetNode(cmdPtr, objv[2], &node1) != TCL_OK) ||
+ (GetNode(cmdPtr, objv[3], &node2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (node1 == node2) {
+ ancestor = node1;
+ goto done;
+ }
+ d1 = Blt_TreeNodeDepth(cmdPtr->tree, node1);
+ d2 = Blt_TreeNodeDepth(cmdPtr->tree, node2);
+ minDepth = MIN(d1, d2);
+ if (minDepth == 0) { /* One of the nodes is root. */
+ ancestor = Blt_TreeRootNode(cmdPtr->tree);
+ goto done;
+ }
+ /*
+ * Traverse back from the deepest node, until the both nodes are
+ * at the same depth. Check if the ancestor node found is the
+ * other node.
+ */
+ for (i = d1; i > minDepth; i--) {
+ node1 = Blt_TreeNodeParent(node1);
+ }
+ if (node1 == node2) {
+ ancestor = node2;
+ goto done;
+ }
+ for (i = d2; i > minDepth; i--) {
+ node2 = Blt_TreeNodeParent(node2);
+ }
+ if (node2 == node1) {
+ ancestor = node1;
+ goto done;
+ }
+
+ /*
+ * First find the mutual ancestor of both nodes. Look at each
+ * preceding ancestor level-by-level for both nodes. Eventually
+ * we'll find a node that's the parent of both ancestors. Then
+ * find the first ancestor in the parent's list of subnodes.
+ */
+ for (i = minDepth; i > 0; i--) {
+ node1 = Blt_TreeNodeParent(node1);
+ node2 = Blt_TreeNodeParent(node2);
+ if (node1 == node2) {
+ ancestor = node2;
+ goto done;
+ }
+ }
+ Tcl_AppendResult(interp, "unknown ancestor", (char *)NULL);
+ return TCL_ERROR;
+ done:
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(ancestor));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AttachOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+AttachOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ char *treeName;
+ char *name;
+ Blt_Tree token;
+ Tcl_Namespace *nsPtr;
+ Tcl_DString dString;
+ int result;
+
+ treeName = Tcl_GetString(objv[2]);
+ if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name)
+ != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+ result = Blt_TreeGetToken(interp, treeName, &token);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ReleaseTreeObject(cmdPtr);
+ cmdPtr->tree = token;
+ cmdPtr->tagTablePtr = Blt_TreeNewTagTable();
+ }
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(Blt_TreeName(cmdPtr->tree), -1));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChildrenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ChildrenOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 3) {
+ Tcl_Obj *objPtr, *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (node = Blt_TreeFirstChild(node); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else if (objc == 4) {
+ int childPos;
+ int inode, count;
+
+ /* Get the node at */
+ if (Tcl_GetIntFromObj(interp, objv[3], &childPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = 0;
+ inode = -1;
+ for (node = Blt_TreeFirstChild(node); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ if (count == childPos) {
+ inode = Blt_TreeNodeId(node);
+ break;
+ }
+ count++;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+ } else if (objc == 5) {
+ int firstPos, lastPos, count;
+ Tcl_Obj *objPtr, *listObjPtr;
+ char *string;
+
+ firstPos = lastPos = Blt_TreeNodeDegree(node) - 1;
+ string = Tcl_GetString(objv[3]);
+ if ((strcmp(string, "end") != 0) &&
+ (Tcl_GetIntFromObj(interp, objv[3], &firstPos) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[4]);
+ if ((strcmp(string, "end") != 0) &&
+ (Tcl_GetIntFromObj(interp, objv[4], &lastPos) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+
+ count = 0;
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (node = Blt_TreeFirstChild(node); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ if ((count >= firstPos) && (count <= lastPos)) {
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ count++;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+
+static Blt_TreeNode
+CopyNodes(dataPtr, node, parent)
+ CopyData *dataPtr;
+ Blt_TreeNode node; /* Node to be copied. */
+ Blt_TreeNode parent; /* New parent for the copied node. */
+{
+ Blt_TreeNode newNode; /* Newly created copy. */
+ char *label;
+
+ newNode = NULL;
+ label = Blt_TreeNodeLabel(node);
+ if (dataPtr->flags & COPY_OVERWRITE) {
+ newNode = Blt_TreeFindChild(parent, label);
+ }
+ if (newNode == NULL) { /* Create node in new parent. */
+ newNode = Blt_TreeCreateNode(dataPtr->destTree, parent, label, -1);
+ }
+ /* Copy the data fields. */
+ {
+ Blt_TreeKey key;
+ Tcl_Obj *objPtr;
+ Blt_TreeKeySearch keyIter;
+
+ for (key = Blt_TreeFirstKey(dataPtr->srcTree, node, &keyIter);
+ key != NULL; key = Blt_TreeNextKey(dataPtr->srcTree, &keyIter)) {
+ if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, dataPtr->srcTree,
+ node, key, &objPtr) == TCL_OK) {
+ Blt_TreeSetValueByKey((Tcl_Interp *)NULL, dataPtr->destTree,
+ newNode, key, objPtr);
+ }
+ }
+ }
+ /* Add tags to destination tree command. */
+ if ((dataPtr->destPtr != NULL) && (dataPtr->flags & COPY_TAGS)) {
+ Blt_TreeTag *tagPtr;
+ Blt_HashEntry *hPtr, *h2Ptr;
+ Blt_HashSearch hashIter;
+
+ for (hPtr =
+ Blt_FirstHashEntry(&dataPtr->srcPtr->tagTablePtr->table, &hashIter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&hashIter)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ h2Ptr = Blt_FindHashEntry(&(tagPtr->nodeTable), (char *)node);
+ if (h2Ptr != NULL) {
+ AddTag(dataPtr->destPtr, newNode, tagPtr->tagName);
+ }
+ }
+ }
+ if (dataPtr->flags & COPY_RECURSE) {
+ Blt_TreeNode child;
+
+ for (child = Blt_TreeFirstChild(node); child != NULL;
+ child = Blt_TreeNextSibling(child)) {
+ if (CopyNodes(dataPtr, child, newNode) == NULL) {
+ return NULL;
+ }
+ }
+ }
+ return newNode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CopyOp --
+ *
+ * t0 copy node tree node
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CopyOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeCmd *srcPtr, *destPtr;
+ Blt_Tree srcTree, destTree;
+ Blt_TreeNode srcNode, destNode;
+ CopyData data;
+ int nArgs, nSwitches;
+ char *string;
+ Blt_TreeNode root;
+ register int i;
+
+ if (GetNode(cmdPtr, objv[2], &srcNode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ srcTree = cmdPtr->tree;
+ srcPtr = cmdPtr;
+
+ /* Find the first switch. */
+ for(i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ }
+ nArgs = i - 2;
+ nSwitches = objc - i;
+ if (nArgs < 2) {
+ string = Tcl_GetString(objv[0]);
+ Tcl_AppendResult(interp, "must specify source and destination nodes: ",
+ "should be \"", string,
+ " copy srcNode ?destTree? destNode ?switches?",
+ (char *)NULL);
+ return TCL_ERROR;
+
+ }
+ if (nArgs == 3) {
+ /*
+ * The tree name is either the name of a tree command (first choice)
+ * or an internal tree object.
+ */
+ string = Tcl_GetString(objv[3]);
+ destPtr = GetTreeCmd(cmdPtr->dataPtr, interp, string);
+ if (destPtr != NULL) {
+ destTree = destPtr->tree;
+ } else {
+ /* Try to get the tree as an internal tree data object. */
+ if (Blt_TreeGetToken(interp, string, &destTree) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ objv++, objc--;
+ } else {
+ destPtr = cmdPtr;
+ destTree = destPtr->tree;
+ }
+
+ root = NULL;
+ if (destPtr == NULL) {
+ if (GetForeignNode(interp, destTree, objv[3], &destNode) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ if (GetNode(destPtr, objv[3], &destNode) != TCL_OK) {
+ goto error;
+ }
+ }
+ if (srcNode == destNode) {
+ Tcl_AppendResult(interp, "source and destination nodes are the same",
+ (char *)NULL);
+ goto error;
+ }
+ memset((char *)&data, 0, sizeof(data));
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, copySwitches, nSwitches, objv + 4,
+ (char *)&data, 0) < 0) {
+ goto error;
+ }
+ data.destPtr = destPtr;
+ data.destTree = destTree;
+ data.srcPtr = srcPtr;
+ data.srcTree = srcTree;
+
+ if ((srcTree == destTree) && (data.flags & COPY_RECURSE) &&
+ (Blt_TreeIsAncestor(srcNode, destNode))) {
+ Tcl_AppendResult(interp, "can't make cyclic copy: ",
+ "source node is an ancestor of the destination",
+ (char *)NULL);
+ goto error;
+ }
+
+ /* Copy nodes to destination. */
+ root = CopyNodes(&data, srcNode, destNode, data.label);
+ if (root != NULL) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(root));
+ if (data.label != NULL) {
+ Blt_TreeRelabelNode(data.destTree, root, data.label);
+ }
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ error:
+ if (destPtr == NULL) {
+ Blt_TreeReleaseToken(destTree);
+ }
+ return (root == NULL) ? TCL_ERROR : TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DepthOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DegreeOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int degree;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ degree = Blt_TreeNodeDegree(node);
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), degree);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes one or more nodes from the tree. Nodes may be
+ * specified by their id (a number) or a tag.
+ *
+ * Tags have to be handled carefully here. We can't use the
+ * normal GetTaggedNode, NextTaggedNode, etc. routines because
+ * they walk hashtables while we're deleting nodes. Also,
+ * remember that deleting a node recursively deletes all its
+ * children. If a parent and its children have the same tag, its
+ * possible that the tag list may contain nodes than no longer
+ * exist. So save the node indices in a list and then delete
+ * then in a second pass.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+DeleteOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int i;
+ char *string;
+
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (isdigit(UCHAR(string[0]))) {
+ if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ DeleteNode(cmdPtr, node);
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ Blt_HashSearch cursor;
+ Blt_Chain *chainPtr;
+ Blt_ChainLink *linkPtr, *nextPtr;
+ int inode;
+
+ if ((strcmp(string, "all") == 0) ||
+ (strcmp(string, "root") == 0)) {
+ node = Blt_TreeRootNode(cmdPtr->tree);
+ DeleteNode(cmdPtr, node);
+ continue;
+ }
+ tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string);
+ if (tablePtr == NULL) {
+ goto error;
+ }
+ /*
+ * Generate a list of tagged nodes. Save the inode instead
+ * of the node itself since a pruned branch may contain
+ * more tagged nodes.
+ */
+ chainPtr = Blt_ChainCreate();
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ node = Blt_GetHashValue(hPtr);
+ Blt_ChainAppend(chainPtr, (ClientData)Blt_TreeNodeId(node));
+ }
+ /*
+ * Iterate through this list to delete the nodes. By
+ * side-effect the tag table is deleted and Uids are
+ * released.
+ */
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ inode = (int)Blt_ChainGetValue(linkPtr);
+ node = Blt_TreeGetNode(cmdPtr->tree, inode);
+ if (node != NULL) {
+ DeleteNode(cmdPtr, node);
+ }
+ }
+ Blt_ChainDestroy(chainPtr);
+ }
+ }
+ return TCL_OK;
+ error:
+ Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+ Blt_TreeName(cmdPtr->tree), (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DepthOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DepthOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int depth;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ depth = Blt_TreeNodeDepth(cmdPtr->tree, node);
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), depth);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DumpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DumpOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode top;
+ Tcl_DString dString;
+ register Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+ PrintNode(cmdPtr, top, node, &dString);
+ }
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DumpfileOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DumpfileOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode top;
+ Tcl_Channel channel;
+ Tcl_DString dString;
+ char *fileName;
+ int result;
+ register Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fileName = Tcl_GetString(objv[3]);
+ channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+ PrintNode(cmdPtr, top, node, &dString);
+ }
+ result = Tcl_Write(channel, Tcl_DStringValue(&dString), -1);
+ Tcl_Close(interp, channel);
+ Tcl_DStringFree(&dString);
+ if (result <= 0) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ExistsOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int bool;
+
+ bool = TRUE;
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ bool = FALSE;
+ } else if (objc == 4) {
+ Tcl_Obj *valueObjPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node,
+ string, &valueObjPtr) != TCL_OK) {
+ bool = FALSE;
+ }
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+FindOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ FindData data;
+ int count, result;
+ register int i;
+ Tcl_Obj **objArr;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ memset(&data, 0, sizeof(data));
+ data.maxDepth = -1;
+ data.order = TREE_POSTORDER;
+ data.flags = 0;
+ objArr = NULL;
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, findSwitches, objc - 3, objv + 3,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ count = 0;
+ if (Blt_SwitchChanged(findSwitches, "-glob", (char *)NULL)) {
+ count++;
+ data.flags &= ~PATTERN_MASK;
+ data.flags |= PATTERN_GLOB;
+ }
+ if (Blt_SwitchChanged(findSwitches, "-regexp", (char *)NULL)) {
+ count++;
+ data.flags &= ~PATTERN_MASK;
+ data.flags |= PATTERN_REGEXP;
+ }
+ if (Blt_SwitchChanged(findSwitches, "-exact", (char *)NULL)) {
+ count++;
+ data.flags &= ~PATTERN_MASK;
+ data.flags |= PATTERN_EXACT;
+ }
+ if ((data.flags & MATCH_NOCASE) && (data.pattern != NULL)) {
+ strtolower(data.pattern);
+ }
+ if (data.maxDepth >= 0) {
+ data.maxDepth += Blt_TreeNodeDepth(cmdPtr->tree, node);
+ }
+ if (count > 1) {
+ /* Two many patterns supplied. */
+ }
+ if (data.command != NULL) {
+ char **p;
+
+ count = 0;
+ for (p = data.command; *p != NULL; p++) {
+ count++;
+ }
+ objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+ for (i = 0; i < count; i++) {
+ objArr[i] = Tcl_NewStringObj(data.command[i], -1);
+ Tcl_IncrRefCount(objArr[i]);
+ }
+ data.objv = objArr;
+ data.objc = count + 1;
+ }
+ data.listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ data.cmdPtr = cmdPtr;
+ if (data.order == TREE_BREADTHFIRST) {
+ result = Blt_TreeApplyBFS(node, MatchNodeProc, &data);
+ } else {
+ result = Blt_TreeApplyDFS(node, MatchNodeProc, &data, data.order);
+ }
+ if (data.command != NULL) {
+ for (i = 0; i < count; i++) {
+ Tcl_DecrRefCount(objArr[i]);
+ }
+ Blt_Free(objArr);
+ }
+ Blt_FreeSwitches(findSwitches, (char *)&data, 0);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, data.listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindChildOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindChildOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node, child;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ child = Blt_TreeFindChild(node, Tcl_GetString(objv[3]));
+ if (child != NULL) {
+ inode = Blt_TreeNodeId(child);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FirstChildOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FirstChildOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreeFirstChild(node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 3) {
+ Blt_TreeKey *key;
+ Tcl_Obj *valueObjPtr, *listObjPtr;
+ Blt_TreeKey *keys;
+
+ keys = GetKeys(cmdPtr->tree, node);
+
+ /* Add the key-value pairs to a new Tcl_Obj */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (key = keys; *key != NULL; key++) {
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, *key,
+ &valueObjPtr) == TCL_OK) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(*key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Blt_Free(keys);
+ return TCL_OK;
+ } else {
+ Tcl_Obj *valueObjPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, string,
+ &valueObjPtr) != TCL_OK) {
+ if (objc == 4) {
+ Tcl_DString dString;
+ char *path;
+
+ path = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
+ node, FALSE, &dString);
+ Tcl_AppendResult(interp, "can't find field \"", string,
+ "\" in \"", path, (char *)NULL);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ /* Default to given value */
+ valueObjPtr = objv[4];
+ }
+ Tcl_SetObjResult(interp, valueObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ inode = -1;
+ if (GetNode(cmdPtr, objv[2], &node) == TCL_OK) {
+ inode = Blt_TreeNodeId(node);
+ } else {
+ register int i;
+ int nObjs;
+ Tcl_Obj **objArr;
+ Blt_TreeNode parent;
+ char *string;
+
+ if (Tcl_ListObjGetElements(interp, objv[2], &nObjs, &objArr)
+ != TCL_OK) {
+ goto done; /* Can't split object. */
+ }
+ parent = Blt_TreeRootNode(cmdPtr->tree);
+ for (i = 0; i < nObjs; i++) {
+ string = Tcl_GetString(objArr[i]);
+ if (string[0] == '\0') {
+ continue;
+ }
+ node = Blt_TreeFindChild(parent, string);
+ if (node == NULL) {
+ goto done; /* Can't find component */
+ }
+ parent = node;
+ }
+ inode = Blt_TreeNodeId(node);
+ }
+ done:
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InsertOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode parent, child;
+ InsertData data;
+
+ child = NULL;
+ if (GetNode(cmdPtr, objv[2], &parent) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Initialize switch flags */
+ memset(&data, 0, sizeof(data));
+ data.insertPos = -1; /* Default to append node. */
+ data.parent = parent;
+ data.inode = -1;
+
+ if (Blt_ProcessObjSwitches(interp, insertSwitches, objc - 3, objv + 3,
+ (char *)&data, 0) < 0) {
+ goto error;
+ }
+ if (data.inode > 0) {
+ Blt_TreeNode node;
+
+ node = Blt_TreeGetNode(cmdPtr->tree, data.inode);
+ if (node != NULL) {
+ Tcl_AppendResult(interp, "can't reissue node id \"",
+ Blt_Itoa(data.inode), "\": already exists.", (char *)NULL);
+ goto error;
+ }
+ child = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, data.label,
+ data.inode, data.insertPos);
+ } else {
+ child = Blt_TreeCreateNode(cmdPtr->tree, parent, data.label,
+ data.insertPos);
+ }
+ if (child == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new node", (char *)NULL);
+ goto error;
+ }
+ if (data.tags != NULL) {
+ register char **p;
+
+ for (p = data.tags; *p != NULL; p++) {
+ if (AddTag(cmdPtr, child, *p) != TCL_OK) {
+ goto error;
+ }
+ }
+ }
+ if (data.dataPairs != NULL) {
+ register char **p;
+ char *key;
+ Tcl_Obj *objPtr;
+
+ for (p = data.dataPairs; *p != NULL; p++) {
+ key = *p;
+ p++;
+ if (*p == NULL) {
+ Tcl_AppendResult(interp, "missing value for \"", key, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ objPtr = Tcl_NewStringObj(*p, -1);
+ if (Blt_TreeSetValue(interp, cmdPtr->tree, child, key, objPtr)
+ != TCL_OK) {
+ goto error;
+ }
+ }
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(child));
+ Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
+ return TCL_OK;
+
+ error:
+ if (child != NULL) {
+ Blt_TreeDeleteNode(cmdPtr->tree, child);
+ }
+ Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsAncestorOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsAncestorOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node1, node2;
+ int bool;
+
+ if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
+ (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ bool = Blt_TreeIsAncestor(node1, node2);
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsBeforeOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsBeforeOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node1, node2;
+ int bool;
+
+ if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
+ (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ bool = Blt_TreeIsBefore(node1, node2);
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsLeafOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsLeafOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeIsLeaf(node));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsRootOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IsRootOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int bool;
+
+ if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = (node == Blt_TreeRootNode(cmdPtr->tree));
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec isOps[] =
+{
+ {"ancestor", 1, (Blt_Op)IsAncestorOp, 5, 5, "node1 node2",},
+ {"before", 1, (Blt_Op)IsBeforeOp, 5, 5, "node1 node2",},
+ {"leaf", 1, (Blt_Op)IsLeafOp, 4, 4, "node",},
+ {"root", 1, (Blt_Op)IsRootOp, 4, 4, "node",},
+};
+
+static int nIsOps = sizeof(isOps) / sizeof(Blt_OpSpec);
+
+static int
+IsOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nIsOps, isOps, BLT_OP_ARG2, objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (cmdPtr, interp, objc, objv);
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * KeysOp --
+ *
+ * Returns the key names of values for a node or array value.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+KeysOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch hashIter;
+ Blt_HashTable keyTable;
+ Blt_TreeKey key;
+ Blt_TreeKeySearch keyIter;
+ Blt_TreeNode node;
+ TagSearch tagIter;
+ Tcl_Obj *listObjPtr, *objPtr;
+ register int i;
+ int isNew;
+
+ Blt_InitHashTableWithPool(&keyTable, BLT_ONE_WORD_KEYS);
+ for (i = 2; i < objc; i++) {
+ node = FirstTaggedNode(interp, cmdPtr, objv[i], &tagIter);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL;
+ node = NextTaggedNode(node, &tagIter)) {
+ for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter);
+ key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
+ Blt_CreateHashEntry(&keyTable, key, &isNew);
+ }
+ }
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (hPtr = Blt_FirstHashEntry(&keyTable, &hashIter); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&hashIter)) {
+ objPtr = Tcl_NewStringObj(Blt_GetHashKey(&keyTable, hPtr), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Blt_DeleteHashTable(&keyTable);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LabelOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+LabelOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 4) {
+ Blt_TreeRelabelNode(cmdPtr->tree, node, Tcl_GetString(objv[3]));
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), Blt_TreeNodeLabel(node), -1);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LastChildOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LastChildOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreeLastChild(node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ * The big trick here is to not consider the node to be moved in
+ * determining it's new location. Ideally, you would temporarily
+ * pull from the tree and replace it (back in its old location if
+ * something went wrong), but you could still pick the node by
+ * its serial number. So here we make lots of checks for the
+ * node to be moved.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+MoveOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode root, parent, node;
+ Blt_TreeNode before;
+ MoveData data;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetNode(cmdPtr, objv[3], &parent) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ if (node == root) {
+ Tcl_AppendResult(interp, "can't move root node", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (parent == node) {
+ Tcl_AppendResult(interp, "can't move node to self", (char *)NULL);
+ return TCL_ERROR;
+ }
+ data.node = NULL;
+ data.cmdPtr = cmdPtr;
+ data.insertPos = -1;
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, moveSwitches, objc - 4, objv + 4,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ /* Verify they aren't ancestors. */
+ if (Blt_TreeIsAncestor(node, parent)) {
+ Tcl_AppendResult(interp, "can't move node: \"",
+ Tcl_GetString(objv[2]), (char *)NULL);
+ Tcl_AppendResult(interp, "\" is an ancestor of \"",
+ Tcl_GetString(objv[3]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ before = NULL; /* If before is NULL, this appends the
+ * node to the parent's child list. */
+
+ if (data.node != NULL) { /* -before or -after */
+ if (Blt_TreeNodeParent(data.node) != parent) {
+ Tcl_AppendResult(interp, Tcl_GetString(objv[2]),
+ " isn't the parent of ", Blt_TreeNodeLabel(data.node),
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_SwitchChanged(moveSwitches, "-before", (char *)NULL)) {
+ before = data.node;
+ if (before == node) {
+ Tcl_AppendResult(interp, "can't move node before itself",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ before = Blt_TreeNextSibling(data.node);
+ if (before == node) {
+ Tcl_AppendResult(interp, "can't move node after itself",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ } else if (data.insertPos >= 0) { /* -at */
+ int count; /* Tracks the current list index. */
+ Blt_TreeNode child;
+
+ /*
+ * If the node is in the list, ignore it when determining the
+ * "before" node using the -at index. An index of -1 means to
+ * append the node to the list.
+ */
+ count = 0;
+ for(child = Blt_TreeFirstChild(parent); child != NULL;
+ child = Blt_TreeNextSibling(child)) {
+ if (child == node) {
+ continue; /* Ignore the node to be moved. */
+ }
+ if (count == data.insertPos) {
+ before = child;
+ break;
+ }
+ count++;
+ }
+ }
+ if (Blt_TreeMoveNode(cmdPtr->tree, node, parent, before) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't move node ", Tcl_GetString(objv[2]),
+ " to ", Tcl_GetString(objv[3]), (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NextOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreeNextNode(Blt_TreeRootNode(cmdPtr->tree), node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextSiblingOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NextSiblingOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreeNextSibling(node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyCreateOp --
+ *
+ * tree0 notify create ?flags? command arg
+ *----------------------------------------------------------------------
+ */
+static int
+NotifyCreateOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ NotifyInfo *notifyPtr;
+ NotifyData data;
+ char *string;
+ char idString[200];
+ int isNew, nArgs;
+ Blt_HashEntry *hPtr;
+ int count;
+ register int i;
+
+ count = 0;
+ for (i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] != '-') {
+ break;
+ }
+ count++;
+ }
+ data.mask = 0;
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, notifySwitches, count, objv + 3,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ notifyPtr = Blt_Malloc(sizeof(NotifyInfo));
+
+ nArgs = objc - i;
+
+ /* Stash away the command in structure and pass that to the notifier. */
+ notifyPtr->objv = Blt_Malloc((nArgs + 2) * sizeof(Tcl_Obj *));
+ for (count = 0; i < objc; i++, count++) {
+ Tcl_IncrRefCount(objv[i]);
+ notifyPtr->objv[count] = objv[i];
+ }
+ notifyPtr->objc = nArgs + 2;
+ notifyPtr->cmdPtr = cmdPtr;
+ if (data.mask == 0) {
+ data.mask = TREE_NOTIFY_ALL;
+ }
+ notifyPtr->mask = data.mask;
+
+ sprintf(idString, "notify%d", cmdPtr->notifyCounter++);
+ hPtr = Blt_CreateHashEntry(&(cmdPtr->notifyTable), idString, &isNew);
+ Blt_SetHashValue(hPtr, notifyPtr);
+
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NotifyDeleteOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ NotifyInfo *notifyPtr;
+ Blt_HashEntry *hPtr;
+ register int i, j;
+ char *string;
+
+ for (i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown notify name \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ notifyPtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashEntry(&(cmdPtr->notifyTable), hPtr);
+ for (j = 0; j < (notifyPtr->objc - 2); j++) {
+ Tcl_DecrRefCount(notifyPtr->objv[j]);
+ }
+ Blt_Free(notifyPtr->objv);
+ Blt_Free(notifyPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyInfoOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyInfoOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ NotifyInfo *notifyPtr;
+ Blt_HashEntry *hPtr;
+ Tcl_DString dString;
+ char *string;
+ int i;
+
+ string = Tcl_GetString(objv[3]);
+ hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown notify name \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ notifyPtr = Blt_GetHashValue(hPtr);
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString, string); /* Copy notify Id */
+ Tcl_DStringStartSublist(&dString);
+ if (notifyPtr->mask & TREE_NOTIFY_CREATE) {
+ Tcl_DStringAppendElement(&dString, "-create");
+ }
+ if (notifyPtr->mask & TREE_NOTIFY_DELETE) {
+ Tcl_DStringAppendElement(&dString, "-delete");
+ }
+ if (notifyPtr->mask & TREE_NOTIFY_MOVE) {
+ Tcl_DStringAppendElement(&dString, "-move");
+ }
+ if (notifyPtr->mask & TREE_NOTIFY_SORT) {
+ Tcl_DStringAppendElement(&dString, "-sort");
+ }
+ if (notifyPtr->mask & TREE_NOTIFY_RELABEL) {
+ Tcl_DStringAppendElement(&dString, "-relabel");
+ }
+ if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
+ Tcl_DStringAppendElement(&dString, "-whenidle");
+ }
+ Tcl_DStringEndSublist(&dString);
+ Tcl_DStringStartSublist(&dString);
+ for (i = 0; i < (notifyPtr->objc - 2); i++) {
+ Tcl_DStringAppendElement(&dString, Tcl_GetString(notifyPtr->objv[i]));
+ }
+ Tcl_DStringEndSublist(&dString);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyNamesOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Tcl_Obj *objPtr, *listObjPtr;
+ char *notifyId;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ notifyId = Blt_GetHashKey(&(cmdPtr->notifyTable), hPtr);
+ objPtr = Tcl_NewStringObj(notifyId, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec notifyOps[] =
+{
+ {"create", 1, (Blt_Op)NotifyCreateOp, 4, 0, "?flags? command",},
+ {"delete", 1, (Blt_Op)NotifyDeleteOp, 3, 0, "notifyId...",},
+ {"info", 1, (Blt_Op)NotifyInfoOp, 4, 4, "notifyId",},
+ {"names", 1, (Blt_Op)NotifyNamesOp, 3, 3, "",},
+};
+
+static int nNotifyOps = sizeof(notifyOps) / sizeof(Blt_OpSpec);
+
+static int
+NotifyOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nNotifyOps, notifyOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (cmdPtr, interp, objc, objv);
+ return result;
+}
+
+
+/*ARGSUSED*/
+static int
+ParentOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreeNodeParent(node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PathOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PathOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ Tcl_DString dString;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node, FALSE, &dString);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+
+static int
+ComparePositions(n1Ptr, n2Ptr)
+ Blt_TreeNode *n1Ptr, *n2Ptr;
+{
+ if (*n1Ptr == *n2Ptr) {
+ return 0;
+ }
+ if (Blt_TreeIsBefore(*n1Ptr, *n2Ptr)) {
+ return -1;
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PositionOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PositionOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ PositionData data;
+ Blt_TreeNode *nodeArr, *nodePtr;
+ Blt_TreeNode node;
+ Blt_TreeNode parent, lastParent;
+ Tcl_Obj *listObjPtr, *objPtr;
+ int i;
+ int position;
+ Tcl_DString dString;
+ int n;
+
+ memset((char *)&data, 0, sizeof(data));
+ /* Process switches */
+ n = Blt_ProcessObjSwitches(interp, positionSwitches, objc - 2, objv + 2,
+ (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
+ if (n < 0) {
+ return TCL_ERROR;
+ }
+ objc -= n + 2, objv += n + 2;
+
+ /* Collect the node ids into an array */
+ nodeArr = Blt_Malloc((objc + 1) * sizeof(Blt_TreeNode));
+ for (i = 0; i < objc; i++) {
+ if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+ Blt_Free(nodeArr);
+ return TCL_ERROR;
+ }
+ nodeArr[i] = node;
+ }
+ nodeArr[i] = NULL;
+
+ if (data.sort) { /* Sort the nodes by depth-first order
+ * if requested. */
+ qsort((char *)nodeArr, objc, sizeof(Blt_TreeNode),
+ (QSortCompareProc *)ComparePositions);
+ }
+
+ position = 0; /* Suppress compiler warning. */
+ lastParent = NULL;
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_DStringInit(&dString);
+ for (nodePtr = nodeArr; *nodePtr != NULL; nodePtr++) {
+ parent = Blt_TreeNodeParent(*nodePtr);
+ if ((parent != NULL) && (parent == lastParent)) {
+ /*
+ * Since we've sorted the nodes already, we can safely
+ * assume that if two consecutive nodes have the same
+ * parent, the first node came before the second. If
+ * this is the case, use the last node as a starting
+ * point.
+ */
+
+ /*
+ * Note that we start comparing from the last node,
+ * not its successor. Some one may give us the same
+ * node more than once.
+ */
+ node = *(nodePtr - 1); /* Can't get here unless there's
+ * more than one node. */
+ for(/*empty*/; node != NULL; node = Blt_TreeNextSibling(node)) {
+ if (node == *nodePtr) {
+ break;
+ }
+ position++;
+ }
+ } else {
+ /* The fallback is to linearly search through the
+ * parent's list of children, counting the number of
+ * preceding siblings. Except for nodes with many
+ * siblings (100+), this should be okay. */
+ position = Blt_TreeNodePosition(*nodePtr);
+ }
+ if (data.sort) {
+ lastParent = parent; /* Update the last parent. */
+ }
+ /*
+ * Add an element in the form "parent -at position" to the
+ * list that we're generating.
+ */
+ if (data.withId) {
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*nodePtr));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ if (data.withParent) {
+ char *string;
+
+ Tcl_DStringSetLength(&dString, 0); /* Clear the string. */
+ string = (parent == NULL) ? "" : Blt_Itoa(Blt_TreeNodeId(parent));
+ Tcl_DStringAppendElement(&dString, string);
+ Tcl_DStringAppendElement(&dString, "-at");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(position));
+ objPtr = Tcl_NewStringObj(Tcl_DStringValue(&dString), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ } else {
+ objPtr = Tcl_NewIntObj(position);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ Tcl_DStringFree(&dString);
+ Blt_Free(nodeArr);
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PreviousOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PreviousOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreePrevNode(Blt_TreeRootNode(cmdPtr->tree), node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+PrevSiblingOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ int inode;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ inode = -1;
+ node = Blt_TreePrevSibling(node);
+ if (node != NULL) {
+ inode = Blt_TreeNodeId(node);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestoreOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RestoreOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode root;
+ RestoreData data;
+ Tcl_DString dString;
+ char *entry, *eol, *next;
+ char saved;
+ int result;
+
+ if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ memset((char *)&data, 0, sizeof(data));
+ Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
+ data.root = root;
+
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ result = TCL_OK;
+ nLines = 0;
+ Tcl_DStringInit(&dString);
+ entry = eol = Tcl_GetString(objv[3]);
+ next = entry;
+ while (*eol != '\0') {
+ /* Find end-of-line */
+ for (eol = next; (*eol != '\n') && (*eol != '\0'); eol++) {
+ /*empty*/
+ }
+ /*
+ * Since we don't own the string (the Tcl_Obj could be shared),
+ * save the current end-of-line character (it's either a NUL
+ * or NL) so we can NUL-terminate the line for the call to
+ * Tcl_SplitList and repair it when we're done.
+ */
+ saved = *eol;
+ *eol = '\0';
+ next = eol + 1;
+ nLines++;
+ if (Tcl_CommandComplete(entry)) {
+ char **elemArr;
+ int nElem;
+
+ if (Tcl_SplitList(interp, entry, &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem > 0) {
+ result = RestoreNode(cmdPtr, nElem, elemArr, &data);
+ Blt_Free(elemArr);
+ if (result != TCL_OK) {
+ *eol = saved;
+ break;
+ }
+ }
+ entry = next;
+ }
+ *eol = saved;
+ }
+ Blt_DeleteHashTable(&data.idTable);
+ return result;
+}
+
+static int
+ReadEntry(interp, channel, argcPtr, argvPtr)
+ Tcl_Interp *interp;
+ Tcl_Channel channel;
+ int *argcPtr;
+ char ***argvPtr;
+{
+ Tcl_DString dString;
+ int result;
+ char *entry;
+
+ if (*argvPtr != NULL) {
+ Blt_Free(*argvPtr);
+ *argvPtr = NULL;
+ }
+ Tcl_DStringInit(&dString);
+ entry = NULL;
+ while (Tcl_Gets(channel, &dString) >= 0) {
+ nLines++;
+ Tcl_DStringAppend(&dString, "\n", 1);
+ entry = Tcl_DStringValue(&dString);
+ if (Tcl_CommandComplete(entry)) {
+ result = Tcl_SplitList(interp, entry, argcPtr, argvPtr);
+ Tcl_DStringFree(&dString);
+ return result;
+ }
+ }
+ Tcl_DStringFree(&dString);
+ if (entry == NULL) {
+ *argcPtr = 0; /* EOF */
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "error reading file: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestorefileOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RestorefileOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode root;
+ int nElem;
+ char **elemArr;
+ char *fileName;
+ int result;
+ Tcl_Channel channel;
+ RestoreData data;
+
+ if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fileName = Tcl_GetString(objv[3]);
+ channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ memset((char *)&data, 0, sizeof(data));
+ Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
+ data.root = root;
+
+ /* Process switches */
+ if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4,
+ (char *)&data, 0) < 0) {
+ Tcl_Close(interp, channel);
+ return TCL_ERROR;
+ }
+ elemArr = NULL;
+ nLines = 0;
+ for (;;) {
+ result = ReadEntry(interp, channel, &nElem, &elemArr);
+ if ((result != TCL_OK) || (nElem == 0)) {
+ break;
+ }
+ result = RestoreNode(cmdPtr, nElem, elemArr, &data);
+ if (result != TCL_OK) {
+ break;
+ }
+ }
+ if (elemArr != NULL) {
+ Blt_Free(elemArr);
+ }
+ Tcl_Close(interp, channel);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RootOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RootOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode root;
+
+ if (objc == 3) {
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeChangeRoot(cmdPtr->tree, node);
+ }
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(root));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SetOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ char *string;
+ TagSearch cursor;
+
+ string = Tcl_GetString(objv[2]);
+ if (isdigit(UCHAR(*string))) {
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+ if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SizeOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SizeOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeSize(node));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagForgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TagForgetOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+
+ for (i = 3; i < objc; i++) {
+ ForgetTag(cmdPtr, Tcl_GetString(objv[i]));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagNamesOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+ Tcl_Obj *listObjPtr, *objPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ objPtr = Tcl_NewStringObj("all", -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+
+ objPtr = Tcl_NewStringObj("root", -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ objPtr = Tcl_NewStringObj(tagPtr->tagName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else {
+ register int i;
+ Blt_TreeNode node;
+ Blt_HashEntry *hPtr, *h2Ptr;
+ Blt_HashTable tagTable;
+ int isNew;
+
+ Blt_InitHashTable(&tagTable, BLT_STRING_KEYS);
+ for (i = 3; i < objc; i++) {
+ if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+ goto error;
+ }
+ if (node == Blt_TreeRootNode(cmdPtr->tree)) {
+ Blt_CreateHashEntry(&tagTable, "root", &isNew);
+ }
+ for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table,
+ &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ h2Ptr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node);
+ if (h2Ptr != NULL) {
+ Blt_CreateHashEntry(&tagTable, tagPtr->tagName, &isNew);
+ }
+ }
+ }
+ for (hPtr = Blt_FirstHashEntry(&tagTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ objPtr = Tcl_NewStringObj(Blt_GetHashKey(&tagTable, hPtr), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Blt_DeleteHashTable(&tagTable);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ error:
+ Tcl_DecrRefCount(listObjPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNodesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagNodesOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable nodeTable;
+ Blt_TreeNode node;
+ Tcl_Obj *listObjPtr;
+ Tcl_Obj *objPtr;
+ char *string;
+ int isNew;
+ register int i;
+
+ Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
+ for (i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (strcmp(string, "all") == 0) {
+ break;
+ } else if (strcmp(string, "root") == 0) {
+ Blt_CreateHashEntry(&nodeTable,
+ (char *)Blt_TreeRootNode(cmdPtr->tree), &isNew);
+ continue;
+ } else {
+ Blt_HashTable *tablePtr;
+
+ tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string);
+ if (tablePtr != NULL) {
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ node = Blt_GetHashValue(hPtr);
+ Blt_CreateHashEntry(&nodeTable, (char *)node, &isNew);
+ }
+ continue;
+ }
+ }
+ Tcl_AppendResult(interp, "can't find a tag \"", string, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Blt_DeleteHashTable(&nodeTable);
+ return TCL_OK;
+
+ error:
+ Blt_DeleteHashTable(&nodeTable);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagAddOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagAddOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ register int i;
+ char *string;
+ TagSearch cursor;
+
+ string = Tcl_GetString(objv[3]);
+ if (isdigit(UCHAR(string[0]))) {
+ Tcl_AppendResult(interp, "bad tag \"", string,
+ "\": can't start with a digit", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
+ Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 4; i < objc; i++) {
+ node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+ if (AddTag(cmdPtr, node, string) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagDeleteOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ char *string;
+ Blt_HashTable *tablePtr;
+
+ string = Tcl_GetString(objv[3]);
+ if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
+ Tcl_AppendResult(interp, "can't delete reserved tag \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string);
+ if (tablePtr != NULL) {
+ register int i;
+ Blt_TreeNode node;
+ TagSearch cursor;
+ Blt_HashEntry *hPtr;
+
+ for (i = 4; i < objc; i++) {
+ node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL;
+ node = NextTaggedNode(node, &cursor)) {
+ hPtr = Blt_FindHashEntry(tablePtr, (char *)node);
+ if (hPtr != NULL) {
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDumpOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagDumpOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ register Blt_TreeNode root, node;
+ Tcl_DString dString;
+ TagSearch cursor;
+ register int i;
+
+ Tcl_DStringInit(&dString);
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ for (i = 3; i < objc; i++) {
+ node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+ PrintNode(cmdPtr, root, node, &dString);
+ }
+ }
+ Tcl_DStringResult(interp, &dString);
+ Tcl_DStringFree(&dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec tagOps[] = {
+ {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag node...",},
+ {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag node...",},
+ {"dump", 2, (Blt_Op)TagDumpOp, 4, 0, "tag...",},
+ {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
+ {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?node...?",},
+ {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
+};
+
+static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
+
+static int
+TagOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv,
+ 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (cmdPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceCreateOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TraceCreateOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_TreeNode node;
+ TraceInfo *tracePtr;
+ char *string, *key, *command;
+ char *tagName;
+ char idString[200];
+ int flags, isNew;
+
+ string = Tcl_GetString(objv[3]);
+ if (isdigit(UCHAR(*string))) {
+ if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tagName = NULL;
+ } else {
+ tagName = Blt_Strdup(string);
+ node = NULL;
+ }
+ key = Tcl_GetString(objv[4]);
+ string = Tcl_GetString(objv[5]);
+ flags = GetTraceFlags(string);
+ if (flags < 0) {
+ Tcl_AppendResult(interp, "unknown flag in \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ command = Tcl_GetString(objv[6]);
+ /* Stash away the command in structure and pass that to the trace. */
+ tracePtr = Blt_Malloc(strlen(command) + sizeof(TraceInfo));
+ strcpy(tracePtr->command, command);
+ tracePtr->cmdPtr = cmdPtr;
+ tracePtr->withTag = tagName;
+ tracePtr->node = node;
+ tracePtr->traceToken = Blt_TreeCreateTrace(cmdPtr->tree, node, key,
+ flags, TreeTraceProc, tracePtr);
+
+ sprintf(idString, "trace%d", cmdPtr->traceCounter++);
+ hPtr = Blt_CreateHashEntry(&(cmdPtr->traceTable), idString, &isNew);
+ Blt_SetHashValue(hPtr, tracePtr);
+
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TraceDeleteOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TraceInfo *tracePtr;
+ Blt_HashEntry *hPtr;
+ register int i;
+ char *key;
+
+ for (i = 3; i < objc; i++) {
+ key = Tcl_GetString(objv[i]);
+ hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown trace \"", key, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tracePtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashEntry(&(cmdPtr->traceTable), hPtr);
+ Blt_TreeDeleteTrace(tracePtr->traceToken);
+ if (tracePtr->withTag != NULL) {
+ Blt_Free(tracePtr->withTag);
+ }
+ Blt_Free(tracePtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TraceNamesOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp, Blt_GetHashKey(&(cmdPtr->traceTable), hPtr));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceInfoOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TraceInfoOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TraceInfo *tracePtr;
+ struct Blt_TreeTraceStruct *tokenPtr;
+ Blt_HashEntry *hPtr;
+ Tcl_DString dString;
+ char string[5];
+ char *key;
+
+ key = Tcl_GetString(objv[3]);
+ hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown trace \"", key, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ tracePtr = Blt_GetHashValue(hPtr);
+ if (tracePtr->withTag != NULL) {
+ Tcl_DStringAppendElement(&dString, tracePtr->withTag);
+ } else {
+ int inode;
+
+ inode = Blt_TreeNodeId(tracePtr->node);
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(inode));
+ }
+ tokenPtr = (struct Blt_TreeTraceStruct *)tracePtr->traceToken;
+ Tcl_DStringAppendElement(&dString, tokenPtr->key);
+ PrintTraceFlags(tokenPtr->mask, string);
+ Tcl_DStringAppendElement(&dString, string);
+ Tcl_DStringAppendElement(&dString, tracePtr->command);
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec traceOps[] =
+{
+ {"create", 1, (Blt_Op)TraceCreateOp, 7, 7, "node key how command",},
+ {"delete", 1, (Blt_Op)TraceDeleteOp, 3, 0, "id...",},
+ {"info", 1, (Blt_Op)TraceInfoOp, 4, 4, "id",},
+ {"names", 1, (Blt_Op)TraceNamesOp, 3, 3, "",},
+};
+
+static int nTraceOps = sizeof(traceOps) / sizeof(Blt_OpSpec);
+
+static int
+TraceOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTraceOps, traceOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (cmdPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ Tcl_Obj *valueObjPtr;
+ char *string;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ string = Tcl_GetString(objv[3]);
+ if (Blt_TreeGetValue(interp, cmdPtr->tree, node, string, &valueObjPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (valueObjPtr->typePtr != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(valueObjPtr->typePtr->name, -1));
+ } else {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("string", -1));
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnsetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+UnsetOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ if (isdigit(UCHAR(*string))) {
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ TagSearch cursor;
+
+ node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
+ if (node == NULL) {
+ return TCL_ERROR;
+ }
+ for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+ if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+typedef struct {
+ TreeCmd *cmdPtr;
+ unsigned int flags;
+ int type;
+ int mode;
+ char *key;
+ char *command;
+} SortData;
+
+#define SORT_RECURSE (1<<2)
+#define SORT_DECREASING (1<<3)
+#define SORT_PATHNAME (1<<4)
+
+enum SortTypes { SORT_DICTIONARY, SORT_REAL, SORT_INTEGER, SORT_ASCII,
+ SORT_COMMAND };
+
+enum SortModes { SORT_FLAT, SORT_REORDER };
+
+static Blt_SwitchSpec sortSwitches[] =
+{
+ {BLT_SWITCH_VALUE, "-ascii", Blt_Offset(SortData, type), 0, 0,
+ SORT_ASCII},
+ {BLT_SWITCH_STRING, "-command", Blt_Offset(SortData, command), 0},
+ {BLT_SWITCH_FLAG, "-decreasing", Blt_Offset(SortData, flags), 0, 0,
+ SORT_DECREASING},
+ {BLT_SWITCH_VALUE, "-dictionary", Blt_Offset(SortData, type), 0, 0,
+ SORT_DICTIONARY},
+ {BLT_SWITCH_VALUE, "-integer", Blt_Offset(SortData, type), 0, 0,
+ SORT_INTEGER},
+ {BLT_SWITCH_STRING, "-key", Blt_Offset(SortData, key), 0},
+ {BLT_SWITCH_FLAG, "-path", Blt_Offset(SortData, flags), 0, 0,
+ SORT_PATHNAME},
+ {BLT_SWITCH_VALUE, "-real", Blt_Offset(SortData, type), 0, 0,
+ SORT_REAL},
+ {BLT_SWITCH_VALUE, "-recurse", Blt_Offset(SortData, flags), 0, 0,
+ SORT_RECURSE},
+ {BLT_SWITCH_VALUE, "-reorder", Blt_Offset(SortData, mode), 0, 0,
+ SORT_REORDER},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static SortData sortData;
+
+static int
+CompareNodes(n1Ptr, n2Ptr)
+ Blt_TreeNode *n1Ptr, *n2Ptr;
+{
+ TreeCmd *cmdPtr = sortData.cmdPtr;
+ char *s1, *s2;
+ int result;
+ Tcl_DString dString1, dString2;
+
+ s1 = s2 = "";
+ result = 0;
+
+ if (sortData.flags & SORT_PATHNAME) {
+ Tcl_DStringInit(&dString1);
+ Tcl_DStringInit(&dString2);
+ }
+ if (sortData.key != NULL) {
+ Tcl_Obj *valueObjPtr;
+
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n1Ptr,
+ sortData.key, &valueObjPtr) == TCL_OK) {
+ s1 = Tcl_GetString(valueObjPtr);
+ }
+ if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n2Ptr,
+ sortData.key, &valueObjPtr) == TCL_OK) {
+ s2 = Tcl_GetString(valueObjPtr);
+ }
+ } else if (sortData.flags & SORT_PATHNAME) {
+ Blt_TreeNode root;
+
+ root = Blt_TreeRootNode(cmdPtr->tree);
+ s1 = GetNodePath(cmdPtr, root, *n1Ptr, FALSE, &dString1);
+ s2 = GetNodePath(cmdPtr, root, *n2Ptr, FALSE, &dString2);
+ } else {
+ s1 = Blt_TreeNodeLabel(*n1Ptr);
+ s2 = Blt_TreeNodeLabel(*n2Ptr);
+ }
+ switch (sortData.type) {
+ case SORT_ASCII:
+ result = strcmp(s1, s2);
+ break;
+
+ case SORT_COMMAND:
+ if (sortData.command == NULL) {
+ result = Blt_DictionaryCompare(s1, s2);
+ } else {
+ Tcl_DString dsCmd, dsName;
+ char *qualName;
+
+ result = 0; /* Hopefully this will be Ok even if the
+ * Tcl command fails to return the correct
+ * result. */
+ Tcl_DStringInit(&dsCmd);
+ Tcl_DStringAppend(&dsCmd, sortData.command, -1);
+ Tcl_DStringInit(&dsName);
+ qualName = Blt_GetQualifiedName(
+ Blt_GetCommandNamespace(cmdPtr->interp, cmdPtr->cmdToken),
+ Tcl_GetCommandName(cmdPtr->interp, cmdPtr->cmdToken), &dsName);
+ Tcl_DStringAppendElement(&dsCmd, qualName);
+ Tcl_DStringFree(&dsName);
+ Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n1Ptr)));
+ Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n2Ptr)));
+ Tcl_DStringAppendElement(&dsCmd, s1);
+ Tcl_DStringAppendElement(&dsCmd, s2);
+ result = Tcl_GlobalEval(cmdPtr->interp, Tcl_DStringValue(&dsCmd));
+ Tcl_DStringFree(&dsCmd);
+
+ if ((result != TCL_OK) ||
+ (Tcl_GetInt(cmdPtr->interp,
+ Tcl_GetStringResult(cmdPtr->interp), &result) != TCL_OK)) {
+ Tcl_BackgroundError(cmdPtr->interp);
+ }
+ Tcl_ResetResult(cmdPtr->interp);
+ }
+ break;
+
+ case SORT_DICTIONARY:
+ result = Blt_DictionaryCompare(s1, s2);
+ break;
+
+ case SORT_INTEGER:
+ {
+ int i1, i2;
+
+ if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) {
+ if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = i1 - i2;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+
+ case SORT_REAL:
+ {
+ double r1, r2;
+
+ if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) {
+ if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+ }
+ if (result == 0) {
+ result = Blt_TreeNodeId(*n1Ptr) - Blt_TreeNodeId(*n2Ptr);
+ }
+ if (sortData.flags & SORT_DECREASING) {
+ result = -result;
+ }
+ if (sortData.flags & SORT_PATHNAME) {
+ Tcl_DStringFree(&dString1);
+ Tcl_DStringFree(&dString2);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortApplyProc --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortApplyProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ TreeCmd *cmdPtr = clientData;
+
+ if (!Blt_TreeIsLeaf(node)) {
+ Blt_TreeSortNode(cmdPtr->tree, node, CompareNodes);
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SortOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode top;
+ SortData data;
+ int result;
+
+ if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+ /* Process switches */
+ memset(&data, 0, sizeof(data));
+ data.cmdPtr = cmdPtr;
+ if (Blt_ProcessObjSwitches(interp, sortSwitches, objc - 3, objv + 3,
+ (char *)&data, 0) < 0) {
+ return TCL_ERROR;
+ }
+ if (data.command != NULL) {
+ data.type = SORT_COMMAND;
+ }
+ data.cmdPtr = cmdPtr;
+ sortData = data;
+ if (data.mode == SORT_FLAT) {
+ Blt_TreeNode *p, *nodeArr, node;
+ int nNodes;
+ Tcl_Obj *objPtr, *listObjPtr;
+ int i;
+
+ if (data.flags & SORT_RECURSE) {
+ nNodes = Blt_TreeSize(top);
+ } else {
+ nNodes = Blt_TreeNodeDegree(top);
+ }
+ nodeArr = Blt_Malloc(nNodes * sizeof(Blt_TreeNode));
+ assert(nodeArr);
+ p = nodeArr;
+ if (data.flags & SORT_RECURSE) {
+ for(node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+ *p++ = node;
+ }
+ } else {
+ for (node = Blt_TreeFirstChild(top); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ *p++ = node;
+ }
+ }
+ qsort((char *)nodeArr, nNodes, sizeof(Blt_TreeNode),
+ (QSortCompareProc *)CompareNodes);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (p = nodeArr, i = 0; i < nNodes; i++, p++) {
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*p));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Blt_Free(nodeArr);
+ result = TCL_OK;
+ } else {
+ if (data.flags & SORT_RECURSE) {
+ result = Blt_TreeApply(top, SortApplyProc, cmdPtr);
+ } else {
+ result = SortApplyProc(top, cmdPtr, TREE_PREORDER);
+ }
+ }
+ Blt_FreeSwitches(sortSwitches, (char *)&data, 0);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ValuesOp --
+ *
+ * Returns the names of values for a node or array value.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ValuesOp(cmdPtr, interp, objc, objv)
+ TreeCmd *cmdPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node;
+ Tcl_Obj *listObjPtr;
+
+ if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ if (objc == 4) {
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (Blt_TreeArrayNames(interp, cmdPtr->tree, node, string, listObjPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ Blt_TreeKey *key, *keys;
+ Tcl_Obj *objPtr;
+
+ keys = GetKeys(cmdPtr->tree, node);
+ for (key = keys; *key != NULL; key++) {
+ objPtr = Tcl_NewStringObj(*key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Blt_Free(keys);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeInstObjCmd --
+ *
+ * This procedure is invoked to process commands on behalf of
+ * the tree object.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec treeOps[] =
+{
+ {"ancestor", 2, (Blt_Op)AncestorOp, 4, 4, "node1 node2",},
+ {"apply", 1, (Blt_Op)ApplyOp, 3, 0, "node ?switches?",},
+ {"attach", 2, (Blt_Op)AttachOp, 2, 3, "?tree?",},
+ {"children", 2, (Blt_Op)ChildrenOp, 3, 5, "node ?first? ?last?",},
+ {"copy", 2, (Blt_Op)CopyOp, 4, 0,
+ "srcNode ?destTree? destNode ?switches?",},
+ {"degree", 2, (Blt_Op)DegreeOp, 3, 0, "node",},
+ {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "node ?node...?",},
+ {"depth", 3, (Blt_Op)DepthOp, 3, 3, "node",},
+ {"dump", 4, (Blt_Op)DumpOp, 3, 3, "node",},
+ {"dumpfile", 5, (Blt_Op)DumpfileOp, 4, 4, "node fileName",},
+ {"exists", 1, (Blt_Op)ExistsOp, 3, 4, "node ?key?",},
+ {"find", 4, (Blt_Op)FindOp, 3, 0, "node ?switches?",},
+ {"findchild", 5, (Blt_Op)FindChildOp, 4, 4, "node name",},
+ {"firstchild", 3, (Blt_Op)FirstChildOp, 3, 3, "node",},
+ {"get", 1, (Blt_Op)GetOp, 3, 5, "node ?key? ?defaultValue?",},
+ {"index", 3, (Blt_Op)IndexOp, 3, 3, "name",},
+ {"insert", 3, (Blt_Op)InsertOp, 3, 0, "parent ?switches?",},
+ {"is", 2, (Blt_Op)IsOp, 2, 0, "oper args...",},
+ {"keys", 1, (Blt_Op)KeysOp, 3, 0, "node ?node...?",},
+ {"label", 3, (Blt_Op)LabelOp, 3, 4, "node ?newLabel?",},
+ {"lastchild", 3, (Blt_Op)LastChildOp, 3, 3, "node",},
+ {"move", 1, (Blt_Op)MoveOp, 4, 0, "node newParent ?switches?",},
+ {"next", 4, (Blt_Op)NextOp, 3, 3, "node",},
+ {"nextsibling", 5, (Blt_Op)NextSiblingOp, 3, 3, "node",},
+ {"notify", 2, (Blt_Op)NotifyOp, 2, 0, "args...",},
+ {"parent", 3, (Blt_Op)ParentOp, 3, 3, "node",},
+ {"path", 3, (Blt_Op)PathOp, 3, 3, "node",},
+ {"position", 2, (Blt_Op)PositionOp, 3, 0, "?switches? node...",},
+ {"previous", 5, (Blt_Op)PreviousOp, 3, 3, "node",},
+ {"prevsibling", 5, (Blt_Op)PrevSiblingOp, 3, 3, "node",},
+ {"restore", 7, (Blt_Op)RestoreOp, 4, 4, "node dataString",},
+ {"restorefile", 8, (Blt_Op)RestorefileOp, 4, 4, "node fileName",},
+ {"root", 2, (Blt_Op)RootOp, 2, 3, "?node?",},
+ {"set", 3, (Blt_Op)SetOp, 3, 0, "node ?key value...?",},
+ {"size", 2, (Blt_Op)SizeOp, 3, 3, "node",},
+ {"sort", 2, (Blt_Op)SortOp, 3, 0, "node ?flags...?",},
+ {"tag", 2, (Blt_Op)TagOp, 3, 0, "args...",},
+ {"trace", 2, (Blt_Op)TraceOp, 2, 0, "args...",},
+ {"type", 2, (Blt_Op)TypeOp, 4, 4, "node key",},
+ {"unset", 3, (Blt_Op)UnsetOp, 3, 0, "node ?key...?",},
+ {"values", 1, (Blt_Op)ValuesOp, 3, 4, "node ?key?",},
+};
+
+static int nTreeOps = sizeof(treeOps) / sizeof(Blt_OpSpec);
+
+static int
+TreeInstObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST *objv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ TreeCmd *cmdPtr = clientData;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTreeOps, treeOps, BLT_OP_ARG1, objc, objv,
+ BLT_OP_LINEAR_SEARCH);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(cmdPtr);
+ result = (*proc) (cmdPtr, interp, objc, objv);
+ Tcl_Release(cmdPtr);
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TreeInstDeleteProc --
+ *
+ * Deletes the command associated with the tree. This is
+ * called only when the command associated with the tree is
+ * destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TreeInstDeleteProc(clientData)
+ ClientData clientData;
+{
+ TreeCmd *cmdPtr = clientData;
+
+ ReleaseTreeObject(cmdPtr);
+ if (cmdPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(cmdPtr->tablePtr, cmdPtr->hashPtr);
+ }
+ Blt_DeleteHashTable(&(cmdPtr->traceTable));
+ Blt_Free(cmdPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GenerateName --
+ *
+ * Generates an unique tree command name. Tree names are in
+ * the form "treeN", where N is a non-negative integer. Check
+ * each name generated to see if it is already a tree. We want
+ * to recycle names if possible.
+ *
+ * Results:
+ * Returns the unique name. The string itself is stored in the
+ * dynamic string passed into the routine.
+ *
+ * ----------------------------------------------------------------------
+ */
+static char *
+GenerateName(interp, prefix, suffix, resultPtr)
+ Tcl_Interp *interp;
+ char *prefix, *suffix;
+ Tcl_DString *resultPtr;
+{
+
+ int n;
+ Tcl_Namespace *nsPtr;
+ char string[200];
+ Tcl_CmdInfo cmdInfo;
+ Tcl_DString dString;
+ char *treeName, *name;
+
+ /*
+ * Parse the command and put back so that it's in a consistent
+ * format.
+ *
+ * t1 <current namespace>::t1
+ * n1::t1 <current namespace>::n1::t1
+ * ::t1 ::t1
+ * ::n1::t1 ::n1::t1
+ */
+ treeName = NULL; /* Suppress compiler warning. */
+ for (n = 0; n < INT_MAX; n++) {
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, prefix, -1);
+ sprintf(string, "tree%d", n);
+ Tcl_DStringAppend(&dString, string, -1);
+ Tcl_DStringAppend(&dString, suffix, -1);
+ treeName = Tcl_DStringValue(&dString);
+ if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
+ "\"", (char *)NULL);
+ return NULL;
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ treeName = Blt_GetQualifiedName(nsPtr, name, resultPtr);
+ /*
+ * Check if the command already exists.
+ */
+ if (Tcl_GetCommandInfo(interp, treeName, &cmdInfo)) {
+ continue;
+ }
+ if (!Blt_TreeExists(interp, treeName)) {
+ /*
+ * We want the name of the tree command and the underlying
+ * tree object to be the same. Check that the free command
+ * name isn't an already a tree object name.
+ */
+ break;
+ }
+ }
+ return treeName;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeCreateOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TreeCreateOp(clientData, interp, objc, objv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeCmdInterpData *dataPtr = clientData;
+ char *treeName;
+ Tcl_DString dString;
+ Blt_Tree token;
+
+ treeName = NULL;
+ if (objc == 3) {
+ treeName = Tcl_GetString(objv[2]);
+ }
+ Tcl_DStringInit(&dString);
+ if (treeName == NULL) {
+ treeName = GenerateName(interp, "", "", &dString);
+ } else {
+ char *p;
+
+ p = strstr(treeName, "#auto");
+ if (p != NULL) {
+ *p = '\0';
+ treeName = GenerateName(interp, treeName, p + 5, &dString);
+ *p = '#';
+ } else {
+ char *name;
+ Tcl_CmdInfo cmdInfo;
+ Tcl_Namespace *nsPtr;
+
+ nsPtr = NULL;
+ /*
+ * Parse the command and put back so that it's in a consistent
+ * format.
+ *
+ * t1 <current namespace>::t1
+ * n1::t1 <current namespace>::n1::t1
+ * ::t1 ::t1
+ * ::n1::t1 ::n1::t1
+ */
+ if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name)
+ != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+ /*
+ * Check if the command already exists.
+ */
+ if (Tcl_GetCommandInfo(interp, treeName, &cmdInfo)) {
+ Tcl_AppendResult(interp, "a command \"", treeName,
+ "\" already exists", (char *)NULL);
+ goto error;
+ }
+ if (Blt_TreeExists(interp, treeName)) {
+ Tcl_AppendResult(interp, "a tree \"", treeName,
+ "\" already exists", (char *)NULL);
+ goto error;
+ }
+ }
+ }
+ if (treeName == NULL) {
+ goto error;
+ }
+ if (Blt_TreeCreate(interp, treeName, &token) == TCL_OK) {
+ int isNew;
+ TreeCmd *cmdPtr;
+
+ cmdPtr = Blt_Calloc(1, sizeof(TreeCmd));
+ assert(cmdPtr);
+ cmdPtr->dataPtr = dataPtr;
+ cmdPtr->tree = token;
+ cmdPtr->interp = interp;
+ Blt_InitHashTable(&(cmdPtr->traceTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(cmdPtr->notifyTable), BLT_STRING_KEYS);
+ cmdPtr->tagTablePtr = Blt_TreeNewTagTable();
+ cmdPtr->cmdToken = Tcl_CreateObjCommand(interp, treeName,
+ (Tcl_ObjCmdProc *)TreeInstObjCmd, cmdPtr, TreeInstDeleteProc);
+ cmdPtr->tablePtr = &dataPtr->treeTable;
+ cmdPtr->hashPtr = Blt_CreateHashEntry(cmdPtr->tablePtr, (char *)cmdPtr,
+ &isNew);
+ Blt_SetHashValue(cmdPtr->hashPtr, cmdPtr);
+ Tcl_SetResult(interp, treeName, TCL_VOLATILE);
+ Tcl_DStringFree(&dString);
+ Blt_TreeCreateEventHandler(cmdPtr->tree, TREE_NOTIFY_ALL,
+ TreeEventProc, cmdPtr);
+ return TCL_OK;
+ }
+ error:
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeDestroyOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TreeDestroyOp(clientData, interp, objc, objv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeCmdInterpData *dataPtr = clientData;
+ TreeCmd *cmdPtr;
+ char *string;
+ register int i;
+
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ cmdPtr = GetTreeCmd(dataPtr, interp, string);
+ if (cmdPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a tree named \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteCommandFromToken(interp, cmdPtr->cmdToken);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TreeNamesOp(clientData, interp, objc, objv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeCmdInterpData *dataPtr = clientData;
+ TreeCmd *cmdPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Tcl_Obj *objPtr, *listObjPtr;
+ Tcl_DString dString;
+ char *qualName;
+
+ Tcl_DStringInit(&dString);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmdPtr = Blt_GetHashValue(hPtr);
+ qualName = Blt_GetQualifiedName(
+ Blt_GetCommandNamespace(interp, cmdPtr->cmdToken),
+ Tcl_GetCommandName(interp, cmdPtr->cmdToken), &dString);
+ if (objc == 3) {
+ if (!Tcl_StringMatch(qualName, Tcl_GetString(objv[2]))) {
+ continue;
+ }
+ }
+ objPtr = Tcl_NewStringObj(qualName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Tcl_DStringFree(&dString);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeObjCmd --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec treeCmdOps[] =
+{
+ {"create", 1, (Blt_Op)TreeCreateOp, 2, 3, "?name?",},
+ {"destroy", 1, (Blt_Op)TreeDestroyOp, 3, 0, "name...",},
+ {"names", 1, (Blt_Op)TreeNamesOp, 2, 3, "?pattern?...",},
+};
+
+static int nCmdOps = sizeof(treeCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+TreeObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+
+ proc = Blt_GetOpFromObj(interp, nCmdOps, treeCmdOps, BLT_OP_ARG1, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (clientData, interp, objc, objv);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TreeInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the "tree" command
+ * is deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Removes the hash table managing all tree names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TreeInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ TreeCmdInterpData *dataPtr = clientData;
+
+ /* All tree instances should already have been destroyed when
+ * their respective Tcl commands were deleted. */
+ Blt_DeleteHashTable(&dataPtr->treeTable);
+ Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+/*ARGSUSED*/
+static int
+CompareDictionaryCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int result;
+ char *s1, *s2;
+
+ s1 = Tcl_GetString(objv[1]);
+ s2 = Tcl_GetString(objv[2]);
+ result = Blt_DictionaryCompare(s1, s2);
+ result = (result > 0) ? -1 : (result < 0) ? 1 : 0;
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ExitCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int code;
+
+ if (Tcl_GetIntFromObj(interp, objv[1], &code) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#ifdef TCL_THREADS
+ Tcl_Exit(code);
+#else
+ exit(code);
+#endif
+ /*NOTREACHED*/
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_TreeInit --
+ *
+ * This procedure is invoked to initialize the "tree" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds a new entry into a global Tcl
+ * associative array.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_TreeInit(interp)
+ Tcl_Interp *interp;
+{
+ TreeCmdInterpData *dataPtr; /* Interpreter-specific data. */
+ static Blt_ObjCmdSpec cmdSpec = {
+ "tree", TreeObjCmd,
+ };
+ static Blt_ObjCmdSpec compareSpec = {
+ "compare", CompareDictionaryCmd,
+ };
+ static Blt_ObjCmdSpec exitSpec = {
+ "exit", ExitCmd,
+ };
+ if (Blt_InitObjCmd(interp, "blt::util", &compareSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_InitObjCmd(interp, "blt::util", &exitSpec) == NULL) {
+ return TCL_ERROR;
+ }
+
+ dataPtr = GetTreeCmdInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeCmdGetToken(interp, string, treePtr)
+ Tcl_Interp *interp;
+ char *string;
+ Blt_Tree *treePtr;
+{
+ TreeCmdInterpData *dataPtr;
+ TreeCmd *cmdPtr;
+
+ dataPtr = GetTreeCmdInterpData(interp);
+ cmdPtr = GetTreeCmd(dataPtr, interp, string);
+ if (cmdPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a tree associated with \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *treePtr = cmdPtr->tree;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCmdGetTagTable --
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeCmdGetTagTable(interp, treeName, dataPtrPtr)
+ Tcl_Interp *interp;
+ char *treeName;
+ Blt_TreeTagTable **dataPtrPtr;
+{
+ TreeCmdInterpData *dataPtr;
+ TreeCmd *cmdPtr;
+
+ dataPtr = GetTreeCmdInterpData(interp);
+ cmdPtr = GetTreeCmd(dataPtr, interp, treeName);
+ if (cmdPtr == NULL) {
+ return TCL_ERROR;
+ }
+ cmdPtr->tagTablePtr->refCount++;
+ *dataPtrPtr = cmdPtr->tagTablePtr;
+ return TCL_OK;
+}
+
+/* Dump tree to dbm */
+/* Convert node data to datablock */
+
+#endif /* NO_TREE */
+
diff --git a/blt/src/bltTreeView.c b/blt/src/bltTreeView.c
new file mode 100644
index 00000000000..6f773dd9129
--- /dev/null
+++ b/blt/src/bltTreeView.c
@@ -0,0 +1,5245 @@
+/*
+ * bltTreeView.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+
+#define BUTTON_IPAD 1
+#define BUTTON_SIZE 7
+#define COLUMN_PAD 2
+#define FOCUS_WIDTH 1
+#define ICON_PADX 2
+#define ICON_PADY 1
+#define INSET_PAD 0
+#define LABEL_PADX 3
+#define LABEL_PADY 0
+
+#define ODD(x) ((x) | 0x01)
+
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define DEF_ICON_WIDTH 16
+#define DEF_ICON_HEIGHT 16
+
+static Tcl_FreeInternalRepProc FreeEntryInternalRep;
+static Tcl_UpdateStringProc UpdateStringOfEntry;
+static Tcl_SetFromAnyProc SetEntryObjFromAny;
+
+static Tcl_ObjType entryObjType = {
+ "BLT TreeView Entry",
+ FreeEntryInternalRep, /* Called when an object is freed. */
+ NULL, /* Copies an internal representation
+ * from one object to another. */
+ UpdateStringOfEntry, /* Creates string representation from
+ * an object's internal representation. */
+ SetEntryObjFromAny, /* Creates valid internal representation
+ * from an object's string representation. */
+};
+
+static Blt_TreeApplyProc DeleteApplyProc;
+static Blt_TreeApplyProc CreateApplyProc;
+
+extern Blt_CustomOption bltTreeViewDataOption;
+
+static Blt_OptionParseProc ObjToTree;
+static Blt_OptionPrintProc TreeToObj;
+static Blt_OptionFreeProc FreeTree;
+static Blt_CustomOption treeOption =
+{
+ ObjToTree, TreeToObj, FreeTree, NULL,
+};
+
+static Blt_OptionParseProc ObjToImages;
+static Blt_OptionPrintProc ImagesToObj;
+static Blt_OptionFreeProc FreeImages;
+Blt_CustomOption bltTreeViewImagesOption =
+{
+ /* Contains a pointer to the widget that's currently being
+ * configured. This is used in the custom configuration parse
+ * routine for images. */
+ ObjToImages, ImagesToObj, FreeImages, NULL,
+};
+
+static Blt_OptionParseProc ObjToButton;
+static Blt_OptionPrintProc ButtonToObj;
+static Blt_CustomOption buttonOption = {
+ ObjToButton, ButtonToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToUid;
+static Blt_OptionPrintProc UidToObj;
+static Blt_OptionFreeProc FreeUid;
+Blt_CustomOption bltTreeViewUidOption = {
+ ObjToUid, UidToObj, FreeUid, NULL,
+};
+
+static Blt_OptionParseProc ObjToScrollmode;
+static Blt_OptionPrintProc ScrollmodeToObj;
+static Blt_CustomOption scrollmodeOption = {
+ ObjToScrollmode, ScrollmodeToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToSelectmode;
+static Blt_OptionPrintProc SelectmodeToObj;
+static Blt_CustomOption selectmodeOption = {
+ ObjToSelectmode, SelectmodeToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToSeparator;
+static Blt_OptionPrintProc SeparatorToObj;
+static Blt_OptionFreeProc FreeSeparator;
+static Blt_CustomOption separatorOption = {
+ ObjToSeparator, SeparatorToObj, FreeSeparator, NULL,
+};
+
+static Blt_OptionParseProc ObjToLabel;
+static Blt_OptionPrintProc LabelToObj;
+static Blt_OptionFreeProc FreeLabel;
+static Blt_CustomOption labelOption =
+{
+ ObjToLabel, LabelToObj, FreeLabel, NULL,
+};
+
+
+#define DEF_BUTTON_ACTIVE_BG_COLOR RGB_WHITE
+#define DEF_BUTTON_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_BUTTON_ACTIVE_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_BUTTON_ACTIVE_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_BUTTON_BORDER_WIDTH "1"
+#if (TK_MAJOR_VERSION == 4)
+#define DEF_BUTTON_CLOSE_RELIEF "flat"
+#define DEF_BUTTON_OPEN_RELIEF "flat"
+#else
+#define DEF_BUTTON_CLOSE_RELIEF "solid"
+#define DEF_BUTTON_OPEN_RELIEF "solid"
+#endif
+#define DEF_BUTTON_IMAGES (char *)NULL
+#define DEF_BUTTON_NORMAL_BG_COLOR RGB_WHITE
+#define DEF_BUTTON_NORMAL_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_BUTTON_NORMAL_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_BUTTON_NORMAL_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_BUTTON_SIZE "7"
+
+#define DEF_ENTRY_BG_COLOR (char *)NULL
+#define DEF_ENTRY_BG_MONO (char *)NULL
+#define DEF_ENTRY_BIND_TAGS "Entry all"
+#define DEF_ENTRY_COMMAND (char *)NULL
+#define DEF_ENTRY_DATA (char *)NULL
+#define DEF_ENTRY_FG_COLOR (char *)NULL
+#define DEF_ENTRY_FG_MONO (char *)NULL
+#define DEF_ENTRY_FONT (char *)NULL
+#define DEF_ENTRY_HEIGHT (char *)NULL
+#define DEF_ENTRY_ICONS (char *)NULL
+#define DEF_ENTRY_ACTIVE_ICONS (char *)NULL
+#define DEF_ENTRY_IMAGES (char *)NULL
+#define DEF_ENTRY_LABEL (char *)NULL
+#define DEF_ENTRY_SHADOW_COLOR (char *)NULL
+#define DEF_ENTRY_SHADOW_MONO (char *)NULL
+#define DEF_ENTRY_TEXT (char *)NULL
+
+
+#define DEF_TV_ICONS \
+ "blt::tv::normalOpenFolder blt::tv::normalCloseFolder"
+#define DEF_TV_ACTIVE_ICONS \
+ "blt::tv::activeOpenFolder blt::tv::activeCloseFolder"
+#define DEF_TV_ACTIVE_BG_COLOR RGB_LIGHTBLUE0
+#define DEF_TV_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TV_ACTIVE_FG_COLOR RGB_BLACK
+#define DEF_TV_ACTIVE_RELIEF "flat"
+#define DEF_TV_ACTIVE_SELECT_BG_COLOR "#ffffea"
+#define DEF_TV_ACTIVE_STIPPLE "gray25"
+#define DEF_TV_ALLOW_DUPLICATES "yes"
+#define DEF_TV_BACKGROUND RGB_WHITE
+#define DEF_TV_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_TV_BUTTON "auto"
+#define DEF_TV_COMMAND (char *)NULL
+#define DEF_TV_CURSOR (char *)NULL
+#define DEF_TV_RESIZE_CURSOR "arrow"
+#define DEF_TV_DASHES "dot"
+#define DEF_TV_EXPORT_SELECTION "no"
+#define DEF_TV_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TV_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_TV_FLAT "no"
+#define DEF_TV_FOCUS_DASHES "dot"
+#define DEF_TV_FOCUS_EDIT "no"
+#define DEF_TV_FOCUS_FG_COLOR STD_COLOR_ACTIVE_FG
+#define DEF_TV_FOCUS_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TV_FONT STD_FONT
+#define DEF_TV_HEIGHT "400"
+#define DEF_TV_HIDE_LEAVES "no"
+#define DEF_TV_HIDE_ROOT "yes"
+#define DEF_TV_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TV_HIGHLIGHT_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TV_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_TV_HIGHLIGHT_WIDTH "2"
+#define DEF_TV_LINE_COLOR RGB_GREY50
+#define DEF_TV_LINE_MONO STD_MONO_NORMAL_FG
+#define DEF_TV_LINE_SPACING "0"
+#define DEF_TV_LINE_WIDTH "1"
+#define DEF_TV_MAKE_PATH "no"
+#define DEF_TV_NEW_TAGS "no"
+#define DEF_TV_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TV_NORMAL_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_TV_RELIEF "sunken"
+#define DEF_TV_SCROLL_INCREMENT "20"
+#define DEF_TV_SCROLL_MODE "hierbox"
+#define DEF_TV_SELECT_BG_COLOR RGB_LIGHTBLUE1
+#define DEF_TV_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_TV_SELECT_BORDER_WIDTH "1"
+#define DEF_TV_SELECT_CMD (char *)NULL
+#define DEF_TV_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_TV_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_TV_SELECT_MODE "single"
+#define DEF_TV_SELECT_RELIEF "flat"
+#define DEF_TV_SEPARATOR (char *)NULL
+#define DEF_TV_SHOW_ROOT "yes"
+#define DEF_TV_SHOW_TITLES "yes"
+#define DEF_TV_SORT_SELECTION "no"
+#define DEF_TV_TAKE_FOCUS "1"
+#define DEF_TV_TEXT_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TV_TEXT_MONO STD_MONO_NORMAL_FG
+#define DEF_TV_TRIMLEFT ""
+#define DEF_TV_WIDTH "200"
+
+Blt_ConfigSpec bltTreeViewButtonSpecs[] =
+{
+ {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
+ DEF_BUTTON_ACTIVE_BG_MONO, Blt_Offset(TreeView, button.activeBorder),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
+ DEF_BUTTON_ACTIVE_BG_COLOR, Blt_Offset(TreeView, button.activeBorder),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-activefg", "activeForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_BUTTON_ACTIVE_FG_COLOR, Blt_Offset(TreeView, button.activeFgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_BUTTON_ACTIVE_FG_MONO, Blt_Offset(TreeView, button.activeFgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_NORMAL_BG_COLOR, Blt_Offset(TreeView, button.border),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_NORMAL_BG_MONO, Blt_Offset(TreeView, button.border),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,
+ 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_BUTTON_BORDER_WIDTH, Blt_Offset(TreeView, button.borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-closerelief", "closeRelief", "Relief",
+ DEF_BUTTON_CLOSE_RELIEF, Blt_Offset(TreeView, button.closeRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_NORMAL_FG_COLOR, Blt_Offset(TreeView, button.fgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_NORMAL_FG_MONO, Blt_Offset(TreeView, button.fgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_CUSTOM, "-images", "images", "Images",
+ DEF_BUTTON_IMAGES, Blt_Offset(TreeView, button.images),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_RELIEF, "-openrelief", "openRelief", "Relief",
+ DEF_BUTTON_OPEN_RELIEF, Blt_Offset(TreeView, button.openRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-size", "size", "Size", DEF_BUTTON_SIZE,
+ Blt_Offset(TreeView, button.reqSize), 0},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+Blt_ConfigSpec bltTreeViewEntrySpecs[] =
+{
+ {BLT_CONFIG_CUSTOM, "-activeicons", (char *)NULL, (char *)NULL,
+ DEF_ENTRY_ICONS, Blt_Offset(TreeViewEntry, activeIcons),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_ENTRY_BIND_TAGS, Blt_Offset(TreeViewEntry, tagsUid),
+ BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+ {BLT_CONFIG_CUSTOM, "-button", (char *)NULL, (char *)NULL,
+ DEF_TV_BUTTON, Blt_Offset(TreeViewEntry, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
+ {BLT_CONFIG_CUSTOM, "-closecommand", (char *)NULL, (char *)NULL,
+ DEF_ENTRY_COMMAND, Blt_Offset(TreeViewEntry, closeCmd),
+ BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+ {BLT_CONFIG_CUSTOM, "-data", "data", "data",
+ DEF_ENTRY_DATA, 0, BLT_CONFIG_NULL_OK, &bltTreeViewDataOption},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_ENTRY_FONT, Blt_Offset(TreeViewEntry, font), 0},
+ {BLT_CONFIG_COLOR, "-foreground", (char *)NULL, (char *)NULL,
+ DEF_ENTRY_FG_COLOR, Blt_Offset(TreeViewEntry, color),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DISTANCE, "-height", "height", "Height",
+ DEF_ENTRY_HEIGHT, Blt_Offset(TreeViewEntry, reqHeight),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-icons", (char *)NULL, (char *)NULL,
+ DEF_ENTRY_ICONS, Blt_Offset(TreeViewEntry, icons),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_CUSTOM, "-label", "label", "Label",
+ DEF_ENTRY_LABEL, Blt_Offset(TreeViewEntry, labelUid), 0,
+ &labelOption},
+ {BLT_CONFIG_CUSTOM, "-opencommand", (char *)NULL, (char *)NULL,
+ DEF_ENTRY_COMMAND, Blt_Offset(TreeViewEntry, openCmd),
+ BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+ {BLT_CONFIG_SHADOW, "-shadow", "shadow", "Shadow",
+ DEF_ENTRY_SHADOW_COLOR, Blt_Offset(TreeViewEntry, shadow),
+ BLT_CONFIG_NULL_OK | BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_SHADOW, "-shadow", "shadow", "Shadow",
+ DEF_ENTRY_SHADOW_MONO, Blt_Offset(TreeViewEntry, shadow),
+ BLT_CONFIG_NULL_OK | BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+Blt_ConfigSpec bltTreeViewSpecs[] =
+{
+ {BLT_CONFIG_BORDER,
+ "-activebackground", "activeBackground", "ActiveBackground",
+ DEF_TV_ACTIVE_BG_COLOR, Blt_Offset(TreeView, activeBorder),
+ BLT_CONFIG_COLOR_ONLY | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BORDER,
+ "-activebackground", "activeBackground", "ActiveBackground",
+ DEF_TV_ACTIVE_BG_MONO, Blt_Offset(TreeView, activeBorder),
+ BLT_CONFIG_MONO_ONLY | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-activefg", "activeForeground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+ DEF_TV_ACTIVE_FG_COLOR, Blt_Offset(TreeView, activeFgColor), 0},
+ {BLT_CONFIG_CUSTOM, "-activeicons", "activeIcons", "Icons",
+ DEF_TV_ACTIVE_ICONS, Blt_Offset(TreeView, activeIcons),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_BITFLAG,
+ "-allowduplicates", "allowDuplicates", "AllowDuplicates",
+ DEF_TV_ALLOW_DUPLICATES, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_ALLOW_DUPLICATES},
+ {BLT_CONFIG_BITFLAG, "-autocreate", "autoCreate", "AutoCreate",
+ DEF_TV_MAKE_PATH, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_FILL_ANCESTORS},
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TV_BACKGROUND, Blt_Offset(TreeView, border), 0},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TV_BORDER_WIDTH, Blt_Offset(TreeView, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-button", "button", "Button",
+ DEF_TV_BUTTON, Blt_Offset(TreeView, buttonFlags),
+ BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
+ {BLT_CONFIG_STRING, "-closecommand", "closeCommand", "CloseCommand",
+ DEF_TV_COMMAND, Blt_Offset(TreeView, closeCmd),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_TV_CURSOR, Blt_Offset(TreeView, cursor), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes",
+ DEF_TV_DASHES, Blt_Offset(TreeView, dashes),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITFLAG, "-exportselection", "exportSelection",
+ "ExportSelection", DEF_TV_EXPORT_SELECTION,
+ Blt_Offset(TreeView, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)TV_SELECT_EXPORT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_BOOLEAN, "-flat", "flat", "Flat",
+ DEF_TV_FLAT, Blt_Offset(TreeView, flatView),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
+ DEF_TV_FOCUS_DASHES, Blt_Offset(TreeView, focusDashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR,
+ "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_TV_FOCUS_FG_COLOR, Blt_Offset(TreeView, focusColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR,
+ "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_TV_FOCUS_FG_MONO, Blt_Offset(TreeView, focusColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TV_FONT, Blt_Offset(TreeView, treeColumn.font), 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TV_TEXT_COLOR, Blt_Offset(TreeView, treeColumn.fgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TV_TEXT_MONO, Blt_Offset(TreeView, treeColumn.fgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_DISTANCE, "-height", "height", "Height",
+ DEF_TV_HEIGHT, Blt_Offset(TreeView, reqHeight),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITFLAG, "-hideleaves", "hideLeaves", "HideLeaves",
+ DEF_TV_HIDE_LEAVES, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_LEAVES},
+ {BLT_CONFIG_BITFLAG, "-hideroot", "hideRoot", "HideRoot",
+ DEF_TV_HIDE_ROOT, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_ROOT},
+ {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_TV_HIGHLIGHT_BG_COLOR,
+ Blt_Offset(TreeView, highlightBgColor), BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground",
+ DEF_TV_HIGHLIGHT_BG_MONO, Blt_Offset(TreeView, highlightBgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_TV_HIGHLIGHT_COLOR, Blt_Offset(TreeView, highlightColor), 0},
+ {BLT_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_TV_HIGHLIGHT_WIDTH, Blt_Offset(TreeView, highlightWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-icons", "icons", "Icons",
+ DEF_TV_ICONS, Blt_Offset(TreeView, icons),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+ DEF_TV_LINE_COLOR, Blt_Offset(TreeView, lineColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+ DEF_TV_LINE_MONO, Blt_Offset(TreeView, lineColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_DISTANCE, "-linespacing", "lineSpacing", "LineSpacing",
+ DEF_TV_LINE_SPACING, Blt_Offset(TreeView, leader),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-linewidth", "lineWidth", "LineWidth",
+ DEF_TV_LINE_WIDTH, Blt_Offset(TreeView, lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITFLAG, "-newtags", "newTags", "NewTags",
+ DEF_TV_NEW_TAGS, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_NEW_TAGS},
+ {BLT_CONFIG_STRING, "-opencommand", "openCommand", "OpenCommand",
+ DEF_TV_COMMAND, Blt_Offset(TreeView, openCmd),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TV_RELIEF, Blt_Offset(TreeView, relief), 0},
+ {BLT_CONFIG_CURSOR, "-resizecursor", "resizeCursor", "ResizeCursor",
+ DEF_TV_RESIZE_CURSOR, Blt_Offset(TreeView, resizeCursor), 0},
+ {BLT_CONFIG_CUSTOM, "-scrollmode", "scrollMode", "ScrollMode",
+ DEF_TV_SCROLL_MODE, Blt_Offset(TreeView, scrollMode),
+ BLT_CONFIG_DONT_SET_DEFAULT, &scrollmodeOption},
+ {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TV_SELECT_BG_MONO, Blt_Offset(TreeView, selBorder),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TV_SELECT_BG_COLOR, Blt_Offset(TreeView, selBorder),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_DISTANCE,
+ "-selectborderwidth", "selectBorderWidth", "BorderWidth",
+ DEF_TV_SELECT_BORDER_WIDTH, Blt_Offset(TreeView, selBorderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ DEF_TV_SELECT_CMD, Blt_Offset(TreeView, selectCmd),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TV_SELECT_FG_MONO, Blt_Offset(TreeView, selFgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TV_SELECT_FG_COLOR, Blt_Offset(TreeView, selFgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
+ DEF_TV_SELECT_MODE, Blt_Offset(TreeView, selectMode),
+ BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
+ {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+ DEF_TV_SELECT_RELIEF, Blt_Offset(TreeView, selRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-separator", "separator", "Separator",
+ DEF_TV_SEPARATOR, Blt_Offset(TreeView, pathSep),
+ BLT_CONFIG_NULL_OK, &separatorOption},
+ {BLT_CONFIG_BITFLAG, "-showtitles", "showTitles", "ShowTitles",
+ DEF_TV_SHOW_TITLES, Blt_Offset(TreeView, flags), 0,
+ (Blt_CustomOption *)TV_SHOW_COLUMN_TITLES},
+ {BLT_CONFIG_BITFLAG, "-sortselection", "sortSelection", "SortSelection",
+ DEF_TV_SORT_SELECTION, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_SELECT_SORTED},
+ {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_TV_TAKE_FOCUS, Blt_Offset(TreeView, takeFocus),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-tree", "tree", "Tree",
+ "", Blt_Offset(TreeView, tree), BLT_CONFIG_NULL_OK, &treeOption},
+ {BLT_CONFIG_STRING, "-trim", "trim", "Trim",
+ DEF_TV_TRIMLEFT, Blt_Offset(TreeView, trimLeft),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
+ DEF_TV_WIDTH, Blt_Offset(TreeView, reqWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING,
+ "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ (char *)NULL, Blt_Offset(TreeView, xScrollCmdPrefix),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DISTANCE,
+ "-xscrollincrement", "xScrollIncrement", "ScrollIncrement",
+ DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, xScrollUnits),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING,
+ "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ (char *)NULL, Blt_Offset(TreeView, yScrollCmdPrefix),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DISTANCE,
+ "-yscrollincrement", "yScrollIncrement", "ScrollIncrement",
+ DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, yScrollUnits),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static Blt_TreeNotifyEventProc TreeEventProc;
+static Blt_TreeTraceProc TreeTraceProc;
+static Tcl_CmdDeleteProc WidgetInstCmdDeleteProc;
+static Tcl_FreeProc DestroyTreeView;
+static Tcl_IdleProc DisplayTreeView;
+static Tk_EventProc TreeViewEventProc;
+
+static int ComputeVisibleEntries _ANSI_ARGS_((TreeView *tvPtr));
+
+EXTERN int Blt_TreeCmdGetToken _ANSI_ARGS_((Tcl_Interp *interp, char *treeName,
+ Blt_Tree *treePtr));
+
+EXTERN int Blt_TreeCmdGetTagTable _ANSI_ARGS_((Tcl_Interp *interp,
+ char *treeName, Blt_TreeTagTable **tablePtrPtr));
+
+#ifdef __STDC__
+extern Blt_TreeApplyProc Blt_TreeViewSortApplyProc;
+static Blt_BindPickProc PickButton, PickEntry, PickColumn;
+static Blt_BindTagProc GetTags, GetColumnTags;
+static Tcl_FreeProc DestroyEntry;
+static Tcl_ObjCmdProc TreeViewObjCmd;
+static Tk_ImageChangedProc ImageChangedProc;
+static Tk_SelectionProc SelectionProc;
+#endif /* __STDC__ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewEventuallyRedraw --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewEventuallyRedraw(tvPtr)
+ TreeView *tvPtr;
+{
+ if ((tvPtr->tkwin != NULL) && ((tvPtr->flags & TV_REDRAW) == 0)) {
+ tvPtr->flags |= TV_REDRAW;
+ Tcl_DoWhenIdle(DisplayTreeView, tvPtr);
+ }
+}
+
+static void
+TraceColumns(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ Blt_TreeCreateTrace(tvPtr->tree, NULL /* Node */, columnPtr->key,
+ TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET,
+ TreeTraceProc, tvPtr);
+ }
+}
+
+void
+Blt_TreeViewTraceColumn(tvPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+{
+ Blt_TreeCreateTrace(tvPtr->tree, NULL /* Node */, columnPtr->key,
+ TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET,
+ TreeTraceProc, tvPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToTree --
+ *
+ * Convert the string representing the name of a tree object
+ * into a tree token.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToTree(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
+ Blt_Tree tree;
+ char *string;
+
+ tree = NULL;
+ string = Tcl_GetString(objPtr);
+ if ((string[0] != '\0') &&
+ (Blt_TreeGetToken(interp, string, &tree) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ *treePtr = tree;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeToObj --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+TreeToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ Blt_Tree tree = *(Blt_Tree *)(widgRec + offset);
+
+ if (tree == NULL) {
+ return Tcl_NewStringObj("", -1);
+ } else {
+ return Tcl_NewStringObj(Blt_TreeName(tree), -1);
+ }
+}
+
+/*ARGSUSED*/
+static void
+FreeTree(clientData, display, widgRec, offset)
+ ClientData clientData;
+ Display *display; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
+
+ if (*treePtr != NULL) {
+ Blt_TreeNode root;
+ TreeView *tvPtr = clientData;
+
+ /*
+ * Release the current tree, removing any entry fields.
+ */
+ root = Blt_TreeRootNode(*treePtr);
+ Blt_TreeApply(root, DeleteApplyProc, tvPtr);
+ Blt_TreeViewClearSelection(tvPtr);
+ Blt_TreeReleaseToken(*treePtr);
+ if (tvPtr->tagTablePtr != NULL) {
+ Blt_TreeReleaseTagTable(tvPtr->tagTablePtr);
+ tvPtr->tagTablePtr = NULL;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToScrollmode --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToScrollmode(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* New legend position string */
+ char *widgRec;
+ int offset;
+{
+ char *string;
+ char c;
+ int *modePtr = (int *)(widgRec + offset);
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ if ((c == 'l') && (strcmp(string, "listbox") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_LISTBOX;
+ } else if ((c == 't') && (strcmp(string, "treeview") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_HIERBOX;
+ } else if ((c == 'h') && (strcmp(string, "hiertable") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_HIERBOX;
+ } else if ((c == 'c') && (strcmp(string, "canvas") == 0)) {
+ *modePtr = BLT_SCROLL_MODE_CANVAS;
+ } else {
+ Tcl_AppendResult(interp, "bad scroll mode \"", string,
+ "\": should be \"treeview\", \"listbox\", or \"canvas\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollmodeToObj --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ScrollmodeToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ int mode = *(int *)(widgRec + offset);
+
+ switch (mode) {
+ case BLT_SCROLL_MODE_LISTBOX:
+ return Tcl_NewStringObj("listbox", -1);
+ case BLT_SCROLL_MODE_HIERBOX:
+ return Tcl_NewStringObj("hierbox", -1);
+ case BLT_SCROLL_MODE_CANVAS:
+ return Tcl_NewStringObj("canvas", -1);
+ default:
+ return Tcl_NewStringObj("unknown scroll mode", -1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToSelectmode --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSelectmode(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ char *string;
+ char c;
+ int *modePtr = (int *)(widgRec + offset);
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ if ((c == 's') && (strcmp(string, "single") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
+ *modePtr = SELECT_MODE_MULTIPLE;
+ } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else {
+ Tcl_AppendResult(interp, "bad select mode \"", string,
+ "\": should be \"single\" or \"multiple\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectmodeToObj --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SelectmodeToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ int mode = *(int *)(widgRec + offset);
+
+ switch (mode) {
+ case SELECT_MODE_SINGLE:
+ return Tcl_NewStringObj("single", -1);
+ case SELECT_MODE_MULTIPLE:
+ return Tcl_NewStringObj("multiple", -1);
+ default:
+ return Tcl_NewStringObj("unknown scroll mode", -1);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToButton --
+ *
+ * Convert a string to one of three values.
+ * 0 - false, no, off
+ * 1 - true, yes, on
+ * 2 - auto
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToButton(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ char *string;
+ int *flagsPtr = (int *)(widgRec + offset);
+
+ string = Tcl_GetString(objPtr);
+ if ((string[0] == 'a') && (strcmp(string, "auto") == 0)) {
+ *flagsPtr &= ~BUTTON_MASK;
+ *flagsPtr |= BUTTON_AUTO;
+ } else {
+ int bool;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *flagsPtr &= ~BUTTON_MASK;
+ if (bool) {
+ *flagsPtr |= BUTTON_SHOW;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonToObj --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ButtonToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ int bool;
+ unsigned int flags = *(int *)(widgRec + offset);
+
+ bool = (flags & BUTTON_MASK);
+ if (bool == BUTTON_AUTO) {
+ return Tcl_NewStringObj("auto", 4);
+ } else {
+ return Tcl_NewBooleanObj(bool);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToScrollmode --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSeparator(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ char **sepPtr = (char **)(widgRec + offset);
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (*string == '\0') {
+ *sepPtr = SEPARATOR_LIST;
+ } else if (strcmp(string, "none") == 0) {
+ *sepPtr = SEPARATOR_NONE;
+ } else {
+ *sepPtr = Blt_Strdup(string);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SeparatorToObj --
+ *
+ * Results:
+ * The string representation of the separator is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SeparatorToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ char *separator = *(char **)(widgRec + offset);
+
+ if (separator == SEPARATOR_NONE) {
+ return Tcl_NewStringObj("", -1);
+ } else if (separator == SEPARATOR_LIST) {
+ return Tcl_NewStringObj("list", -1);
+ } else {
+ return Tcl_NewStringObj(separator, -1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeSeparator --
+ *
+ * Free the UID from the widget record, setting it to NULL.
+ *
+ * Results:
+ * The UID in the widget record is set to NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeSeparator(clientData, display, widgRec, offset)
+ ClientData clientData;
+ Display *display; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ char *separator = *(char **)(widgRec + offset);
+
+ if ((separator != SEPARATOR_LIST) && (separator != SEPARATOR_NONE)) {
+ Blt_Free(separator);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToLabel --
+ *
+ * Convert the string representing the label.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToLabel(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ UID *labelPtr = (UID *)(widgRec + offset);
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (string[0] != '\0') {
+ TreeView *tvPtr = clientData;
+
+ *labelPtr = Blt_TreeViewGetUid(tvPtr, string);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeToObj --
+ *
+ * Results:
+ * The string of the entry's label is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+LabelToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ UID labelUid = *(UID *)(widgRec + offset);
+ char *string;
+
+ if (labelUid == NULL) {
+ TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+
+ string = Blt_TreeNodeLabel(entryPtr->node);
+ } else {
+ string = labelUid;
+ }
+ return Tcl_NewStringObj(string, -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeLabel(clientData, display, widgRec, offset)
+ ClientData clientData;
+ Display *display; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ UID *labelPtr = (UID *)(widgRec + offset);
+
+ if (*labelPtr != NULL) {
+ TreeView *tvPtr = clientData;
+
+ Blt_TreeViewFreeUid(tvPtr, *labelPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewGetUid --
+ *
+ * Gets or creates a unique string identifier. Strings are
+ * reference counted. The string is placed into a hashed table
+ * local to the treeview.
+ *
+ * Results:
+ * Returns the pointer to the hashed string.
+ *
+ *----------------------------------------------------------------------
+ */
+UID
+Blt_TreeViewGetUid(tvPtr, string)
+ TreeView *tvPtr;
+ char *string;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+ int refCount;
+
+ hPtr = Blt_CreateHashEntry(&tvPtr->uidTable, string, &isNew);
+ if (isNew) {
+ refCount = 1;
+ } else {
+ refCount = (int)Blt_GetHashValue(hPtr);
+ refCount++;
+ }
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ return Blt_GetHashKey(&tvPtr->uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewFreeUid --
+ *
+ * Releases the uid. Uids are reference counted, so only when
+ * the reference count is zero (i.e. no one else is using the
+ * string) is the entry removed from the hash table.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewFreeUid(tvPtr, uid)
+ TreeView *tvPtr;
+ UID uid;
+{
+ Blt_HashEntry *hPtr;
+ int refCount;
+
+ hPtr = Blt_FindHashEntry(&tvPtr->uidTable, uid);
+ assert(hPtr != NULL);
+ refCount = (int)Blt_GetHashValue(hPtr);
+ refCount--;
+ if (refCount > 0) {
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ } else {
+ Blt_DeleteHashEntry(&tvPtr->uidTable, hPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToUid --
+ *
+ * Converts the string to a Uid. Uid's are hashed, reference
+ * counted strings.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToUid(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ TreeView *tvPtr = clientData;
+ UID *uidPtr = (UID *)(widgRec + offset);
+ UID newId;
+ char *string;
+
+ newId = NULL;
+ string = Tcl_GetString(objPtr);
+ if (*string != '\0') {
+ newId = Blt_TreeViewGetUid(tvPtr, string);
+ }
+ *uidPtr = newId;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UidToObj --
+ *
+ * Returns the uid as a string.
+ *
+ * Results:
+ * The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+UidToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ UID uid = *(UID *)(widgRec + offset);
+
+ return Tcl_NewStringObj(((uid == NULL) ? "" : uid), -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeUid --
+ *
+ * Free the UID from the widget record, setting it to NULL.
+ *
+ * Results:
+ * The UID in the widget record is set to NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeUid(clientData, display, widgRec, offset)
+ ClientData clientData;
+ Display *display; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ UID *uidPtr = (UID *)(widgRec + offset);
+
+ if (*uidPtr != NULL) {
+ TreeView *tvPtr = clientData;
+
+ Blt_TreeViewFreeUid(tvPtr, *uidPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+ ClientData clientData;
+ int x, y, width, height; /* Not used. */
+ int imageWidth, imageHeight;/* Not used. */
+{
+ TreeView *tvPtr = clientData;
+
+ tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+}
+
+TreeViewImage
+Blt_TreeViewGetImage(tvPtr, imageName)
+ TreeView *tvPtr;
+ char *imageName;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+ struct TreeViewImageStruct *iPtr;
+
+ hPtr = Blt_CreateHashEntry(&tvPtr->imageTable, imageName, &isNew);
+ if (isNew) {
+ Tk_Image tkImage;
+ int width, height;
+
+ tkImage = Tk_GetImage(tvPtr->interp, tvPtr->tkwin, imageName,
+ ImageChangedProc, tvPtr);
+ if (tkImage == NULL) {
+ Blt_DeleteHashEntry(&tvPtr->imageTable, hPtr);
+ return NULL;
+ }
+ Tk_SizeOfImage(tkImage, &width, &height);
+ iPtr = Blt_Malloc(sizeof(struct TreeViewImageStruct));
+ iPtr->tkImage = tkImage;
+ iPtr->hashPtr = hPtr;
+ iPtr->refCount = 1;
+ iPtr->width = width;
+ iPtr->height = height;
+ Blt_SetHashValue(hPtr, iPtr);
+ } else {
+ iPtr = Blt_GetHashValue(hPtr);
+ iPtr->refCount++;
+ }
+ return iPtr;
+}
+
+void
+Blt_TreeViewFreeImage(tvPtr, iPtr)
+ TreeView *tvPtr;
+ struct TreeViewImageStruct *iPtr;
+{
+ iPtr->refCount--;
+ if (iPtr->refCount == 0) {
+ Blt_DeleteHashEntry(&tvPtr->imageTable, iPtr->hashPtr);
+ Tk_FreeImage(iPtr->tkImage);
+ Blt_Free(iPtr);
+ }
+}
+
+static void
+DumpImageTable(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ struct TreeViewImageStruct *iPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&tvPtr->imageTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ iPtr = Blt_GetHashValue(hPtr);
+ Tk_FreeImage(iPtr->tkImage);
+ Blt_Free(iPtr);
+ }
+ Blt_DeleteHashTable(&tvPtr->imageTable);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToImages --
+ *
+ * Convert a list of image names into Tk images.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToImages(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
+ char *widgRec;
+ int offset;
+{
+ Tcl_Obj **objv;
+ TreeView *tvPtr = clientData;
+ TreeViewImage **iPtrPtr = (TreeViewImage **)(widgRec + offset);
+ TreeViewImage *images;
+ int objc;
+ int result;
+
+ result = TCL_OK;
+ images = NULL;
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 0) {
+ register int i;
+
+ images = Blt_Malloc(sizeof(TreeViewImage *) * (objc + 1));
+ assert(images);
+ for (i = 0; i < objc; i++) {
+ images[i] = Blt_TreeViewGetImage(tvPtr, Tcl_GetString(objv[i]));
+ if (images[i] == NULL) {
+ result = TCL_ERROR;
+ break;
+ }
+ }
+ images[i] = NULL;
+ }
+ *iPtrPtr = images;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImagesToObj --
+ *
+ * Converts the image into its string representation (its name).
+ *
+ * Results:
+ * The name of the image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ImagesToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ TreeViewImage *images = *(TreeViewImage **)(widgRec + offset);
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (images != NULL) {
+ register TreeViewImage *iPtr;
+ Tcl_Obj *objPtr;
+
+ for (iPtr = images; *iPtr != NULL; iPtr++) {
+ objPtr = Tcl_NewStringObj(Blt_NameOfImage((*iPtr)->tkImage), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+
+ }
+ }
+ return listObjPtr;
+}
+
+/*ARGSUSED*/
+static void
+FreeImages(clientData, display, widgRec, offset)
+ ClientData clientData;
+ Display *display; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ TreeViewImage *images = *(TreeViewImage **)(widgRec + offset);
+
+ if (images != NULL) {
+ register TreeViewImage *iPtr;
+ TreeView *tvPtr = clientData;
+
+ for (iPtr = images; *iPtr != NULL; iPtr++) {
+ Blt_TreeViewFreeImage(tvPtr, *iPtr);
+ }
+ Blt_Free(images);
+ }
+}
+
+int
+Blt_TreeViewApply(tvPtr, entryPtr, proc, flags)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr; /* Root entry of subtree. */
+ TreeViewApplyProc *proc; /* Procedure to call for each entry. */
+ unsigned int flags;
+{
+ if ((flags & ENTRY_HIDDEN) &&
+ (Blt_TreeViewEntryIsHidden(tvPtr, entryPtr))) {
+ return TCL_OK; /* Hidden node. */
+ }
+ if ((flags & ENTRY_HIDDEN) && (entryPtr->flags & ENTRY_HIDDEN)) {
+ return TCL_OK; /* Hidden node. */
+ }
+ if (((flags & ENTRY_CLOSED) == 0) ||
+ ((entryPtr->flags & ENTRY_CLOSED) == 0)) {
+ TreeViewEntry *childPtr;
+ Blt_TreeNode node, next;
+
+ next = NULL;
+ for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
+ node = next) {
+ next = Blt_TreeNextSibling(node);
+ /*
+ * Get the next child before calling Blt_TreeViewApply
+ * recursively. This is because the apply callback may
+ * delete the node and its link.
+ */
+ childPtr = NodeToEntry(tvPtr, node);
+ if (Blt_TreeViewApply(tvPtr, childPtr, proc, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ if ((tvPtr->flags & TV_HIDE_LEAVES) && (Blt_TreeIsLeaf(entryPtr->node))) {
+ return TRUE;
+ }
+ return (entryPtr->flags & ENTRY_HIDDEN) ? TRUE : FALSE;
+}
+
+int
+Blt_TreeViewEntryIsMapped(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ /* Don't check if the entry itself is open, only that its
+ * ancestors are. */
+ if (Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)) {
+ return FALSE;
+ }
+ if (entryPtr == tvPtr->rootPtr) {
+ return TRUE;
+ }
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ while (entryPtr != tvPtr->rootPtr) {
+ if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
+ return FALSE;
+ }
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ }
+ return TRUE;
+}
+
+TreeViewEntry *
+Blt_TreeViewFirstChild(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_TreeNode node;
+
+ for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ if (!Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)) {
+ return entryPtr;
+ }
+ }
+ return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewLastChild(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_TreeNode node;
+
+ for (node = Blt_TreeLastChild(entryPtr->node); node != NULL;
+ node = Blt_TreePrevSibling(node)) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ if (!Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)) {
+ return entryPtr;
+ }
+ }
+ return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewNextSibling(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_TreeNode node;
+
+ for (node = Blt_TreeNextSibling(entryPtr->node); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ if (!Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)) {
+ return entryPtr;
+ }
+ }
+ return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewPrevSibling(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_TreeNode node;
+
+ for (node = Blt_TreePrevSibling(entryPtr->node); node != NULL;
+ node = Blt_TreePrevSibling(node)) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ if (!Blt_TreeViewEntryIsHidden(tvPtr, entryPtr)) {
+ return entryPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewPrevEntry --
+ *
+ * Returns the "previous" node in the tree. This node (in
+ * depth-first order) is its parent if the node has no siblings
+ * that are previous to it. Otherwise it is the last descendant
+ * of the last sibling. In this case, descend the sibling's
+ * hierarchy, using the last child at any ancestor, until we
+ * we find a leaf.
+ *
+ *----------------------------------------------------------------------
+ */
+TreeViewEntry *
+Blt_TreeViewPrevEntry(tvPtr, entryPtr, mask)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ unsigned int mask;
+{
+ TreeViewEntry *prevPtr;
+
+ if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
+ return NULL; /* The root is the first node. */
+ }
+ prevPtr = Blt_TreeViewPrevSibling(tvPtr, entryPtr);
+ if (prevPtr == NULL) {
+ /* There are no siblings previous to this one, so pick the parent. */
+ prevPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ } else {
+ /*
+ * Traverse down the right-most thread in order to select the
+ * last entry. Stop if we find a "closed" entry or reach a leaf.
+ */
+ entryPtr = prevPtr;
+ while ((entryPtr->flags & mask) == 0) {
+ entryPtr = Blt_TreeViewLastChild(tvPtr, entryPtr);
+ if (entryPtr == NULL) {
+ break; /* Found a leaf. */
+ }
+ prevPtr = entryPtr;
+ }
+ }
+ if (prevPtr == NULL) {
+ return NULL;
+ }
+ return prevPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewNextNode --
+ *
+ * Returns the "next" node in relation to the given node.
+ * The next node (in depth-first order) is either the first
+ * child of the given node the next sibling if the node has
+ * no children (the node is a leaf). If the given node is the
+ * last sibling, then try it's parent next sibling. Continue
+ * until we either find a next sibling for some ancestor or
+ * we reach the root node. In this case the current node is
+ * the last node in the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+TreeViewEntry *
+Blt_TreeViewNextEntry(tvPtr, entryPtr, mask)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ unsigned int mask;
+{
+ TreeViewEntry *nextPtr;
+
+ if ((((tvPtr->flags & TV_HIDE_LEAVES) == 0) ||
+ (!Blt_TreeIsLeaf(entryPtr->node))) && (entryPtr->flags & mask) == 0) {
+ /* Pick the first sub-node. */
+ nextPtr = Blt_TreeViewFirstChild(tvPtr, entryPtr);
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ }
+ /*
+ * Back up until we can find a level where we can pick a
+ * "next sibling". For the last entry we'll thread our
+ * way back to the root.
+ */
+ while (entryPtr != tvPtr->rootPtr) {
+ nextPtr = Blt_TreeViewNextSibling(tvPtr, entryPtr);
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ }
+ return NULL; /* At root, no next node. */
+}
+
+void
+Blt_TreeViewConfigureButtons(tvPtr)
+ TreeView *tvPtr;
+{
+ GC newGC;
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ gcMask = GCForeground;
+ gcValues.foreground = buttonPtr->fgColor->pixel;
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->normalGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
+ }
+ buttonPtr->normalGC = newGC;
+
+ gcMask = (GCForeground | GCLineWidth);
+ gcValues.foreground = tvPtr->lineColor->pixel;
+ gcValues.line_width = tvPtr->lineWidth;
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->lineGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->lineGC);
+ }
+ buttonPtr->lineGC = newGC;
+
+ gcMask = GCForeground;
+ gcValues.foreground = buttonPtr->activeFgColor->pixel;
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (buttonPtr->activeGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
+ }
+ buttonPtr->activeGC = newGC;
+
+ buttonPtr->width = buttonPtr->height = ODD(buttonPtr->reqSize);
+ if (buttonPtr->images != NULL) {
+ register int i;
+ int width, height;
+
+ for (i = 0; i < 2; i++) {
+ if (buttonPtr->images[i] == NULL) {
+ break;
+ }
+ width = TreeViewImageWidth(buttonPtr->images[i]);
+ height = TreeViewImageWidth(buttonPtr->images[i]);
+ if (buttonPtr->width < width) {
+ buttonPtr->width = width;
+ }
+ if (buttonPtr->height < height) {
+ buttonPtr->height = height;
+ }
+ }
+ }
+ buttonPtr->width += 2 * buttonPtr->borderWidth;
+ buttonPtr->height += 2 * buttonPtr->borderWidth;
+}
+
+void
+Blt_TreeViewConfigureEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ GC newGC;
+
+ newGC = NULL;
+ if ((entryPtr->font != NULL) || (entryPtr->color != NULL)) {
+ Tk_Font font;
+ XColor *colorPtr;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ font = CHOOSE(tvPtr->treeColumn.font, entryPtr->font);
+ colorPtr = CHOOSE(tvPtr->treeColumn.fgColor, entryPtr->color);
+ gcMask = GCForeground | GCFont;
+ gcValues.foreground = colorPtr->pixel;
+ gcValues.font = Tk_FontId(font);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ }
+ if (entryPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, entryPtr->gc);
+ }
+ /* Assume all changes require a new layout. */
+ entryPtr->gc = newGC;
+ entryPtr->flags |= ENTRY_DIRTY;
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+}
+
+/*ARGSUSED*/
+static int
+SetEntryObjFromAny(interp, objPtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+{
+#ifdef notdef
+ fprintf(stderr, "SetEntryObjFromAny\n");
+#endif
+ Tcl_AppendResult(interp, "can't reset entry object", (char *)NULL);
+ return TCL_ERROR;
+}
+
+static void
+UpdateStringOfEntry(objPtr)
+ Tcl_Obj *objPtr; /* Entry object whose string rep to update. */
+{
+ TreeViewEntry *entryPtr;
+ char *label;
+
+#ifdef notdef
+ fprintf(stderr, "UpdateStringOfEntry\n");
+#endif
+ entryPtr = (TreeViewEntry *)objPtr->internalRep.otherValuePtr;
+ label = GETLABEL(entryPtr);
+ objPtr->bytes = label;
+ objPtr->length = strlen(label);
+}
+
+void
+Blt_TreeViewDestroyValue(tvPtr, valuePtr)
+ TreeView *tvPtr;
+ TreeViewValue *valuePtr;
+{
+ if (valuePtr->image != NULL) {
+ Blt_TreeViewFreeImage(tvPtr, valuePtr->image);
+ }
+ if (valuePtr->stylePtr != NULL) {
+ Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
+ }
+ if (valuePtr->textPtr != NULL) {
+ Blt_Free(valuePtr->textPtr);
+ }
+}
+
+
+static void
+DestroyEntry(data)
+ DestroyData data;
+{
+ TreeViewEntry *entryPtr = (TreeViewEntry *)data;
+ TreeView *tvPtr = entryPtr->tvPtr;
+
+ bltTreeViewImagesOption.clientData = tvPtr;
+ bltTreeViewUidOption.clientData = tvPtr;
+ labelOption.clientData = tvPtr;
+ Blt_FreeObjOptions(bltTreeViewEntrySpecs, (char *)entryPtr, tvPtr->display,
+ 0);
+
+ Blt_TreeClearTags(tvPtr->tagTablePtr, entryPtr->node);
+ if (entryPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, entryPtr->gc);
+ }
+ if (entryPtr->shadow.color != NULL) {
+ Tk_FreeColor(entryPtr->shadow.color);
+ }
+ /* Delete the chain of data values from the entry. */
+ if (entryPtr->values != NULL) {
+ TreeViewValue *valuePtr, *nextPtr;
+
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = nextPtr) {
+ nextPtr = valuePtr->nextPtr;
+ Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+ }
+ entryPtr->values = NULL;
+ }
+ if (entryPtr->fullName != NULL) {
+ Blt_Free(entryPtr->fullName);
+ }
+ if (entryPtr->textPtr != NULL) {
+ Blt_Free(entryPtr->textPtr);
+ }
+ Blt_PoolFreeItem(tvPtr->entryPool, entryPtr);
+}
+
+TreeViewEntry *
+Blt_TreeViewParentEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_TreeNode node;
+
+ if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
+ return NULL;
+ }
+ node = Blt_TreeNodeParent(entryPtr->node);
+ if (node == NULL) {
+ return NULL;
+ }
+ return NodeToEntry(tvPtr, node);
+}
+
+static void
+FreeEntryInternalRep(objPtr)
+ Tcl_Obj *objPtr; /* Entry object whose string rep to update. */
+{
+ TreeViewEntry *entryPtr;
+ TreeView *tvPtr;
+
+#ifdef notdef
+ fprintf(stderr, "FreeEntryInternalRep\n");
+#endif
+ entryPtr = (TreeViewEntry *)objPtr->internalRep.otherValuePtr;
+
+ tvPtr = entryPtr->tvPtr;
+ if (tvPtr->flags & TV_DESTROYED) {
+ /*
+ * If we know that the treeview is being destroyed, we don't
+ * need to reset the various focus/active/selection pointers
+ * or delete the individual bindings.
+ */
+ DestroyEntry((DestroyData)entryPtr);
+ } else {
+ if (entryPtr == tvPtr->activePtr) {
+ tvPtr->activePtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ }
+ if (entryPtr == tvPtr->activeButtonPtr) {
+ tvPtr->activeButtonPtr = NULL;
+ }
+ if (entryPtr == tvPtr->focusPtr) {
+ tvPtr->focusPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr);
+ }
+ if (entryPtr == tvPtr->selAnchorPtr) {
+ tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+ }
+ Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+ Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+ Blt_DeleteBindings(tvPtr->bindTable, entryPtr);
+ Blt_DeleteBindings(tvPtr->buttonBindTable, entryPtr);
+ entryPtr->node = NULL;
+ Tcl_EventuallyFree(entryPtr, DestroyEntry);
+ /*
+ * Indicate that the screen layout of the hierarchy may have changed
+ * because this node was deleted. The screen positions of the nodes
+ * in tvPtr->visibleArr are invalidated.
+ */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+}
+
+int
+Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+ return (hPtr != NULL);
+}
+
+void
+Blt_TreeViewSelectEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&tvPtr->selectTable, (char *)entryPtr, &isNew);
+ if (isNew) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_ChainAppend(tvPtr->selChainPtr, entryPtr);
+ Blt_SetHashValue(hPtr, linkPtr);
+ }
+}
+
+void
+Blt_TreeViewDeselectEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+ if (hPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+
+ linkPtr = Blt_GetHashValue(hPtr);
+ Blt_ChainDeleteLink(tvPtr->selChainPtr, linkPtr);
+ Blt_DeleteHashEntry(&tvPtr->selectTable, hPtr);
+ }
+}
+
+char *
+Blt_TreeViewGetFullName(tvPtr, entryPtr, checkEntryLabel, resultPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ int checkEntryLabel;
+ Tcl_DString *resultPtr;
+{
+ Blt_TreeNode node;
+ char **names; /* Used the stack the component names. */
+ char *staticSpace[64];
+ int level;
+ register int i;
+
+ level = Blt_TreeNodeDepth(tvPtr->tree, entryPtr->node);
+ if (tvPtr->rootPtr->labelUid == NULL) {
+ level--;
+ }
+ if (level > 64) {
+ names = Blt_Malloc((level + 2) * sizeof(char *));
+ assert(names);
+ } else {
+ names = staticSpace;
+ }
+ for (i = level; i >= 0; i--) {
+ /* Save the name of each ancestor in the name array. */
+ if (checkEntryLabel) {
+ names[i] = GETLABEL(entryPtr);
+ } else {
+ names[i] = Blt_TreeNodeLabel(entryPtr->node);
+ }
+ node = Blt_TreeNodeParent(entryPtr->node);
+ if (node != NULL) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ }
+ }
+ Tcl_DStringInit(resultPtr);
+ if (level >= 0) {
+ if ((tvPtr->pathSep == SEPARATOR_LIST) ||
+ (tvPtr->pathSep == SEPARATOR_NONE)) {
+ for (i = 0; i <= level; i++) {
+ Tcl_DStringAppendElement(resultPtr, names[i]);
+ }
+ } else {
+ Tcl_DStringAppend(resultPtr, names[0], -1);
+ for (i = 1; i <= level; i++) {
+ Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
+ Tcl_DStringAppend(resultPtr, names[i], -1);
+ }
+ }
+ } else {
+ if ((tvPtr->pathSep != SEPARATOR_LIST) &&
+ (tvPtr->pathSep != SEPARATOR_NONE)) {
+ Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
+ }
+ }
+ if (names != staticSpace) {
+ Blt_Free(names);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+
+int
+Blt_TreeViewCloseEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ char *cmd;
+
+ if (entryPtr->flags & ENTRY_CLOSED) {
+ return TCL_OK; /* Entry is already closed. */
+ }
+ entryPtr->flags |= ENTRY_CLOSED;
+
+ /*
+ * Invoke the entry's "close" command, if there is one. Otherwise
+ * try the treeview's global "close" command.
+ */
+ cmd = CHOOSE(tvPtr->closeCmd, entryPtr->closeCmd);
+ if (cmd != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
+ Tcl_Preserve(entryPtr);
+ result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_Release(entryPtr);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ tvPtr->flags |= TV_LAYOUT;
+ return TCL_OK;
+}
+
+
+int
+Blt_TreeViewOpenEntry(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ char *cmd;
+
+ if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
+ return TCL_OK; /* Entry is already open. */
+ }
+ entryPtr->flags &= ~ENTRY_CLOSED;
+ /*
+ * If there's a "open" command proc specified for the entry, use
+ * that instead of the more general "open" proc for the entire
+ * treeview.
+ */
+ cmd = CHOOSE(tvPtr->openCmd, entryPtr->openCmd);
+ if (cmd != NULL) {
+ Tcl_DString dString;
+ int result;
+
+ Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
+ Tcl_Preserve(entryPtr);
+ result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_Release(entryPtr);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ tvPtr->flags |= TV_LAYOUT;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewCreateEntry --
+ *
+ * This procedure is called by the Tree object when a node is
+ * created and inserted into the tree. It adds a new treeview
+ * entry field to the node.
+ *
+ * Results:
+ * Returns TCL_OK is the entry was added, TCL_ERROR otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewCreateEntry(tvPtr, node, objc, objv)
+ TreeView *tvPtr;
+ Blt_TreeNode node; /* Node that has just been created. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_ChainLink *linkPtr;
+ Tcl_Obj *objPtr;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+
+ /* Create the entry structure */
+ entryPtr = Blt_PoolAllocItem(tvPtr->entryPool, sizeof(TreeViewEntry));
+ memset(entryPtr, 0, sizeof(TreeViewEntry));
+
+ entryPtr->flags = tvPtr->buttonFlags | ENTRY_CLOSED;
+ entryPtr->tvPtr = tvPtr;
+
+ entryPtr->labelUid = NULL;
+ entryPtr->node = node;
+
+ bltTreeViewImagesOption.clientData = tvPtr;
+ bltTreeViewUidOption.clientData = tvPtr;
+ labelOption.clientData = tvPtr;
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, objc, objv, (char *)entryPtr, 0) != TCL_OK) {
+ DestroyEntry((DestroyData)entryPtr);
+ return TCL_ERROR;
+ }
+ Blt_TreeViewConfigureEntry(tvPtr, entryPtr);
+
+ objPtr = Tcl_NewObj();
+ /*
+ * Reference counts for entry objects are initialized to 0. They
+ * are incremented as they are inserted into the tree via the
+ * Blt_TreeSetValue call.
+ */
+ objPtr->refCount = 0;
+ objPtr->internalRep.otherValuePtr = (VOID *)entryPtr;
+ objPtr->bytes = NULL;
+ objPtr->length = 0;
+ objPtr->typePtr = &entryObjType;
+ /*
+ * Check if there are values that need to be added
+ */
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, node,
+ tvPtr->treeColumn.key, objPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreePrivateValue(tvPtr->interp, tvPtr->tree, node,
+ tvPtr->treeColumn.key);
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CreateApplyProc(node, clientData, order)
+ Blt_TreeNode node; /* Node that has just been created. */
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ TreeView *tvPtr = clientData;
+ return Blt_TreeViewCreateEntry(tvPtr, node, 0, (Tcl_Obj **)NULL);
+}
+
+/*ARGSUSED*/
+static int
+DeleteApplyProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ TreeView *tvPtr = clientData;
+ /*
+ * Unsetting the tree value triggers a call back to destroy the entry
+ * and also releases the Tcl_Obj that contains it.
+ */
+ return Blt_TreeUnsetValueByKey(tvPtr->interp, tvPtr->tree, node,
+ tvPtr->treeColumn.key);
+}
+
+static int
+TreeEventProc(clientData, eventPtr)
+ ClientData clientData;
+ Blt_TreeNotifyEvent *eventPtr;
+{
+ Blt_TreeNode node;
+ TreeView *tvPtr = clientData;
+ TreeViewEntry *entryPtr;
+
+ node = Blt_TreeGetNode(eventPtr->tree, eventPtr->inode);
+ switch (eventPtr->type) {
+ case TREE_NOTIFY_CREATE:
+ return Blt_TreeViewCreateEntry(tvPtr, node, 0, (Tcl_Obj **)NULL);
+ case TREE_NOTIFY_DELETE:
+ /*
+ * Unsetting the tree value triggers a call back to destroy
+ * the entry and also releases the Tcl_Obj that contains it.
+ */
+ if (node != NULL) {
+ if (Blt_TreeUnsetValueByKey(tvPtr->interp, eventPtr->tree, node,
+ tvPtr->treeColumn.key) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ break;
+ case TREE_NOTIFY_RELABEL:
+ if (node != NULL) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ entryPtr->flags |= ENTRY_DIRTY;
+ }
+ /*FALLTHRU*/
+ case TREE_NOTIFY_MOVE:
+ case TREE_NOTIFY_SORT:
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ break;
+ default:
+ /* empty */
+ break;
+ }
+ return TCL_OK;
+}
+
+static TreeViewValue *
+FindValue(entryPtr, columnPtr)
+ TreeViewEntry *entryPtr;
+ TreeViewColumn *columnPtr;
+{
+ register TreeViewValue *valuePtr;
+
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ if (valuePtr->columnPtr == columnPtr) {
+ return valuePtr;
+ }
+ }
+ return NULL;
+}
+
+Tcl_Obj *
+Blt_TreeViewGetData(entryPtr, key)
+ TreeViewEntry *entryPtr;
+ Blt_TreeKey key;
+{
+ Tcl_Obj *objPtr;
+
+ if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, entryPtr->tvPtr->tree,
+ entryPtr->node, key, &objPtr) != TCL_OK) {
+ return NULL;
+ }
+ return objPtr;
+}
+
+void
+Blt_TreeViewAddValue(entryPtr, columnPtr)
+ TreeViewEntry *entryPtr;
+ TreeViewColumn *columnPtr;
+{
+ if (FindValue(entryPtr, columnPtr) == NULL) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Blt_TreeViewGetData(entryPtr, columnPtr->key);
+ if (objPtr != NULL) {
+ TreeViewValue *valuePtr;
+
+ /* Add a new value only if a data entry exists. */
+ valuePtr = Blt_PoolAllocItem(entryPtr->tvPtr->valuePool,
+ sizeof(TreeViewValue));
+ valuePtr->columnPtr = columnPtr;
+ valuePtr->image = NULL;
+ valuePtr->nextPtr = entryPtr->values;
+ valuePtr->textPtr = NULL;
+ valuePtr->width = valuePtr->height = 0;
+ valuePtr->stylePtr = NULL;
+ entryPtr->values = valuePtr;
+ }
+ }
+ entryPtr->tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ entryPtr->flags |= ENTRY_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeTraceProc --
+ *
+ * Mirrors the individual values of the tree object (they must
+ * also be listed in the widget's columns chain). This is because
+ * it must track and save the sizes of each individual data
+ * entry, rather than re-computing all the sizes each time the
+ * widget is redrawn.
+ *
+ * This procedure is called by the Tree object when a node data
+ * value is set unset.
+ *
+ * Results:
+ * Returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TreeTraceProc(clientData, interp, node, key, flags)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ Blt_TreeNode node; /* Node that has just been updated. */
+ Blt_TreeKey key; /* Key of value that's been updated. */
+ unsigned int flags;
+{
+ Blt_HashEntry *hPtr;
+ TreeView *tvPtr = clientData;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ TreeViewValue *valuePtr, *nextPtr, *lastPtr;
+
+ entryPtr = NodeToEntry(tvPtr, node);
+ if (entryPtr == NULL) {
+ return TCL_OK;
+ }
+ switch (flags & (TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_UNSET)) {
+ case TREE_TRACE_WRITE:
+ hPtr = Blt_FindHashEntry(&tvPtr->columnTable, key);
+ if (hPtr == NULL) {
+ return TCL_OK; /* Data value isn't used by widget. */
+ }
+ columnPtr = Blt_GetHashValue(hPtr);
+ if (columnPtr != &tvPtr->treeColumn) {
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ entryPtr->flags |= ENTRY_DIRTY;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ break;
+
+ case TREE_TRACE_UNSET:
+ lastPtr = NULL;
+ for(valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = nextPtr) {
+ nextPtr = valuePtr->nextPtr;
+ if (valuePtr->columnPtr->key == key) {
+ Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+ if (lastPtr == NULL) {
+ entryPtr->values = nextPtr;
+ } else {
+ lastPtr->nextPtr = nextPtr;
+ }
+ entryPtr->flags |= ENTRY_DIRTY;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ break;
+ }
+ lastPtr = valuePtr;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return TCL_OK;
+}
+
+
+static void
+GetValueSize(tvPtr, entryPtr, valuePtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ TreeViewValue *valuePtr;
+{
+ Tcl_Obj *objPtr;
+ TextLayout *textPtr;
+ TreeViewColumn *columnPtr;
+ TreeViewImage image;
+ char *string;
+ int width, height;
+
+ columnPtr = valuePtr->columnPtr;
+ valuePtr->width = valuePtr->height = 0;
+ objPtr = Blt_TreeViewGetData(entryPtr, columnPtr->key);
+ if (objPtr == NULL) {
+ return; /* No data ??? */
+ }
+ string = Tcl_GetString(objPtr);
+ if (string[0] == '@') { /* Name of Tk image. */
+ image = Blt_TreeViewGetImage(tvPtr, string + 1);
+ if (image == NULL) {
+ goto handleString;
+ }
+ width = TreeViewImageWidth(image);
+ height = TreeViewImageHeight(image);
+ textPtr = NULL;
+ } else { /* Text string. */
+ TextStyle ts;
+
+ handleString:
+ Blt_InitTextStyle(&ts);
+ ts.font = columnPtr->font;
+ ts.anchor = TK_ANCHOR_NW;
+ ts.justify = TK_JUSTIFY_LEFT;
+ textPtr = Blt_GetTextLayout(string, &ts);
+ image = NULL;
+ width = textPtr->width;
+ height = textPtr->height;
+ }
+ valuePtr->width = width;
+ valuePtr->height = height;
+ if (valuePtr->image != NULL) {
+ Blt_TreeViewFreeImage(tvPtr, valuePtr->image);
+ }
+ if (valuePtr->textPtr != NULL) {
+ Blt_Free(valuePtr->textPtr);
+ }
+ valuePtr->image = image;
+ valuePtr->textPtr = textPtr;
+}
+
+static void
+GetRowExtents(tvPtr, entryPtr, widthPtr, heightPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ int *widthPtr, *heightPtr;
+{
+ TreeViewValue *valuePtr;
+ int valueWidth; /* Width of individual value. */
+ int width, height; /* Compute dimensions of row. */
+
+ width = height = 0;
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ if ((entryPtr->flags & ENTRY_DIRTY) ||
+ (valuePtr->columnPtr->flags & COLUMN_DIRTY)) {
+ GetValueSize(tvPtr, entryPtr, valuePtr);
+ }
+ if (valuePtr->height > height) {
+ height = valuePtr->height;
+ }
+ if (valuePtr->columnPtr->maxWidth < valuePtr->width) {
+ valuePtr->columnPtr->maxWidth = valuePtr->width;
+ }
+ valueWidth = valuePtr->width;
+ width += valueWidth;
+ }
+ *widthPtr = width;
+ *heightPtr = height;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewNearestEntry --
+ *
+ * Finds the entry closest to the given screen X-Y coordinates
+ * in the viewport.
+ *
+ * Results:
+ * Returns the pointer to the closest node. If no node is
+ * visible (nodes may be hidden), NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+TreeViewEntry *
+Blt_TreeViewNearestEntry(tvPtr, x, y, selectOne)
+ TreeView *tvPtr;
+ int x, y;
+ int selectOne;
+{
+ TreeViewEntry *lastPtr, *entryPtr;
+ register TreeViewEntry **p;
+
+ /*
+ * We implicitly can pick only visible entries. So make sure that
+ * the tree exists.
+ */
+ if (tvPtr->nVisible == 0) {
+ return NULL;
+ }
+ if (y < tvPtr->titleHeight) {
+ return (selectOne) ? tvPtr->visibleArr[0] : NULL;
+ }
+ /*
+ * Since the entry positions were previously computed in world
+ * coordinates, convert Y-coordinate from screen to world
+ * coordinates too.
+ */
+ y = WORLDY(tvPtr, y);
+ lastPtr = tvPtr->visibleArr[0];
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ entryPtr = *p;
+ /*
+ * If the start of the next entry starts beyond the point,
+ * use the last entry.
+ */
+ if (entryPtr->worldY > y) {
+ return (selectOne) ? entryPtr : NULL;
+ }
+ if (y < (entryPtr->worldY + entryPtr->height)) {
+ return entryPtr; /* Found it. */
+ }
+ lastPtr = entryPtr;
+ }
+ return (selectOne) ? lastPtr : NULL;
+}
+
+static void
+GetColumnTags(table, object, list)
+ Blt_BindTable table;
+ ClientData object;
+ Blt_List list;
+{
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+
+ tvPtr = Blt_GetBindingData(table);
+ columnPtr = object;
+ if (columnPtr->type == TV_ITEM_RULE) {
+ Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, "Rule"), 0);
+ } else {
+ Blt_ListAppend(list, (char *)object, 0);
+ if (columnPtr->tagsUid != NULL) {
+ int nNames;
+ char **names;
+ register char **p;
+
+ if (Tcl_SplitList((Tcl_Interp *)NULL, columnPtr->tagsUid, &nNames,
+ &names) == TCL_OK) {
+ for (p = names; *p != NULL; p++) {
+ Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, *p), 0);
+ }
+ Blt_Free(names);
+ }
+ }
+ }
+}
+
+/*ARGSUSED*/
+static ClientData
+PickColumn(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates of the test point. */
+{
+ TreeView *tvPtr = clientData;
+ TreeViewColumn *columnPtr;
+
+ if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) == 0) {
+ return NULL;
+ }
+ if (tvPtr->flags & TV_DIRTY) {
+ /* Can't trust selected entry, if entries have been added or
+ * deleted. */
+ if (tvPtr->flags & TV_LAYOUT) {
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ ComputeVisibleEntries(tvPtr);
+ }
+ columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, SEARCH_Y);
+ if ((columnPtr != NULL) && (columnPtr->flags & COLUMN_RULE_PICKED)) {
+ columnPtr->flags &= ~COLUMN_RULE_PICKED;
+ return &columnPtr->rule;
+ }
+ return columnPtr;
+}
+
+static void
+GetTags(table, object, list)
+ Blt_BindTable table;
+ ClientData object;
+ Blt_List list;
+{
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+
+ tvPtr = Blt_GetBindingData(table);
+ Blt_ListAppend(list, (char *)object, 0);
+ entryPtr = object;
+ if (entryPtr->tagsUid != NULL) {
+ int nNames;
+ char **names;
+ register char **p;
+
+ if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
+ &names) == TCL_OK) {
+ for (p = names; *p != NULL; p++) {
+ Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, *p), 0);
+ }
+ Blt_Free(names);
+ }
+ }
+ Blt_TreeViewGetTags(tvPtr->interp, tvPtr, entryPtr, list);
+}
+
+/*ARGSUSED*/
+static ClientData
+PickButton(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates of the test point. */
+{
+ TreeView *tvPtr = clientData;
+ register TreeViewEntry *entryPtr;
+
+ if (tvPtr->flags & TV_DIRTY) {
+ /* Can't trust selected entry, if entries have been added or
+ * deleted. */
+ if (tvPtr->flags & TV_LAYOUT) {
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ ComputeVisibleEntries(tvPtr);
+ }
+ if (tvPtr->nVisible == 0) {
+ return (ClientData)0;
+ }
+ entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
+ if (entryPtr == NULL) {
+ return (ClientData)0;
+ }
+ if (entryPtr->flags & ENTRY_HAS_BUTTON) {
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ int left, right, top, bottom;
+#define BUTTON_PAD 2
+ left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
+ right = left + buttonPtr->width + 2 * BUTTON_PAD;
+ top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
+ bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
+ x = WORLDX(tvPtr, x);
+ y = WORLDY(tvPtr, y);
+ if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
+ return entryPtr;
+ }
+ }
+ return NULL;
+}
+
+/*ARGSUSED*/
+static ClientData
+PickEntry(clientData, x, y)
+ ClientData clientData;
+ int x, y; /* Screen coordinates of the test point. */
+{
+ TreeView *tvPtr = clientData;
+ register TreeViewEntry *entryPtr;
+ int labelX;
+
+ if (tvPtr->flags & TV_DIRTY) {
+ /* Can't trust the selected entry if nodes have been added or
+ * deleted. So recompute the layout. */
+ if (tvPtr->flags & TV_LAYOUT) {
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ ComputeVisibleEntries(tvPtr);
+ }
+ if (tvPtr->nVisible == 0) {
+ return NULL;
+ }
+ entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
+ if (entryPtr == NULL) {
+ return NULL;
+ }
+ x = WORLDX(tvPtr, x);
+ y = WORLDY(tvPtr, y);
+ labelX = entryPtr->worldX;
+ if (!tvPtr->flatView) {
+ labelX += ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
+ }
+ if (entryPtr->flags & ENTRY_HAS_BUTTON) {
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ int left, right, top, bottom;
+
+ left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
+ right = left + buttonPtr->width + 2 * BUTTON_PAD;
+ top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
+ bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
+ if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
+ return NULL;
+ }
+ }
+ return entryPtr;
+}
+
+static void
+GetEntryExtents(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Tk_Font font;
+ TreeViewImage *icons;
+ char *label;
+ int entryWidth, entryHeight;
+ int width, height;
+
+ /*
+ * FIXME: Use of DIRTY flag inconsistent. When does it
+ * mean "dirty entry"? When does it mean "dirty column"?
+ * Does it matter? probably
+ */
+ if ((entryPtr->flags & ENTRY_DIRTY) || (tvPtr->flags & TV_UPDATE)) {
+ entryPtr->iconWidth = entryPtr->iconHeight = 0;
+ icons = CHOOSE(tvPtr->icons, entryPtr->icons);
+ if (icons != NULL) {
+ register int i;
+
+ for (i = 0; i < 2; i++) {
+ if (icons[i] == NULL) {
+ break;
+ }
+ if (entryPtr->iconWidth < TreeViewImageWidth(icons[i])) {
+ entryPtr->iconWidth = TreeViewImageWidth(icons[i]);
+ }
+ if (entryPtr->iconHeight < TreeViewImageHeight(icons[i])) {
+ entryPtr->iconHeight = TreeViewImageHeight(icons[i]);
+ }
+ }
+ }
+ if ((icons == NULL) || (icons[0] == NULL)) {
+ entryPtr->iconWidth = DEF_ICON_WIDTH;
+ entryPtr->iconHeight = DEF_ICON_HEIGHT;
+ }
+ entryPtr->iconWidth += 2 * ICON_PADX;
+ entryPtr->iconHeight += 2 * ICON_PADY;
+ entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+ entryWidth = 0;
+ font = CHOOSE(tvPtr->treeColumn.font, entryPtr->font);
+ if (entryPtr->fullName != NULL) {
+ Blt_Free(entryPtr->fullName);
+ entryPtr->fullName = NULL;
+ }
+ if (entryPtr->textPtr != NULL) {
+ Blt_Free(entryPtr->textPtr);
+ entryPtr->textPtr = NULL;
+ }
+
+ label = GETLABEL(entryPtr);
+ if (label[0] == '\0') {
+ Tk_FontMetrics fontMetrics;
+
+ Tk_GetFontMetrics(font, &fontMetrics);
+ width = height = fontMetrics.linespace;
+ } else {
+ TextStyle ts;
+
+ Blt_InitTextStyle(&ts);
+ ts.shadow.offset = entryPtr->shadow.offset;
+ ts.font = font;
+
+ if (tvPtr->flatView) {
+ Tcl_DString dString;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+ entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ entryPtr->textPtr = Blt_GetTextLayout(entryPtr->fullName, &ts);
+ } else {
+ entryPtr->textPtr = Blt_GetTextLayout(label, &ts);
+ }
+ width = entryPtr->textPtr->width;
+ height = entryPtr->textPtr->height;
+ }
+ width += 2 * (FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth);
+ height += 2 * (FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth);
+ width = ODD(width);
+ if (entryPtr->reqHeight > height) {
+ height = entryPtr->reqHeight;
+ }
+ height = ODD(height);
+ entryWidth = width;
+ if (entryHeight < height) {
+ entryHeight = height;
+ }
+ entryPtr->labelWidth = width;
+ entryPtr->labelHeight = height;
+ } else {
+ entryHeight = entryPtr->labelHeight;
+ entryWidth = entryPtr->labelWidth;
+ }
+ /*
+ * Find the maximum height of the data value entries. This also has
+ * the side effect of contributing the maximum width of the column.
+ */
+ GetRowExtents(tvPtr, entryPtr, &width, &height);
+ if (entryHeight < height) {
+ entryHeight = height;
+ }
+ entryPtr->width = entryWidth + COLUMN_PAD;
+ entryPtr->height = entryHeight + tvPtr->leader;
+ /*
+ * Force the height of the entry to an even number. This is to
+ * make the dots or the vertical line segments coincide with the
+ * start of the horizontal lines.
+ */
+ if (entryPtr->height & 0x01) {
+ entryPtr->height++;
+ }
+ entryPtr->flags &= ~ENTRY_DIRTY;
+}
+
+/*
+ * TreeView Procedures
+ */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTreeView --
+ *
+ * ----------------------------------------------------------------------
+ */
+static TreeView *
+CreateTreeView(interp, objPtr, className)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr; /* Name of the new widget. */
+ char *className;
+{
+ Tcl_DString dString;
+ Tk_Window tkwin;
+ TreeView *tvPtr;
+ char *name;
+ int result;
+
+ name = Tcl_GetString(objPtr);
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), name,
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ Tk_SetClass(tkwin, className);
+
+ tvPtr = Blt_Calloc(1, sizeof(TreeView));
+ assert(tvPtr);
+ tvPtr->tkwin = tkwin;
+ tvPtr->display = Tk_Display(tkwin);
+ tvPtr->interp = interp;
+ tvPtr->flags = TV_HIDE_ROOT | TV_SHOW_COLUMN_TITLES | TV_DIRTY | TV_LAYOUT;
+ tvPtr->leader = 0;
+ tvPtr->dashes = 1;
+ tvPtr->highlightWidth = 2;
+ tvPtr->selBorderWidth = 1;
+ tvPtr->borderWidth = 2;
+ tvPtr->relief = TK_RELIEF_SUNKEN;
+ tvPtr->selRelief = TK_RELIEF_FLAT;
+ tvPtr->scrollMode = BLT_SCROLL_MODE_HIERBOX;
+ tvPtr->selectMode = SELECT_MODE_SINGLE;
+ tvPtr->button.closeRelief = tvPtr->button.openRelief = TK_RELIEF_SOLID;
+ tvPtr->reqWidth = 200;
+ tvPtr->reqHeight = 400;
+ tvPtr->xScrollUnits = tvPtr->yScrollUnits = 20;
+ tvPtr->lineWidth = 1;
+ tvPtr->button.borderWidth = 1;
+ tvPtr->colChainPtr = Blt_ChainCreate();
+ tvPtr->buttonFlags = BUTTON_AUTO;
+ tvPtr->selChainPtr = Blt_ChainCreate();
+ tvPtr->tagTablePtr = Blt_TreeNewTagTable();
+ Blt_InitHashTable(&tvPtr->columnTable, BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&tvPtr->imageTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&tvPtr->uidTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&tvPtr->styleTable, BLT_STRING_KEYS);
+ tvPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr,
+ PickEntry, GetTags);
+ tvPtr->buttonBindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr,
+ PickButton, GetTags);
+ tvPtr->columnBindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr,
+ PickColumn, GetColumnTags);
+ tvPtr->entryPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ tvPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, tvPtr);
+#endif
+ /* Create a default column to display the view of the tree. */
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "BLT TreeView ", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tkwin), -1);
+ result = Blt_TreeViewInitColumn(tvPtr, &tvPtr->treeColumn,
+ Tcl_DStringValue(&dString), "", 0, (Tcl_Obj *CONST *)NULL);
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ Tk_DestroyWindow(tkwin);
+ return NULL;
+ }
+ Blt_ChainAppend(tvPtr->colChainPtr, &tvPtr->treeColumn);
+ tvPtr->editPtr = Blt_TreeViewCreateEditor(tvPtr, className);
+ return tvPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTreeView --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a TreeView at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTreeView(dataPtr)
+ DestroyData dataPtr; /* Pointer to the widget record. */
+{
+ TreeView *tvPtr = (TreeView *)dataPtr;
+ TreeViewButton *buttonPtr = &tvPtr->button;
+
+ tvPtr->flags |= TV_DESTROYED;
+
+ treeOption.clientData = tvPtr;
+ bltTreeViewImagesOption.clientData = tvPtr;
+ Blt_FreeObjOptions(bltTreeViewSpecs, (char *)tvPtr, tvPtr->display, 0);
+
+ if (tvPtr->tkwin != NULL) {
+ Tk_DeleteSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING);
+ }
+ if (tvPtr->lineGC != NULL) {
+ Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
+ }
+ if (tvPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
+ }
+ if (tvPtr->visibleArr != NULL) {
+ Blt_Free(tvPtr->visibleArr);
+ }
+ if (tvPtr->flatArr != NULL) {
+ Blt_Free(tvPtr->flatArr);
+ }
+ if (tvPtr->levelInfo != NULL) {
+ Blt_Free(tvPtr->levelInfo);
+ }
+ if (buttonPtr->activeGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
+ }
+ if (buttonPtr->normalGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
+ }
+ if (buttonPtr->lineGC != NULL) {
+ Tk_FreeGC(tvPtr->display, buttonPtr->lineGC);
+ }
+ if (tvPtr->drawable != None) {
+ Tk_FreePixmap(tvPtr->display, tvPtr->drawable);
+ }
+
+ Blt_TreeViewDestroyColumns(tvPtr);
+ Blt_DestroyBindingTable(tvPtr->bindTable);
+ Blt_DestroyBindingTable(tvPtr->buttonBindTable);
+ Blt_DestroyBindingTable(tvPtr->columnBindTable);
+ Blt_ChainDestroy(tvPtr->selChainPtr);
+ Blt_DeleteHashTable(&tvPtr->selectTable);
+ Blt_DeleteHashTable(&tvPtr->uidTable);
+ if (tvPtr->tagTablePtr != NULL) {
+ Blt_TreeReleaseTagTable(tvPtr->tagTablePtr);
+ }
+ Blt_PoolDestroy(tvPtr->entryPool);
+ Blt_PoolDestroy(tvPtr->valuePool);
+ DumpImageTable(tvPtr);
+ Blt_Free(tvPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeViewEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on hierarchy widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TreeViewEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ TreeView *tvPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ } else if (eventPtr->type == ConfigureNotify) {
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ tvPtr->flags |= TV_FOCUS;
+ } else {
+ tvPtr->flags &= ~TV_FOCUS;
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (tvPtr->tkwin != NULL) {
+ tvPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(tvPtr->interp, tvPtr->cmdToken);
+ }
+ if (tvPtr->flags & TV_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTreeView, tvPtr);
+ }
+ if (tvPtr->flags & TV_SELECT_PENDING) {
+ Tcl_CancelIdleCall(Blt_TreeViewSelectCmdProc, tvPtr);
+ }
+ Tcl_EventuallyFree(tvPtr, DestroyTreeView);
+ }
+}
+
+/* Selection Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is
+ * requested by someone. It returns part or all of the selection
+ * in a buffer provided by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored at
+ * buffer. Buffer is filled (or partially filled) with a
+ * NUL-terminated string containing part or all of the
+ * selection, as given by offset and maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectionProc(clientData, offset, buffer, maxBytes)
+ ClientData clientData; /* Information about the widget. */
+ int offset; /* Offset within selection of first
+ * character to be returned. */
+ char *buffer; /* Location in which to place
+ * selection. */
+ int maxBytes; /* Maximum number of bytes to place
+ * at buffer, not including terminating
+ * NULL character. */
+{
+ Tcl_DString dString;
+ TreeView *tvPtr = clientData;
+ TreeViewEntry *entryPtr;
+ int size;
+
+ if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
+ return -1;
+ }
+ /*
+ * Retrieve the names of the selected entries.
+ */
+ Tcl_DStringInit(&dString);
+ if (tvPtr->flags & TV_SELECT_SORTED) {
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ } else {
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, ENTRY_MASK)) {
+ if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+ Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ }
+ size = Tcl_DStringLength(&dString) - offset;
+ strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
+ Tcl_DStringFree(&dString);
+ buffer[maxBytes] = '\0';
+ return (size > maxBytes) ? maxBytes : size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WidgetInstCmdDeleteProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+WidgetInstCmdDeleteProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ TreeView *tvPtr = clientData;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this
+ * procedure destroys the widget.
+ */
+ if (tvPtr->tkwin != NULL) {
+ Tk_Window tkwin;
+
+ tkwin = tvPtr->tkwin;
+ tvPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_TreeViewConfigureWidget --
+ *
+ * This procedure is called to process an objv/objc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewConfigureWidget(interp, tvPtr, objc, objv, flags)
+ Tcl_Interp *interp;
+ TreeView *tvPtr; /* Information about widget; may or may not
+ * already have values for some values. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+ int flags;
+{
+ GC newGC;
+ XGCValues gcValues;
+ int oldView, setupTree;
+ unsigned long gcMask;
+
+ oldView = tvPtr->flatView;
+
+ treeOption.clientData = tvPtr;
+ bltTreeViewImagesOption.clientData = tvPtr;
+ if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
+ objc, objv, (char *)tvPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * GC for dotted vertical line.
+ */
+ gcMask = (GCForeground | GCLineWidth);
+ gcValues.foreground = tvPtr->lineColor->pixel;
+ gcValues.line_width = tvPtr->lineWidth;
+ if (tvPtr->dashes > 0) {
+ gcMask |= (GCLineStyle | GCDashList);
+ gcValues.line_style = LineOnOffDash;
+ gcValues.dashes = tvPtr->dashes;
+ }
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (tvPtr->lineGC != NULL) {
+ Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
+ }
+ tvPtr->lineGC = newGC;
+
+ /*
+ * GC for active label. Dashed outline.
+ */
+ gcMask = GCForeground | GCLineStyle;
+ gcValues.foreground = tvPtr->focusColor->pixel;
+ gcValues.line_style = (LineIsDashed(tvPtr->focusDashes))
+ ? LineOnOffDash : LineSolid;
+ newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(tvPtr->focusDashes)) {
+ tvPtr->focusDashes.offset = 2;
+ Blt_SetDashes(tvPtr->display, newGC, &tvPtr->focusDashes);
+ }
+ if (tvPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
+ }
+ tvPtr->focusGC = newGC;
+
+ Blt_TreeViewConfigureButtons(tvPtr);
+ tvPtr->inset = tvPtr->highlightWidth + tvPtr->borderWidth + INSET_PAD;
+
+ setupTree = FALSE;
+
+ /*
+ * If no tree object was named, allocate a new one. The name will
+ * be the same as the widget pathname.
+ */
+ if (tvPtr->tree == NULL) {
+ Blt_Tree token;
+ char *string;
+
+ string = Tk_PathName(tvPtr->tkwin);
+ if (Blt_TreeCreate(interp, string, &token) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->tree = token;
+ setupTree = TRUE;
+ }
+
+ /*
+ * If the tree object was changed, we need to setup the new one.
+ */
+ if (Blt_ObjConfigModified(bltTreeViewSpecs, "-tree", (char *)NULL)) {
+ setupTree = TRUE;
+ }
+
+ /*
+ * These options change the layout of the box. Mark the widget for update.
+ */
+ if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font",
+ "-linespacing", "-width", "-height", "-hide*", "-tree", "-flat",
+ (char *)NULL)) {
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+ }
+ if ((tvPtr->flatView != oldView) ||
+ (Blt_ObjConfigModified(bltTreeViewSpecs, "-hideleaves",
+ (char *)NULL))) {
+ TreeViewEntry *entryPtr;
+
+ tvPtr->flags |= TV_DIRTY;
+ /* Mark all entries dirty. */
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ entryPtr->flags |= ENTRY_DIRTY;
+ }
+ if ((!tvPtr->flatView) && (tvPtr->flatArr != NULL)) {
+ Blt_Free(tvPtr->flatArr);
+ tvPtr->flatArr = NULL;
+ }
+ }
+
+ /*
+ * If the tree view was changed, mark all the nodes dirty (we'll
+ * be switching back to either the full path name or the label)
+ * and free the array representing the flattened view of the tree.
+ */
+ if (tvPtr->flatView != oldView) {
+ TreeViewEntry *entryPtr;
+
+ tvPtr->flags |= TV_DIRTY;
+ /* Mark all entries dirty. */
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ entryPtr->flags |= ENTRY_DIRTY;
+ }
+ if ((!tvPtr->flatView) && (tvPtr->flatArr != NULL)) {
+ Blt_Free(tvPtr->flatArr);
+ tvPtr->flatArr = NULL;
+ }
+ }
+ if ((tvPtr->reqHeight != Tk_ReqHeight(tvPtr->tkwin)) ||
+ (tvPtr->reqWidth != Tk_ReqWidth(tvPtr->tkwin))) {
+ Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
+ }
+
+ if (setupTree) {
+ Blt_TreeNode root;
+
+ Blt_TreeCreateEventHandler(tvPtr->tree,
+ TREE_NOTIFY_ALL | TREE_NOTIFY_FOREIGN_ONLY,
+ TreeEventProc,
+ tvPtr);
+ TraceColumns(tvPtr);
+ root = Blt_TreeRootNode(tvPtr->tree);
+
+ /* Automatically add view-entry values to the new tree. */
+ Blt_TreeApply(root, CreateApplyProc, tvPtr);
+ tvPtr->focusPtr = tvPtr->rootPtr = NodeToEntry(tvPtr, root);
+ tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->rootPtr);
+
+ /* Automatically open the root node. */
+ if (Blt_TreeViewOpenEntry(tvPtr, tvPtr->rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((tvPtr->flags & TV_NEW_TAGS) ||
+ (Blt_TreeCmdGetTagTable(interp, Blt_TreeName(tvPtr->tree),
+ &tvPtr->tagTablePtr) != TCL_OK)) {
+ tvPtr->tagTablePtr = Blt_TreeNewTagTable();
+ }
+ }
+
+ if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font", "-color",
+ (char *)NULL)) {
+ Blt_TreeViewConfigureColumn(tvPtr, &tvPtr->treeColumn);
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetCoordinates --
+ *
+ * Determines the maximum height of all visible entries.
+ *
+ * 1. Sets the worldY coordinate for all mapped/open entries.
+ * 2. Determines if entry needs a button.
+ * 3. Collects the minimum height of open/mapped entries. (Do for all
+ * entries upon insert).
+ * 4. Figures out horizontal extent of each entry (will be width of
+ * tree view column).
+ * 5. Collects maximum icon size for each level.
+ * 6. The height of its vertical line
+ *
+ * Results:
+ * Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ * The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetCoordinates(tvPtr, entryPtr, yPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ int *yPtr;
+{
+ int depth;
+
+ entryPtr->worldY = -1;
+ entryPtr->lineHeight = -1;
+ if ((entryPtr != tvPtr->rootPtr) &&
+ (Blt_TreeViewEntryIsHidden(tvPtr, entryPtr))) {
+ return; /* If the entry is hidden, then do nothing. */
+ }
+ entryPtr->worldY = *yPtr;
+ entryPtr->lineHeight = -(*yPtr);
+ *yPtr += entryPtr->height;
+
+ depth = DEPTH(tvPtr, entryPtr->node) + 1;
+ if (tvPtr->levelInfo[depth].labelWidth < entryPtr->labelWidth) {
+ tvPtr->levelInfo[depth].labelWidth = entryPtr->labelWidth;
+ }
+ if (tvPtr->levelInfo[depth].iconWidth < entryPtr->iconWidth) {
+ tvPtr->levelInfo[depth].iconWidth = entryPtr->iconWidth;
+ }
+ tvPtr->levelInfo[depth].iconWidth |= 0x01;
+
+ if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
+ TreeViewEntry *bottomPtr, *childPtr;
+
+ bottomPtr = entryPtr;
+ for (childPtr = Blt_TreeViewFirstChild(tvPtr, entryPtr);
+ childPtr != NULL;
+ childPtr = Blt_TreeViewNextSibling(tvPtr, childPtr)) {
+ ResetCoordinates(tvPtr, childPtr, yPtr);
+ bottomPtr = childPtr;
+ }
+ entryPtr->lineHeight += bottomPtr->worldY;
+ }
+}
+
+static void
+AdjustColumns(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ double weight;
+ int nOpen;
+ int size, avail, ration, growth;
+
+ growth = VPORTWIDTH(tvPtr) - tvPtr->worldWidth;
+ nOpen = 0;
+ weight = 0.0;
+ /* Find out how many columns still have space available */
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ if ((columnPtr->hidden) ||
+ (columnPtr->weight == 0.0) ||
+ (columnPtr->width >= columnPtr->max) ||
+ (columnPtr->reqWidth > 0)) {
+ continue;
+ }
+ nOpen++;
+ weight += columnPtr->weight;
+ }
+
+ while ((nOpen > 0) && (weight > 0.0) && (growth > 0)) {
+ ration = (int)(growth / weight);
+ if (ration == 0) {
+ ration = 1;
+ }
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ if ((columnPtr->hidden) ||
+ (columnPtr->weight == 0.0) ||
+ (columnPtr->width >= columnPtr->max) ||
+ (columnPtr->reqWidth > 0)) {
+ continue;
+ }
+ size = (int)(ration * columnPtr->weight);
+ if (size > growth) {
+ size = growth;
+ }
+ avail = columnPtr->max - columnPtr->width;
+ if (size > avail) {
+ size = avail;
+ nOpen--;
+ weight -= columnPtr->weight;
+ }
+ growth -= size;
+ columnPtr->width += size;
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_TreeViewComputeLayout --
+ *
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change (such as
+ * font, linespacing).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeFlatLayout(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry **p;
+ TreeViewEntry *entryPtr;
+ int count;
+ int maxX;
+ int y;
+ /*
+ * Pass 1: Reinitialize column sizes and loop through all nodes.
+ *
+ * 1. Recalculate the size of each entry as needed.
+ * 2. The maximum depth of the tree.
+ * 3. Minimum height of an entry. Dividing this by the
+ * height of the widget gives a rough estimate of the
+ * maximum number of visible entries.
+ * 4. Build an array to hold level information to be filled
+ * in on pass 2.
+ */
+ if (tvPtr->flags & (TV_DIRTY | TV_UPDATE)) {
+ int position;
+
+ position = 1;
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->maxWidth = 0;
+ columnPtr->max = SHRT_MAX;
+ if (columnPtr->reqMax > 0) {
+ columnPtr->max = columnPtr->reqMax;
+ }
+ columnPtr->position = position;
+ position++;
+ }
+ tvPtr->minHeight = SHRT_MAX;
+ tvPtr->depth = 0;
+ tvPtr->nEntries = Blt_TreeSize(tvPtr->rootPtr->node);
+ if (tvPtr->flatArr != NULL) {
+ Blt_Free(tvPtr->flatArr);
+ }
+ tvPtr->flatArr =
+ Blt_Malloc(sizeof(TreeViewEntry *) * (tvPtr->nEntries + 1));
+ assert(tvPtr->flatArr);
+ tvPtr->depth = 0;
+ count = 0;
+ p = tvPtr->flatArr;
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ if ((tvPtr->flags & TV_HIDE_ROOT) && (entryPtr == tvPtr->rootPtr)) {
+ continue;
+ }
+ entryPtr->lineHeight = 0;
+ if (Blt_TreeViewEntryIsMapped(tvPtr, entryPtr)) {
+ GetEntryExtents(tvPtr, entryPtr);
+ if (tvPtr->minHeight > entryPtr->height) {
+ tvPtr->minHeight = entryPtr->height;
+ }
+ entryPtr->flags &= ~ENTRY_HAS_BUTTON;
+ *p++ = entryPtr;
+ count++;
+ }
+ }
+ tvPtr->flatArr[count] = NULL;
+ tvPtr->nEntries = count;
+
+ if (tvPtr->levelInfo != NULL) {
+ Blt_Free(tvPtr->levelInfo);
+ }
+ tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
+ assert(tvPtr->levelInfo);
+ tvPtr->flags &= ~(TV_DIRTY | TV_UPDATE);
+ tvPtr->flags |= TV_SORT_PENDING;
+ }
+
+ if (tvPtr->flags & TV_SORT_PENDING) {
+ Blt_TreeViewSortFlatView(tvPtr);
+ tvPtr->flags &= ~TV_SORT_PENDING;
+ }
+
+ tvPtr->levelInfo[0].labelWidth = tvPtr->levelInfo[0].x =
+ tvPtr->levelInfo[0].iconWidth = 0;
+ /*
+ * Pass 2: Loop through all open/mapped nodes.
+ *
+ * 1. Set world y-coordinates for entries. We must defer
+ * setting the x-coordinates until we know the maximum
+ * icon sizes at each level.
+ * 2. Compute the maximum depth of the tree.
+ * 3. Build an array to hold level information.
+ */
+ y = 0;
+ count = 0;
+ for(p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ entryPtr->flatIndex = count++;
+ entryPtr->worldY = y;
+ entryPtr->lineHeight = 0;
+ y += entryPtr->height;
+ if (tvPtr->levelInfo[0].labelWidth < entryPtr->labelWidth) {
+ tvPtr->levelInfo[0].labelWidth = entryPtr->labelWidth;
+ }
+ if (tvPtr->levelInfo[0].iconWidth < entryPtr->iconWidth) {
+ tvPtr->levelInfo[0].iconWidth = entryPtr->iconWidth;
+ }
+ }
+ tvPtr->levelInfo[0].iconWidth |= 0x01;
+ tvPtr->worldHeight = y; /* Set the scroll height of the hierarchy. */
+ if (tvPtr->worldHeight < 1) {
+ tvPtr->worldHeight = 1;
+ }
+ maxX = tvPtr->levelInfo[0].iconWidth + tvPtr->levelInfo[0].labelWidth;
+ tvPtr->treeColumn.maxWidth = maxX;
+ tvPtr->flags |= TV_VIEWPORT;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeTreeLayout --
+ *
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change (such as
+ * font, linespacing).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeTreeLayout(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ int maxX, x, y;
+ int sum;
+ register int i;
+
+ /*
+ * Pass 1: Reinitialize column sizes and loop through all nodes.
+ *
+ * 1. Recalculate the size of each entry as needed.
+ * 2. The maximum depth of the tree.
+ * 3. Minimum height of an entry. Dividing this by the
+ * height of the widget gives a rough estimate of the
+ * maximum number of visible entries.
+ * 4. Build an array to hold level information to be filled
+ * in on pass 2.
+ */
+ if (tvPtr->flags & TV_DIRTY) {
+ int position;
+
+ position = 1;
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->maxWidth = 0;
+ columnPtr->max = SHRT_MAX;
+ if (columnPtr->reqMax > 0) {
+ columnPtr->max = columnPtr->reqMax;
+ }
+ columnPtr->position = position;
+ position++;
+ }
+ tvPtr->minHeight = SHRT_MAX;
+ tvPtr->depth = 0;
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ GetEntryExtents(tvPtr, entryPtr);
+ if (tvPtr->minHeight > entryPtr->height) {
+ tvPtr->minHeight = entryPtr->height;
+ }
+ /*
+ * Determine if the entry should display a button
+ * (indicating that it has children) and mark the
+ * entry accordingly.
+ */
+ entryPtr->flags &= ~ENTRY_HAS_BUTTON;
+ if (entryPtr->flags & BUTTON_SHOW) {
+ entryPtr->flags |= ENTRY_HAS_BUTTON;
+ } else if (entryPtr->flags & BUTTON_AUTO) {
+ if (tvPtr->flags & TV_HIDE_LEAVES) {
+ /* Check that a non-leaf child exists */
+ if (Blt_TreeViewFirstChild(tvPtr, entryPtr) != NULL) {
+ entryPtr->flags |= ENTRY_HAS_BUTTON;
+ }
+ } else if (!Blt_TreeIsLeaf(entryPtr->node)) {
+ entryPtr->flags |= ENTRY_HAS_BUTTON;
+ }
+ }
+ /* Determine the depth of the tree. */
+ if (tvPtr->depth < DEPTH(tvPtr, entryPtr->node)) {
+ tvPtr->depth = DEPTH(tvPtr, entryPtr->node);
+ }
+ }
+ Blt_TreeViewSortTreeView(tvPtr);
+
+ if (tvPtr->levelInfo != NULL) {
+ Blt_Free(tvPtr->levelInfo);
+ }
+ tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
+ assert(tvPtr->levelInfo);
+ tvPtr->flags &= ~TV_DIRTY;
+ }
+ for (i = 0; i <= (tvPtr->depth + 1); i++) {
+ tvPtr->levelInfo[i].labelWidth = tvPtr->levelInfo[i].x =
+ tvPtr->levelInfo[i].iconWidth = 0;
+ }
+ /*
+ * Pass 2: Loop through all open/mapped nodes.
+ *
+ * 1. Set world y-coordinates for entries. We must defer
+ * setting the x-coordinates until we know the maximum
+ * icon sizes at each level.
+ * 2. Compute the maximum depth of the tree.
+ * 3. Build an array to hold level information.
+ */
+ y = 0;
+ if (tvPtr->flags & TV_HIDE_ROOT) {
+ /* If the root entry is to be hidden, cheat by offsetting
+ * the y-coordinates by the height of the entry. */
+ y = -(tvPtr->rootPtr->height);
+ }
+ ResetCoordinates(tvPtr, tvPtr->rootPtr, &y);
+ tvPtr->worldHeight = y; /* Set the scroll height of the hierarchy. */
+ if (tvPtr->worldHeight < 1) {
+ tvPtr->worldHeight = 1;
+ }
+ sum = maxX = 0;
+ for (i = 0; i <= (tvPtr->depth + 1); i++) {
+ sum += tvPtr->levelInfo[i].iconWidth;
+ if (i <= tvPtr->depth) {
+ tvPtr->levelInfo[i + 1].x = sum;
+ }
+ x = sum + tvPtr->levelInfo[i].labelWidth;
+ if (x > maxX) {
+ maxX = x;
+ }
+ }
+ tvPtr->treeColumn.maxWidth = maxX;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_TreeViewComputeLayout --
+ *
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change (such as
+ * font, linespacing).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewComputeLayout(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ int sum;
+
+ if (tvPtr->flatView) {
+ ComputeFlatLayout(tvPtr);
+ } else {
+ ComputeTreeLayout(tvPtr);
+ }
+ /* The width of the widget (in world coordinates) is the sum
+ * of the column widths. */
+
+ tvPtr->worldWidth = tvPtr->titleHeight = 0;
+ sum = 0;
+ columnPtr = NULL;
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->width = 0;
+ if (!columnPtr->hidden) {
+ if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) &&
+ (tvPtr->titleHeight < columnPtr->textPtr->height)) {
+ tvPtr->titleHeight = columnPtr->textPtr->height;
+ }
+ if (columnPtr->reqWidth > 0) {
+ columnPtr->width = columnPtr->reqWidth;
+ } else {
+ /* The computed width of a column is the maximum of
+ * the title width and the widest entry. */
+ columnPtr->width = MAX(columnPtr->titleWidth,
+ columnPtr->maxWidth);
+ /* Check that the width stays within any constraints that
+ * have been set. */
+ if ((columnPtr->reqMin > 0) &&
+ (columnPtr->reqMin > columnPtr->width)) {
+ columnPtr->width = columnPtr->reqMin;
+ }
+ if ((columnPtr->reqMax > 0) &&
+ (columnPtr->reqMax < columnPtr->width)) {
+ columnPtr->width = columnPtr->reqMax;
+ }
+ }
+ columnPtr->width += PADDING(columnPtr->pad) +
+ 2 * columnPtr->borderWidth;
+ }
+ columnPtr->worldX = sum;
+ sum += columnPtr->width;
+ }
+ tvPtr->worldWidth = sum;
+ if (VPORTWIDTH(tvPtr) > sum) {
+ AdjustColumns(tvPtr);
+ }
+ sum = 0;
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->worldX = sum;
+ sum += columnPtr->width;
+ }
+ if (tvPtr->titleHeight > 0) {
+ /* If any headings are displayed, add some extra padding to
+ * the height. */
+ tvPtr->titleHeight += 4;
+ }
+ /* tvPtr->worldWidth += 10; */
+ if (tvPtr->yScrollUnits < 1) {
+ tvPtr->yScrollUnits = 1;
+ }
+ if (tvPtr->xScrollUnits < 1) {
+ tvPtr->xScrollUnits = 1;
+ }
+ if (tvPtr->worldWidth < 1) {
+ tvPtr->worldWidth = 1;
+ }
+ tvPtr->flags &= ~TV_LAYOUT;
+ tvPtr->flags |= TV_SCROLL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeVisibleEntries --
+ *
+ * The entries visible in the viewport (the widget's window) are
+ * inserted into the array of visible nodes.
+ *
+ * Results:
+ * Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ * The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ComputeVisibleEntries(tvPtr)
+ TreeView *tvPtr;
+{
+ int height;
+ int level;
+ int nSlots;
+ int x, maxX;
+ int xOffset, yOffset;
+
+ xOffset = Blt_AdjustViewport(tvPtr->xOffset, tvPtr->worldWidth,
+ VPORTWIDTH(tvPtr), tvPtr->xScrollUnits, tvPtr->scrollMode);
+ yOffset = Blt_AdjustViewport(tvPtr->yOffset,
+ tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
+ tvPtr->scrollMode);
+
+ if ((xOffset != tvPtr->xOffset) || (yOffset != tvPtr->yOffset)) {
+ tvPtr->yOffset = yOffset;
+ tvPtr->xOffset = xOffset;
+ tvPtr->flags |= TV_VIEWPORT;
+ }
+ height = VPORTHEIGHT(tvPtr);
+
+ /* Allocate worst case number of slots for entry array. */
+ nSlots = (height / tvPtr->minHeight) + 3;
+ if (nSlots != tvPtr->nVisible) {
+ if (tvPtr->visibleArr != NULL) {
+ Blt_Free(tvPtr->visibleArr);
+ }
+ tvPtr->visibleArr = Blt_Calloc(nSlots, sizeof(TreeViewEntry *));
+ assert(tvPtr->visibleArr);
+ }
+ tvPtr->nVisible = 0;
+
+ if (tvPtr->rootPtr->flags & ENTRY_HIDDEN) {
+ return TCL_OK; /* Root node is hidden. */
+ }
+ /* Find the node where the view port starts. */
+ if (tvPtr->flatView) {
+ register TreeViewEntry **p, *entryPtr;
+
+ /* Find the starting entry visible in the viewport. It can't
+ * be hidden or any of it's ancestors closed. */
+ again:
+ for (p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ if ((entryPtr->worldY + entryPtr->height) > tvPtr->yOffset) {
+ break;
+ }
+ }
+ /*
+ * If we can't find the starting node, then the view must be
+ * scrolled down, but some nodes were deleted. Reset the view
+ * back to the top and try again.
+ */
+ if (*p == NULL) {
+ if (tvPtr->yOffset == 0) {
+ return TCL_OK; /* All entries are hidden. */
+ }
+ tvPtr->yOffset = 0;
+ goto again;
+ }
+
+
+ maxX = 0;
+ height += tvPtr->yOffset;
+ for (/* empty */; *p != NULL; p++) {
+ entryPtr = *p;
+ entryPtr->worldX = LEVELX(0) + tvPtr->treeColumn.worldX;
+ x = entryPtr->worldX + ICONWIDTH(0) + entryPtr->width;
+ if (x > maxX) {
+ maxX = x;
+ }
+ if (entryPtr->worldY >= height) {
+ break;
+ }
+ tvPtr->visibleArr[tvPtr->nVisible] = *p;
+ tvPtr->nVisible++;
+ }
+ tvPtr->visibleArr[tvPtr->nVisible] = NULL;
+ } else {
+ TreeViewEntry *entryPtr;
+
+ entryPtr = tvPtr->rootPtr;
+ while ((entryPtr->worldY + entryPtr->height) <= tvPtr->yOffset) {
+ for (entryPtr = Blt_TreeViewLastChild(tvPtr, entryPtr);
+ entryPtr != NULL;
+ entryPtr = Blt_TreeViewPrevSibling(tvPtr, entryPtr)) {
+ if (entryPtr->worldY <= tvPtr->yOffset) {
+ break;
+ }
+ }
+ /*
+ * If we can't find the starting node, then the view must be
+ * scrolled down, but some nodes were deleted. Reset the view
+ * back to the top and try again.
+ */
+ if (entryPtr == NULL) {
+ if (tvPtr->yOffset == 0) {
+ return TCL_OK; /* All entries are hidden. */
+ }
+ tvPtr->yOffset = 0;
+ continue;
+ }
+ }
+
+ height += tvPtr->yOffset;
+ maxX = 0;
+ for (/* empty */; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, ENTRY_MASK)) {
+ /*
+ * Compute and save the entry's X-coordinate now that we know
+ * what the maximum level offset for the entire TreeView is.
+ */
+ level = DEPTH(tvPtr, entryPtr->node);
+ entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
+
+ x = entryPtr->worldX + ICONWIDTH(level) + ICONWIDTH(level + 1) +
+ entryPtr->width;
+ if (x > maxX) {
+ maxX = x;
+ }
+ if (entryPtr->worldY >= height) {
+ break;
+ }
+ tvPtr->visibleArr[tvPtr->nVisible] = entryPtr;
+ tvPtr->nVisible++;
+ }
+ tvPtr->visibleArr[tvPtr->nVisible] = NULL;
+ }
+ /*
+ * -------------------------------------------------------------------
+ *
+ * Note: It's assumed that the view port always starts at or
+ * over an entry. Check that a change in the hierarchy
+ * (e.g. closing a node) hasn't left the viewport beyond
+ * the last entry. If so, adjust the viewport to start
+ * on the last entry.
+ *
+ * -------------------------------------------------------------------
+ */
+ if (tvPtr->xOffset > (tvPtr->worldWidth - tvPtr->xScrollUnits)) {
+ tvPtr->xOffset = tvPtr->worldWidth - tvPtr->xScrollUnits;
+ }
+ if (tvPtr->yOffset > (tvPtr->worldHeight - tvPtr->yScrollUnits)) {
+ tvPtr->yOffset = tvPtr->worldHeight - tvPtr->yScrollUnits;
+ }
+ tvPtr->xOffset = Blt_AdjustViewport(tvPtr->xOffset,
+ tvPtr->worldWidth, VPORTWIDTH(tvPtr), tvPtr->xScrollUnits,
+ tvPtr->scrollMode);
+ tvPtr->yOffset = Blt_AdjustViewport(tvPtr->yOffset,
+ tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
+ tvPtr->scrollMode);
+ tvPtr->flags &= ~TV_DIRTY;
+ return TCL_OK;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawVerticals --
+ *
+ * Draws vertical lines for the ancestor nodes. While the entry
+ * of the ancestor may not be visible, its vertical line segment
+ * does extent into the viewport. So walk back up the hierarchy
+ * drawing lines until we get to the root.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Vertical lines are drawn for the ancestor nodes.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawVerticals(tvPtr, entryPtr, drawable)
+ TreeView *tvPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ TreeViewEntry *entryPtr; /* Entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ int height, level;
+ int x, y;
+ int x1, y1, x2, y2;
+
+ while (entryPtr != tvPtr->rootPtr) {
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ if (entryPtr == NULL) {
+ break;
+ }
+ level = DEPTH(tvPtr, entryPtr->node);
+ /*
+ * World X-coordinates aren't computed only for entries that are
+ * outside the view port. So for each off-screen ancestor node
+ * compute it here too.
+ */
+ entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
+ x = SCREENX(tvPtr, entryPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+ height = MAX(entryPtr->iconHeight, tvPtr->button.height);
+ y += (height - tvPtr->button.height) / 2;
+ x1 = x2 = x + ICONWIDTH(level) + ICONWIDTH(level + 1) / 2;
+ y1 = y + tvPtr->button.height / 2;
+ y2 = y1 + entryPtr->lineHeight;
+ if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
+ y1 += entryPtr->height;
+ }
+ /*
+ * Clip the line's Y-coordinates at the viewport borders.
+ */
+ if (y1 < 0) {
+ y1 = (y1 & 0x1); /* Make sure the dotted line starts on
+ * the same even/odd pixel. */
+ }
+ if (y2 > Tk_Height(tvPtr->tkwin)) {
+ y2 = Tk_Height(tvPtr->tkwin);
+ }
+ if ((y1 < Tk_Height(tvPtr->tkwin)) && (y2 > 0)) {
+ XDrawLine(tvPtr->display, drawable, tvPtr->lineGC,
+ x1, y1, x2, y2);
+ }
+ }
+}
+
+void
+Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable)
+ TreeView *tvPtr; /* Widget record containing the
+ * attribute information for rules. */
+ TreeViewColumn *columnPtr;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ int x, y1, y2;
+
+ x = SCREENX(tvPtr, columnPtr->worldX) +
+ columnPtr->width + tvPtr->ruleMark - tvPtr->ruleAnchor - 1;
+
+ y1 = tvPtr->titleHeight + tvPtr->inset;
+ y2 = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
+ XDrawLine(tvPtr->display, drawable, columnPtr->rule.gc, x, y1, x, y2);
+ tvPtr->flags = TOGGLE(tvPtr->flags, TV_RULE_ACTIVE);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Blt_TreeViewDrawButton --
+ *
+ * Draws a button for the given entry. The button is drawn
+ * centered in the region immediately to the left of the origin
+ * of the entry (computed in the layout routines). The height
+ * and width of the button were previously calculated from the
+ * average row height.
+ *
+ * button height = entry height - (2 * some arbitrary padding).
+ * button width = button height.
+ *
+ * The button may have a border. The symbol (either a plus or
+ * minus) is slight smaller than the width or height minus the
+ * border.
+ *
+ * x,y origin of entry
+ *
+ * +---+
+ * | + | icon label
+ * +---+
+ * closed
+ *
+ * |----|----| horizontal offset
+ *
+ * +---+
+ * | - | icon label
+ * +---+
+ * open
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+void
+Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable)
+ TreeView *tvPtr; /* Widget record containing the
+ * attribute information for
+ * buttons. */
+ TreeViewEntry *entryPtr; /* Entry. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ Tk_3DBorder border;
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ TreeViewImage image;
+ int relief;
+ int width, height;
+ int x, y;
+ int yBot, yTop;
+ int y1, y2;
+
+ width = ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
+ height = MAX(entryPtr->iconHeight, buttonPtr->height);
+ entryPtr->buttonX = (width - buttonPtr->width) / 2;
+ entryPtr->buttonY = (height - buttonPtr->height) / 2;
+
+ x = SCREENX(tvPtr, entryPtr->worldX) + entryPtr->buttonX;
+ y = SCREENY(tvPtr, entryPtr->worldY) + entryPtr->buttonY;
+
+ if (entryPtr == tvPtr->activeButtonPtr) {
+ border = buttonPtr->activeBorder;
+ } else {
+ border = buttonPtr->border;
+ }
+ if (entryPtr->flags & ENTRY_CLOSED) {
+ relief = buttonPtr->closeRelief;
+ } else {
+ relief = buttonPtr->openRelief;
+ }
+ yBot = Tk_Height(tvPtr->tkwin) - (tvPtr->inset - INSET_PAD);
+ yTop = tvPtr->titleHeight + tvPtr->inset;
+
+ /*
+ * FIXME: 1) button overlays column title.
+ * 2) background rectangle drawn when image is available.
+ * 3) does "flat" relief always mean line border?
+ */
+ y1 = y;
+ y2 = y + buttonPtr->height;
+ if (y1 < yTop) {
+ y1 = yTop;
+ }
+ if (y2 > yBot) {
+ y2 = yBot;
+ }
+ if (y2 < y1) {
+ return;
+ }
+ if (relief == TK_RELIEF_SOLID) {
+ relief = TK_RELIEF_FLAT;
+ }
+ Tk_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y1,
+ buttonPtr->width, y2 - y1, buttonPtr->borderWidth, relief);
+
+ x += buttonPtr->borderWidth;
+ y += buttonPtr->borderWidth;
+ width = buttonPtr->width - (2 * buttonPtr->borderWidth);
+ height = buttonPtr->height - (2 * buttonPtr->borderWidth);
+
+ image = NULL;
+ if (buttonPtr->images != NULL) { /* Open or close button image? */
+ image = buttonPtr->images[0];
+ if (((entryPtr->flags & ENTRY_CLOSED) == 0) &&
+ (buttonPtr->images[1] != NULL)) {
+ image = buttonPtr->images[1];
+ }
+ }
+ if (image != NULL) { /* Image or rectangle? */
+ Tk_RedrawImage(TreeViewImageData(image), 0, 0, width, height, drawable,
+ x, y);
+ } else {
+ int top, bottom, left, right;
+ XSegment segments[6];
+ XSegment *s;
+ GC gc;
+
+ gc = (entryPtr == tvPtr->activeButtonPtr)
+ ? buttonPtr->activeGC : buttonPtr->normalGC;
+ s = segments;
+ if (relief == TK_RELIEF_FLAT) {
+
+ /* Draw the box outline */
+
+ left = x - buttonPtr->borderWidth;
+ top = y - buttonPtr->borderWidth;
+ right = left + buttonPtr->width - 1;
+ bottom = top + buttonPtr->height - 1;
+ if (bottom >= yTop) {
+ if (top < yTop) {
+ top = yTop;
+ } else {
+ s->x1 = left;
+ s->x2 = right;
+ s->y2 = s->y1 = top;
+ s++;
+ }
+ s->x2 = s->x1 = right;
+ s->y1 = top;
+ s->y2 = bottom;
+ s++;
+ s->x2 = s->x1 = left;
+ s->y1 = top;
+#ifdef WIN32
+ s->y2 = bottom + 1;
+#else
+ s->y2 = bottom;
+#endif
+ s++;
+ s->x1 = left;
+#ifdef WIN32
+ s->x2 = right + 1;
+#else
+ s->x2 = right;
+#endif
+ s->y2 = s->y1 = bottom;
+ s++;
+ }
+ }
+ top = y + height / 2;
+ if (top >= yTop) { /* Draw the horizontal line. */
+ left = x + BUTTON_IPAD;
+ right = x + width - BUTTON_IPAD;
+ s->y1 = s->y2 = top;
+ s->x1 = left;
+#ifdef WIN32
+ s->x2 = right;
+#else
+ s->x2 = right - 1;
+#endif
+ s++;
+ }
+ if (entryPtr->flags & ENTRY_CLOSED) { /* Draw the vertical
+ * line for the plus. */
+ top = y + BUTTON_IPAD;
+ bottom = y + height - BUTTON_IPAD;
+ if (bottom > yTop) {
+ if (top < yTop) {
+ top = yTop;
+ }
+ s->y1 = top;
+#ifdef WIN32
+ s->y2 = bottom;
+#else
+ s->y2 = bottom - 1;
+#endif
+ s->x1 = s->x2 = x + width / 2;
+ s++;
+ }
+ }
+ XDrawSegments(tvPtr->display, drawable, gc, segments, s - segments);
+ }
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Blt_TreeViewIconImage --
+ *
+ * Selects the correct image for the entry's icon depending upon
+ * the current state of the entry: active/inactive normal/selected.
+ *
+ * active - normal
+ * active - selected
+ * inactive - normal
+ * inactive - selected
+ *
+ * Results:
+ * Returns the image for the icon.
+ *
+ * ---------------------------------------------------------------------------
+ */
+TreeViewImage
+Blt_TreeViewIconImage(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ TreeViewImage *icons;
+ TreeViewImage image;
+
+ int isActive, hasFocus;
+
+ isActive = (entryPtr == tvPtr->activePtr);
+ hasFocus = (entryPtr == tvPtr->focusPtr);
+ icons = NULL;
+ if (isActive) {
+ icons = CHOOSE(tvPtr->activeIcons, entryPtr->activeIcons);
+ }
+ if (icons == NULL) {
+ icons = CHOOSE(tvPtr->icons, entryPtr->icons);
+ }
+ image = NULL;
+ if (icons != NULL) { /* Selected or normal icon? */
+ image = icons[0];
+ if ((hasFocus) && (icons[1] != NULL)) {
+ image = icons[1];
+ }
+ }
+ return image;
+}
+
+
+int
+Blt_TreeViewDrawIcon(tvPtr, entryPtr, x, y, drawable)
+ TreeView *tvPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ TreeViewEntry *entryPtr; /* Entry to display. */
+ int x, y;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ TreeViewImage image;
+
+ image = Blt_TreeViewIconImage(tvPtr, entryPtr);
+
+ if (image != NULL) { /* Image or default icon bitmap? */
+ int entryHeight;
+ int level;
+ int maxY;
+ int top, bottom;
+ int topInset, botInset;
+ int width, height;
+
+ level = DEPTH(tvPtr, entryPtr->node);
+ entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+ height = TreeViewImageHeight(image);
+ width = TreeViewImageWidth(image);
+ if (tvPtr->flatView) {
+ x += (ICONWIDTH(0) - width) / 2;
+ } else {
+ x += (ICONWIDTH(level + 1) - width) / 2;
+ }
+ y += (entryHeight - height) / 2;
+ botInset = tvPtr->inset - INSET_PAD;
+ topInset = tvPtr->titleHeight + tvPtr->inset;
+ maxY = Tk_Height(tvPtr->tkwin) - botInset;
+ top = 0;
+ bottom = y + height;
+ if (y < topInset) {
+ height += y - topInset;
+ top = -y + topInset;
+ y = topInset;
+ } else if (bottom >= maxY) {
+ height = maxY - y;
+ }
+ Tk_RedrawImage(TreeViewImageData(image), 0, top, width, height,
+ drawable, x, y);
+ }
+ return (image != NULL);
+}
+
+static int
+DrawLabel(tvPtr, entryPtr, x, y, drawable)
+ TreeView *tvPtr; /* Widget record. */
+ TreeViewEntry *entryPtr; /* Entry attribute information. */
+ int x, y;
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ char *label;
+ int entryHeight;
+ int isFocused;
+ int width, height; /* Width and height of label. */
+
+ entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+ isFocused = ((entryPtr == tvPtr->focusPtr) &&
+ (tvPtr->flags & TV_FOCUS));
+
+ /* Includes padding, selection 3-D border, and focus outline. */
+ width = entryPtr->labelWidth;
+ height = entryPtr->labelHeight;
+
+ /* Center the label, if necessary, vertically along the entry row. */
+ if (height < entryHeight) {
+ y += (entryHeight - height) / 2;
+ }
+ if (isFocused) { /* Focus outline */
+ XDrawRectangle(tvPtr->display, drawable, tvPtr->focusGC,
+ x, y, width - 1, height - 1);
+ }
+ x += FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth;
+ y += FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth;
+
+ label = GETLABEL(entryPtr);
+ if (label[0] != '\0') {
+ GC gc;
+ TextStyle ts;
+ Tk_Font font;
+ XColor *normalColor, *activeColor;
+ int selected;
+
+ selected = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
+ font = CHOOSE(tvPtr->treeColumn.font, entryPtr->font);
+ normalColor = CHOOSE(tvPtr->treeColumn.fgColor,entryPtr->color);
+ activeColor = (selected) ? tvPtr->selFgColor : normalColor;
+ gc = (entryPtr->gc == NULL) ? tvPtr->treeColumn.gc : entryPtr->gc;
+ Blt_SetDrawTextStyle(&ts, font, gc, normalColor, activeColor,
+ entryPtr->shadow.color, 0.0, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0,
+ entryPtr->shadow.offset);
+ ts.state = (selected || (entryPtr->gc == NULL)) ? STATE_ACTIVE : 0;
+ Blt_DrawTextLayout(tvPtr->tkwin, drawable, entryPtr->textPtr,
+ &ts, x, y);
+ }
+ return entryHeight;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawFlatEntry --
+ *
+ * Draws a button for the given entry. Note that buttons should only
+ * be drawn if the entry has sub-entries to be opened or closed. It's
+ * the responsibility of the calling routine to ensure this.
+ *
+ * The button is drawn centered in the region immediately to the left
+ * of the origin of the entry (computed in the layout routines). The
+ * height and width of the button were previously calculated from the
+ * average row height.
+ *
+ * button height = entry height - (2 * some arbitrary padding).
+ * button width = button height.
+ *
+ * The button has a border. The symbol (either a plus or minus) is
+ * slight smaller than the width or height minus the border.
+ *
+ * x,y origin of entry
+ *
+ * +---+
+ * | + | icon label
+ * +---+
+ * closed
+ *
+ * |----|----| horizontal offset
+ *
+ * +---+
+ * | - | icon label
+ * +---+
+ * open
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawFlatEntry(tvPtr, entryPtr, drawable)
+ TreeView *tvPtr; /* Widget record containing the attribute
+ * information for buttons. */
+ TreeViewEntry *entryPtr; /* Entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ int level;
+ int x, y;
+
+ entryPtr->flags &= ~ENTRY_REDRAW;
+
+ x = SCREENX(tvPtr, entryPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+ if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, x, y, drawable)) {
+ x -= (DEF_ICON_WIDTH * 2) / 3;
+ }
+ level = 0;
+ x += ICONWIDTH(level);
+ /* Entry label. */
+ DrawLabel(tvPtr, entryPtr, x, y, drawable);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawTreeEntry --
+ *
+ * Draws a button for the given entry. Note that buttons should only
+ * be drawn if the entry has sub-entries to be opened or closed. It's
+ * the responsibility of the calling routine to ensure this.
+ *
+ * The button is drawn centered in the region immediately to the left
+ * of the origin of the entry (computed in the layout routines). The
+ * height and width of the button were previously calculated from the
+ * average row height.
+ *
+ * button height = entry height - (2 * some arbitrary padding).
+ * button width = button height.
+ *
+ * The button has a border. The symbol (either a plus or minus) is
+ * slight smaller than the width or height minus the border.
+ *
+ * x,y origin of entry
+ *
+ * +---+
+ * | + | icon label
+ * +---+
+ * closed
+ *
+ * |----|----| horizontal offset
+ *
+ * +---+
+ * | - | icon label
+ * +---+
+ * open
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawTreeEntry(tvPtr, entryPtr, drawable)
+ TreeView *tvPtr; /* Widget record. */
+ TreeViewEntry *entryPtr; /* Entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ int buttonY;
+ int level;
+ int width, height;
+ int x, y;
+ int x1, y1, x2, y2;
+
+ entryPtr->flags &= ~ENTRY_REDRAW;
+
+ x = SCREENX(tvPtr, entryPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+
+ level = DEPTH(tvPtr, entryPtr->node);
+ width = ICONWIDTH(level);
+ height = MAX(entryPtr->iconHeight, buttonPtr->height);
+
+ entryPtr->buttonX = (width - buttonPtr->width) / 2;
+ entryPtr->buttonY = (height - buttonPtr->height) / 2;
+
+ buttonY = y + entryPtr->buttonY;
+
+ x1 = x + (width / 2);
+ y1 = y2 = buttonY + (buttonPtr->height / 2);
+ x2 = x1 + (ICONWIDTH(level) + ICONWIDTH(level + 1)) / 2;
+
+ if ((Blt_TreeNodeParent(entryPtr->node) != NULL) &&
+ (tvPtr->lineWidth > 0)) {
+ /*
+ * For every node except root, draw a horizontal line from
+ * the vertical bar to the middle of the icon.
+ */
+ XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x1, y1, x2, y2);
+ }
+ if (((entryPtr->flags & ENTRY_CLOSED) == 0) && (tvPtr->lineWidth > 0)) {
+ /*
+ * Entry is open, draw vertical line.
+ */
+ y2 = y1 + entryPtr->lineHeight;
+ if (y2 > Tk_Height(tvPtr->tkwin)) {
+ y2 = Tk_Height(tvPtr->tkwin); /* Clip line at window border. */
+ }
+ XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x2, y1, x2, y2);
+ }
+ if ((entryPtr->flags & ENTRY_HAS_BUTTON) && (entryPtr != tvPtr->rootPtr)) {
+ /*
+ * Except for the root, draw a button for every entry that
+ * needs one. The displayed button can be either a Tk image
+ * or a rectangle with plus or minus sign.
+ */
+ Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable);
+ }
+ x += ICONWIDTH(level);
+
+ if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, x, y, drawable)) {
+ x -= (DEF_ICON_WIDTH * 2) / 3;
+ }
+ x += ICONWIDTH(level + 1) + 4;
+
+ /* Entry label. */
+ DrawLabel(tvPtr, entryPtr, x, y, drawable);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawValue --
+ *
+ * Draws a column value for the given entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawValue(tvPtr, columnPtr, entryPtr, drawable)
+ TreeView *tvPtr; /* Widget record. */
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr; /* Node of entry to be drawn. */
+ Drawable drawable; /* Pixmap or window to draw into. */
+{
+ TreeViewValue *valuePtr;
+ int width;
+ int x, y;
+
+ /* Draw the background of the value. */
+ x = SCREENX(tvPtr, columnPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+ if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+ Tk_Fill3DRectangle(tvPtr->tkwin, drawable, tvPtr->selBorder,
+ x, y - 1, columnPtr->width, entryPtr->height + 1,
+ tvPtr->selBorderWidth, tvPtr->selRelief);
+ }
+ /* Check if there's a corresponding value in the entry. */
+ valuePtr = FindValue(entryPtr, columnPtr);
+ if (valuePtr == NULL) {
+ return; /* No value. */
+ }
+ x += columnPtr->pad.side1 + columnPtr->borderWidth;
+ width = columnPtr->width - (2 * columnPtr->borderWidth +
+ PADDING(columnPtr->pad));
+ if (width > valuePtr->width) {
+ switch(columnPtr->justify) {
+ case TK_JUSTIFY_RIGHT:
+ x += (width - valuePtr->width);
+ break;
+ case TK_JUSTIFY_CENTER:
+ x += (width - valuePtr->width) / 2;
+ break;
+ case TK_JUSTIFY_LEFT:
+ break;
+ }
+ }
+ if (valuePtr->image != NULL) {
+ Tk_RedrawImage(TreeViewImageData(valuePtr->image), 0, 0,
+ valuePtr->width, valuePtr->height, drawable, x, y);
+ } else {
+ TextStyle ts;
+ XColor *color;
+
+ if (entryPtr->color != NULL) {
+ XSetForeground(tvPtr->display, columnPtr->gc,
+ entryPtr->color->pixel);
+ color = entryPtr->color;
+ } else {
+ color = columnPtr->fgColor;
+ }
+ Blt_SetDrawTextStyle(&ts, columnPtr->font, columnPtr->gc, color,
+ tvPtr->selFgColor, entryPtr->shadow.color, 0.0, TK_ANCHOR_NW,
+ TK_JUSTIFY_LEFT, 0, entryPtr->shadow.offset);
+ Blt_DrawTextLayout(tvPtr->tkwin, drawable, valuePtr->textPtr,
+ &ts, x, y);
+ if (entryPtr->color != NULL) {
+ XSetForeground(tvPtr->display, columnPtr->gc,
+ columnPtr->fgColor->pixel);
+ }
+ }
+}
+
+static void
+DrawTitle(tvPtr, columnPtr, drawable, x)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+ Drawable drawable;
+ int x;
+{
+ GC gc;
+ TextStyle ts;
+ Tk_3DBorder border;
+ XColor *fgColor;
+ int columnWidth;
+ int width;
+ int x0, cx, xOffset;
+
+ columnWidth = columnPtr->width;
+ cx = x;
+ if (columnPtr->position == Blt_ChainGetLength(tvPtr->colChainPtr)) {
+ columnWidth = Tk_Width(tvPtr->tkwin) - x;
+ } else if (columnPtr->position == 1) {
+ columnWidth += x;
+ cx = 0;
+ }
+ x0 = x + columnPtr->borderWidth;
+
+ if (columnPtr == tvPtr->activeColumnPtr) {
+ border = columnPtr->activeTitleBorder;
+ gc = columnPtr->activeTitleGC;
+ fgColor = columnPtr->activeTitleFgColor;
+ } else {
+ border = columnPtr->titleBorder;
+ gc = columnPtr->titleGC;
+ fgColor = columnPtr->titleFgColor;
+ }
+ Tk_Fill3DRectangle(tvPtr->tkwin, drawable, border, cx + 1,
+ tvPtr->inset + 1, columnWidth - 2, tvPtr->titleHeight - 2, 0,
+ TK_RELIEF_FLAT);
+ width = columnPtr->width;
+ xOffset = x0 + columnPtr->pad.side1 + 1;
+ if (width > columnPtr->textPtr->width) {
+ x += (width - columnPtr->textPtr->width) / 2;
+ }
+ if (columnPtr == tvPtr->sortColumnPtr) {
+ /* Make sure there's room for the sorting-direction triangle. */
+ if ((x - xOffset) <= (SORT_MARKER_WIDTH + 1)) {
+ x = xOffset + SORT_MARKER_WIDTH + 1;
+ }
+ }
+ Blt_SetDrawTextStyle(&ts, columnPtr->titleFont, gc, fgColor,
+ tvPtr->selFgColor, columnPtr->titleShadow.color, 0.0, TK_ANCHOR_NW,
+ TK_JUSTIFY_LEFT, 0, columnPtr->titleShadow.offset);
+ Blt_DrawTextLayout(tvPtr->tkwin, drawable, columnPtr->textPtr, &ts, x,
+ tvPtr->inset + 1);
+ if ((columnPtr == tvPtr->sortColumnPtr) && (tvPtr->flatView)) {
+ XPoint triangle[4];
+ int y;
+
+ y = tvPtr->inset + tvPtr->titleHeight / 2 - 2;
+ if (tvPtr->flags & TV_DECREASING) {
+ triangle[0].x = x0 + SORT_MARKER_OFFSET + 1;
+ triangle[0].y = y - SORT_MARKER_OFFSET / 2;
+ triangle[1].x = triangle[0].x + SORT_MARKER_OFFSET;
+ triangle[1].y = triangle[0].y + SORT_MARKER_OFFSET;
+ triangle[2].x = triangle[0].x - SORT_MARKER_OFFSET;
+ triangle[2].y = triangle[0].y + SORT_MARKER_OFFSET;
+ triangle[3].x = triangle[0].x;
+ triangle[3].y = triangle[0].y;
+ } else {
+ triangle[0].x = x0 + SORT_MARKER_OFFSET + 1;
+ triangle[0].y = y + SORT_MARKER_OFFSET / 2;
+ triangle[1].x = triangle[0].x - SORT_MARKER_OFFSET;
+ triangle[1].y = triangle[0].y - SORT_MARKER_OFFSET;
+ triangle[2].x = triangle[0].x + SORT_MARKER_OFFSET;
+ triangle[2].y = triangle[0].y - SORT_MARKER_OFFSET;
+ triangle[3].x = triangle[0].x;
+ triangle[3].y = triangle[0].y;
+ }
+ XFillPolygon(tvPtr->display, drawable, gc, triangle, 4, Convex,
+ CoordModeOrigin);
+ XDrawLines(tvPtr->display, drawable, gc, triangle, 4,
+ CoordModeOrigin);
+ }
+ Tk_Draw3DRectangle(tvPtr->tkwin, drawable, border, cx, tvPtr->inset,
+ columnWidth, tvPtr->titleHeight, 1, TK_RELIEF_RAISED);
+}
+
+void
+Blt_TreeViewDrawHeadings(tvPtr, drawable)
+ TreeView *tvPtr;
+ Drawable drawable;
+{
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ int x;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ if (columnPtr->hidden) {
+ continue;
+ }
+ x = SCREENX(tvPtr, columnPtr->worldX);
+ if ((x + columnPtr->width) < 0) {
+ continue; /* Don't draw columns before the left edge. */
+ }
+ if (x > Tk_Width(tvPtr->tkwin)) {
+ break; /* Discontinue when a column starts beyond
+ * the right edge. */
+ }
+ DrawTitle(tvPtr, columnPtr, drawable, x);
+ }
+}
+
+static void
+DrawTreeView(tvPtr, drawable, x)
+ TreeView *tvPtr;
+ Drawable drawable;
+ int x;
+{
+ register TreeViewEntry **p;
+
+ /*
+ * Draw the backgrounds of selected entries first. The vertical
+ * lines connecting child entries will be draw on top.
+ */
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
+ int y;
+
+ y = SCREENY(tvPtr, (*p)->worldY) - 1;
+ Tk_Fill3DRectangle(tvPtr->tkwin, drawable, tvPtr->selBorder,
+ x, y, tvPtr->treeColumn.width, (*p)->height + 1,
+ tvPtr->selBorderWidth, tvPtr->selRelief);
+ }
+ }
+ if (tvPtr->lineWidth > 0) {
+ /* Draw all the vertical lines from topmost node. */
+ DrawVerticals(tvPtr, tvPtr->visibleArr[0], drawable);
+ }
+
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ DrawTreeEntry(tvPtr, *p, drawable);
+ }
+}
+
+static void
+DrawFlatView(tvPtr, drawable, x)
+ TreeView *tvPtr;
+ Drawable drawable;
+ int x;
+{
+ register TreeViewEntry **p;
+
+ /*
+ * Draw the backgrounds of selected entries first. The vertical
+ * lines connecting child entries will be draw on top.
+ */
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
+ int y;
+
+ y = SCREENY(tvPtr, (*p)->worldY) - 1;
+ Tk_Fill3DRectangle(tvPtr->tkwin, drawable, tvPtr->selBorder,
+ x, y, tvPtr->treeColumn.width, (*p)->height + 1,
+ tvPtr->selBorderWidth, tvPtr->selRelief);
+ }
+ }
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ DrawFlatEntry(tvPtr, *p, drawable);
+ }
+}
+
+void
+Blt_TreeViewDrawOuterBorders(tvPtr, drawable)
+ TreeView *tvPtr;
+ Drawable drawable;
+{
+ /* Draw 3D border just inside of the focus highlight ring. */
+ if ((tvPtr->borderWidth > 0) && (tvPtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(tvPtr->tkwin, drawable, tvPtr->border,
+ tvPtr->highlightWidth, tvPtr->highlightWidth,
+ Tk_Width(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
+ Tk_Height(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
+ tvPtr->borderWidth, tvPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if (tvPtr->highlightWidth > 0) {
+ XColor *color;
+ GC gc;
+
+ color = (tvPtr->flags & TV_FOCUS)
+ ? tvPtr->highlightColor : tvPtr->highlightBgColor;
+ gc = Tk_GCForColor(color, drawable);
+ Tk_DrawFocusHighlight(tvPtr->tkwin, gc, tvPtr->highlightWidth,
+ drawable);
+ }
+ tvPtr->flags &= ~TV_BORDERS;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayTreeView --
+ *
+ * This procedure is invoked to display the widget.
+ *
+ * Recompute the layout of the text if necessary. This is
+ * necessary if the world coordinate system has changed.
+ * Specifically, the following may have occurred:
+ *
+ * 1. a text attribute has changed (font, linespacing, etc.).
+ * 2. an entry's option changed, possibly resizing the entry.
+ *
+ * This is deferred to the display routine since potentially
+ * many of these may occur.
+ *
+ * Set the vertical and horizontal scrollbars. This is done
+ * here since the window width and height are needed for the
+ * scrollbar calculations.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayTreeView(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Blt_ChainLink *linkPtr;
+ TreeView *tvPtr = clientData;
+ TreeViewColumn *columnPtr;
+ int width, height;
+ int x;
+
+ tvPtr->flags &= ~TV_REDRAW;
+ if (tvPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ if (tvPtr->flags & TV_LAYOUT) {
+ /*
+ * Recompute the layout when entries are opened/closed,
+ * inserted/deleted, or when text attributes change (such as
+ * font, linespacing).
+ */
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ if (tvPtr->flags & TV_SCROLL) {
+ /*
+ * Scrolling means that the view port has changed and that the
+ * visible entries need to be recomputed.
+ */
+ ComputeVisibleEntries(tvPtr);
+ Blt_PickCurrentItem(tvPtr->bindTable);
+ Blt_PickCurrentItem(tvPtr->buttonBindTable);
+
+ width = VPORTWIDTH(tvPtr);
+ height = VPORTHEIGHT(tvPtr);
+ if (tvPtr->flags & TV_XSCROLL) {
+ if (tvPtr->xScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(tvPtr->interp, tvPtr->xScrollCmdPrefix,
+ (double)tvPtr->xOffset / tvPtr->worldWidth,
+ (double)(tvPtr->xOffset + width) / tvPtr->worldWidth);
+ }
+ }
+ if (tvPtr->flags & TV_YSCROLL) {
+ if (tvPtr->yScrollCmdPrefix != NULL) {
+ Blt_UpdateScrollbar(tvPtr->interp, tvPtr->yScrollCmdPrefix,
+ (double)tvPtr->yOffset / tvPtr->worldHeight,
+ (double)(tvPtr->yOffset + height) / tvPtr->worldHeight);
+ }
+ }
+ tvPtr->flags &= ~TV_SCROLL;
+ }
+ if (tvPtr->reqWidth == 0) {
+ tvPtr->reqWidth = tvPtr->worldWidth + 2 * tvPtr->inset;
+ Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth,
+ tvPtr->reqHeight);
+ }
+ if (!Tk_IsMapped(tvPtr->tkwin)) {
+ return;
+ }
+ if ((tvPtr->drawable == None) ||
+ (tvPtr->drawWidth != Tk_Width(tvPtr->tkwin)) ||
+ (tvPtr->drawHeight != Tk_Height(tvPtr->tkwin))) {
+ if (tvPtr->drawable != None) {
+ Tk_FreePixmap(tvPtr->display, tvPtr->drawable);
+ }
+ tvPtr->drawWidth = Tk_Width(tvPtr->tkwin);
+ tvPtr->drawHeight = Tk_Height(tvPtr->tkwin);
+ tvPtr->drawable = Tk_GetPixmap(tvPtr->display,
+ Tk_WindowId(tvPtr->tkwin),
+ tvPtr->drawWidth, tvPtr->drawHeight,
+ Tk_Depth(tvPtr->tkwin));
+ tvPtr->flags |= TV_VIEWPORT;
+ }
+
+ if ((tvPtr->flags & TV_RULE_ACTIVE) &&
+ (tvPtr->resizeColumnPtr != NULL)) {
+ Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr,
+ tvPtr->drawable);
+ }
+ Tk_Fill3DRectangle(tvPtr->tkwin, tvPtr->drawable, tvPtr->border,
+ 0, 0, Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin), 0,
+ TK_RELIEF_FLAT);
+
+ if (tvPtr->nVisible > 0) {
+ register TreeViewEntry **p;
+ Tk_3DBorder border;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->flags &= ~COLUMN_DIRTY;
+ if (columnPtr->hidden) {
+ continue;
+ }
+ x = SCREENX(tvPtr, columnPtr->worldX);
+ if ((x + columnPtr->width) < 0) {
+ continue; /* Don't draw columns before the left edge. */
+ }
+ if (x > Tk_Width(tvPtr->tkwin)) {
+ break; /* Discontinue when a column starts beyond
+ * the right edge. */
+ }
+ /* Clear the column background. */
+ border = CHOOSE(tvPtr->border, columnPtr->border);
+ Tk_Fill3DRectangle(tvPtr->tkwin, tvPtr->drawable, border, x, 0,
+ columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
+
+ if (columnPtr != &tvPtr->treeColumn) {
+ for (p = tvPtr->visibleArr; *p != NULL; p++) {
+ DrawValue(tvPtr, columnPtr, *p, tvPtr->drawable);
+ }
+ } else {
+ if (tvPtr->flatView) {
+ DrawFlatView(tvPtr, tvPtr->drawable, x);
+ } else {
+ DrawTreeView(tvPtr, tvPtr->drawable, x);
+ }
+ }
+ if (columnPtr->relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tvPtr->tkwin, tvPtr->drawable, border,
+ x, 0, columnPtr->width, Tk_Height(tvPtr->tkwin),
+ columnPtr->borderWidth, columnPtr->relief);
+ }
+ }
+ }
+
+ if (tvPtr->flags & TV_SHOW_COLUMN_TITLES) {
+ Blt_TreeViewDrawHeadings(tvPtr, tvPtr->drawable);
+ }
+ Blt_TreeViewDrawOuterBorders(tvPtr, tvPtr->drawable);
+ if ((tvPtr->flags & TV_RULE_NEEDED) &&
+ (tvPtr->resizeColumnPtr != NULL)) {
+ Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr,
+ tvPtr->drawable);
+ }
+ /* Now copy the new view to the window. */
+ XCopyArea(tvPtr->display, tvPtr->drawable, Tk_WindowId(tvPtr->tkwin),
+ tvPtr->lineGC, 0, 0, Tk_Width(tvPtr->tkwin),
+ Tk_Height(tvPtr->tkwin), 0, 0);
+ tvPtr->flags &= ~TV_VIEWPORT;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSelectCmdProc --
+ *
+ * Invoked at the next idle point whenever the current
+ * selection changes. Executes some application-specific code
+ * in the -selectcommand option. This provides a way for
+ * applications to handle selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSelectCmdProc(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ TreeView *tvPtr = clientData;
+
+ Tcl_Preserve(tvPtr);
+ if (tvPtr->selectCmd != NULL) {
+ tvPtr->flags &= ~TV_SELECT_PENDING;
+ if (Tcl_GlobalEval(tvPtr->interp, tvPtr->selectCmd) != TCL_OK) {
+ Tcl_BackgroundError(tvPtr->interp);
+ }
+ }
+ Tcl_Release(tvPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeViewObjCmd --
+ *
+ * This procedure is invoked to process the Tcl command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+TreeViewObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST *objv; /* Argument strings. */
+{
+ Tcl_CmdInfo cmdInfo;
+ Tcl_Obj *initObjv[2];
+ TreeView *tvPtr;
+ char *className;
+ char *string;
+
+ string = Tcl_GetString(objv[0]);
+ if (objc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", string,
+ " pathName ?option value?...\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ className = (string[0] == 'h') ? "Hiertable" : "TreeView";
+ tvPtr = CreateTreeView(interp, objv[1], className);
+ if (tvPtr == NULL) {
+ return TCL_ERROR;
+ }
+ tvPtr->cmdToken = Tcl_CreateObjCommand(interp, Tk_PathName(tvPtr->tkwin),
+ Blt_TreeViewWidgetInstCmd, tvPtr, WidgetInstCmdDeleteProc);
+
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tvPtr->tkwin, tvPtr->cmdToken);
+#endif
+ Tk_CreateSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING, SelectionProc,
+ tvPtr, XA_STRING);
+ Tk_CreateEventHandler(tvPtr->tkwin, ExposureMask | StructureNotifyMask |
+ FocusChangeMask, TreeViewEventProc, tvPtr);
+ /*
+ * Invoke a procedure to initialize various bindings on treeview
+ * entries. If the procedure doesn't already exist, source it
+ * from "$blt_library/treeview.tcl". We deferred sourcing the
+ * file until now so that the variable $blt_library could be set
+ * within a script.
+ */
+ if (!Tcl_GetCommandInfo(interp, "blt::tv::Initialize", &cmdInfo)) {
+ char cmd[200];
+ sprintf(cmd, "set className %s\n\
+source [file join $blt_library treeview.tcl]\n\
+unset className\n", className);
+ if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
+ char info[200];
+
+ sprintf(info, "\n (while loading bindings for %.50s)",
+ Tcl_GetString(objv[0]));
+ Tcl_AddErrorInfo(interp, info);
+ goto error;
+ }
+ }
+
+ initObjv[0] = Tcl_NewStringObj("blt::tv::Initialize", -1);
+ initObjv[1] = objv[1];
+ if (Tcl_EvalObjv(interp, 2, initObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
+ goto error;
+ }
+ Tcl_DecrRefCount(initObjv[0]);
+ bltTreeViewImagesOption.clientData = tvPtr;
+ if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, "button", "Button",
+ bltTreeViewButtonSpecs, 0, (Tcl_Obj **)NULL, (char *)tvPtr, 0)
+ != TCL_OK) {
+ goto error;
+ }
+ if (Blt_TreeViewConfigureWidget(interp, tvPtr, objc - 2, objv + 2, 0)
+ != TCL_OK) {
+ goto error;
+ }
+
+ /*
+ * Configure the default column after configuring the widget so
+ * that we can use its global resources (font, color, border,
+ * etc) in building the GCs.
+ */
+ Blt_TreeViewConfigureColumn(tvPtr, &tvPtr->treeColumn);
+
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1));
+ return TCL_OK;
+ error:
+ Tk_DestroyWindow(tvPtr->tkwin);
+ return TCL_ERROR;
+}
+
+int
+Blt_TreeViewInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_ObjCmdSpec cmdSpec[] = {
+ { "treeview", TreeViewObjCmd, },
+ { "hiertable", TreeViewObjCmd, }
+ };
+
+ if (Blt_InitObjCmd(interp, "blt", cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_InitObjCmd(interp, "blt", cmdSpec + 1) == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_RegisterObjType(&entryObjType);
+ return TCL_OK;
+}
+
+#endif /* NO_TREEVIEW */
diff --git a/blt/src/bltTreeView.h b/blt/src/bltTreeView.h
new file mode 100644
index 00000000000..895d3510778
--- /dev/null
+++ b/blt/src/bltTreeView.h
@@ -0,0 +1,1041 @@
+/*
+ * bltTreeView.h --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+
+#ifndef BLT_TREEVIEW_H
+#define BLT_TREEVIEW_H
+
+#include "bltImage.h"
+#include "bltHash.h"
+#include "bltChain.h"
+#include "bltTree.h"
+#include "bltTile.h"
+#include "bltBind.h"
+#include "bltObjConfig.h"
+
+#if HAVE_UTF
+#else
+#define Tcl_NumUtfChars(s,n) (((n) == -1) ? strlen((s)) : (n))
+#define Tcl_UtfAtIndex(s,i) ((s) + (i))
+#endif
+
+#define END (-1)
+#define SEPARATOR_LIST ((char *)NULL)
+#define SEPARATOR_NONE ((char *)-1)
+#define SORT_MARKER_OFFSET 3
+#define SORT_MARKER_WIDTH ((2 * SORT_MARKER_OFFSET) + 1)
+
+#define SEARCH_Y 1
+
+typedef char *UID;
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define UCHAR(c) ((unsigned char) (c))
+
+#define TOGGLE(x, mask) (((x) & (mask)) ? ((x) & ~(mask)) : ((x) | (mask)))
+
+
+#define SCREENX(h, wx) ((wx) - (h)->xOffset + (h)->inset)
+#define SCREENY(h, wy) ((wy) - (h)->yOffset + (h)->inset + (h)->titleHeight)
+
+#define WORLDX(h, sx) ((sx) - (h)->inset + (h)->xOffset)
+#define WORLDY(h, sy) ((sy) - ((h)->inset + (h)->titleHeight) + (h)->yOffset)
+
+#define VPORTWIDTH(h) (Tk_Width((h)->tkwin) - 2 * (h)->inset)
+#define VPORTHEIGHT(h) \
+ (Tk_Height((h)->tkwin) - (h)->titleHeight - 2 * (h)->inset)
+
+#define ICONWIDTH(d) (tvPtr->levelInfo[(d)].iconWidth)
+#define LEVELX(d) (tvPtr->levelInfo[(d)].x)
+
+#define DEPTH(h, n) \
+ (((h)->flatView) ? 0 : Blt_TreeNodeDepth((h)->tree, (n)))
+
+#define SELECT_MODE_SINGLE (1<<0)
+#define SELECT_MODE_MULTIPLE (1<<1)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Internal hierarchy widget flags:
+ *
+ * TV_LAYOUT The layout of the hierarchy needs to be recomputed.
+ *
+ * TV_REDRAW A redraw request is pending for the widget.
+ *
+ * TV_XSCROLL X-scroll request is pending.
+ *
+ * TV_YSCROLL Y-scroll request is pending.
+ *
+ * TV_SCROLL Both X-scroll and Y-scroll requests are pending.
+ *
+ * TV_FOCUS The widget is receiving keyboard events.
+ * Draw the focus highlight border around the widget.
+ *
+ * TV_DIRTY The hierarchy has changed. It may invalidate
+ * the locations and pointers to entries. The widget
+ * will need to recompute its layout.
+ *
+ * TV_BORDERS The borders of the widget (highlight ring and
+ * 3-D border) need to be redrawn.
+ *
+ * TV_VIEWPORT Indicates that the viewport has changed in some
+ * way: the size of the viewport, the location of
+ * the viewport, or the contents of the viewport.
+ *
+ * TV_DESTROYED Indicates that the treeview is in the process
+ * of being destroyed. This lets us speed up the
+ * destruction of entries since we don't need to
+ * check reference counts.
+ *
+ */
+
+#define TV_LAYOUT (1<<0)
+#define TV_REDRAW (1<<1)
+#define TV_XSCROLL (1<<2)
+#define TV_YSCROLL (1<<3)
+#define TV_SCROLL (TV_XSCROLL | TV_YSCROLL)
+#define TV_FOCUS (1<<4)
+#define TV_DIRTY (1<<5)
+#define TV_UPDATE (1<<6)
+#define TV_BORDERS (1<<7)
+#define TV_VIEWPORT (1<<8)
+#define TV_DESTROYED (1<<9)
+#define TV_SORTED (1<<10)
+#define TV_SORT_PENDING (1<<11)
+
+/*
+ * Rule related flags: Rules are XOR-ed lines. We need to track whether
+ * they have been drawn or not.
+ *
+ * TV_RULE_ACTIVE Indicates that a rule is currently being drawn
+ * for a column.
+ *
+ *
+ * TV_RULE_NEEDED Indicates that a rule is needed (but not yet
+ * drawn) for a column.
+ */
+
+#define TV_RULE_ACTIVE (1<<15)
+#define TV_RULE_NEEDED (1<<16)
+
+/*
+ * Selection related flags:
+ *
+ * TV_SELECT_EXPORT Export the selection to X11.
+ *
+ * TV_SELECT_PENDING A "selection" command idle task is pending.
+ *
+ * TV_SELECT_CLEAR Clear selection flag of entry.
+ *
+ * TV_SELECT_SET Set selection flag of entry.
+ *
+ * TV_SELECT_TOGGLE Toggle selection flag of entry.
+ *
+ * TV_SELECT_MASK Mask of selection set/clear/toggle flags.
+ *
+ * TV_SELECT_SORTED Indicates if the entries in the selection
+ * should be sorted or displayed in the order
+ * they were selected.
+ *
+ */
+#define TV_SELECT_CLEAR (1<<16)
+#define TV_SELECT_EXPORT (1<<17)
+#define TV_SELECT_PENDING (1<<18)
+#define TV_SELECT_SET (1<<19)
+#define TV_SELECT_TOGGLE (TV_SELECT_SET | TV_SELECT_CLEAR)
+#define TV_SELECT_MASK (TV_SELECT_SET | TV_SELECT_CLEAR)
+#define TV_SELECT_SORTED (1<<20)
+
+/*
+ * Miscellaneous flags:
+ *
+ * TV_ALLOW_DUPLICATES When inserting new entries, create
+ * duplicate entries.
+ *
+ * TV_FILL_ANCESTORS Automatically create ancestor entries
+ * as needed when inserting a new entry.
+ *
+ * TV_HIDE_ROOT Don't display the root entry.
+ *
+ * TV_HIDE_LEAVES Don't display entries that are leaves.
+ *
+ * TV_SHOW_COLUMN_TITLES Indicates whether to draw titles over each
+ * column.
+ *
+ */
+#define TV_ALLOW_DUPLICATES (1<<21)
+#define TV_FILL_ANCESTORS (1<<22)
+#define TV_HIDE_ROOT (1<<23)
+#define TV_HIDE_LEAVES (1<<24)
+#define TV_SHOW_COLUMN_TITLES (1<<25)
+#define TV_AUTO_SORT (1<<26)
+#define TV_DECREASING (1<<27)
+#define TV_NEW_TAGS (1<<28)
+
+#define TV_ITEM_COLUMN 1
+#define TV_ITEM_RULE 2
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Internal entry flags:
+ *
+ * ENTRY_HAS_BUTTON Indicates that a button needs to be
+ * drawn for this entry.
+ *
+ * ENTRY_CLOSED Indicates that the entry is closed and
+ * its subentries are not displayed.
+ *
+ * ENTRY_HIDDEN Indicates that the entry is hidden (i.e.
+ * can not be viewed by opening or scrolling).
+ *
+ * BUTTON_AUTO
+ * BUTTON_SHOW
+ * BUTTON_MASK
+ *
+ * -------------------------------------------------------------------------
+ */
+#define ENTRY_CLOSED (1<<0)
+#define ENTRY_HIDDEN (1<<1)
+#define ENTRY_NOT_LEAF (1<<2)
+#define ENTRY_MASK (ENTRY_CLOSED | ENTRY_HIDDEN)
+
+#define ENTRY_HAS_BUTTON (1<<3)
+#define ENTRY_ICON (1<<4)
+#define ENTRY_DIRTY (1<<5)
+#define ENTRY_REDRAW (1<<6)
+
+#define BUTTON_AUTO (1<<8)
+#define BUTTON_SHOW (1<<9)
+#define BUTTON_MASK (BUTTON_AUTO | BUTTON_SHOW)
+
+#define COLUMN_RULE_PICKED (1<<1)
+#define COLUMN_DIRTY (1<<2)
+
+#define STYLE_TEXT (0)
+#define STYLE_COMBOBOX (1)
+#define STYLE_CHECKBOX (2)
+
+#define STYLE_LAYOUT (1<<3)
+
+typedef struct TreeViewStruct TreeView;
+typedef struct TreeViewEntryStruct TreeViewEntry;
+typedef struct TreeViewStyleClassStruct TreeViewStyleClass;
+typedef struct TreeViewStyleStruct TreeViewStyle;
+
+typedef int (TreeViewCompareProc) _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ char *pattern));
+
+typedef TreeViewEntry *(TreeViewIterProc) _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, unsigned int mask));
+
+/*
+ * TreeViewImage --
+ *
+ * Since instances of the same Tk image can be displayed in
+ * different windows with possibly different color palettes, Tk
+ * internally stores each instance in a linked list. But if
+ * the instances are used in the same widget and therefore use
+ * the same color palette, this adds a lot of overhead,
+ * especially when deleting instances from the linked list.
+ *
+ * For the treeview widget, we never need more than a single
+ * instance of an image, regardless of how many times it's used.
+ * Cache the image, maintaining a reference count for each
+ * image used in the widget. It's likely that the hierarchy
+ * widget will use many instances of the same image (for example
+ * the open/close icons).
+ */
+
+typedef struct TreeViewImageStruct {
+ Tk_Image tkImage; /* The Tk image being cached. */
+
+ int refCount; /* Reference count for this image. */
+
+ short int width, height; /* Dimensions of the cached image. */
+
+ Blt_HashEntry *hashPtr; /* Hash table pointer to the image. */
+
+} *TreeViewImage;
+
+#define TreeViewImageHeight(image) ((image)->height)
+#define TreeViewImageWidth(image) ((image)->width)
+#define TreeViewImageData(image) ((image)->tkImage)
+
+typedef struct TreeViewEditorStruct TreeViewEditor;
+typedef struct TreeViewColumnStruct TreeViewColumn;
+
+typedef struct {
+ int type; /* Always TV_RULE */
+ int lineWidth;
+ Blt_Dashes dashes;
+ GC gc;
+ TreeViewColumn *columnPtr;
+} Rule;
+
+/*
+ * TreeViewColumn --
+ *
+ * A column describes how to display a field of data in the tree.
+ * It may display a title that you can bind to. It may display a
+ * rule for resizing the column. Columns may be hidden, and have
+ * attributes (foreground color, background color, font, etc)
+ * that override those designated globally for the treeview
+ * widget.
+ */
+struct TreeViewColumnStruct {
+ int type; /* Always TV_COLUMN */
+ Blt_TreeKey key; /* Data cell identifier for current tree. */
+ int position; /* Position of column in list. Used
+ * to indicate the first and last
+ * columns. */
+ UID tagsUid; /* List of binding tags for this
+ * entry. UID, not a string, because
+ * in the typical case most columns
+ * will have the same bindtags. */
+
+ TreeView *tvPtr;
+
+ /* Title-related information */
+ char *text; /* Text displayed in column heading as its
+ * title. By default, this is the same as
+ * the data cell name. */
+ Tk_Font titleFont; /* Font to draw title in. */
+ Shadow titleShadow;
+
+ XColor *titleFgColor; /* Foreground color of text displayed in
+ * the heading */
+ Tk_3DBorder titleBorder; /* Background color of the column's heading. */
+
+ GC titleGC;
+
+ XColor *activeTitleFgColor; /* Foreground color of text heading when
+ * the column is activated.*/
+ Tk_3DBorder activeTitleBorder;
+
+ GC activeTitleGC;
+
+ TextLayout *textPtr;
+ short int titleWidth, titleHeight;
+
+ TreeViewImage image; /* Image displayed in column heading */
+ char *command; /* Tcl command to be executed by the
+ * column's "invoke" operation. */
+
+ char *sortCmd; /* Command to sort column by */
+
+ /* General information */
+ int hidden; /* Indicates if the column is displayed */
+ int state; /* Indicates if column title can invoked. */
+ int editable; /* Indicates if column can be edited. */
+
+ int max; /* Maximum space allowed for column. */
+ int reqMin, reqMax; /* Minimum width of column. Does not include
+ * any padding or the borderwidth of column.
+ * Overrides the computed width. */
+
+ int reqWidth; /* Requested width of column. Does not include
+ * any padding or the borderwidth of column.
+ * Overrides computed width. */
+
+ int maxWidth; /* Width of the widest entry in the column. */
+
+ int worldX; /* Starting x-coordinate of column */
+
+ double weight; /* Growth factor for column. Zero indicates
+ * that the column can not be resized. */
+
+ int width; /* Computed width of column. */
+
+ Tk_3DBorder border; /* Background color of column. */
+ int borderWidth; /* Specifies the border width of the column. */
+ int relief; /* Relief of the column. */
+ Blt_Pad pad; /* Specifies horizontal padding on either
+ * side of the column. */
+
+ Tk_Font font; /* Font used for entries in the column. */
+ XColor *fgColor; /* Foreground color used for entries. */
+ GC gc;
+ Tk_Justify justify; /* Specifies how the text or image is
+ * justified within the column. */
+
+ Blt_ChainLink *linkPtr;
+
+ Rule rule;
+ unsigned int flags;
+};
+
+
+struct TreeViewStyleStruct {
+ int refCount; /* Usage reference count. A reference
+ * count of zero indicates that the
+ * style may be freed. */
+ unsigned int flags; /* Bit field containing both the style
+ * type and various flags. */
+ char *name; /* Instance name. */
+ TreeViewStyleClass *classPtr;
+ /* Contains class-specific information such
+ * as configuration specifications and
+ * configure, draw, etc. routines. */
+ Blt_HashEntry *hashPtr; /* If non-NULL, points to the hash
+ * table entry for the style. A style
+ * that's been deleted, but still in
+ * use (non-zero reference count) will
+ * have no hash table entry.
+ */
+};
+
+typedef struct TreeViewValueStruct {
+ TreeViewColumn *columnPtr; /* Column in which the value is located. */
+ short int width, height; /* Dimensions of value. */
+ TreeViewImage image; /* If non-NULL, is a Tk_Image to be drawn
+ * in the column entry. */
+ TreeViewStyle *stylePtr; /* Style information for cell
+ * displaying value. */
+ TextLayout *textPtr;
+ struct TreeViewValueStruct *nextPtr;
+} TreeViewValue;
+
+typedef TreeViewStyle *(StyleCreateProc) _ANSI_ARGS_((TreeView *tvPtr));
+typedef void (StyleFreeProc) _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewStyle *stylePtr));
+typedef void (StyleDrawProc) _ANSI_ARGS_((TreeView *tvPtr, Drawable drawable,
+ TreeViewEntry *entryPtr, TreeViewStyle *stylePtr,
+ TreeViewValue *valuePtr, int x, int y));
+typedef void (StyleConfigProc) _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewStyle *stylePtr));
+
+struct TreeViewStyleClassStruct {
+ Blt_ConfigSpec *specsPtr; /* Style configuration specifications */
+ StyleConfigProc *configProc;
+ StyleDrawProc *drawProc;
+ StyleFreeProc *freeProc;
+} ;
+
+/*
+ * TreeViewEntry --
+ *
+ * Contains data-specific information how to represent the data
+ * of a node of the hierarchy.
+ *
+ */
+struct TreeViewEntryStruct {
+ Blt_TreeNode node; /* Node containing entry */
+ int worldX, worldY; /* X-Y position in world coordinates
+ * where the entry is positioned. */
+
+ short int width, height; /* Dimensions of the entry. This includes
+ * the size of its columns. */
+
+ int reqHeight; /* Requested height of the entry.
+ * Overrides computed height. */
+
+ int lineHeight; /* Length of the vertical line segment. */
+
+ unsigned int flags; /* Flags for this entry. For the
+ * definitions of the various bit
+ * fields see below. */
+
+ UID tagsUid; /* List of binding tags for this
+ * entry. UID, not a string, because
+ * in the typical case most entries
+ * will have the same bindtags. */
+ TreeView *tvPtr;
+
+ UID openCmd, closeCmd; /* Tcl commands to invoke when entries
+ * are opened or closed. They override
+ * those specified globally. */
+ /*
+ * Button information:
+ */
+ short int buttonX;
+ short int buttonY; /* X-Y coordinate offsets from to
+ * upper left corner of the entry to
+ * the upper-left corner of the
+ * button. Used to pick the
+ * button quickly */
+
+ TreeViewImage *icons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+
+ TreeViewImage *activeIcons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+
+ short int iconWidth;
+ short int iconHeight; /* Maximum dimensions for icons and
+ * buttons for this entry. This is
+ * used to align the button, icon, and
+ * text. */
+ /*
+ * Label information:
+ */
+ TextLayout *textPtr;
+
+ short int labelWidth;
+
+ short int labelHeight;
+
+ UID labelUid; /* Text displayed right of the icon. */
+
+ Tk_Font font; /* Font of label. Overrides global font
+ * specification. */
+ char *fullName;
+
+ int flatIndex;
+
+ ClientData data; /* pre-fetched data for sorting */
+
+ XColor *color; /* Color of label. Overrides default
+ * text color specification. */
+ GC gc;
+
+ Shadow shadow;
+
+ TreeViewValue *values; /* List of column-related information
+ * for each data value in the node.
+ * Non-NULL only if there are value
+ * entries. */
+};
+
+/*
+ * TreeViewButton --
+ *
+ * A button is the open/close indicator at the far left of the
+ * entry. It is displayed as a plus or minus in a solid
+ * colored box with optionally an border. It has both "active"
+ * and "normal" colors.
+ */
+typedef struct {
+ XColor *fgColor; /* Foreground color. */
+
+ Tk_3DBorder border; /* Background color. */
+
+ XColor *activeFgColor; /* Active foreground color. */
+
+ Tk_3DBorder activeBorder; /* Active background color. */
+
+ GC lineGC, normalGC, activeGC;
+
+ int reqSize;
+
+ int borderWidth;
+
+ int openRelief, closeRelief;
+
+ int width, height;
+
+ TreeViewImage *images;
+
+} TreeViewButton;
+
+/*
+ * LevelInfo --
+ *
+ */
+typedef struct {
+ int x;
+ int iconWidth;
+ int labelWidth;
+} LevelInfo;
+
+/*
+ * SortInfo --
+ *
+ * Bookkeeping for sorting the tree. Sorting can occur
+ * automatically as new entries are added to the widget, so
+ * we need to retain this information.
+ */
+typedef struct {
+ int manual; /* Indicates if manual or automatic sorting
+ * is performed. */
+
+ char *field; /* Field to be sorted. */
+
+ int mode; /* Type of sorting to be performed. See
+ * below for valid values. */
+
+ char *command; /* Sort command. */
+
+ int decreasing; /* Indicates entries should be sorted
+ * in decreasing order. */
+
+ TreeViewColumn *columnPtr; /* Column to use for sorting criteria. */
+
+} SortInfo;
+
+/*
+ * TreeView --
+ *
+ * A TreeView is a widget that displays an hierarchical table
+ * of one or more entries.
+ *
+ * Entries are positioned in "world" coordinates, referring to
+ * the virtual treeview. Coordinate 0,0 is the upper-left corner
+ * of the root entry and the bottom is the end of the last entry.
+ * The widget's Tk window acts as view port into this virtual
+ * space. The treeview's xOffset and yOffset fields specify the
+ * location of the view port in the virtual world. Scrolling the
+ * viewport is therefore simply changing the xOffset and/or
+ * yOffset fields and redrawing.
+ *
+ * Note that world coordinates are integers, not signed short
+ * integers like X11 screen coordinates. It's very easy to
+ * create a hierarchy taller than 0x7FFF pixels.
+ */
+struct TreeViewStruct {
+ Tcl_Interp *interp;
+
+ Tcl_Command cmdToken; /* Token for widget's Tcl command. */
+
+ Blt_Tree tree; /* Token holding internal tree. */
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_TreeTagTable *tagTablePtr;
+
+ /* TreeView specific fields. */
+
+ Tk_Window tkwin; /* Window that embodies the widget.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up.*/
+
+ Display *display; /* Display containing widget; needed,
+ * among other things, to release
+ * resources after tkwin has already
+ * gone away. */
+
+ Blt_HashTable columnTable; /* Table of column information. */
+ Blt_Chain *colChainPtr; /* Chain of columns. Same as the hash
+ * table above but maintains the order
+ * in which columns are displayed. */
+
+ unsigned int flags; /* For bitfield definitions, see below */
+
+ int inset; /* Total width of all borders,
+ * including traversal highlight and
+ * 3-D border. Indicates how much
+ * interior stuff must be offset from
+ * outside edges to leave room for
+ * borders. */
+
+ Tk_3DBorder border; /* 3D border surrounding the window
+ * (viewport). */
+
+ int borderWidth; /* Width of 3D border. */
+
+ int relief; /* 3D border relief. */
+
+
+ int highlightWidth; /* Width in pixels of highlight to
+ * draw around widget when it has the
+ * focus. <= 0 means don't draw a
+ * highlight. */
+
+ XColor *highlightBgColor; /* Color for drawing traversal
+ * highlight area when highlight is
+ * off. */
+
+ XColor *highlightColor; /* Color for drawing traversal highlight. */
+
+ char *pathSep; /* Pathname separators */
+
+ char *trimLeft; /* Leading characters to trim from
+ * pathnames */
+
+ /*
+ * Entries are connected by horizontal and vertical lines. They
+ * may be drawn dashed or solid.
+ */
+ int lineWidth; /* Width of lines connecting entries */
+
+ int dashes; /* Dash on-off value. */
+
+ XColor *lineColor; /* Color of connecting lines. */
+
+ /*
+ * Button Information:
+ *
+ * The button is the open/close indicator at the far left of the
+ * entry. It is usually displayed as a plus or minus in a solid
+ * colored box with optionally an border. It has both "active" and
+ * "normal" colors.
+ */
+ TreeViewButton button;
+
+ /*
+ * Selection Information:
+ *
+ * The selection is the rectangle that contains a selected entry.
+ * There may be many selected entries. It is displayed as a solid
+ * colored box with optionally a 3D border.
+ */
+ Tk_3DBorder selBorder; /* Background color of an highlighted
+ * entry.*/
+
+ int selRelief; /* Relief of selected items. Currently
+ * is always raised. */
+
+ int selBorderWidth; /* Border width of a selected entry.*/
+
+ XColor *selFgColor; /* Text color of a selected entry. */
+
+ TreeViewEntry *selAnchorPtr; /* Fixed end of selection (i.e. entry
+ * at which selection was started.) */
+ TreeViewEntry *selMarkPtr;
+
+ int selectMode; /* Selection style: "single" or
+ * "multiple". */
+
+ char *selectCmd; /* Tcl script that's invoked whenever
+ * the selection changes. */
+
+ Blt_HashTable selectTable; /* Hash table of currently selected
+ * entries. */
+
+ Blt_Chain *selChainPtr; /* Chain of currently selected
+ * entries. Contains the same
+ * information as the above hash
+ * table, but maintains the order in
+ * which entries are selected.
+ */
+
+ int leader; /* Number of pixels padding between
+ * entries. */
+
+ Tk_Cursor cursor; /* X Cursor */
+
+ Tk_Cursor resizeCursor; /* Resize Cursor */
+
+ int reqWidth, reqHeight; /* Requested dimensions of the
+ * treeview widget's window. */
+
+ GC lineGC; /* GC for drawing dotted line between
+ * entries. */
+
+ XColor *activeFgColor;
+
+ Tk_3DBorder activeBorder;
+
+ XColor *focusColor;
+
+ Blt_Dashes focusDashes; /* Dash on-off value. */
+
+ GC focusGC; /* Graphics context for the active
+ * label. */
+
+ TreeViewEditor *editPtr;
+
+ TreeViewEntry *activePtr; /* Last active entry. */
+
+ TreeViewEntry *focusPtr; /* Entry that currently has focus. */
+
+ TreeViewEntry *activeButtonPtr; /* Pointer to last active button */
+
+ TreeViewEntry *fromPtr;
+
+ int xScrollUnits, yScrollUnits; /* # of pixels per scroll unit. */
+
+ /* Command strings to control horizontal and vertical
+ * scrollbars. */
+ char *xScrollCmdPrefix, *yScrollCmdPrefix;
+
+ int scrollMode; /* Selects mode of scrolling: either
+ * BLT_SCROLL_MODE_HIERBOX,
+ * BLT_SCROLL_MODE_LISTBOX,
+ * or BLT_SCROLL_MODE_CANVAS. */
+ /*
+ * Total size of all "open" entries. This represents the range of
+ * world coordinates.
+ */
+ int worldWidth, worldHeight;
+
+ int xOffset, yOffset; /* Translation between view port and
+ * world origin. */
+
+ short int minHeight; /* Minimum entry height. Used to to
+ * compute what the y-scroll unit
+ * should be. */
+ short int titleHeight; /* Height of column titles. */
+
+ LevelInfo *levelInfo;
+
+ /*
+ * Scanning information:
+ */
+ int scanAnchorX, scanAnchorY;
+ /* Scan anchor in screen coordinates. */
+ int scanX, scanY; /* X-Y world coordinate where the scan
+ * started. */
+
+ Blt_HashTable imageTable; /* Table of Tk images */
+
+ Blt_HashTable uidTable; /* Table of strings. */
+
+ Blt_HashTable styleTable; /* Table of cell styles. */
+
+ TreeViewEntry *rootPtr; /* Root entry of tree. */
+
+ TreeViewEntry **visibleArr; /* Array of visible entries */
+
+ int nVisible; /* Number of entries in the above array */
+
+ int nEntries; /* Number of entries in tree. */
+
+ int nextSerial;
+
+ int buttonFlags; /* Global button indicator for all
+ * entries. This may be overridden by
+ * the entry's -button option. */
+
+ char *openCmd, *closeCmd; /* Tcl commands to invoke when entries
+ * are opened or closed. */
+
+ TreeViewImage *icons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+ TreeViewImage *activeIcons; /* Tk images displayed for the entry.
+ * The first image is the icon
+ * displayed to the left of the
+ * entry's label. The second is icon
+ * displayed when entry is "open". */
+ char *takeFocus;
+
+ ClientData clientData;
+
+ Blt_BindTable bindTable; /* Binding information for entries. */
+
+ Blt_BindTable buttonBindTable; /* Binding information for buttons. */
+
+ Blt_BindTable columnBindTable; /* Binding information for columns. */
+
+ TreeViewColumn treeColumn;
+
+ TreeViewColumn *activeColumnPtr; /* Column title currently active. */
+
+ TreeViewColumn *resizeColumnPtr; /* Column that is being resized. */
+
+ int depth;
+
+ int flatView; /* Indicates if the view of the tree
+ * has been flattened. */
+
+ TreeViewEntry **flatArr; /* Flattened array of entries. */
+
+ char *sortField; /* Field to be sorted. */
+
+ int sortType; /* Type of sorting to be performed. See
+ * below for valid values. */
+
+ char *sortCmd; /* Sort command. */
+
+ int sortIsDecreasing; /* Indicates entries should be sorted
+ * in decreasing order. */
+
+ TreeViewColumn *sortColumnPtr;/* Column to use for sorting criteria. */
+
+ Pixmap drawable; /* Pixmap used to cache the entries
+ * displayed. The pixmap is saved so
+ * that only selected elements can be
+ * drawn quicky. */
+
+ short int drawWidth, drawHeight;
+
+ short int ruleAnchor, ruleMark;
+
+ Blt_Pool entryPool;
+ Blt_Pool valuePool;
+};
+
+
+extern UID Blt_TreeViewGetUid _ANSI_ARGS_((TreeView *tvPtr, char *string));
+extern void Blt_TreeViewFreeUid _ANSI_ARGS_((TreeView *tvPtr, UID uid));
+
+extern void Blt_TreeViewEventuallyRedraw _ANSI_ARGS_((TreeView *tvPtr));
+extern Tcl_ObjCmdProc Blt_TreeViewWidgetInstCmd;
+extern TreeViewEntry *Blt_TreeViewNearestEntry _ANSI_ARGS_((TreeView *tvPtr,
+ int x, int y, int flags));
+extern char *Blt_TreeViewGetFullName _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, int checkEntryLabel, Tcl_DString *dsPtr));
+extern void Blt_TreeViewSelectCmdProc _ANSI_ARGS_((ClientData clientData));
+extern void Blt_TreeViewInsertText _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, char *string, int extra, int insertPos));
+extern void Blt_TreeViewComputeLayout _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewPercentSubst _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, char *command, Tcl_DString *resultPtr));
+extern void Blt_TreeViewDrawButton _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, Drawable drawable));
+extern void Blt_TreeViewDrawOuterBorders _ANSI_ARGS_((TreeView *tvPtr,
+ Drawable drawable));
+extern int Blt_TreeViewDrawIcon _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, int x, int y, Drawable drawable));
+extern void Blt_TreeViewDrawHeadings _ANSI_ARGS_((TreeView *tvPtr,
+ Drawable drawable));
+extern void Blt_TreeViewDrawRule _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewColumn *columnPtr, Drawable drawable));
+
+extern void Blt_TreeViewConfigureButtons _ANSI_ARGS_((TreeView *tvPtr));
+extern int Blt_TreeViewConfigureWidget _ANSI_ARGS_((Tcl_Interp *interp,
+ TreeView *tvPtr, int objc, Tcl_Obj *CONST *objv, int flags));
+extern int Blt_TreeViewScreenToIndex _ANSI_ARGS_((TreeView *tvPtr,
+ int x, int y));
+
+extern void Blt_TreeViewFreeImage _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewImage image));
+extern TreeViewImage Blt_TreeViewGetImage _ANSI_ARGS_((TreeView *tvPtr,
+ char *imageName));
+extern void Blt_TreeViewAddValue _ANSI_ARGS_((TreeViewEntry *entryPtr,
+ TreeViewColumn *columnPtr));
+extern int Blt_TreeViewInitColumn _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewColumn *columnPtr, char *name, char *defaultLabel, int objc,
+ Tcl_Obj *CONST *objv));
+extern void Blt_TreeViewDestroyValue _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewValue *valuePtr));
+extern void Blt_TreeViewDestroyColumns _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewAllocateColumnUids _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewFreeColumnUids _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewConfigureColumn _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewColumn *columnPtr));
+extern TreeViewColumn *Blt_TreeViewNearestColumn _ANSI_ARGS_((TreeView *tvPtr,
+ int x, int y, int flags));
+
+extern void Blt_TreeViewDrawRule _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewColumn *columnPtr, Drawable drawable));
+extern int Blt_TreeViewTextOp _ANSI_ARGS_((TreeView *tvPtr, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST *objv));
+extern TreeViewEditor *Blt_TreeViewCreateEditor _ANSI_ARGS_((TreeView *tvPtr,
+ char *className));
+extern Tcl_Obj *Blt_TreeViewGetData _ANSI_ARGS_((TreeViewEntry *entryPtr,
+ Blt_TreeKey key));
+
+extern int Blt_TreeViewCreateEntry _ANSI_ARGS_((TreeView *tvPtr,
+ Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv));
+extern void Blt_TreeViewConfigureEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern int Blt_TreeViewOpenEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern int Blt_TreeViewCloseEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewNextEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewPrevEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewParentEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern int Blt_TreeViewGetEntry _ANSI_ARGS_((TreeView *tvPtr, Tcl_Obj *objPtr,
+ TreeViewEntry **entryPtrPtr));
+extern int Blt_TreeViewEntryIsMapped _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern int Blt_TreeViewEntryIsHidden _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewNextSibling _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewPrevSibling _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewFirstChild _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *parentPtr));
+extern TreeViewEntry *Blt_TreeViewLastChild _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewParentEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+
+typedef int (TreeViewApplyProc) _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+
+extern int Blt_TreeViewApply _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr, TreeViewApplyProc *proc, unsigned int mask));
+
+extern int Blt_TreeViewColumnOp _ANSI_ARGS_((TreeView *tvPtr,
+ Tcl_Interp *interp, int objc, Tcl_Obj *CONST *objv));
+extern int Blt_TreeViewSortOp _ANSI_ARGS_((TreeView *tvPtr, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST *objv));
+
+extern void Blt_TreeViewSortFlatView _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewSortTreeView _ANSI_ARGS_((TreeView *tvPtr));
+
+extern int Blt_TreeViewEntryIsSelected _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern void Blt_TreeViewSelectEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern void Blt_TreeViewDeselectEntry _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern void Blt_TreeViewPruneSelection _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern void Blt_TreeViewClearSelection _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewClearTags _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+extern void Blt_TreeViewGetTags _ANSI_ARGS_((Tcl_Interp *interp,
+ TreeView *tvPtr, TreeViewEntry *entryPtr, Blt_List list));
+extern void Blt_TreeViewTraceColumn _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewColumn *columnPtr));
+extern TreeViewImage Blt_TreeViewIconImage _ANSI_ARGS_((TreeView *tvPtr,
+ TreeViewEntry *entryPtr));
+#define CHOOSE(default, override) \
+ (((override) == NULL) ? (default) : (override))
+
+#define GETLABEL(e) \
+ (((e)->labelUid != NULL) ? (e)->labelUid : Blt_TreeNodeLabel((e)->node))
+
+static INLINE TreeViewEntry *
+NodeToEntry(tvPtr, node)
+ TreeView *tvPtr;
+ Blt_TreeNode node;
+{
+ Tcl_Obj *objPtr;
+
+ if (Blt_TreeGetValueByKey(tvPtr->interp, tvPtr->tree, node,
+ tvPtr->treeColumn.key, &objPtr) != TCL_OK) {
+ abort();
+ return NULL;
+ }
+ return (TreeViewEntry *)objPtr->internalRep.otherValuePtr;
+}
+
+#endif /* BLT_TREEVIEW_H */
diff --git a/blt/src/bltTreeViewCmd.c b/blt/src/bltTreeViewCmd.c
new file mode 100644
index 00000000000..0bbf15b0210
--- /dev/null
+++ b/blt/src/bltTreeViewCmd.c
@@ -0,0 +1,5206 @@
+/*
+ * bltTreeViewCmd.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include "bltList.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#ifdef __STDC__
+static TreeViewCompareProc ExactCompare, GlobCompare, RegexpCompare;
+static TreeViewApplyProc ShowEntryApplyProc, HideEntryApplyProc,
+ MapAncestorsApplyProc, FixSelectionsApplyProc;
+static Tk_LostSelProc LostSelection;
+static TreeViewApplyProc SelectEntryApplyProc;
+#endif /* __STDC__ */
+
+extern Blt_CustomOption bltTreeViewImagesOption;
+extern Blt_CustomOption bltTreeViewUidOption;
+
+extern Blt_ConfigSpec bltTreeViewButtonSpecs[];
+extern Blt_ConfigSpec bltTreeViewSpecs[];
+extern Blt_ConfigSpec bltTreeViewEntrySpecs[];
+
+#define TAG_UNKNOWN (1<<0)
+#define TAG_RESERVED (1<<1)
+#define TAG_USER_DEFINED (1<<2)
+
+#define TAG_SINGLE (1<<3)
+#define TAG_MULTIPLE (1<<4)
+#define TAG_ALL (1<<5)
+
+typedef struct {
+ int tagType;
+ TreeView *tvPtr;
+ Blt_HashSearch cursor;
+ TreeViewEntry *entryPtr;
+} TagInfo;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SkipSeparators --
+ *
+ * Moves the character pointer past one of more separators.
+ *
+ * Results:
+ * Returns the updates character pointer.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+SkipSeparators(path, separator, length)
+ char *path, *separator;
+ int length;
+{
+ while ((path[0] == separator[0]) &&
+ (strncmp(path, separator, length) == 0)) {
+ path += length;
+ }
+ return path;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteNode --
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DeleteNode(tvPtr, node)
+ TreeView *tvPtr;
+ Blt_TreeNode node;
+{
+ Blt_TreeNode root;
+
+ Blt_TreeClearTags(tvPtr->tagTablePtr, node);
+ root = Blt_TreeRootNode(tvPtr->tree);
+ if (node == root) {
+ Blt_TreeNode next;
+ /* Don't delete the root node. Simply clean out the tree. */
+ for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
+ next = Blt_TreeNextSibling(node);
+ Blt_TreeDeleteNode(tvPtr->tree, node);
+ }
+ } else if (Blt_TreeIsAncestor(root, node)) {
+ Blt_TreeDeleteNode(tvPtr->tree, node);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SplitPath --
+ *
+ * Returns the trailing component of the given path. Trailing
+ * separators are ignored.
+ *
+ * Results:
+ * Returns the string of the tail component.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SplitPath(tvPtr, path, depthPtr, compPtrPtr)
+ TreeView *tvPtr;
+ char *path;
+ int *depthPtr;
+ char ***compPtrPtr;
+{
+ int skipLen, pathLen;
+ int depth, listSize;
+ char **components;
+ register char *p;
+ char *sep;
+
+ if (tvPtr->pathSep == SEPARATOR_LIST) {
+ if (Tcl_SplitList(tvPtr->interp, path, depthPtr, compPtrPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+ pathLen = strlen(path);
+
+ skipLen = strlen(tvPtr->pathSep);
+ path = SkipSeparators(path, tvPtr->pathSep, skipLen);
+ depth = pathLen / skipLen;
+
+ listSize = (depth + 1) * sizeof(char *);
+ components = Blt_Malloc(listSize + (pathLen + 1));
+ assert(components);
+ p = (char *)components + listSize;
+ strcpy(p, path);
+
+ sep = strstr(p, tvPtr->pathSep);
+ depth = 0;
+ while ((*p != '\0') && (sep != NULL)) {
+ *sep = '\0';
+ components[depth++] = p;
+ p = SkipSeparators(sep + skipLen, tvPtr->pathSep, skipLen);
+ sep = strstr(p, tvPtr->pathSep);
+ }
+ if (*p != '\0') {
+ components[depth++] = p;
+ }
+ components[depth] = NULL;
+ *depthPtr = depth;
+ *compPtrPtr = components;
+ return TCL_OK;
+}
+
+
+static TreeViewEntry *
+LastEntry(tvPtr, entryPtr, mask)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ unsigned int mask;
+{
+ Blt_TreeNode next;
+ TreeViewEntry *nextPtr;
+
+ next = Blt_TreeLastChild(entryPtr->node);
+ while (next != NULL) {
+ nextPtr = NodeToEntry(tvPtr, next);
+ if ((nextPtr->flags & mask) != mask) {
+ break;
+ }
+ entryPtr = nextPtr;
+ next = Blt_TreeLastChild(next);
+ }
+ return entryPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowEntryApplyProc --
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ShowEntryApplyProc(tvPtr, entryPtr)
+ TreeView *tvPtr; /* Not used. */
+ TreeViewEntry *entryPtr;
+{
+ entryPtr->flags &= ~ENTRY_HIDDEN;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideEntryApplyProc --
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+HideEntryApplyProc(tvPtr, entryPtr)
+ TreeView *tvPtr; /* Not used. */
+ TreeViewEntry *entryPtr;
+{
+ entryPtr->flags |= ENTRY_HIDDEN;
+ return TCL_OK;
+}
+
+static void
+MapAncestors(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ while (entryPtr != tvPtr->rootPtr) {
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
+ tvPtr->flags |= TV_LAYOUT;
+ entryPtr->flags &= ~(ENTRY_CLOSED | ENTRY_HIDDEN);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapAncestorsApplyProc --
+ *
+ * If a node in mapped, then all its ancestors must be mapped also.
+ * This routine traverses upwards and maps each unmapped ancestor.
+ * It's assumed that for any mapped ancestor, all it's ancestors
+ * will already be mapped too.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+MapAncestorsApplyProc(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ /*
+ * Make sure that all the ancestors of this entry are mapped too.
+ */
+ while (entryPtr != tvPtr->rootPtr) {
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ if ((entryPtr->flags & (ENTRY_HIDDEN | ENTRY_CLOSED)) == 0) {
+ break; /* Assume ancestors are also mapped. */
+ }
+ entryPtr->flags &= ~(ENTRY_HIDDEN | ENTRY_CLOSED);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindPath --
+ *
+ * Finds the node designated by the given path. Each path
+ * component is searched for as the tree is traversed.
+ *
+ * A leading character string is trimmed off the path if it
+ * matches the one designated (see the -trimleft option).
+ *
+ * If no separator is designated (see the -separator
+ * configuration option), the path is considered a Tcl list.
+ * Otherwise the each component of the path is separated by a
+ * character string. Leading and trailing separators are
+ * ignored. Multiple separators are treated as one.
+ *
+ * Results:
+ * Returns the pointer to the designated node. If any component
+ * can't be found, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeViewEntry *
+FindPath(tvPtr, rootPtr, path)
+ TreeView *tvPtr;
+ TreeViewEntry *rootPtr;
+ char *path;
+{
+ Blt_TreeNode child;
+ char **compArr;
+ char *name;
+ int nComp;
+ register char **p;
+ TreeViewEntry *entryPtr;
+
+ /* Trim off characters that we don't want */
+ if (tvPtr->trimLeft != NULL) {
+ register char *s1, *s2;
+
+ /* Trim off leading character string if one exists. */
+ for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+ if (*s1 != *s2) {
+ break;
+ }
+ }
+ if (*s2 == '\0') {
+ path = s1;
+ }
+ }
+ if (*path == '\0') {
+ return rootPtr;
+ }
+ name = path;
+ entryPtr = rootPtr;
+ if (tvPtr->pathSep == SEPARATOR_NONE) {
+ child = Blt_TreeFindChild(entryPtr->node, name);
+ if (child == NULL) {
+ goto error;
+ }
+ return NodeToEntry(tvPtr, child);
+ }
+
+ if (SplitPath(tvPtr, path, &nComp, &compArr) != TCL_OK) {
+ return NULL;
+ }
+ for (p = compArr; *p != NULL; p++) {
+ name = *p;
+ child = Blt_TreeFindChild(entryPtr->node, name);
+ if (child == NULL) {
+ Blt_Free(compArr);
+ goto error;
+ }
+ entryPtr = NodeToEntry(tvPtr, child);
+ }
+ Blt_Free(compArr);
+ return entryPtr;
+ error:
+ {
+ Tcl_DString dString;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
+ Tcl_AppendResult(tvPtr->interp, "can't find node \"", name,
+ "\" in parent node \"", Tcl_DStringValue(&dString), "\"",
+ (char *)NULL);
+ Tcl_DStringFree(&dString);
+ }
+ return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NodeToObj --
+ *
+ * Converts a node pointer to a string representation.
+ * The string contains the node's index which is unique.
+ *
+ * Results:
+ * The string representation of the node is returned. Note that
+ * the string is stored statically, so that callers must save the
+ * string before the next call to this routine overwrites the
+ * static array again.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tcl_Obj *
+NodeToObj(node)
+ Blt_TreeNode node;
+{
+ char string[200];
+
+ sprintf(string, "%d", Blt_TreeNodeId(node));
+ return Tcl_NewStringObj(string, -1);
+}
+
+
+static int
+GetEntryFromSpecialId(tvPtr, string, entryPtrPtr)
+ TreeView *tvPtr;
+ char *string;
+ TreeViewEntry **entryPtrPtr;
+{
+ Blt_TreeNode node;
+ TreeViewEntry *fromPtr, *entryPtr;
+ char c;
+
+ entryPtr = NULL;
+ fromPtr = tvPtr->fromPtr;
+ if (fromPtr == NULL) {
+ fromPtr = tvPtr->focusPtr;
+ }
+ if (fromPtr == NULL) {
+ fromPtr = tvPtr->rootPtr;
+ }
+ c = string[0];
+ if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(tvPtr->interp, tvPtr->tkwin, string, &x, &y) == TCL_OK) {
+ *entryPtrPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
+ }
+ } else if ((c == 'b') && (strcmp(string, "bottom") == 0)) {
+ if (tvPtr->flatView) {
+ entryPtr = tvPtr->flatArr[tvPtr->nEntries - 1];
+ } else {
+ entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+ }
+ } else if ((c == 't') && (strcmp(string, "top") == 0)) {
+ if (tvPtr->flatView) {
+ entryPtr = tvPtr->flatArr[0];
+ } else {
+ entryPtr = tvPtr->rootPtr;
+ if (tvPtr->flags & TV_HIDE_ROOT) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, ENTRY_MASK);
+ }
+ }
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+ } else if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ entryPtr = tvPtr->selAnchorPtr;
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ entryPtr = tvPtr->focusPtr;
+ if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, tvPtr->rootPtr,
+ ENTRY_MASK);
+ }
+ } else if ((c == 'r') && (strcmp(string, "root") == 0)) {
+ entryPtr = tvPtr->rootPtr;
+ } else if ((c == 'p') && (strcmp(string, "parent") == 0)) {
+ if (fromPtr != tvPtr->rootPtr) {
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, fromPtr);
+ }
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ /* Can't trust picked item, if entries have been
+ * added or deleted. */
+ if (!(tvPtr->flags & TV_DIRTY)) {
+ entryPtr = Blt_GetCurrentItem(tvPtr->bindTable);
+ if (entryPtr == NULL) {
+ entryPtr = Blt_GetCurrentItem(tvPtr->buttonBindTable);
+ }
+ }
+ } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+ entryPtr = fromPtr;
+ if (tvPtr->flatView) {
+ int i;
+
+ i = entryPtr->flatIndex - 1;
+ if (i >= 0) {
+ entryPtr = tvPtr->flatArr[i];
+ }
+ } else {
+ entryPtr = Blt_TreeViewPrevEntry(tvPtr, fromPtr, ENTRY_MASK);
+ if (entryPtr == NULL) {
+ entryPtr = fromPtr;
+ }
+ if ((entryPtr == tvPtr->rootPtr) &&
+ (tvPtr->flags & TV_HIDE_ROOT)) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr,
+ ENTRY_MASK);
+ }
+ }
+ } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+ entryPtr = fromPtr;
+ if (tvPtr->flatView) {
+ int i;
+
+ i = entryPtr->flatIndex + 1;
+ if (i < tvPtr->nEntries) {
+ entryPtr = tvPtr->flatArr[i];
+ }
+ } else {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, fromPtr, ENTRY_MASK);
+ if (entryPtr == NULL) {
+ entryPtr = fromPtr;
+ }
+ if ((entryPtr == tvPtr->rootPtr) &&
+ (tvPtr->flags & TV_HIDE_ROOT)) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr,
+ ENTRY_MASK);
+ }
+ }
+ } else if (((c == 'l') && (strcmp(string, "last") == 0)) ||
+ ((c == 'p') && (strcmp(string, "prev") == 0))) {
+ entryPtr = fromPtr;
+ if (tvPtr->flatView) {
+ int i;
+
+ i = entryPtr->flatIndex - 1;
+ if (i < 0) {
+ i = tvPtr->nEntries - 1;
+ }
+ entryPtr = tvPtr->flatArr[i];
+ } else {
+ entryPtr = Blt_TreeViewPrevEntry(tvPtr, fromPtr, ENTRY_MASK);
+ if (entryPtr == NULL) {
+ entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+ }
+ if ((entryPtr == tvPtr->rootPtr) &&
+ (tvPtr->flags & TV_HIDE_ROOT)) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr,
+ ENTRY_MASK);
+ }
+ }
+ } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
+ entryPtr = fromPtr;
+ if (tvPtr->flatView) {
+ int i;
+
+ i = entryPtr->flatIndex + 1;
+ if (i >= tvPtr->nEntries) {
+ i = 0;
+ }
+ entryPtr = tvPtr->flatArr[i];
+ } else {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, fromPtr, ENTRY_MASK);
+ if (entryPtr == NULL) {
+ if (tvPtr->flags & TV_HIDE_ROOT) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, tvPtr->rootPtr,
+ ENTRY_MASK);
+ } else {
+ entryPtr = tvPtr->rootPtr;
+ }
+ }
+ }
+ } else if ((c == 'n') && (strcmp(string, "nextsibling") == 0)) {
+ node = Blt_TreeNextSibling(fromPtr->node);
+ if (node != NULL) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ }
+ } else if ((c == 'p') && (strcmp(string, "prevsibling") == 0)) {
+ node = Blt_TreePrevSibling(fromPtr->node);
+ if (node != NULL) {
+ entryPtr = NodeToEntry(tvPtr, node);
+ }
+ } else if ((c == 'v') && (strcmp(string, "view.top") == 0)) {
+ if (tvPtr->nVisible > 0) {
+ entryPtr = tvPtr->visibleArr[0];
+ }
+ } else if ((c == 'v') && (strcmp(string, "view.bottom") == 0)) {
+ if (tvPtr->nVisible > 0) {
+ entryPtr = tvPtr->visibleArr[tvPtr->nVisible - 1];
+ }
+ } else {
+ return TCL_ERROR;
+ }
+ *entryPtrPtr = entryPtr;
+ return TCL_OK;
+}
+
+static int
+GetTagInfo(tvPtr, objPtr, infoPtr)
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TagInfo *infoPtr;
+{
+ char *tagName;
+
+ tagName = Tcl_GetString(objPtr);
+ infoPtr->tagType = TAG_RESERVED | TAG_SINGLE;
+ infoPtr->entryPtr = NULL;
+
+ if (strcmp(tagName, "all") == 0) {
+ infoPtr->entryPtr = tvPtr->rootPtr;
+ infoPtr->tagType |= TAG_ALL;
+ } else {
+ Blt_HashTable *tablePtr;
+
+ tablePtr = Blt_TreeTagHashTable(tvPtr->tagTablePtr, tagName);
+ if (tablePtr != NULL) {
+ Blt_HashEntry *hPtr;
+
+ infoPtr->tagType = TAG_USER_DEFINED; /* Empty tags are not
+ * an error. */
+ hPtr = Blt_FirstHashEntry(tablePtr, &infoPtr->cursor);
+ if (hPtr != NULL) {
+ Blt_TreeNode node;
+
+ node = Blt_GetHashValue(hPtr);
+ infoPtr->entryPtr = NodeToEntry(tvPtr, node);
+ if (tablePtr->numEntries > 1) {
+ infoPtr->tagType |= TAG_MULTIPLE;
+ }
+ }
+ } else {
+ infoPtr->tagType = TAG_UNKNOWN;
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+void
+Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list)
+ Tcl_Interp *interp; /* Not used. */
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ Blt_List list;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&tvPtr->tagTablePtr->table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ hPtr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)entryPtr->node);
+ if (hPtr != NULL) {
+ Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, tagPtr->tagName),0);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddTag --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+AddTag(tvPtr, node, tagName)
+ TreeView *tvPtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ TreeViewEntry *entryPtr;
+
+ if (isdigit(UCHAR(tagName[0]))) {
+ Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
+ "\": can't start with digit", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tagName[0] == '@') {
+ Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
+ "\": can't start with \"@\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tvPtr->fromPtr = NULL;
+ if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+ Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
+ "\": is a special id", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Add the tag to the node. */
+ if (strcmp(tagName, "all") != 0) {
+ Blt_TreeAddTag(tvPtr->tagTablePtr, node, tagName);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HasTag --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+HasTag(tvPtr, node, tagName)
+ TreeView *tvPtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ return Blt_TreeHasTag(tvPtr->tagTablePtr, node, tagName);
+}
+
+static TreeViewEntry *
+FirstTaggedEntry(infoPtr)
+ TagInfo *infoPtr;
+{
+ return infoPtr->entryPtr;
+}
+
+static int
+FindTaggedEntries(tvPtr, objPtr, infoPtr)
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TagInfo *infoPtr;
+{
+ char *tagName;
+ TreeViewEntry *entryPtr;
+
+ tagName = Tcl_GetString(objPtr);
+ tvPtr->fromPtr = NULL;
+ if (isdigit(UCHAR(tagName[0]))) {
+ int inode;
+ Blt_TreeNode node;
+
+ if (Tcl_GetIntFromObj(tvPtr->interp, objPtr, &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ node = Blt_TreeGetNode(tvPtr->tree, inode);
+ infoPtr->entryPtr = NodeToEntry(tvPtr, node);
+ infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
+ } else if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+ infoPtr->entryPtr = entryPtr;
+ infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
+ } else {
+ GetTagInfo(tvPtr, objPtr, infoPtr);
+ }
+ return TCL_OK;
+}
+
+static TreeViewEntry *
+NextTaggedEntry(infoPtr)
+ TagInfo *infoPtr;
+{
+ TreeViewEntry *entryPtr;
+
+ entryPtr = NULL;
+ if (infoPtr->entryPtr != NULL) {
+ TreeView *tvPtr = infoPtr->entryPtr->tvPtr;
+
+ if (infoPtr->tagType & TAG_ALL) {
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, infoPtr->entryPtr, 0);
+ } else if (infoPtr->tagType & TAG_MULTIPLE) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_NextHashEntry(&infoPtr->cursor);
+ if (hPtr != NULL) {
+ Blt_TreeNode node;
+
+ node = Blt_GetHashValue(hPtr);
+ entryPtr = NodeToEntry(tvPtr, node);
+ }
+ }
+ infoPtr->entryPtr = entryPtr;
+ }
+ return entryPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEntryFromObj2 --
+ *
+ * Converts a string into node pointer. The string may be in one
+ * of the following forms:
+ *
+ * NNN - inode.
+ * "active" - Currently active node.
+ * "anchor" - anchor of selected region.
+ * "current" - Currently picked node in bindtable.
+ * "focus" - The node currently with focus.
+ * "root" - Root node.
+ * "end" - Last open node in the entire hierarchy.
+ * "next" - Next open node from the currently active
+ * node. Wraps around back to top.
+ * "last" - Previous open node from the currently active
+ * node. Wraps around back to bottom.
+ * "up" - Next open node from the currently active
+ * node. Does not wrap around.
+ * "down" - Previous open node from the currently active
+ * node. Does not wrap around.
+ * "nextsibling" - Next sibling of the current node.
+ * "prevsibling" - Previous sibling of the current node.
+ * "parent" - Parent of the current node.
+ * "view.top" - Top of viewport.
+ * "view.bottom" - Bottom of viewport.
+ * @x,y - Closest node to the specified X-Y position.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via nodePtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr)
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewEntry **entryPtrPtr;
+{
+ Tcl_Interp *interp;
+ char *string;
+ TagInfo info;
+
+ interp = tvPtr->interp;
+
+ string = Tcl_GetString(objPtr);
+ *entryPtrPtr = NULL;
+ if (isdigit(UCHAR(string[0]))) {
+ Blt_TreeNode node;
+ int inode;
+
+ if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ node = Blt_TreeGetNode(tvPtr->tree, inode);
+ if (node != NULL) {
+ *entryPtrPtr = NodeToEntry(tvPtr, node);
+ }
+ return TCL_OK; /* Node Id. */
+ }
+ if (GetEntryFromSpecialId(tvPtr, string, entryPtrPtr) == TCL_OK) {
+ return TCL_OK; /* Special Id. */
+ }
+ if (GetTagInfo(tvPtr, objPtr, &info) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find tag or id \"", string,
+ "\" in \"", Tk_PathName(tvPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (info.tagType & TAG_MULTIPLE) {
+ Tcl_AppendResult(interp, "more than one entry tagged as \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *entryPtrPtr = info.entryPtr;
+ return TCL_OK; /* Singleton tag. */
+}
+
+static int
+GetEntryFromObj(tvPtr, objPtr, entryPtrPtr)
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewEntry **entryPtrPtr;
+{
+ tvPtr->fromPtr = NULL;
+ return GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewGetEntry --
+ *
+ * Returns an entry based upon its index.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * The pointer to the node is returned via nodePtr.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewGetEntry(tvPtr, objPtr, entryPtrPtr)
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewEntry **entryPtrPtr;
+{
+ TreeViewEntry *entryPtr;
+
+ if (GetEntryFromObj(tvPtr, objPtr, &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (entryPtr == NULL) {
+ Tcl_ResetResult(tvPtr->interp);
+ Tcl_AppendResult(tvPtr->interp, "can't find entry \"",
+ Tcl_GetString(objPtr), "\" in \"", Tk_PathName(tvPtr->tkwin),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *entryPtrPtr = entryPtr;
+ return TCL_OK;
+}
+
+static Blt_TreeNode
+GetNthNode(parent, position)
+ Blt_TreeNode parent;
+ int position;
+{
+ Blt_TreeNode node;
+ int count;
+
+ count = 0;
+ for(node = Blt_TreeFirstChild(parent); node != NULL;
+ node = Blt_TreeNextSibling(node)) {
+ if (count == position) {
+ return node;
+ }
+ }
+ return Blt_TreeLastChild(parent);
+}
+
+static TreeViewEntry *
+GetNthEntry(tvPtr, parentPtr, position)
+ TreeView *tvPtr;
+ TreeViewEntry *parentPtr;
+ int position;
+{
+ TreeViewEntry *entryPtr;
+ int count;
+
+ count = 0;
+ for(entryPtr = Blt_TreeViewFirstChild(tvPtr, parentPtr);
+ entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextSibling(tvPtr, entryPtr)) {
+ if (count == position) {
+ return entryPtr;
+ }
+ }
+ return Blt_TreeViewLastChild(tvPtr, parentPtr);
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+void
+Blt_TreeViewPercentSubst(tvPtr, entryPtr, command, resultPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ char *command;
+ Tcl_DString *resultPtr;
+{
+ register char *last, *p;
+ char *fullName;
+ Tcl_DString dString;
+
+ /*
+ * Get the full path name of the node, in case we need to
+ * substitute for it.
+ */
+ fullName = Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+ Tcl_DStringInit(resultPtr);
+ /* Append the widget name and the node .t 0 */
+ for (last = p = command; *p != '\0'; p++) {
+ if (*p == '%') {
+ char *string;
+ char buf[3];
+
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ *p = '%';
+ }
+ switch (*(p + 1)) {
+ case '%': /* Percent sign */
+ string = "%";
+ break;
+ case 'W': /* Widget name */
+ string = Tk_PathName(tvPtr->tkwin);
+ break;
+ case 'P': /* Full pathname */
+ string = fullName;
+ break;
+ case 'p': /* Name of the node */
+ string = GETLABEL(entryPtr);
+ break;
+ case '#': /* Node identifier */
+ string = Blt_Itoa(Blt_TreeNodeId(entryPtr->node));
+ break;
+ default:
+ if (*(p + 1) == '\0') {
+ p--;
+ }
+ buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+ string = buf;
+ break;
+ }
+ Tcl_DStringAppend(resultPtr, string, -1);
+ p++;
+ last = p + 1;
+ }
+ }
+ if (p > last) {
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, last, -1);
+ }
+ Tcl_DStringFree(&dString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectEntryApplyProc --
+ *
+ * Sets the selection flag for a node. The selection flag is
+ * set/cleared/toggled based upon the flag set in the hierarchy
+ * widget.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectEntryApplyProc(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ switch (tvPtr->flags & TV_SELECT_MASK) {
+ case TV_SELECT_CLEAR:
+ Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+ break;
+
+ case TV_SELECT_SET:
+ Blt_TreeViewSelectEntry(tvPtr, entryPtr);
+ break;
+
+ case TV_SELECT_TOGGLE:
+ hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+ if (hPtr != NULL) {
+ Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+ } else {
+ Blt_TreeViewSelectEntry(tvPtr, entryPtr);
+ }
+ break;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyInvokeSelectCmd --
+ *
+ * Queues a request to execute the -selectcommand code associated
+ * with the widget at the next idle point. Invoked whenever the
+ * selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyInvokeSelectCmd(tvPtr)
+ TreeView *tvPtr;
+{
+ if (!(tvPtr->flags & TV_SELECT_PENDING)) {
+ tvPtr->flags |= TV_SELECT_PENDING;
+ Tcl_DoWhenIdle(Blt_TreeViewSelectCmdProc, tvPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewPruneSelection --
+ *
+ * The root entry being deleted or closed. Deselect any of its
+ * descendants that are currently selected.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If any of the entry's descendants are deselected the widget
+ * is redrawn and the a selection command callback is invoked
+ * (if there's one configured).
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewPruneSelection(tvPtr, rootPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *rootPtr;
+{
+ Blt_ChainLink *linkPtr, *nextPtr;
+ TreeViewEntry *entryPtr;
+ int selectionChanged;
+
+ /*
+ * Check if any of the currently selected entries are a descendant
+ * of of the current root entry. Deselect the entry and indicate
+ * that the treeview widget needs to be redrawn.
+ */
+ selectionChanged = FALSE;
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ if (Blt_TreeIsAncestor(rootPtr->node, entryPtr->node)) {
+ Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+ selectionChanged = TRUE;
+ }
+ }
+ if (selectionChanged) {
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ if (tvPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(tvPtr);
+ }
+ }
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeView operations
+ *
+ * --------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+FocusOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ TreeViewEntry *entryPtr;
+
+ if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((entryPtr != NULL) && (entryPtr != tvPtr->focusPtr)) {
+ if (entryPtr->flags & ENTRY_HIDDEN) {
+ /* Doesn't make sense to set focus to a node you can't see. */
+ MapAncestors(tvPtr, entryPtr);
+ }
+ /* Changing focus can only affect the visible entries. The
+ * entry layout stays the same. */
+ if (tvPtr->focusPtr != NULL) {
+ tvPtr->focusPtr->flags |= ENTRY_REDRAW;
+ }
+ entryPtr->flags |= ENTRY_REDRAW;
+ tvPtr->flags |= TV_SCROLL;
+ tvPtr->focusPtr = entryPtr;
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr);
+ if (tvPtr->focusPtr != NULL) {
+ Tcl_SetObjResult(interp, NodeToObj(tvPtr->focusPtr->node));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BboxOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BboxOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ TreeViewEntry *entryPtr;
+ int width, height, yBot;
+ int left, top, right, bottom;
+ int screen;
+ int lWidth;
+ char *string;
+
+ if (tvPtr->flags & TV_LAYOUT) {
+ /*
+ * The layout is dirty. Recompute it now, before we use the
+ * world dimensions. But remember, the "bbox" operation isn't
+ * valid for hidden entries (since they're not visible, they
+ * don't have world coordinates).
+ */
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ left = tvPtr->worldWidth;
+ top = tvPtr->worldHeight;
+ right = bottom = 0;
+
+ screen = FALSE;
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-screen") == 0)) {
+ screen = TRUE;
+ objc--, objv++;
+ }
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if ((string[0] == 'a') && (strcmp(string, "all") == 0)) {
+ left = top = 0;
+ right = tvPtr->worldWidth;
+ bottom = tvPtr->worldHeight;
+ break;
+ }
+ if (GetEntryFromObj(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (entryPtr == NULL) {
+ continue;
+ }
+ if (entryPtr->flags & ENTRY_HIDDEN) {
+ continue;
+ }
+ yBot = entryPtr->worldY + entryPtr->height;
+ height = VPORTHEIGHT(tvPtr);
+ if ((yBot <= tvPtr->yOffset) &&
+ (entryPtr->worldY >= (tvPtr->yOffset + height))) {
+ continue;
+ }
+ if (bottom < yBot) {
+ bottom = yBot;
+ }
+ if (top > entryPtr->worldY) {
+ top = entryPtr->worldY;
+ }
+ lWidth = ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
+ if (right < (entryPtr->worldX + entryPtr->width + lWidth)) {
+ right = (entryPtr->worldX + entryPtr->width + lWidth);
+ }
+ if (left > entryPtr->worldX) {
+ left = entryPtr->worldX;
+ }
+ }
+
+ if (screen) {
+ width = VPORTWIDTH(tvPtr);
+ height = VPORTHEIGHT(tvPtr);
+ /*
+ * Do a min-max text for the intersection of the viewport and
+ * the computed bounding box. If there is no intersection, return
+ * the empty string.
+ */
+ if ((right < tvPtr->xOffset) || (bottom < tvPtr->yOffset) ||
+ (left >= (tvPtr->xOffset + width)) ||
+ (top >= (tvPtr->yOffset + height))) {
+ return TCL_OK;
+ }
+ /* Otherwise clip the coordinates at the view port boundaries. */
+ if (left < tvPtr->xOffset) {
+ left = tvPtr->xOffset;
+ } else if (right > (tvPtr->xOffset + width)) {
+ right = tvPtr->xOffset + width;
+ }
+ if (top < tvPtr->yOffset) {
+ top = tvPtr->yOffset;
+ } else if (bottom > (tvPtr->yOffset + height)) {
+ bottom = tvPtr->yOffset + height;
+ }
+ left = SCREENX(tvPtr, left), top = SCREENY(tvPtr, top);
+ right = SCREENX(tvPtr, right), bottom = SCREENY(tvPtr, bottom);
+ }
+ if ((left < right) && (top < bottom)) {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(left));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(top));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(right - left));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(bottom - top));
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonActivateOp --
+ *
+ * Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *oldPtr, *newPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ newPtr = NULL;
+ } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tvPtr->treeColumn.hidden) {
+ return TCL_OK;
+ }
+ if ((newPtr != NULL) && !(newPtr->flags & ENTRY_HAS_BUTTON)) {
+ newPtr = NULL;
+ }
+ oldPtr = tvPtr->activeButtonPtr;
+ tvPtr->activeButtonPtr = newPtr;
+ if (newPtr != oldPtr) {
+ Drawable drawable;
+
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if ((oldPtr != NULL) && (oldPtr != tvPtr->rootPtr)) {
+ Blt_TreeViewDrawButton(tvPtr, oldPtr, drawable);
+ }
+ if ((newPtr != NULL) && (newPtr != tvPtr->rootPtr)) {
+ Blt_TreeViewDrawButton(tvPtr, newPtr, drawable);
+ }
+ Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonBindOp --
+ *
+ * .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonBindOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ ClientData object;
+ TreeViewEntry *entryPtr;
+ char *string;
+
+ /*
+ * Individual entries are selected by id only. All other strings
+ * are interpreted as a binding tag. For example, if one binds to
+ * "focus", it is assumed that this refers to a bind tag, not the
+ * entry with focus.
+ */
+ string = Tcl_GetString(objv[3]);
+ if (isdigit(UCHAR(string[0]))) {
+ int inode;
+ Blt_TreeNode node;
+
+ if (Tcl_GetIntFromObj(tvPtr->interp, objv[3], &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ node = Blt_TreeGetNode(tvPtr->tree, inode);
+ object = NodeToEntry(tvPtr, node);
+ } else if (GetEntryFromSpecialId(tvPtr, string, &entryPtr) == TCL_OK) {
+ if (entryPtr != NULL) {
+ return TCL_OK; /* Special id doesn't currently exist. */
+ }
+ object = entryPtr;
+ } else {
+ /* Assume that this is a bindtag. */
+ object = Blt_TreeViewGetUid(tvPtr, string);
+ }
+ return Blt_ConfigureBindingsFromObj(interp, tvPtr->buttonBindTable,
+ object, objc - 4, objv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h button configure option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ButtonConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ bltTreeViewButtonSpecs, (char *)tvPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
+ }
+ bltTreeViewImagesOption.clientData = tvPtr;
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
+ bltTreeViewButtonSpecs, objc - 3, objv + 3, (char *)tvPtr,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewConfigureButtons(tvPtr);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonOp --
+ *
+ * This procedure handles button operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec buttonOps[] =
+{
+ {"activate", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
+ {"bind", 1, (Blt_Op)ButtonBindOp, 4, 6, "tagName ?sequence command?",},
+ {"cget", 2, (Blt_Op)ButtonCgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)ButtonConfigureOp, 3, 0, "?option value?...",},
+ {"highlight", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
+};
+
+static int nButtonOps = sizeof(buttonOps) / sizeof(Blt_OpSpec);
+
+static int
+ButtonOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nButtonOps, buttonOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
+ (char *)tvPtr, objv[2], 0);
+}
+
+/*ARGSUSED*/
+static int
+CloseOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ TagInfo info;
+ int recurse, result;
+ register int i;
+
+ recurse = FALSE;
+
+ if (objc > 2) {
+ char *string;
+ int length;
+
+ string = Tcl_GetString(objv[2]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-recurse", length) == 0)) {
+ objv++, objc--;
+ recurse = TRUE;
+ }
+ }
+ for (i = 2; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ /*
+ * Clear the selections for any entries that may have become
+ * hidden by closing the node.
+ */
+ Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+
+ /*
+ * -----------------------------------------------------------
+ *
+ * Check if either the "focus" entry or selection anchor
+ * is in this hierarchy. Must move it or disable it before
+ * we close the node. Otherwise it may be deleted by a Tcl
+ * "close" script, and we'll be left pointing to a bogus
+ * memory location.
+ *
+ * -----------------------------------------------------------
+ */
+ if ((tvPtr->focusPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+ tvPtr->focusPtr = entryPtr;
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr);
+ }
+ if ((tvPtr->selAnchorPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node,
+ tvPtr->selAnchorPtr->node))) {
+ tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+ }
+ if ((tvPtr->activePtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
+ tvPtr->activePtr = entryPtr;
+ }
+ if (recurse) {
+ result = Blt_TreeViewApply(tvPtr, entryPtr,
+ Blt_TreeViewCloseEntry, 0);
+ } else {
+ result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ /* Closing a node may affect the visible entries but not the
+ * the world layout of the entries. */
+ /*FIXME: This is only for flattened entries. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process an objv/objc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * the widget.
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 2) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
+ (char *)tvPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ bltTreeViewSpecs, (char *)tvPtr, objv[2], 0);
+ }
+ if (Blt_TreeViewConfigureWidget(interp, tvPtr, objc - 2, objv + 2,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CurselectionOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ TreeViewEntry *entryPtr;
+ Tcl_Obj *listObjPtr, *objPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (tvPtr->flags & TV_SELECT_SORTED) {
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ entryPtr = Blt_ChainGetValue(linkPtr);
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+
+ }
+ } else {
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, ENTRY_MASK)) {
+ if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind tagOrId sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ ClientData object;
+ TreeViewEntry *entryPtr;
+ char *string;
+
+ /*
+ * Entries are selected by id only. All other strings are
+ * interpreted as a binding tag.
+ */
+ string = Tcl_GetString(objv[2]);
+ if (isdigit(UCHAR(string[0]))) {
+ Blt_TreeNode node;
+ int inode;
+
+ if (Tcl_GetIntFromObj(tvPtr->interp, objv[2], &inode) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ node = Blt_TreeGetNode(tvPtr->tree, inode);
+ object = NodeToEntry(tvPtr, node);
+ } else if (GetEntryFromSpecialId(tvPtr, string, &entryPtr) == TCL_OK) {
+ if (entryPtr != NULL) {
+ return TCL_OK; /* Special id doesn't currently exist. */
+ }
+ object = entryPtr;
+ } else {
+ /* Assume that this is a bindtag. */
+ object = Blt_TreeViewGetUid(tvPtr, string);
+ }
+ return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
+ objc - 3, objv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryActivateOp --
+ *
+ * Selects the entry to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *newPtr, *oldPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ newPtr = NULL;
+ } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tvPtr->treeColumn.hidden) {
+ return TCL_OK;
+ }
+ oldPtr = tvPtr->activePtr;
+ tvPtr->activePtr = newPtr;
+ if (newPtr != oldPtr) {
+ if (tvPtr->flags & TV_DIRTY) {
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ } else {
+ Drawable drawable;
+ int x, y;
+
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if (oldPtr != NULL) {
+ x = SCREENX(tvPtr, oldPtr->worldX);
+ if (!tvPtr->flatView) {
+ x += ICONWIDTH(DEPTH(tvPtr, oldPtr->node));
+ }
+ y = SCREENY(tvPtr, oldPtr->worldY);
+ oldPtr->flags |= ENTRY_ICON;
+ Blt_TreeViewDrawIcon(tvPtr, oldPtr, x, y, drawable);
+ }
+ if (newPtr != NULL) {
+ x = SCREENX(tvPtr, newPtr->worldX);
+ if (!tvPtr->flatView) {
+ x += ICONWIDTH(DEPTH(tvPtr, newPtr->node));
+ }
+ y = SCREENY(tvPtr, newPtr->worldY);
+ newPtr->flags |= ENTRY_ICON;
+ Blt_TreeViewDrawIcon(tvPtr, newPtr, x, y, drawable);
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h entryconfigure node node node node option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EntryConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int nIds, configObjc;
+ Tcl_Obj *CONST *configObjv;
+ register int i;
+ TreeViewEntry *entryPtr;
+ TagInfo info;
+
+ /* Figure out where the option value pairs begin */
+ objc -= 3, objv += 3;
+ for (i = 0; i < objc; i++) {
+ if (Blt_ObjIsOption(bltTreeViewEntrySpecs, objv[i], 0)) {
+ break;
+ }
+ }
+ nIds = i; /* # of tags or ids specified */
+ configObjc = objc - i; /* # of options specified */
+ configObjv = objv + i; /* Start of options in objv */
+
+ bltTreeViewImagesOption.clientData = tvPtr;
+ bltTreeViewUidOption.clientData = tvPtr;
+
+ for (i = 0; i < nIds; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if (configObjc == 0) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr,
+ (Tcl_Obj *)NULL, 0);
+ } else if (configObjc == 1) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr,
+ configObjv[0], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, configObjc, configObjv,
+ (char *)entryPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewConfigureEntry(tvPtr, entryPtr);
+ if (Blt_ObjConfigModified(bltTreeViewEntrySpecs, "-font",
+ (char *)NULL)) {
+ tvPtr->flags |= TV_UPDATE;
+ }
+ }
+ }
+ tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsOpenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsBeforeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *e1Ptr, *e2Ptr;
+ int bool;
+
+ if ((Blt_TreeViewGetEntry(tvPtr, objv[3], &e1Ptr) != TCL_OK) ||
+ (Blt_TreeViewGetEntry(tvPtr, objv[4], &e2Ptr) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ bool = Blt_TreeIsBefore(e1Ptr->node, e2Ptr->node);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsHiddenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsHiddenOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int bool;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = (entryPtr->flags & ENTRY_HIDDEN);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsOpenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsOpenOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int bool;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = ((entryPtr->flags & ENTRY_CLOSED) == 0);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+EntryChildrenOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *parentPtr;
+ Tcl_Obj *listObjPtr, *objPtr;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &parentPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (objc == 4) {
+ TreeViewEntry *entryPtr;
+
+ for (entryPtr = Blt_TreeViewFirstChild(tvPtr, parentPtr);
+ entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextSibling(tvPtr, entryPtr)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else if (objc == 6) {
+ TreeViewEntry *entryPtr, *lastPtr, *firstPtr;
+ int firstPos, lastPos;
+ int nNodes;
+
+ if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
+ (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nNodes = Blt_TreeNodeDegree(parentPtr->node);
+ if (nNodes == 0) {
+ return TCL_OK;
+ }
+ if ((lastPos == END) || (lastPos >= nNodes)) {
+ lastPtr = Blt_TreeViewLastChild(tvPtr, parentPtr);
+ } else {
+ lastPtr = GetNthEntry(tvPtr, parentPtr, lastPos);
+ }
+ if ((firstPos == END) || (firstPos >= nNodes)) {
+ firstPtr = Blt_TreeViewLastChild(tvPtr, parentPtr);
+ } else {
+ firstPtr = GetNthEntry(tvPtr, parentPtr, firstPos);
+ }
+ if ((lastPos != END) && (firstPos > lastPos)) {
+ for (entryPtr = lastPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewPrevEntry(tvPtr, entryPtr, 0)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (entryPtr == firstPtr) {
+ break;
+ }
+ }
+ } else {
+ for (entryPtr = firstPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (entryPtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " ",
+ Tcl_GetString(objv[1]), " ",
+ Tcl_GetString(objv[2]), " tagOrId ?first last?",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryDeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 5) {
+ int entryPos;
+ Blt_TreeNode node;
+ /*
+ * Delete a single child node from a hierarchy specified
+ * by its numeric position.
+ */
+ if (Blt_GetPositionFromObj(interp, objv[3], &entryPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (entryPos >= (int)Blt_TreeNodeDegree(entryPtr->node)) {
+ return TCL_OK; /* Bad first index */
+ }
+ if (entryPos == END) {
+ node = Blt_TreeLastChild(entryPtr->node);
+ } else {
+ node = GetNthNode(entryPtr->node, entryPos);
+ }
+ DeleteNode(tvPtr, node);
+ } else {
+ int firstPos, lastPos;
+ Blt_TreeNode node, first, last, next;
+ int nEntries;
+ /*
+ * Delete range of nodes in hierarchy specified by first/last
+ * positions.
+ */
+ if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
+ (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nEntries = Blt_TreeNodeDegree(entryPtr->node);
+ if (nEntries == 0) {
+ return TCL_OK;
+ }
+ if (firstPos == END) {
+ firstPos = nEntries - 1;
+ }
+ if (firstPos >= nEntries) {
+ Tcl_AppendResult(interp, "first position \"",
+ Tcl_GetString(objv[4]), " is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((lastPos == END) || (lastPos >= nEntries)) {
+ lastPos = nEntries - 1;
+ }
+ if (firstPos > lastPos) {
+ Tcl_AppendResult(interp, "bad range: \"", Tcl_GetString(objv[4]),
+ " > ", Tcl_GetString(objv[5]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ first = GetNthNode(entryPtr->node, firstPos);
+ last = GetNthNode(entryPtr->node, lastPos);
+ for (node = first; node != NULL; node = next) {
+ next = Blt_TreeNextSibling(node);
+ DeleteNode(tvPtr, node);
+ if (node == last) {
+ break;
+ }
+ }
+ }
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntrySizeOp --
+ *
+ * Counts the number of entries at this node.
+ *
+ * Results:
+ * A standard Tcl result. If an error occurred TCL_ERROR is
+ * returned and interp->result will contain an error message.
+ * Otherwise, TCL_OK is returned and interp->result contains
+ * the number of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EntrySizeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int length, sum, recurse;
+ char *string;
+
+ recurse = FALSE;
+ string = Tcl_GetString(objv[3]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-recurse", length) == 0)) {
+ objv++, objc--;
+ recurse = TRUE;
+ }
+ if (objc == 3) {
+ Tcl_AppendResult(interp, "missing node argument: should be \"",
+ Tcl_GetString(objv[0]), " entry open node\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (recurse) {
+ sum = Blt_TreeSize(entryPtr->node);
+ } else {
+ sum = Blt_TreeNodeDegree(entryPtr->node);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sum));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryOp --
+ *
+ * This procedure handles entry operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec entryOps[] =
+{
+ {"activate", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
+ /*bbox*/
+ /*bind*/
+ {"cget", 2, (Blt_Op)EntryCgetOp, 5, 5, "tagOrId option",},
+ {"children", 2, (Blt_Op)EntryChildrenOp, 4, 6,
+ "tagOrId firstPos lastPos",},
+ /*close*/
+ {"configure", 2, (Blt_Op)EntryConfigureOp, 4, 0,
+ "tagOrId ?tagOrId...? ?option value?...",},
+ {"delete", 2, (Blt_Op)EntryDeleteOp, 5, 6, "tagOrId firstPos ?lastPos?",},
+ /*focus*/
+ /*hide*/
+ {"highlight", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
+ /*index*/
+ {"isbefore", 3, (Blt_Op)EntryIsBeforeOp, 5, 5, "tagOrId tagOrId",},
+ {"ishidden", 3, (Blt_Op)EntryIsHiddenOp, 4, 4, "tagOrId",},
+ {"isopen", 3, (Blt_Op)EntryIsOpenOp, 4, 4, "tagOrId",},
+ /*move*/
+ /*nearest*/
+ /*open*/
+ /*see*/
+ /*show*/
+ {"size", 1, (Blt_Op)EntrySizeOp, 4, 5, "?-recurse? tagOrId",},
+ /*toggle*/
+};
+static int nEntryOps = sizeof(entryOps) / sizeof(Blt_OpSpec);
+
+static int
+EntryOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nEntryOps, entryOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+ExactCompare(interp, name, pattern)
+ Tcl_Interp *interp; /* Not used. */
+ char *name;
+ char *pattern;
+{
+ return (strcmp(name, pattern) == 0);
+}
+
+/*ARGSUSED*/
+static int
+GlobCompare(interp, name, pattern)
+ Tcl_Interp *interp; /* Not used. */
+ char *name;
+ char *pattern;
+{
+ return Tcl_StringMatch(name, pattern);
+}
+
+static int
+RegexpCompare(interp, name, pattern)
+ Tcl_Interp *interp;
+ char *name;
+ char *pattern;
+{
+ return Tcl_RegExpMatch(interp, name, pattern);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ * Find one or more nodes based upon the pattern provided.
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain a
+ * list of the node serial identifiers.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+FindOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *firstPtr, *lastPtr;
+ int nMatches, maxMatches;
+ char c;
+ int length;
+ TreeViewCompareProc *compareProc;
+ TreeViewIterProc *nextProc;
+ int invertMatch; /* normal search mode (matching entries) */
+ char *namePattern, *fullPattern;
+ char *execCmd;
+ register int i;
+ int result;
+ char *pattern, *option;
+ Tcl_DString dString;
+ Blt_List options;
+ Blt_ListNode node;
+ char *addTag, *withTag;
+ register TreeViewEntry *entryPtr;
+ char *string;
+ Tcl_Obj *listObjPtr, *objPtr;
+
+ invertMatch = FALSE;
+ maxMatches = 0;
+ execCmd = namePattern = fullPattern = NULL;
+ compareProc = ExactCompare;
+ nextProc = Blt_TreeViewNextEntry;
+ options = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ withTag = addTag = NULL;
+
+ entryPtr = tvPtr->rootPtr;
+ /*
+ * Step 1: Process flags for find operation.
+ */
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] != '-') {
+ break;
+ }
+ option = string + 1;
+ length = strlen(option);
+ c = option[0];
+ if ((c == 'e') && (length > 2) &&
+ (strncmp(option, "exact", length) == 0)) {
+ compareProc = ExactCompare;
+ } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+ compareProc = GlobCompare;
+ } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+ compareProc = RegexpCompare;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "nonmatching", length) == 0)) {
+ invertMatch = TRUE;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "name", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ namePattern = Tcl_GetString(objv[i]);
+ } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ fullPattern = Tcl_GetString(objv[i]);
+ } else if ((c == 'e') && (length > 2) &&
+ (strncmp(option, "exec", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ execCmd = Tcl_GetString(objv[i]);
+ } else if ((c == 'a') && (length > 1) &&
+ (strncmp(option, "addtag", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ addTag = Tcl_GetString(objv[i]);
+ } else if ((c == 't') && (length > 1) &&
+ (strncmp(option, "tag", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ withTag = Tcl_GetString(objv[i]);
+ } else if ((c == 'c') && (strncmp(option, "count", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ if (Tcl_GetIntFromObj(interp, objv[i], &maxMatches) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (maxMatches < 0) {
+ Tcl_AppendResult(interp, "bad match count \"", objv[i],
+ "\": should be a positive number", (char *)NULL);
+ Blt_ListDestroy(options);
+ return TCL_ERROR;
+ }
+ } else if ((option[0] == '-') && (option[1] == '\0')) {
+ break;
+ } else {
+ /*
+ * Verify that the switch is actually an entry configuration
+ * option.
+ */
+ if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
+ != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad find switch \"", string, "\"",
+ (char *)NULL);
+ Blt_ListDestroy(options);
+ return TCL_ERROR;
+ }
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ /* Save the option in the list of configuration options */
+ node = Blt_ListGetNode(options, (char *)objv[i]);
+ if (node == NULL) {
+ node = Blt_ListCreateNode(options, (char *)objv[i]);
+ Blt_ListAppendNode(options, node);
+ }
+ i++;
+ Blt_ListSetValue(node, Tcl_GetString(objv[i]));
+ }
+ }
+
+ if ((objc - i) > 2) {
+ Blt_ListDestroy(options);
+ Tcl_AppendResult(interp, "too many args", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Step 2: Find the range of the search. Check the order of two
+ * nodes and arrange the search accordingly.
+ *
+ * Note: Be careful to treat "end" as the end of all nodes, instead
+ * of the end of visible nodes. That way, we can search the
+ * entire tree, even if the last folder is closed.
+ */
+ firstPtr = tvPtr->rootPtr; /* Default to root node */
+ lastPtr = LastEntry(tvPtr, firstPtr, 0);
+
+ if (i < objc) {
+ string = Tcl_GetString(objv[i]);
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ firstPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
+ } else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ i++;
+ }
+ if (i < objc) {
+ string = Tcl_GetString(objv[i]);
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ lastPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
+ } else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
+ nextProc = Blt_TreeViewPrevEntry;
+ }
+ nMatches = 0;
+
+ /*
+ * Step 3: Search through the tree and look for nodes that match the
+ * current pattern specifications. Save the name of each of
+ * the matching nodes.
+ */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (entryPtr = firstPtr; entryPtr != NULL;
+ entryPtr = (*nextProc) (tvPtr, entryPtr, 0)) {
+ if (namePattern != NULL) {
+ result = (*compareProc)(interp, Blt_TreeNodeLabel(entryPtr->node),
+ namePattern);
+ if (result == invertMatch) {
+ goto nextEntry; /* Failed to match */
+ }
+ }
+ if (fullPattern != NULL) {
+ Tcl_DString fullName;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &fullName);
+ result = (*compareProc) (interp, Tcl_DStringValue(&fullName),
+ fullPattern);
+ Tcl_DStringFree(&fullName);
+ if (result == invertMatch) {
+ goto nextEntry; /* Failed to match */
+ }
+ }
+ if (withTag != NULL) {
+ result = HasTag(tvPtr, entryPtr->node, withTag);
+ if (result == invertMatch) {
+ goto nextEntry; /* Failed to match */
+ }
+ }
+ for (node = Blt_ListFirstNode(options); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
+ Tcl_ResetResult(interp);
+ Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0);
+ pattern = Blt_ListGetValue(node);
+ objPtr = Tcl_GetObjResult(interp);
+ result = (*compareProc) (interp, Tcl_GetString(objPtr), pattern);
+ if (result == invertMatch) {
+ goto nextEntry; /* Failed to match */
+ }
+ }
+ /*
+ * Someone may actually delete the current node in the "exec"
+ * callback. Preserve the entry.
+ */
+ Tcl_Preserve(entryPtr);
+ if (execCmd != NULL) {
+ Tcl_DString cmdString;
+
+ Blt_TreeViewPercentSubst(tvPtr, entryPtr, execCmd, &cmdString);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
+ Tcl_DStringFree(&cmdString);
+ if (result != TCL_OK) {
+ Tcl_Release(entryPtr);
+ goto error;
+ }
+ }
+ /* A NULL node reference in an entry indicates that the entry
+ * was deleted, but its memory not released yet. */
+ if (entryPtr->node != NULL) {
+ /* Finally, save the matching node name. */
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (addTag != NULL) {
+ AddTag(tvPtr, entryPtr->node, addTag);
+ }
+ }
+
+ Tcl_Release(entryPtr);
+ nMatches++;
+ if ((nMatches == maxMatches) && (maxMatches > 0)) {
+ break;
+ }
+ nextEntry:
+ if (entryPtr == lastPtr) {
+ break;
+ }
+ }
+ Tcl_ResetResult(interp);
+ Blt_ListDestroy(options);
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+
+ missingArg:
+ Tcl_AppendResult(interp, "missing argument for find option \"", objv[i],
+ "\"", (char *)NULL);
+ error:
+ Tcl_DStringFree(&dString);
+ Blt_ListDestroy(options);
+ return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Converts one or more node identifiers to its path component.
+ * The path may be either the single entry name or the full path
+ * of the entry.
+ *
+ * Results:
+ * A standard Tcl result. The interpreter result will contain a
+ * list of the convert names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TagInfo info;
+ TreeViewEntry *entryPtr;
+ int useFullName;
+ register int i;
+ Tcl_DString dString1, dString2;
+ int count;
+
+ useFullName = FALSE;
+ if (objc > 2) {
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-full") == 0)) {
+ useFullName = TRUE;
+ objv++, objc--;
+ }
+ }
+ Tcl_DStringInit(&dString1);
+ Tcl_DStringInit(&dString2);
+ count = 0;
+ for (i = 2; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ Tcl_DStringSetLength(&dString2, 0);
+ count++;
+ if (entryPtr->node == NULL) {
+ Tcl_DStringAppendElement(&dString1, "");
+ continue;
+ }
+ if (useFullName) {
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString2);
+ Tcl_DStringAppendElement(&dString1,
+ Tcl_DStringValue(&dString2));
+ } else {
+ Tcl_DStringAppendElement(&dString1,
+ Blt_TreeNodeLabel(entryPtr->node));
+ }
+ }
+ }
+ /* This handles the single element list problem. */
+ if (count == 1) {
+ Tcl_DStringResult(interp, &dString2);
+ Tcl_DStringFree(&dString1);
+ } else {
+ Tcl_DStringResult(interp, &dString1);
+ Tcl_DStringFree(&dString2);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SearchAndApplyToTree --
+ *
+ * Searches through the current tree and applies a procedure
+ * to matching nodes. The search specification is taken from
+ * the following command-line arguments:
+ *
+ * ?-exact? ?-glob? ?-regexp? ?-nonmatching?
+ * ?-data string?
+ * ?-name string?
+ * ?-full string?
+ * ?--?
+ * ?inode...?
+ *
+ * Results:
+ * A standard Tcl result. If the result is valid, and if the
+ * nonmatchPtr is specified, it returns a boolean value
+ * indicating whether or not the search was inverted. This
+ * is needed to fix things properly for the "hide nonmatching"
+ * case.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SearchAndApplyToTree(tvPtr, interp, objc, objv, proc, nonMatchPtr)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+ TreeViewApplyProc *proc;
+ int *nonMatchPtr; /* returns: inverted search indicator */
+{
+ TreeViewCompareProc *compareProc;
+ int invertMatch; /* normal search mode (matching entries) */
+ char *namePattern, *fullPattern;
+ register int i;
+ int length;
+ int result;
+ char *option, *pattern;
+ char c;
+ Blt_List options;
+ TreeViewEntry *entryPtr;
+ register Blt_ListNode node;
+ char *string;
+ char *withTag;
+ Tcl_Obj *objPtr;
+ TagInfo info;
+
+ options = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ invertMatch = FALSE;
+ namePattern = fullPattern = NULL;
+ compareProc = ExactCompare;
+ withTag = NULL;
+
+ entryPtr = tvPtr->rootPtr;
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] != '-') {
+ break;
+ }
+ option = string + 1;
+ length = strlen(option);
+ c = option[0];
+ if ((c == 'e') && (strncmp(option, "exact", length) == 0)) {
+ compareProc = ExactCompare;
+ } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+ compareProc = GlobCompare;
+ } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+ compareProc = RegexpCompare;
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "nonmatching", length) == 0)) {
+ invertMatch = TRUE;
+ } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ fullPattern = Tcl_GetString(objv[i]);
+ } else if ((c == 'n') && (length > 1) &&
+ (strncmp(option, "name", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ namePattern = Tcl_GetString(objv[i]);
+ } else if ((c == 't') && (length > 1) &&
+ (strncmp(option, "tag", length) == 0)) {
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ i++;
+ withTag = Tcl_GetString(objv[i]);
+ } else if ((option[0] == '-') && (option[1] == '\0')) {
+ break;
+ } else {
+ /*
+ * Verify that the switch is actually an entry configuration option.
+ */
+ if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
+ != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad switch \"", string,
+ "\": must be -exact, -glob, -regexp, -name, -full, or -nonmatching",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((i + 1) == objc) {
+ goto missingArg;
+ }
+ /* Save the option in the list of configuration options */
+ node = Blt_ListGetNode(options, (char *)objv[i]);
+ if (node == NULL) {
+ node = Blt_ListCreateNode(options, (char *)objv[i]);
+ Blt_ListAppendNode(options, node);
+ }
+ i++;
+ Blt_ListSetValue(node, Tcl_GetString(objv[i]));
+ }
+ }
+
+ if ((namePattern != NULL) || (fullPattern != NULL) ||
+ (Blt_ListGetLength(options) > 0)) {
+ /*
+ * Search through the tree and look for nodes that match the
+ * current spec. Apply the input procedure to each of the
+ * matching nodes.
+ */
+ for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ if (namePattern != NULL) {
+ result = (*compareProc) (interp,
+ Blt_TreeNodeLabel(entryPtr->node), namePattern);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ if (fullPattern != NULL) {
+ Tcl_DString dString;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
+ result = (*compareProc) (interp, Tcl_DStringValue(&dString),
+ fullPattern);
+ Tcl_DStringFree(&dString);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ if (withTag != NULL) {
+ result = HasTag(tvPtr, entryPtr->node, withTag);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ for (node = Blt_ListFirstNode(options); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
+ Tcl_ResetResult(interp);
+ if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0)
+ != TCL_OK) {
+ return TCL_ERROR; /* This shouldn't happen. */
+ }
+ pattern = Blt_ListGetValue(node);
+ objPtr = Tcl_GetObjResult(interp);
+ result = (*compareProc)(interp, Tcl_GetString(objPtr), pattern);
+ if (result == invertMatch) {
+ continue; /* Failed to match */
+ }
+ }
+ /* Finally, apply the procedure to the node */
+ (*proc) (tvPtr, entryPtr);
+ }
+ Tcl_ResetResult(interp);
+ Blt_ListDestroy(options);
+ }
+ /*
+ * Apply the procedure to nodes that have been specified
+ * individually.
+ */
+ for ( /*empty*/ ; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ if (nonMatchPtr != NULL) {
+ *nonMatchPtr = invertMatch; /* return "inverted search" status */
+ }
+ return TCL_OK;
+
+ missingArg:
+ Blt_ListDestroy(options);
+ Tcl_AppendResult(interp, "missing pattern for search option \"", objv[i],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+
+}
+
+static int
+FixSelectionsApplyProc(tvPtr, entryPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+{
+ if (entryPtr->flags & ENTRY_HIDDEN) {
+ Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+ if ((tvPtr->focusPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+ if (entryPtr != tvPtr->rootPtr) {
+ entryPtr = Blt_TreeViewParentEntry(tvPtr, entryPtr);
+ tvPtr->focusPtr = (entryPtr == NULL)
+ ? tvPtr->focusPtr : entryPtr;
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr);
+ }
+ }
+ if ((tvPtr->selAnchorPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->selAnchorPtr->node))) {
+ tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+ }
+ if ((tvPtr->activePtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
+ tvPtr->activePtr = NULL;
+ }
+ Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideOp --
+ *
+ * Hides one or more nodes. Nodes can be specified by their
+ * inode, or by matching a name or data value pattern. By
+ * default, the patterns are matched exactly. They can also
+ * be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+HideOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int status, nonmatching;
+
+ status = SearchAndApplyToTree(tvPtr, interp, objc, objv,
+ HideEntryApplyProc, &nonmatching);
+
+ if (status != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * If this was an inverted search, scan back through the
+ * tree and make sure that the parents for all visible
+ * nodes are also visible. After all, if a node is supposed
+ * to be visible, its parent can't be hidden.
+ */
+ if (nonmatching) {
+ Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, MapAncestorsApplyProc, 0);
+ }
+ /*
+ * Make sure that selections are cleared from any hidden
+ * nodes. This wasn't done earlier--we had to delay it until
+ * we fixed the visibility status for the parents.
+ */
+ Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, FixSelectionsApplyProc, 0);
+
+ /* Hiding an entry only effects the visible nodes. */
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ * Mark one or more nodes to be exposed. Nodes can be specified
+ * by their inode, or by matching a name or data value pattern. By
+ * default, the patterns are matched exactly. They can also
+ * be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShowOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (SearchAndApplyToTree(tvPtr, interp, objc, objv, ShowEntryApplyProc,
+ (int *)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Converts one of more words representing indices of the entries
+ * in the hierarchy widget to their respective serial identifiers.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain the
+ * identifier of each inode found. If an inode could not be found,
+ * then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ char *string;
+ TreeViewEntry *fromPtr;
+ int usePath;
+
+ usePath = FALSE;
+ fromPtr = NULL;
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-path") == 0)) {
+ usePath = TRUE;
+ objv++, objc--;
+ }
+ if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &fromPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ objv += 2, objc -= 2;
+ }
+ if (objc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]),
+ " index ?-at tagOrId? ?-path? tagOrId\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tvPtr->fromPtr = fromPtr;
+ if (tvPtr->fromPtr == NULL) {
+ tvPtr->fromPtr = tvPtr->focusPtr;
+ }
+ if (tvPtr->fromPtr == NULL) {
+ tvPtr->fromPtr = tvPtr->rootPtr;
+ }
+ if (usePath) {
+ if (fromPtr == NULL) {
+ fromPtr = tvPtr->rootPtr;
+ }
+ string = Tcl_GetString(objv[2]);
+ entryPtr = FindPath(tvPtr, fromPtr, string);
+ if (entryPtr != NULL) {
+ Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+ }
+ } else {
+ if ((GetEntryFromObj2(tvPtr, objv[2], &entryPtr) == TCL_OK) &&
+ (entryPtr != NULL)) {
+ Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Add new entries into a hierarchy. If no node is specified,
+ * new entries will be added to the root of the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+InsertOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node, parent;
+ int insertPos;
+ int depth, count;
+ char *path;
+ Tcl_Obj *CONST *options;
+ Tcl_Obj *listObjPtr;
+ char **compArr;
+ register char **p;
+ register int n;
+ TreeViewEntry *rootPtr;
+ char *string;
+
+ rootPtr = tvPtr->rootPtr;
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
+ if (objc > 2) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ objv += 2, objc -= 2;
+ } else {
+ Tcl_AppendResult(interp, "missing argument for \"-at\" flag",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (objc == 2) {
+ Tcl_AppendResult(interp, "missing position argument", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_GetPositionFromObj(interp, objv[2], &insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ node = NULL;
+ objc -= 3, objv += 3;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ while (objc > 0) {
+ path = Tcl_GetString(objv[0]);
+ objv++, objc--;
+ /*
+ * Count the option-value pairs that follow. Count until we
+ * spot one that looks like an entry name (i.e. doesn't start
+ * with a minus "-").
+ */
+ for (count = 0; count < objc; count += 2) {
+ if (!Blt_ObjIsOption(bltTreeViewEntrySpecs, objv[count], 0)) {
+ break;
+ }
+ }
+ if (count > objc) {
+ count = objc;
+ }
+ options = objv;
+ objc -= count, objv += count;
+
+ if (tvPtr->trimLeft != NULL) {
+ register char *s1, *s2;
+
+ /* Trim off leading character string if one exists. */
+ for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+ if (*s1 != *s2) {
+ break;
+ }
+ }
+ if (*s2 == '\0') {
+ path = s1;
+ }
+ }
+ /*
+ * Split the path and find the parent node of the path.
+ */
+ compArr = &path;
+ depth = 1;
+ if (tvPtr->pathSep != SEPARATOR_NONE) {
+ if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
+ goto error;
+ }
+ if (depth == 0) {
+ Blt_Free(compArr);
+ continue; /* Root already exists. */
+ }
+ }
+ parent = rootPtr->node;
+ depth--;
+
+ /* Verify each component in the path preceding the tail. */
+ for (n = 0, p = compArr; n < depth; n++, p++) {
+ node = Blt_TreeFindChild(parent, *p);
+ if (node == NULL) {
+ if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
+ Tcl_AppendResult(interp, "can't find path component \"",
+ *p, "\" in \"", path, "\"", (char *)NULL);
+ goto error;
+ }
+ node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
+ if (node == NULL) {
+ goto error;
+ }
+ if (Blt_TreeViewCreateEntry(tvPtr, node, 0, (Tcl_Obj **)NULL)
+ != TCL_OK) {
+ goto error;
+ }
+ }
+ parent = node;
+ }
+ node = NULL;
+ if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) &&
+ (Blt_TreeFindChild(parent, *p) != NULL)) {
+ Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
+ path, "\"", (char *)NULL);
+ goto error;
+ }
+ node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
+ if (node == NULL) {
+ goto error;
+ }
+ if (Blt_TreeViewCreateEntry(tvPtr, node, count, options) != TCL_OK) {
+ goto error;
+ }
+ if (compArr != &path) {
+ Blt_Free(compArr);
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
+ }
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+
+ error:
+ if (compArr != &path) {
+ Blt_Free(compArr);
+ }
+ Tcl_DecrRefCount(listObjPtr);
+ if (node != NULL) {
+ DeleteNode(tvPtr, node);
+ }
+ return TCL_ERROR;
+}
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddOp --
+ *
+ * Add new entries into a hierarchy. If no node is specified,
+ * new entries will be added to the root of the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_SwitchParseProc StringToChild;
+#define INSERT_BEFORE (ClientData)0
+#define INSERT_AFTER (ClientData)1
+static Blt_SwitchCustom beforeSwitch =
+{
+ StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
+};
+static Blt_SwitchCustom afterSwitch =
+{
+ StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
+};
+
+typedef struct {
+ int insertPos;
+ Blt_TreeNode parent;
+} InsertData;
+
+static Blt_SwitchSpec insertSwitches[] =
+{
+ {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0,
+ &afterSwitch},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
+ {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
+ &beforeSwitch},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static int
+AddOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode node, parent;
+ int insertPos;
+ int depth, count;
+ char *path;
+ Tcl_Obj *CONST *options;
+ Tcl_Obj *listObjPtr;
+ char **compArr;
+ register char **p;
+ register int n;
+ TreeViewEntry *rootPtr;
+ char *string;
+
+ memset(&data, 0, sizeof(data));
+ data.maxDepth = -1;
+ data.cmdPtr = cmdPtr;
+
+ /* Process any leading switches */
+ i = Blt_ProcessObjSwitches(interp, addSwitches, objc - 2, objv + 2,
+ (char *)&data, BLT_CONFIG_OBJV_PARTIAL);
+ if (i < 0) {
+ return TCL_ERROR;
+ }
+ i += 2;
+ /* Should have at the starting node */
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "starting node argument is missing",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_TreeViewGetEntry(tvPtr, objv[i], &rootPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ objv += i, objc -= i;
+ node = NULL;
+
+ /* Process sections of path ?options? */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ while (objc > 0) {
+ path = Tcl_GetString(objv[0]);
+ objv++, objc--;
+ /*
+ * Count the option-value pairs that follow. Count until we
+ * spot one that looks like an entry name (i.e. doesn't start
+ * with a minus "-").
+ */
+ for (count = 0; count < objc; count += 2) {
+ if (!Blt_ObjIsOption(bltTreeViewEntrySpecs, objv[count], 0)) {
+ break;
+ }
+ }
+ if (count > objc) {
+ count = objc;
+ }
+ options = objv;
+ objc -= count, objv += count;
+
+ if (tvPtr->trimLeft != NULL) {
+ register char *s1, *s2;
+
+ /* Trim off leading character string if one exists. */
+ for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+ if (*s1 != *s2) {
+ break;
+ }
+ }
+ if (*s2 == '\0') {
+ path = s1;
+ }
+ }
+ /*
+ * Split the path and find the parent node of the path.
+ */
+ compArr = &path;
+ depth = 1;
+ if (tvPtr->pathSep != SEPARATOR_NONE) {
+ if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
+ goto error;
+ }
+ if (depth == 0) {
+ Blt_Free(compArr);
+ continue; /* Root already exists. */
+ }
+ }
+ parent = rootPtr->node;
+ depth--;
+
+ /* Verify each component in the path preceding the tail. */
+ for (n = 0, p = compArr; n < depth; n++, p++) {
+ node = Blt_TreeFindChild(parent, *p);
+ if (node == NULL) {
+ if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
+ Tcl_AppendResult(interp, "can't find path component \"",
+ *p, "\" in \"", path, "\"", (char *)NULL);
+ goto error;
+ }
+ node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
+ if (node == NULL) {
+ goto error;
+ }
+ if (Blt_TreeViewCreateEntry(tvPtr, node, 0, (Tcl_Obj **)NULL)
+ != TCL_OK) {
+ goto error;
+ }
+ }
+ parent = node;
+ }
+ node = NULL;
+ if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) &&
+ (Blt_TreeFindChild(parent, *p) != NULL)) {
+ Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
+ path, "\"", (char *)NULL);
+ goto error;
+ }
+ node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
+ if (node == NULL) {
+ goto error;
+ }
+ if (Blt_TreeViewCreateEntry(tvPtr, node, count, options) != TCL_OK) {
+ goto error;
+ }
+ if (compArr != &path) {
+ Blt_Free(compArr);
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
+ }
+ tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+
+ error:
+ if (compArr != &path) {
+ Blt_Free(compArr);
+ }
+ Tcl_DecrRefCount(listObjPtr);
+ if (node != NULL) {
+ DeleteNode(tvPtr, node);
+ }
+ return TCL_ERROR;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes nodes from the hierarchy. Deletes one or more entries
+ * (except root). In all cases, nodes are removed recursively.
+ *
+ * Note: There's no need to explicitly clean up Entry structures
+ * or request a redraw of the widget. When a node is
+ * deleted in the tree, all of the Tcl_Objs representing
+ * the various data fields are also removed. The treeview
+ * widget store the Entry structure in a data field. So it's
+ * automatically cleaned up when FreeEntryInternalRep is
+ * called.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TagInfo info;
+ TreeViewEntry *entryPtr;
+ register int i;
+
+ for (i = 2; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if (entryPtr == tvPtr->rootPtr) {
+ Blt_TreeNode next, node;
+
+ /*
+ * Don't delete the root node. We implicitly assume
+ * that even an empty tree has at a root. Instead
+ * delete all the children regardless if they're closed
+ * or hidden.
+ */
+ for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
+ node = next) {
+ next = Blt_TreeNextSibling(node);
+ DeleteNode(tvPtr, node);
+ }
+ } else {
+ DeleteNode(tvPtr, entryPtr->node);
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ * Move an entry into a new location in the hierarchy.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeNode parent;
+ TreeViewEntry *srcPtr, *destPtr;
+ char c;
+ int action;
+ char *string;
+ TagInfo info;
+
+#define MOVE_INTO (1<<0)
+#define MOVE_BEFORE (1<<1)
+#define MOVE_AFTER (1<<2)
+ if (FindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[3]);
+ c = string[0];
+ action = MOVE_INTO;
+ if ((c == 'i') && (strcmp(string, "into") == 0)) {
+ action = MOVE_INTO;
+ } else if ((c == 'b') && (strcmp(string, "before") == 0)) {
+ action = MOVE_BEFORE;
+ } else if ((c == 'a') && (strcmp(string, "after") == 0)) {
+ action = MOVE_AFTER;
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", string,
+ "\": should be into, before, or after", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_TreeViewGetEntry(tvPtr, objv[4], &destPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (srcPtr = FirstTaggedEntry(&info); srcPtr != NULL;
+ srcPtr = NextTaggedEntry(&info)) {
+ /* Verify they aren't ancestors. */
+ if (Blt_TreeIsAncestor(srcPtr->node, destPtr->node)) {
+ Tcl_DString dString;
+ char *path;
+
+ path = Blt_TreeViewGetFullName(tvPtr, srcPtr, 1, &dString);
+ Tcl_AppendResult(interp, "can't move node: \"", path,
+ "\" is an ancestor of \"", Tcl_GetString(objv[4]),
+ "\"", (char *)NULL);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ parent = Blt_TreeNodeParent(destPtr->node);
+ if (parent == NULL) {
+ action = MOVE_INTO;
+ }
+ switch (action) {
+ case MOVE_INTO:
+ Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, destPtr->node,
+ (Blt_TreeNode)NULL);
+ break;
+
+ case MOVE_BEFORE:
+ Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent, destPtr->node);
+ break;
+
+ case MOVE_AFTER:
+ Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent,
+ Blt_TreeNextSibling(destPtr->node));
+ break;
+ }
+ }
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewButton *buttonPtr = &tvPtr->button;
+ int x, y; /* Screen coordinates of the test point. */
+ register TreeViewEntry *entryPtr;
+ int isRoot;
+ char *string;
+
+ isRoot = FALSE;
+ string = Tcl_GetString(objv[2]);
+ if (strcmp("-root", string) == 0) {
+ isRoot = TRUE;
+ objv++, objc--;
+ }
+ if (objc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
+ " ?-root? x y\"", (char *)NULL);
+ return TCL_ERROR;
+
+ }
+ if ((Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[2], &x) != TCL_OK) ||
+ (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (tvPtr->nVisible == 0) {
+ return TCL_OK;
+ }
+ if (isRoot) {
+ int rootX, rootY;
+
+ Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
+ x -= rootX;
+ y -= rootY;
+ }
+ entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
+ if (entryPtr == NULL) {
+ return TCL_OK;
+ }
+ x = WORLDX(tvPtr, x);
+ y = WORLDY(tvPtr, y);
+ if (objc > 4) {
+ char *where;
+ int labelX, labelY, depth;
+ TreeViewImage image;
+
+ where = "";
+ if (entryPtr->flags & ENTRY_HAS_BUTTON) {
+ int buttonX, buttonY;
+
+ buttonX = entryPtr->worldX + entryPtr->buttonX;
+ buttonY = entryPtr->worldY + entryPtr->buttonY;
+ if ((x >= buttonX) && (x < (buttonX + buttonPtr->width)) &&
+ (y >= buttonY) && (y < (buttonY + buttonPtr->height))) {
+ where = "button";
+ goto done;
+ }
+ }
+ depth = DEPTH(tvPtr, entryPtr->node);
+
+ image = Blt_TreeViewIconImage(tvPtr, entryPtr);
+ if (image != NULL) {
+ int imageWidth, imageHeight, entryHeight;
+ int imageX, imageY;
+
+ entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+ imageHeight = TreeViewImageHeight(image);
+ imageWidth = TreeViewImageWidth(image);
+ imageX = entryPtr->worldX + ICONWIDTH(depth);
+ imageY = entryPtr->worldY;
+ if (tvPtr->flatView) {
+ imageX += (ICONWIDTH(0) - imageWidth) / 2;
+ } else {
+ imageX += (ICONWIDTH(depth + 1) - imageWidth) / 2;
+ }
+ imageY += (entryHeight - imageHeight) / 2;
+ if ((x >= imageX) && (x <= (imageX + imageWidth)) &&
+ (y >= imageY) && (y < (imageY + imageHeight))) {
+ where = "icon";
+ goto done;
+ }
+ }
+ labelX = entryPtr->worldX + ICONWIDTH(depth);
+ labelY = entryPtr->worldY;
+ if (!tvPtr->flatView) {
+ labelX += ICONWIDTH(depth + 1) + 4;
+ }
+ if ((x >= labelX) && (x < (labelX + entryPtr->labelWidth)) &&
+ (y >= labelY) && (y < (labelY + entryPtr->labelHeight))) {
+ where = "label";
+ }
+ done:
+ if (Tcl_SetVar(interp, Tcl_GetString(objv[4]), where,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+ return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+OpenOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ TagInfo info;
+ int recurse, result;
+ register int i;
+
+ recurse = FALSE;
+ if (objc > 2) {
+ int length;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-recurse", length) == 0)) {
+ objv++, objc--;
+ recurse = TRUE;
+ }
+ }
+ for (i = 2; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if (recurse) {
+ result = Blt_TreeViewApply(tvPtr, entryPtr,
+ Blt_TreeViewOpenEntry, 0);
+ } else {
+ result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Make sure ancestors of this node aren't hidden. */
+ MapAncestors(tvPtr, entryPtr);
+ }
+ }
+ /*FIXME: This is only for flattened entries. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns the node identifiers in a given range.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RangeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr, *firstPtr, *lastPtr;
+ unsigned int mask;
+ int length;
+ Tcl_Obj *listObjPtr, *objPtr;
+ char *string;
+
+ mask = 0;
+ string = Tcl_GetString(objv[2]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-open", length) == 0)) {
+ objv++, objc--;
+ mask |= ENTRY_CLOSED;
+ }
+ if (Blt_TreeViewGetEntry(tvPtr, objv[2], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 3) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ lastPtr = LastEntry(tvPtr, firstPtr, mask);
+ }
+ if (mask & ENTRY_CLOSED) {
+ if (firstPtr->flags & ENTRY_HIDDEN) {
+ Tcl_AppendResult(interp, "first node \"", Tcl_GetString(objv[2]),
+ "\" is hidden.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (lastPtr->flags & ENTRY_HIDDEN) {
+ Tcl_AppendResult(interp, "last node \"", Tcl_GetString(objv[3]),
+ "\" is hidden.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * The relative order of the first/last markers determines the
+ * direction.
+ */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
+ for (entryPtr = lastPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewPrevEntry(tvPtr, entryPtr, mask)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (entryPtr == firstPtr) {
+ break;
+ }
+ }
+ } else {
+ for (entryPtr = firstPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, mask)) {
+ objPtr = NodeToObj(entryPtr->node);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (entryPtr == lastPtr) {
+ break;
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ * Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x, y;
+ char c;
+ unsigned int length;
+ int oper;
+ char *string;
+ Tk_Window tkwin;
+
+#define SCAN_MARK 1
+#define SCAN_DRAGTO 2
+ string = Tcl_GetString(objv[2]);
+ c = string[0];
+ tkwin = tvPtr->tkwin;
+ length = strlen(string);
+ if ((c == 'm') && (strncmp(string, "mark", length) == 0)) {
+ oper = SCAN_MARK;
+ } else if ((c == 'd') && (strncmp(string, "dragto", length) == 0)) {
+ oper = SCAN_DRAGTO;
+ } else {
+ Tcl_AppendResult(interp, "bad scan operation \"", string,
+ "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Blt_GetPixelsFromObj(interp, tkwin, objv[3], 0, &x) != TCL_OK) ||
+ (Blt_GetPixelsFromObj(interp, tkwin, objv[4], 0, &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (oper == SCAN_MARK) {
+ tvPtr->scanAnchorX = x;
+ tvPtr->scanAnchorY = y;
+ tvPtr->scanX = tvPtr->xOffset;
+ tvPtr->scanY = tvPtr->yOffset;
+ } else {
+ int worldX, worldY;
+ int dx, dy;
+
+ dx = tvPtr->scanAnchorX - x;
+ dy = tvPtr->scanAnchorY - y;
+ worldX = tvPtr->scanX + (10 * dx);
+ worldY = tvPtr->scanY + (10 * dy);
+
+ if (worldX < 0) {
+ worldX = 0;
+ } else if (worldX >= tvPtr->worldWidth) {
+ worldX = tvPtr->worldWidth - tvPtr->xScrollUnits;
+ }
+ if (worldY < 0) {
+ worldY = 0;
+ } else if (worldY >= tvPtr->worldHeight) {
+ worldY = tvPtr->worldHeight - tvPtr->yScrollUnits;
+ }
+ tvPtr->xOffset = worldX;
+ tvPtr->yOffset = worldY;
+ tvPtr->flags |= TV_SCROLL;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int width, height;
+ int x, y;
+ Tk_Anchor anchor;
+ int left, right, top, bottom;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ anchor = TK_ANCHOR_W; /* Default anchor is West */
+ if ((string[0] == '-') && (strcmp(string, "-anchor") == 0)) {
+ if (objc == 3) {
+ Tcl_AppendResult(interp, "missing \"-anchor\" argument",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_GetAnchorFromObj(interp, objv[3], &anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ objc -= 2, objv += 2;
+ }
+ if (objc == 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", objv[0],
+ "see ?-anchor anchor? tagOrId\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (entryPtr == NULL) {
+ return TCL_OK;
+ }
+ if (entryPtr->flags & ENTRY_HIDDEN) {
+ MapAncestors(tvPtr, entryPtr);
+ tvPtr->flags |= TV_SCROLL;
+ /*
+ * If the entry wasn't previously exposed, its world coordinates
+ * aren't likely to be valid. So re-compute the layout before
+ * we try to see the viewport to the entry's location.
+ */
+ Blt_TreeViewComputeLayout(tvPtr);
+ }
+ width = VPORTWIDTH(tvPtr);
+ height = VPORTHEIGHT(tvPtr);
+
+ /*
+ * XVIEW: If the entry is left or right of the current view, adjust
+ * the offset. If the entry is nearby, adjust the view just
+ * a bit. Otherwise, center the entry.
+ */
+ left = tvPtr->xOffset;
+ right = tvPtr->xOffset + width;
+
+ switch (anchor) {
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_SW:
+ x = 0;
+ break;
+ case TK_ANCHOR_E:
+ case TK_ANCHOR_NE:
+ case TK_ANCHOR_SE:
+ x = entryPtr->worldX + entryPtr->width +
+ ICONWIDTH(DEPTH(tvPtr, entryPtr->node)) - width;
+ break;
+ default:
+ if (entryPtr->worldX < left) {
+ x = entryPtr->worldX;
+ } else if ((entryPtr->worldX + entryPtr->width) > right) {
+ x = entryPtr->worldX + entryPtr->width - width;
+ } else {
+ x = tvPtr->xOffset;
+ }
+ break;
+ }
+ /*
+ * YVIEW: If the entry is above or below the current view, adjust
+ * the offset. If the entry is nearby, adjust the view just
+ * a bit. Otherwise, center the entry.
+ */
+ top = tvPtr->yOffset;
+ bottom = tvPtr->yOffset + height;
+
+ switch (anchor) {
+ case TK_ANCHOR_N:
+ y = tvPtr->yOffset;
+ break;
+ case TK_ANCHOR_NE:
+ case TK_ANCHOR_NW:
+ y = entryPtr->worldY - (height / 2);
+ break;
+ case TK_ANCHOR_S:
+ case TK_ANCHOR_SE:
+ case TK_ANCHOR_SW:
+ y = entryPtr->worldY + entryPtr->height - height;
+ break;
+ default:
+ if (entryPtr->worldY < top) {
+ y = entryPtr->worldY;
+ } else if ((entryPtr->worldY + entryPtr->height) > bottom) {
+ y = entryPtr->worldY + entryPtr->height - height;
+ } else {
+ y = tvPtr->yOffset;
+ }
+ break;
+ }
+ if ((y != tvPtr->yOffset) || (x != tvPtr->xOffset)) {
+ /* tvPtr->xOffset = x; */
+ tvPtr->yOffset = y;
+ tvPtr->flags |= TV_SCROLL;
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+void
+Blt_TreeViewClearSelection(tvPtr)
+ TreeView *tvPtr;
+{
+ Blt_DeleteHashTable(&tvPtr->selectTable);
+ Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
+ Blt_ChainReset(tvPtr->selChainPtr);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ if (tvPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(tvPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LostSelection --
+ *
+ * This procedure is called back by Tk when the selection is grabbed
+ * away.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is
+ * marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LostSelection(clientData)
+ ClientData clientData; /* Information about the widget. */
+{
+ TreeView *tvPtr = clientData;
+
+ if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
+ return;
+ }
+ Blt_TreeViewClearSelection(tvPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectRange --
+ *
+ * Sets the selection flag for a range of nodes. The range is
+ * determined by two pointers which designate the first/last
+ * nodes of the range.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectRange(tvPtr, fromPtr, toPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *fromPtr, *toPtr;
+{
+ if (tvPtr->flatView) {
+ register int i;
+
+ if (fromPtr->flatIndex > toPtr->flatIndex) {
+ for (i = fromPtr->flatIndex; i >= toPtr->flatIndex; i--) {
+ SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
+ }
+ } else {
+ for (i = fromPtr->flatIndex; i <= toPtr->flatIndex; i++) {
+ SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
+ }
+ }
+ } else {
+ TreeViewEntry *entryPtr;
+ TreeViewIterProc *proc;
+ /* From the range determine the direction to select entries. */
+
+ proc = (Blt_TreeIsBefore(toPtr->node, fromPtr->node))
+ ? Blt_TreeViewPrevEntry : Blt_TreeViewNextEntry;
+ for (entryPtr = fromPtr; entryPtr != NULL;
+ entryPtr = (*proc)(tvPtr, entryPtr, ENTRY_MASK)) {
+ SelectEntryApplyProc(tvPtr, entryPtr);
+ if (entryPtr == toPtr) {
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionAnchorOp --
+ *
+ * Sets the selection anchor to the element given by a index.
+ * The selection anchor is the end of the selection that is fixed
+ * while dragging out a selection with the mouse. The index
+ * "anchor" may be used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionAnchorOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+
+ if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry
+ * is selected. */
+ tvPtr->selAnchorPtr = entryPtr;
+ tvPtr->selMarkPtr = NULL;
+ if (entryPtr != NULL) {
+ Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionClearallOp
+ *
+ * Clears the entire selection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionClearallOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ Blt_TreeViewClearSelection(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionIncludesOp
+ *
+ * Returns 1 if the element indicated by index is currently
+ * selected, 0 if it isn't.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionIncludesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int bool;
+
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionMarkOp --
+ *
+ * Sets the selection mark to the element given by a index.
+ * The selection anchor is the end of the selection that is movable
+ * while dragging out a selection with the mouse. The index
+ * "mark" may be used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionMarkOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+
+ if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (tvPtr->selAnchorPtr == NULL) {
+ Tcl_AppendResult(interp, "selection anchor must be set first",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tvPtr->selMarkPtr != entryPtr) {
+ Blt_ChainLink *linkPtr, *nextPtr;
+ TreeViewEntry *selectPtr;
+
+ /* Deselect entry from the list all the way back to the anchor. */
+ for (linkPtr = Blt_ChainLastLink(tvPtr->selChainPtr); linkPtr != NULL;
+ linkPtr = nextPtr) {
+ nextPtr = Blt_ChainPrevLink(linkPtr);
+ selectPtr = Blt_ChainGetValue(linkPtr);
+ if (selectPtr == tvPtr->selAnchorPtr) {
+ break;
+ }
+ Blt_TreeViewDeselectEntry(tvPtr, selectPtr);
+ }
+ tvPtr->flags &= ~TV_SELECT_MASK;
+ tvPtr->flags |= TV_SELECT_SET;
+ SelectRange(tvPtr, tvPtr->selAnchorPtr, entryPtr);
+ Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+ tvPtr->selMarkPtr = entryPtr;
+
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ if (tvPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(tvPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionPresentOp
+ *
+ * Returns 1 if there is a selection and 0 if it isn't.
+ *
+ * Results:
+ * A standard Tcl result. interp->result will contain a
+ * boolean string indicating if there is a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionPresentOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int bool;
+
+ bool = (Blt_ChainGetLength(tvPtr->selChainPtr) > 0);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionSetOp
+ *
+ * Selects, deselects, or toggles all of the elements in the
+ * range between first and last, inclusive, without affecting the
+ * selection state of elements outside that range.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionSetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *firstPtr, *lastPtr;
+ char *string;
+
+ tvPtr->flags &= ~TV_SELECT_MASK;
+ string = Tcl_GetString(objv[2]);
+ switch (string[0]) {
+ case 's':
+ tvPtr->flags |= TV_SELECT_SET;
+ break;
+ case 'c':
+ tvPtr->flags |= TV_SELECT_CLEAR;
+ break;
+ case 't':
+ tvPtr->flags |= TV_SELECT_TOGGLE;
+ break;
+ }
+ if (Blt_TreeViewGetEntry(tvPtr, objv[3], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((firstPtr->flags & ENTRY_HIDDEN) &&
+ (!(tvPtr->flags & TV_SELECT_CLEAR))) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[3]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ lastPtr = firstPtr;
+ if (objc > 4) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[4], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((lastPtr->flags & ENTRY_HIDDEN) &&
+ (!(tvPtr->flags & TV_SELECT_CLEAR))) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[4]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (firstPtr == lastPtr) {
+ SelectEntryApplyProc(tvPtr, firstPtr);
+ } else {
+ SelectRange(tvPtr, firstPtr, lastPtr);
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry
+ * is selected. */
+ if (tvPtr->selAnchorPtr == NULL) {
+ tvPtr->selAnchorPtr = firstPtr;
+ }
+ if (tvPtr->flags & TV_SELECT_EXPORT) {
+ Tk_OwnSelection(tvPtr->tkwin, XA_PRIMARY, LostSelection, tvPtr);
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ if (tvPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(tvPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ * This procedure handles the individual options for text
+ * selections. The selected text is designated by start and end
+ * indices into the text pool. The selected segment has both a
+ * anchored and unanchored ends.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec selectionOps[] =
+{
+ {"anchor", 1, (Blt_Op)SelectionAnchorOp, 4, 4, "tagOrId",},
+ {"clear", 5, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+ {"clearall", 6, (Blt_Op)SelectionClearallOp, 3, 3, "",},
+ {"includes", 1, (Blt_Op)SelectionIncludesOp, 4, 4, "tagOrId",},
+ {"mark", 1, (Blt_Op)SelectionMarkOp, 4, 4, "tagOrId",},
+ {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, "",},
+ {"set", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+ {"toggle", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+};
+static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
+
+static int
+SelectionOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleCgetOp --
+ *
+ * .t style cget "styleName" -background
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewStyle *stylePtr;
+
+ if (Blt_TreeViewGetStyle(interp, tvPtr, objv[3], &stylePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
+ stylePtr->classPtr->specsPtr, (char *)tvPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure a style.
+ *
+ * .t style configure "styleName" option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for stylePtr; old resources get freed, if there
+ * were any.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewStyle *stylePtr;
+
+ if (Blt_TreeViewGetStyle(interp, tvPtr, objv[3], &stylePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ stylePtr->classPtr->specsPtr, (char *)stylePtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 5) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
+ stylePtr->classPtr->specsPtr, (char *)stylePtr, objv[5], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin,
+ stylePtr->classPtr->specsPtr, objc - 4, objv + 4, (char *)stylePtr,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleCreateOp --
+ *
+ * .t style create type "styleName" -background blue
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleCreateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewStyle *stylePtr;
+ int type;
+ char *string;
+ int isNew;
+ Blt_HashEntry *hPtr;
+ static char *classNames[3] = {
+ "TextStyle", "CheckboxStyle", "ComboboxStyle"
+ };
+
+ string = Tcl_GetString(objv[3]);
+ if (strcmp(string, "text") == 0) {
+ type = STYLE_TEXT;
+ } else if (strcmp(string, "checkbox") == 0) {
+ type = STYLE_CHECKBOX;
+ } else if (strcmp(string, "combobox") == 0) {
+ type = STYLE_COMBOBOX;
+ } else {
+ Tcl_AppendResult(interp, "unknown style type \"", string,
+ "\": should be \"text\", \"checkbox\", or \"combobox\".",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[4]);
+ hPtr = Blt_CreateHashEntry(&tvPtr->styleTable, string, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "style \"", string, "\" already exists.",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ stylePtr = Blt_TreeViewCreateStyle(type, string);
+ stylePtr->hashPtr = hPtr;
+ if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, string,
+ classNames[type], stylePtr->classPtr->specsPtr, objc - 5, objv + 5,
+ (char *)stylePtr, 0) != TCL_OK) {
+ Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+ return TCL_ERROR;
+ }
+ Blt_SetHashValue(hPtr, stylePtr);
+ Tcl_SetObjResult(interp, objv[4]);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleDeleteOp --
+ *
+ * Deletes one or more styles. A style still may be used
+ * after it has been deleted if it's still in use.
+ *
+ * .t style delete "styleName"...
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleDeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewStyle *stylePtr;
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ if (Blt_TreeViewGetStyle(interp, tvPtr, objv[i], &stylePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleNamesOp --
+ *
+ * Lists the names of all the current styles in the treeview widget.
+ *
+ * .t style names
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleNamesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Tcl_Obj *listObjPtr, *objPtr;
+ TreeViewStyle *stylePtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hPtr = Blt_FirstHashEntry(&tvPtr->styleTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ stylePtr = Blt_GetHashValue(hPtr);
+ objPtr = Tcl_NewStringObj(stylePtr->name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleSetOp --
+ *
+ * Sets a style for a given key for all the ids given.
+ *
+ * .t style set styleName key node...
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleSetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeKey key;
+ TreeViewEntry *entryPtr;
+ TreeViewStyle *stylePtr, *oldStylePtr;
+ TagInfo info;
+ int i;
+
+ if (Blt_TreeViewGetStyle(interp, tvPtr, objv[3], &stylePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ key = Blt_TreeGetKey(Tcl_GetString(objv[4]));
+ stylePtr->flags |= STYLE_LAYOUT;
+ for (i = 5; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ register TreeViewValue *valuePtr;
+
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ if (valuePtr->columnPtr->key == key) {
+ stylePtr->refCount++;
+ oldStylePtr = valuePtr->stylePtr;
+ valuePtr->stylePtr = stylePtr;
+ if (oldStylePtr != NULL) {
+ Blt_TreeViewFreeStyle(tvPtr, oldStylePtr);
+ }
+ break;
+ }
+ }
+ }
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleUnsetOp --
+ *
+ * Removes a style for a given key for all the ids given.
+ * The cell's style is returned to its default state.
+ *
+ * .t style unset styleName key node...
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleUnsetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_TreeKey key;
+ TreeViewEntry *entryPtr;
+ TreeViewStyle *stylePtr;
+ TagInfo info;
+ int i;
+
+ if (Blt_TreeViewGetStyle(interp, tvPtr, objv[3], &stylePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ key = Blt_TreeGetKey(Tcl_GetString(objv[4]));
+ stylePtr->flags |= STYLE_LAYOUT;
+ for (i = 5; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ register TreeViewValue *valuePtr;
+
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ if (valuePtr->columnPtr->key == key) {
+ if (valuePtr->stylePtr != NULL) {
+ Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
+ valuePtr->stylePtr = NULL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleOp --
+ *
+ * .t style cget "highlight" -foreground
+ * .t style configure "highlight" -fg blue -bg green
+ * .t style create checkbox "highlight"
+ * .t style create optionmenu "highlight"
+ * .t style create text "highlight"
+ * .t style delete "highlight"
+ * .t style get "mtime" $node
+ * .t style names
+ * .t style set "mtime" "highlight" all
+ * .t style unset "mtime" all
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec styleOps[] = {
+ {"cget", 2, (Blt_Op)StyleCgetOp, 5, 0, "styleName option",},
+ {"configure", 2, (Blt_Op)StyleConfigureOp, 5, 0, "styleName options...",},
+ {"create", 1, (Blt_Op)StyleCreateOp, 5, 0, "type styleName options...",},
+ {"delete", 2, (Blt_Op)StyleDeleteOp, 5, 0, "styleName...",},
+ {"names", 1, (Blt_Op)StyleNamesOp, 3, 0, "",},
+ {"set", 1, (Blt_Op)StyleSetOp, 4, 0, "key styleName tagOrId...",},
+ {"unset", 1, (Blt_Op)StyleUnsetOp, 4, 0, "key tagOrId",},
+};
+
+static int nStyleOps = sizeof(styleOps) / sizeof(Blt_OpSpec);
+
+static int
+StyleOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nStyleOps, styleOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc)(tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagForgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TagForgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+
+ for (i = 3; i < objc; i++) {
+ Blt_TreeForgetTag(tvPtr->tagTablePtr, Tcl_GetString(objv[i]));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagNamesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Tcl_Obj *listObjPtr, *objPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ objPtr = Tcl_NewStringObj("all", -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *tagName;
+ Blt_TreeTagTable *tablePtr = tvPtr->tagTablePtr;
+
+ objPtr = Tcl_NewStringObj("root", -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ for (hPtr = Blt_FirstHashEntry(&tablePtr->table, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ tagName = Blt_GetHashKey(&tablePtr->table, hPtr);
+ objPtr = Tcl_NewStringObj(tagName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else {
+ register int i;
+ TreeViewEntry *entryPtr;
+ Blt_List list;
+ Blt_ListNode listNode;
+
+ for (i = 3; i < objc; i++) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+ Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list);
+ for (listNode = Blt_ListFirstNode(list); listNode != NULL;
+ listNode = Blt_ListNextNode(listNode)) {
+ objPtr = Tcl_NewStringObj(Blt_ListGetKey(listNode), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Blt_ListDestroy(list);
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNodesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagNodesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable nodeTable;
+ Blt_TreeNode node;
+ TagInfo info;
+ Tcl_Obj *listObjPtr;
+ Tcl_Obj *objPtr;
+ TreeViewEntry *entryPtr;
+ int isNew;
+ register int i;
+
+ Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
+ for (i = 3; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ Blt_CreateHashEntry(&nodeTable, (char *)entryPtr->node, &isNew);
+ }
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
+ objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ Blt_DeleteHashTable(&nodeTable);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagAddOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TagAddOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ register int i;
+ char *tagName;
+ TagInfo info;
+
+ tagName = Tcl_GetString(objv[3]);
+ tvPtr->fromPtr = NULL;
+ if (isdigit(UCHAR(tagName[0]))) {
+ Tcl_AppendResult(interp, "invalid tag \"", tagName,
+ "\": can't start with digit", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tagName[0] == '@') {
+ Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
+ "\": can't start with \"@\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+ Tcl_AppendResult(interp, "invalid tag \"", tagName,
+ "\": is a special id", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 4; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if (AddTag(tvPtr, entryPtr->node, tagName) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TagDeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ char *tagName;
+ Blt_HashTable *tablePtr;
+ TagInfo info;
+
+ tagName = Tcl_GetString(objv[3]);
+ tablePtr = Blt_TreeTagHashTable(tvPtr->tagTablePtr, tagName);
+ if (tablePtr != NULL) {
+ register int i;
+ Blt_HashEntry *hPtr;
+ TreeViewEntry *entryPtr;
+
+ for (i = 4; i < objc; i++) {
+ if (FindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ hPtr = Blt_FindHashEntry(tablePtr, (char *)entryPtr->node);
+ if (hPtr != NULL) {
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec tagOps[] = {
+ {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag id...",},
+ {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag id...",},
+ {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
+ {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?id...?",},
+ {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
+};
+
+static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
+
+static int
+TagOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv,
+ 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc)(tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+ToggleOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ TagInfo info;
+
+ if (FindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (entryPtr = FirstTaggedEntry(&info); entryPtr != NULL;
+ entryPtr = NextTaggedEntry(&info)) {
+ if (entryPtr == NULL) {
+ return TCL_OK;
+ }
+ if (entryPtr->flags & ENTRY_CLOSED) {
+ Blt_TreeViewOpenEntry(tvPtr, entryPtr);
+ } else {
+ Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+ if ((tvPtr->focusPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+ tvPtr->focusPtr = entryPtr;
+ Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr);
+ }
+ if ((tvPtr->selAnchorPtr != NULL) &&
+ (Blt_TreeIsAncestor(entryPtr->node,
+ tvPtr->selAnchorPtr->node))) {
+ tvPtr->selAnchorPtr = NULL;
+ }
+ Blt_TreeViewCloseEntry(tvPtr, entryPtr);
+ }
+ }
+ tvPtr->flags |= TV_SCROLL;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+static int
+XViewOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int width, worldWidth;
+
+ width = VPORTWIDTH(tvPtr);
+ worldWidth = tvPtr->worldWidth;
+ if (objc == 2) {
+ double fract;
+ Tcl_Obj *listObjPtr;
+
+ /*
+ * Note that we are bounding the fractions between 0.0 and 1.0
+ * to support the "canvas"-style of scrolling.
+ */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ fract = (double)tvPtr->xOffset / worldWidth;
+ fract = CLAMP(fract, 0.0, 1.0);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+ fract = (double)(tvPtr->xOffset + width) / worldWidth;
+ fract = CLAMP(fract, 0.0, 1.0);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->xOffset,
+ worldWidth, width, tvPtr->xScrollUnits, tvPtr->scrollMode)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->flags |= TV_XSCROLL;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+static int
+YViewOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int height, worldHeight;
+
+ height = VPORTHEIGHT(tvPtr);
+ worldHeight = tvPtr->worldHeight;
+ if (objc == 2) {
+ double fract;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ /* Report first and last fractions */
+ fract = (double)tvPtr->yOffset / worldHeight;
+ fract = CLAMP(fract, 0.0, 1.0);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+ fract = (double)(tvPtr->yOffset + height) / worldHeight;
+ fract = CLAMP(fract, 0.0, 1.0);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->yOffset,
+ worldHeight, height, tvPtr->yScrollUnits, tvPtr->scrollMode)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->flags |= TV_SCROLL;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Blt_TreeViewWidgetInstCmd --
+ *
+ * This procedure is invoked to process commands on behalf of
+ * the treeview widget.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec treeViewOps[] =
+{
+ {"bbox", 2, (Blt_Op)BboxOp, 3, 0, "tagOrId...",},
+ {"bind", 2, (Blt_Op)BindOp, 3, 5, "tagName ?sequence command?",},
+ {"button", 2, (Blt_Op)ButtonOp, 2, 0, "args",},
+ {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+ {"close", 2, (Blt_Op)CloseOp, 2, 0, "?-recurse? tagOrId...",},
+ {"column", 3, (Blt_Op)Blt_TreeViewColumnOp, 2, 0, "oper args",},
+ {"configure", 3, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+ {"curselection", 2, (Blt_Op)CurselectionOp, 2, 2, "",},
+ {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "tagOrId ?tagOrId...?",},
+ {"entry", 1, (Blt_Op)EntryOp, 2, 0, "oper args",},
+ {"find", 2, (Blt_Op)FindOp, 2, 0, "?flags...? ?first last?",},
+ {"focus", 2, (Blt_Op)FocusOp, 3, 3, "tagOrId",},
+ {"get", 1, (Blt_Op)GetOp, 2, 0, "?-full? tagOrId ?tagOrId...?",},
+ {"hide", 1, (Blt_Op)HideOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
+ {"index", 3, (Blt_Op)IndexOp, 3, 6, "?-at tagOrId? ?-path? string",},
+ {"insert", 3, (Blt_Op)InsertOp, 3, 0, "?-at tagOrId? position label ?label...? ?option value?",},
+ {"move", 1, (Blt_Op)MoveOp, 5, 5, "tagOrId into|before|after tagOrId",},
+ {"nearest", 1, (Blt_Op)NearestOp, 4, 5, "x y ?varName?",},
+ {"open", 1, (Blt_Op)OpenOp, 2, 0, "?-recurse? tagOrId...",},
+ {"range", 1, (Blt_Op)RangeOp, 4, 5, "?-open? tagOrId tagOrId",},
+ {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+ {"see", 3, (Blt_Op)SeeOp, 3, 0, "?-anchor anchor? tagOrId",},
+ {"selection", 3, (Blt_Op)SelectionOp, 2, 0, "oper args",},
+ {"show", 2, (Blt_Op)ShowOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
+ {"sort", 2, (Blt_Op)Blt_TreeViewSortOp, 2, 0, "args",},
+ {"tag", 2, (Blt_Op)TagOp, 2, 0, "oper args",},
+ {"text", 2, (Blt_Op)Blt_TreeViewTextOp, 2, 0, "args",},
+ {"toggle", 2, (Blt_Op)ToggleOp, 3, 3, "tagOrId",},
+ {"xview", 1, (Blt_Op)XViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+ {"yview", 1, (Blt_Op)YViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+};
+
+static int nTreeViewOps = sizeof(treeViewOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_TreeViewWidgetInstCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Information about the widget. */
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST *objv; /* Vector of argument strings. */
+{
+ Blt_Op proc;
+ TreeView *tvPtr = clientData;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTreeViewOps, treeViewOps, BLT_OP_ARG1,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(tvPtr);
+ result = (*proc) (tvPtr, interp, objc, objv);
+ Tcl_Release(tvPtr);
+ return result;
+}
+
+#endif /* NO_TREEVIEW */
diff --git a/blt/src/bltTreeViewColumn.c b/blt/src/bltTreeViewColumn.c
new file mode 100644
index 00000000000..1f1c68d2c87
--- /dev/null
+++ b/blt/src/bltTreeViewColumn.c
@@ -0,0 +1,1881 @@
+/*
+ * bltTreeViewColumn.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ * 1. "open" operation should change scroll offset so that as many
+ * new entries (up to half a screen) can be seen.
+ * 2. "open" needs to adjust the scrolloffset so that the same entry
+ * is seen at the same place.
+ */
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include <X11/Xutil.h>
+
+static Blt_OptionParseProc ObjToColumn;
+static Blt_OptionPrintProc ColumnToObj;
+static Blt_OptionParseProc ObjToData;
+static Blt_OptionPrintProc DataToObj;
+
+static char *sortTypeStrings[] = {
+ "dictionary", "ascii", "integer", "real", "command", "none", NULL
+};
+
+enum SortTypeValues {
+ SORT_TYPE_DICTIONARY, SORT_TYPE_ASCII, SORT_TYPE_INTEGER,
+ SORT_TYPE_REAL, SORT_TYPE_COMMAND, SORT_TYPE_NONE
+};
+
+extern Blt_OptionParseProc Blt_ObjToEnum;
+extern Blt_OptionPrintProc Blt_EnumToObj;
+
+static Blt_CustomOption typeOption =
+{
+ Blt_ObjToEnum, Blt_EnumToObj, NULL, (ClientData)sortTypeStrings
+};
+
+static Blt_CustomOption columnOption =
+{
+ ObjToColumn, ColumnToObj, NULL, (ClientData)0
+};
+
+Blt_CustomOption bltTreeViewDataOption =
+{
+ ObjToData, DataToObj, NULL, (ClientData)0,
+};
+
+#define DEF_SORT_COLUMN (char *)NULL
+#define DEF_SORT_COMMAND (char *)NULL
+#define DEF_SORT_DECREASING "no"
+#define DEF_SORT_TYPE "dictionary"
+
+#define TOGGLE(x, mask) \
+ (((x) & (mask)) ? ((x) & ~(mask)) : ((x) | (mask)))
+#define CLAMP(val,low,hi) \
+ (((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#ifdef WIN32
+#define DEF_COLUMN_ACTIVE_TITLE_BG RGB_GREY85
+#else
+#define DEF_COLUMN_ACTIVE_TITLE_BG RGB_GREY90
+#endif
+#define DEF_COLUMN_ACTIVE_TITLE_FG STD_COLOR_ACTIVE_FG
+#define DEF_COLUMN_BACKGROUND (char *)NULL
+#define DEF_COLUMN_BIND_TAGS "all"
+#define DEF_COLUMN_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_COLUMN_COLOR RGB_BLACK
+#define DEF_COLUMN_EDIT "yes"
+#define DEF_COLUMN_FONT STD_FONT
+#define DEF_COLUMN_COMMAND (char *)NULL
+#define DEF_COLUMN_FORMAT_COMMAND (char *)NULL
+#define DEF_COLUMN_HIDE "no"
+#define DEF_COLUMN_JUSTIFY "center"
+#define DEF_COLUMN_MAX "0"
+#define DEF_COLUMN_MIN "0"
+#define DEF_COLUMN_PAD "2"
+#define DEF_COLUMN_RELIEF "flat"
+#define DEF_COLUMN_STATE "normal"
+#define DEF_COLUMN_TEXT (char *)NULL
+#define DEF_COLUMN_TITLE (char *)NULL
+#define DEF_COLUMN_TITLE_BACKGROUND STD_COLOR_NORMAL_BG
+#define DEF_COLUMN_TITLE_FONT STD_FONT
+#define DEF_COLUMN_TITLE_FOREGROUND STD_COLOR_NORMAL_FG
+#define DEF_COLUMN_TITLE_LABEL (char *)NULL
+#define DEF_COLUMN_TITLE_SHADOW (char *)NULL
+#define DEF_COLUMN_WEIGHT "1.0"
+#define DEF_COLUMN_WIDTH "0"
+#define DEF_COLUMN_RULE_DASHES "dot"
+
+#ifdef __STDC__
+static Blt_TreeCompareNodesProc CompareNodes;
+static Blt_TreeApplyProc SortApplyProc;
+#endif /* __STDC__ */
+
+extern Blt_CustomOption bltTreeViewUidOption;
+static Blt_TreeApplyProc SortApplyProc;
+
+static Blt_ConfigSpec columnSpecs[] =
+{
+ {BLT_CONFIG_BORDER, "-activetitlebackground", "activeTitleBackground",
+ "Background", DEF_COLUMN_ACTIVE_TITLE_BG,
+ Blt_Offset(TreeViewColumn, activeTitleBorder), 0},
+ {BLT_CONFIG_COLOR, "-activetitleforeground", "activeTitleForeground",
+ "Foreground", DEF_COLUMN_ACTIVE_TITLE_FG,
+ Blt_Offset(TreeViewColumn, activeTitleFgColor), 0},
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_COLUMN_BACKGROUND, Blt_Offset(TreeViewColumn, border),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_COLUMN_BIND_TAGS, Blt_Offset(TreeViewColumn, tagsUid),
+ BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+ {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_COLUMN_BORDER_WIDTH, Blt_Offset(TreeViewColumn, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-command", "command", "Command",
+ DEF_COLUMN_COMMAND, Blt_Offset(TreeViewColumn, command),
+ BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-edit", "edit", "Edit",
+ DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, editable),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_COLUMN_COLOR, Blt_Offset(TreeViewColumn, fgColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_COLUMN_FONT, Blt_Offset(TreeViewColumn, font), 0},
+ {BLT_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_COLUMN_HIDE, Blt_Offset(TreeViewColumn, hidden),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_COLUMN_JUSTIFY, Blt_Offset(TreeViewColumn, justify),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-max", "max", "Max",
+ DEF_COLUMN_MAX, Blt_Offset(TreeViewColumn, reqMax),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-min", "min", "Min",
+ DEF_COLUMN_MIN, Blt_Offset(TreeViewColumn, reqMin),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-pad", "pad", "Pad",
+ DEF_COLUMN_PAD, Blt_Offset(TreeViewColumn, pad),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_COLUMN_RELIEF, Blt_Offset(TreeViewColumn, relief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DASHES, "-ruledashes", "ruleDashes", "RuleDashes",
+ DEF_COLUMN_RULE_DASHES, Blt_Offset(TreeViewColumn, rule.dashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-sortcommand", "sortCommand", "SortCommand",
+ DEF_SORT_COMMAND, Blt_Offset(TreeViewColumn, sortCmd),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STATE, "-state", "state", "State",
+ DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, state),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-text", "text", "Text",
+ DEF_COLUMN_TITLE_LABEL, Blt_Offset(TreeViewColumn, text), 0},
+ {BLT_CONFIG_BORDER, "-titlebackground", "titleBackground",
+ "TitleBackground", DEF_COLUMN_TITLE_BACKGROUND,
+ Blt_Offset(TreeViewColumn, titleBorder),0},
+ {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+ DEF_COLUMN_TITLE_FONT, Blt_Offset(TreeViewColumn, titleFont), 0},
+ {BLT_CONFIG_COLOR, "-titleforeground", "titleForeground", "TitleForeground",
+ DEF_COLUMN_TITLE_FOREGROUND,
+ Blt_Offset(TreeViewColumn, titleFgColor), 0},
+ {BLT_CONFIG_SHADOW, "-titleshadow", "titleShadow", "TitleShadow",
+ DEF_COLUMN_TITLE_SHADOW, Blt_Offset(TreeViewColumn, titleShadow), 0},
+ {BLT_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+ DEF_COLUMN_WEIGHT, Blt_Offset(TreeViewColumn, weight),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
+ DEF_COLUMN_WIDTH, Blt_Offset(TreeViewColumn, reqWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static Blt_ConfigSpec sortSpecs[] =
+{
+ {BLT_CONFIG_STRING, "-command", "command", "Command",
+ DEF_SORT_COMMAND, Blt_Offset(TreeView, sortCmd),
+ BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-column", "column", "Column",
+ DEF_SORT_COLUMN, Blt_Offset(TreeView, sortColumnPtr),
+ BLT_CONFIG_DONT_SET_DEFAULT, &columnOption},
+ {BLT_CONFIG_BITFLAG, "-decreasing", "decreasing", "Decreasing",
+ DEF_SORT_DECREASING, Blt_Offset(TreeView, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_DECREASING},
+ {BLT_CONFIG_CUSTOM, "-mode", "mode", "Mode",
+ DEF_SORT_TYPE, Blt_Offset(TreeView, sortType), 0, &typeOption},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static int GetColumnFromObj _ANSI_ARGS_((Tcl_Interp *interp, TreeView *tvPtr,
+ Tcl_Obj *objPtr, TreeViewColumn **columnPtrPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColumn --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToColumn(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* New legend position string */
+ char *widgRec;
+ int offset;
+{
+ TreeViewColumn **columnPtrPtr = (TreeViewColumn **)(widgRec + offset);
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (*string == '\0') {
+ *columnPtrPtr = NULL;
+ } else {
+ TreeView *tvPtr = (TreeView *)widgRec;
+
+ if (GetColumnFromObj(interp, tvPtr, objPtr, columnPtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnToString --
+ *
+ * Results:
+ * The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ColumnToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ TreeViewColumn *columnPtr = *(TreeViewColumn **)(widgRec + offset);
+
+ if (columnPtr == NULL) {
+ return Tcl_NewStringObj("", -1);
+ }
+ return Tcl_NewStringObj(columnPtr->key, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToData --
+ *
+ * Convert the string reprsenting a scroll mode, to its numeric
+ * form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToData(clientData, interp, tkwin, objPtr, widgRec, offset)
+ ClientData clientData; /* Node of entry. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ Tcl_Obj *objPtr; /* Tcl_Obj representing new data. */
+ char *widgRec;
+ int offset;
+{
+ Tcl_Obj **objv;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+ char *string;
+ int objc;
+ register int i;
+
+ string = Tcl_GetString(objPtr);
+ if (*string == '\0') {
+ return TCL_OK;
+ }
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 0) {
+ return TCL_OK;
+ }
+ if (objc & 0x1) {
+ Tcl_AppendResult(interp, "data \"", string,
+ "\" must be in even name-value pairs", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Load the keys in reverse */
+ for (i = objc - 2; i >= 0; i -= 2) {
+ TreeView *tvPtr = entryPtr->tvPtr;
+
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
+ columnPtr->key, objv[i + 1]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataToObj --
+ *
+ * Results:
+ * The string representation of the data is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+DataToObj(clientData, interp, tkwin, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec;
+ int offset;
+{
+ Tcl_Obj *listObjPtr, *objPtr;
+ TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+ TreeViewValue **values;
+ TreeViewValue *valuePtr;
+ int i, count;
+
+ /* Values are stored in a list last-to-first. Create an array to
+ * hold the pointer so that we can return them in reverse
+ * order. */
+
+ /* Find out how may values there are. */
+ count = 0;
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr) {
+ count++;
+ }
+ /* Create a temporary array to store the values in reverse
+ * order. */
+ values = Blt_Malloc(sizeof(TreeViewValue *) * count);
+ for (i = count - 1, valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->nextPtr, i--) {
+ values[i] = valuePtr;
+ }
+
+ /* Add the key-value pairs to a new Tcl_Obj */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = 0; i < count; i++) {
+ valuePtr = values[i];
+ objPtr = Tcl_NewStringObj(valuePtr->columnPtr->key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ objPtr = Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key);
+ if (objPtr == NULL) {
+ objPtr = Tcl_NewStringObj("", -1);
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Blt_Free(values);
+ return listObjPtr;
+}
+
+static int
+GetColumnFromObj(interp, tvPtr, objPtr, columnPtrPtr)
+ Tcl_Interp *interp;
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewColumn **columnPtrPtr;
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (strcmp(string, "treeView") == 0) {
+ *columnPtrPtr = &tvPtr->treeColumn;
+ } else {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tvPtr->columnTable, Blt_TreeGetKey(string));
+ if (hPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find column \"", string,
+ "\" in \"", Tk_PathName(tvPtr->tkwin), "\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *columnPtrPtr = Blt_GetHashValue(hPtr);
+ }
+ return TCL_OK;
+}
+
+void
+Blt_TreeViewConfigureColumn(tvPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+{
+ Drawable drawable;
+ GC newGC;
+ TextLayout *textPtr;
+ TextStyle ts;
+ Tk_3DBorder border;
+ XGCValues gcValues;
+ int ruleDrawn;
+ unsigned long gcMask;
+
+ gcMask = GCForeground | GCFont;
+ gcValues.foreground = columnPtr->fgColor->pixel;
+
+ gcValues.font = Tk_FontId(columnPtr->font);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->gc);
+ }
+ columnPtr->gc = newGC;
+
+ gcValues.foreground = columnPtr->titleFgColor->pixel;
+ gcValues.font = Tk_FontId(columnPtr->titleFont);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->titleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+ }
+ columnPtr->titleGC = newGC;
+
+ gcValues.foreground = columnPtr->activeTitleFgColor->pixel;
+ gcValues.font = Tk_FontId(columnPtr->titleFont);
+ newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->activeTitleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->activeTitleGC);
+ }
+ columnPtr->activeTitleGC = newGC;
+
+ memset(&ts, 0, sizeof(TextStyle));
+ ts.font = columnPtr->titleFont;
+ ts.justify = TK_JUSTIFY_LEFT;
+ ts.shadow.offset = columnPtr->titleShadow.offset;
+ textPtr = Blt_GetTextLayout(columnPtr->text, &ts);
+ if (columnPtr->textPtr != NULL) {
+ Blt_Free(columnPtr->textPtr);
+ }
+ columnPtr->textPtr = textPtr;
+ columnPtr->titleWidth = columnPtr->textPtr->width + SORT_MARKER_WIDTH + 1;
+ gcMask = (GCFunction | GCLineWidth | GCLineStyle | GCForeground | GCFont);
+
+ /*
+ * If the rule is active, turn it off (i.e. draw again to erase
+ * it) before changing the GC. If the color changes, we won't be
+ * able to erase the old line, since it will no longer be
+ * correctly XOR-ed with the background.
+ */
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ ruleDrawn = ((tvPtr->flags & TV_RULE_ACTIVE) &&
+ (tvPtr->activeColumnPtr == columnPtr) &&
+ (drawable != None));
+ if (ruleDrawn) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+ gcValues.line_width = LineWidth(columnPtr->rule.lineWidth);
+ gcValues.foreground = columnPtr->fgColor->pixel;
+ if (LineIsDashed(columnPtr->rule.dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ } else {
+ gcValues.line_style = LineSolid;
+ }
+ gcValues.function = GXxor;
+
+ border = CHOOSE(tvPtr->border, columnPtr->border);
+ gcValues.foreground ^= Tk_3DBorderColor(border)->pixel;
+ newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
+ if (columnPtr->rule.gc != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, columnPtr->rule.gc);
+ }
+ if (LineIsDashed(columnPtr->rule.dashes)) {
+ Blt_SetDashes(tvPtr->display, newGC, &columnPtr->rule.dashes);
+ }
+ columnPtr->rule.gc = newGC;
+ if (ruleDrawn) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+ columnPtr->flags |= COLUMN_DIRTY;
+ tvPtr->flags |= TV_UPDATE;
+}
+
+static void
+DestroyColumn(tvPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+{
+ Blt_HashEntry *hPtr;
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ Blt_FreeObjOptions(columnSpecs, (char *)columnPtr, tvPtr->display, 0);
+
+ if (columnPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->gc);
+ }
+ if (columnPtr->titleGC != NULL) {
+ Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+ }
+ if (columnPtr->rule.gc != NULL) {
+ Blt_FreePrivateGC(tvPtr->display, columnPtr->rule.gc);
+ }
+ hPtr = Blt_FindHashEntry(&tvPtr->columnTable, columnPtr->key);
+ if (hPtr != NULL) {
+ Blt_DeleteHashEntry(&tvPtr->columnTable, hPtr);
+ }
+ if (columnPtr->linkPtr != NULL) {
+ Blt_ChainDeleteLink(tvPtr->colChainPtr, columnPtr->linkPtr);
+ }
+ if (columnPtr->text != NULL) {
+ Blt_Free(columnPtr->text);
+ }
+ if (columnPtr->textPtr != NULL) {
+ Blt_Free(columnPtr->textPtr);
+ }
+ if (columnPtr != &tvPtr->treeColumn) {
+ Blt_Free(columnPtr);
+ }
+}
+
+void
+Blt_TreeViewDestroyColumns(tvPtr)
+ TreeView *tvPtr;
+{
+ if (tvPtr->colChainPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ columnPtr->linkPtr = NULL;
+ DestroyColumn(tvPtr, columnPtr);
+ }
+ Blt_ChainDestroy(tvPtr->colChainPtr);
+ tvPtr->colChainPtr = NULL;
+ }
+ Blt_DeleteHashTable(&tvPtr->columnTable);
+}
+
+int
+Blt_TreeViewInitColumn(tvPtr, columnPtr, name, defTitle, objc, objv)
+ TreeView *tvPtr;
+ TreeViewColumn *columnPtr;
+ char *name, *defTitle;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ columnPtr->key = Blt_TreeGetKey(name);
+ columnPtr->text = Blt_Strdup(defTitle);
+ columnPtr->justify = TK_JUSTIFY_CENTER;
+ columnPtr->relief = TK_RELIEF_FLAT;
+ columnPtr->borderWidth = 1;
+ columnPtr->pad.side1 = columnPtr->pad.side2 = 2;
+ columnPtr->state = STATE_NORMAL;
+ columnPtr->weight = 1.0;
+ columnPtr->editable = FALSE;
+ columnPtr->type = TV_ITEM_COLUMN;
+ columnPtr->rule.type = TV_ITEM_RULE;
+ columnPtr->rule.lineWidth = 1;
+ columnPtr->rule.columnPtr = columnPtr;
+ hPtr = Blt_CreateHashEntry(&tvPtr->columnTable, columnPtr->key, &isNew);
+ Blt_SetHashValue(hPtr, columnPtr);
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ if (Blt_ConfigureComponentFromObj(tvPtr->interp, tvPtr->tkwin, name,
+ Tk_GetUid("Column"), columnSpecs, objc, objv, (char *)columnPtr, 0)
+ != TCL_OK) {
+ DestroyColumn(tvPtr, columnPtr);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+static TreeViewColumn *
+CreateColumn(tvPtr, nameObjPtr, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Obj *nameObjPtr;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+
+ columnPtr = Blt_Calloc(1, sizeof(TreeViewColumn));
+ assert(columnPtr);
+ if (Blt_TreeViewInitColumn(tvPtr, columnPtr, Tcl_GetString(nameObjPtr),
+ Tcl_GetString(nameObjPtr), objc, objv) != TCL_OK) {
+ return NULL;
+ }
+ Blt_TreeViewConfigureColumn(tvPtr, columnPtr);
+ return columnPtr;
+}
+
+TreeViewColumn *
+Blt_TreeViewNearestColumn(tvPtr, x, y, flags)
+ TreeView *tvPtr;
+ int x, y;
+ int flags;
+{
+ if (flags & SEARCH_Y) {
+ if ((y < tvPtr->inset) || (y >= (tvPtr->titleHeight + tvPtr->inset))) {
+ return NULL;
+ }
+ }
+ if (tvPtr->nVisible > 0) {
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ int right;
+
+ /*
+ * Determine if the pointer is over the rightmost portion of the
+ * column. This activates the rule.
+ */
+ x = WORLDX(tvPtr, x);
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ right = columnPtr->worldX + columnPtr->width;
+ columnPtr->flags &= ~COLUMN_RULE_PICKED;
+ if ((x >= columnPtr->worldX) && (x <= right)) {
+#define RULE_AREA (8)
+ if (x >= (right - RULE_AREA)) {
+ columnPtr->flags |= COLUMN_RULE_PICKED;
+ }
+ return columnPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnActivateOp --
+ *
+ * Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 4) {
+ Drawable drawable;
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ columnPtr = NULL;
+ } else {
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (((tvPtr->flags & TV_SHOW_COLUMN_TITLES) == 0) ||
+ (columnPtr->hidden) || (columnPtr->state == STATE_DISABLED)) {
+ columnPtr = NULL;
+ }
+ }
+ tvPtr->activeColumnPtr = columnPtr;
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if (drawable != None) {
+ Blt_TreeViewDrawHeadings(tvPtr, drawable);
+ Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
+ }
+ }
+ if (tvPtr->activeColumnPtr != NULL) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(tvPtr->activeColumnPtr->key, -1);
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnBindOp --
+ *
+ * .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnBindOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ ClientData object;
+ TreeViewColumn *columnPtr;
+
+ if (GetColumnFromObj(NULL, tvPtr, objv[3], &columnPtr) == TCL_OK) {
+ object = columnPtr->key;
+ } else {
+ object = Blt_TreeViewGetUid(tvPtr, Tcl_GetString(objv[3]));
+ }
+ return Blt_ConfigureBindingsFromObj(interp, tvPtr->columnBindTable, object,
+ objc - 4, objv + 4);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h entryconfigure node node node node option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ int nOptions, start;
+ register int i;
+
+ /* Figure out where the option value pairs begin */
+ for(i = 3; i < objc; i++) {
+ if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+ break;
+ }
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ start = i;
+ nOptions = objc - start;
+
+ bltTreeViewUidOption.clientData = tvPtr;
+ for (i = 3; i < start; i++) {
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nOptions == 0) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, (Tcl_Obj *)NULL, 0);
+ } else if (nOptions == 1) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs,
+ (char *)columnPtr, objv[start], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
+ columnSpecs, nOptions, objv + start, (char *)columnPtr,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_TreeViewConfigureColumn(tvPtr, columnPtr);
+ }
+ /*FIXME: Makes every change redo everything. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnDeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ register int i;
+
+ for(i = 3; i < objc; i++) {
+ if (GetColumnFromObj(interp, tvPtr, objv[i], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Traverse the tree deleting values associated with the column. */
+ for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ if (entryPtr != NULL) {
+ TreeViewValue *valuePtr, *lastPtr, *nextPtr;
+
+ lastPtr = NULL;
+ for (valuePtr = entryPtr->values; valuePtr != NULL;
+ valuePtr = nextPtr) {
+ nextPtr = valuePtr->nextPtr;
+ if (valuePtr->columnPtr == columnPtr) {
+ Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+ if (lastPtr == NULL) {
+ entryPtr->values = nextPtr;
+ } else {
+ lastPtr->nextPtr = nextPtr;
+ }
+ break;
+ }
+ lastPtr = valuePtr;
+ }
+ }
+ }
+ DestroyColumn(tvPtr, columnPtr);
+ }
+ /* Deleting a column may affect the height of an entry. */
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInsertOp --
+ *
+ * Add new columns to the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInsertOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_ChainLink *beforePtr;
+ Tcl_Obj *CONST *options;
+ TreeViewColumn *columnPtr;
+ TreeViewEntry *entryPtr;
+ int insertPos;
+ int nOptions;
+ int start;
+ register int i;
+
+ if (Blt_GetPositionFromObj(tvPtr->interp, objv[3], &insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((insertPos == -1) ||
+ (insertPos >= Blt_ChainGetLength(tvPtr->colChainPtr))) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = Blt_ChainGetNthLink(tvPtr->colChainPtr, insertPos);
+ }
+ /*
+ * Count the column names that follow. Count the arguments until we
+ * spot one that looks like a configuration option (i.e. starts
+ * with a minus ("-")).
+ */
+ for (i = 4; i < objc; i++) {
+ if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+ break;
+ }
+ }
+ start = i;
+ nOptions = objc - i;
+ options = objv + start;
+
+ for (i = 4; i < start; i++) {
+ if (GetColumnFromObj(NULL, tvPtr, objv[i], &columnPtr) == TCL_OK) {
+ Tcl_AppendResult(interp, "column \"", Tcl_GetString(objv[i]),
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ columnPtr = CreateColumn(tvPtr, objv[i], nOptions, options);
+ if (columnPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (beforePtr == NULL) {
+ columnPtr->linkPtr = Blt_ChainAppend(tvPtr->colChainPtr, columnPtr);
+ } else {
+ columnPtr->linkPtr = Blt_ChainNewLink();
+ Blt_ChainSetValue(columnPtr->linkPtr, columnPtr);
+ Blt_ChainLinkBefore(tvPtr->colChainPtr, columnPtr->linkPtr,
+ beforePtr);
+ }
+ /*
+ * Traverse the tree adding column entries where needed.
+ */
+ for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+ entryPtr = Blt_TreeViewNextEntry(tvPtr, entryPtr, 0)) {
+ Blt_TreeViewAddValue(entryPtr, columnPtr);
+ }
+ Blt_TreeViewTraceColumn(tvPtr, columnPtr);
+ }
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCurrentOp --
+ *
+ * Make the rule to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCurrentOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ TreeViewColumn *columnPtr;
+
+ columnPtr = Blt_GetCurrentItem(tvPtr->columnBindTable);
+ if (columnPtr != NULL) {
+ if (columnPtr->type == TV_ITEM_RULE) {
+ Rule *rulePtr = (Rule *)columnPtr;
+ columnPtr = rulePtr->columnPtr;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(columnPtr->key, -1));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInvokeOp --
+ *
+ * This procedure is called to invoke a column command.
+ *
+ * .h column invoke columnName
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInvokeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '\0') {
+ return TCL_OK;
+ }
+ if (GetColumnFromObj(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((columnPtr->state == STATE_NORMAL) && (columnPtr->command != NULL)) {
+ int result;
+
+ Tcl_Preserve(tvPtr);
+ Tcl_Preserve(columnPtr);
+ result = Tcl_GlobalEval(interp, columnPtr->command);
+ Tcl_Release(columnPtr);
+ Tcl_Release(tvPtr);
+ return result;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnMoveOp --
+ *
+ * Move a column.
+ *
+ * .h column move field1 position
+ *----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnNamesOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ Blt_ChainLink *linkPtr;
+ Tcl_Obj *listObjPtr, *objPtr;
+ TreeViewColumn *columnPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ objPtr = Tcl_NewStringObj(columnPtr->key, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ColumnNearestOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x, y; /* Screen coordinates of the test point. */
+ TreeViewColumn *columnPtr;
+ int flags;
+
+ flags = 0;
+ if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 5) {
+ if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[4], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags |= SEARCH_Y;
+ }
+ columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, flags);
+ if (columnPtr != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(columnPtr->key, -1));
+ }
+ return TCL_OK;
+}
+
+static void
+UpdateMark(tvPtr, newMark)
+ TreeView *tvPtr;
+ int newMark;
+{
+ Drawable drawable;
+ TreeViewColumn *columnPtr;
+ int dx;
+ int width;
+
+ columnPtr = tvPtr->resizeColumnPtr;
+ if (columnPtr == NULL) {
+ return;
+ }
+ drawable = Tk_WindowId(tvPtr->tkwin);
+ if (drawable == None) {
+ return;
+ }
+
+ /* Erase any existing rule. */
+ if (tvPtr->flags & TV_RULE_ACTIVE) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+
+ dx = newMark - tvPtr->ruleAnchor;
+ width = columnPtr->width -
+ (PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth);
+ if ((columnPtr->reqMin > 0) && ((width + dx) < columnPtr->reqMin)) {
+ dx = columnPtr->reqMin - width;
+ }
+ if ((columnPtr->reqMax > 0) && ((width + dx) > columnPtr->reqMax)) {
+ dx = columnPtr->reqMax - width;
+ }
+ if ((width + dx) < 4) {
+ dx = 4 - width;
+ }
+ tvPtr->ruleMark = tvPtr->ruleAnchor + dx;
+
+ /* Redraw the rule if required. */
+ if (tvPtr->flags & TV_RULE_NEEDED) {
+ Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeActivateOp --
+ *
+ * Turns on/off the resize cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeActivateOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewColumn *columnPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[4]);
+ if (string[0] == '\0') {
+ if (tvPtr->cursor != None) {
+ Tk_DefineCursor(tvPtr->tkwin, tvPtr->cursor);
+ } else {
+ Tk_UndefineCursor(tvPtr->tkwin);
+ }
+ tvPtr->resizeColumnPtr = NULL;
+ } else if (GetColumnFromObj(interp, tvPtr, objv[4], &columnPtr)
+ == TCL_OK) {
+ if (tvPtr->resizeCursor != None) {
+ Tk_DefineCursor(tvPtr->tkwin, tvPtr->resizeCursor);
+ }
+ tvPtr->resizeColumnPtr = columnPtr;
+ } else {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeAnchorOp --
+ *
+ * Set the anchor for the resize.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeAnchorOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x;
+
+ if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->ruleAnchor = x;
+ tvPtr->flags |= TV_RULE_NEEDED;
+ UpdateMark(tvPtr, x);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeMarkOp --
+ *
+ * Sets the resize mark. The distance between the mark and the anchor
+ * is the delta to change the width of the active column.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeMarkOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int x;
+
+ if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->flags |= TV_RULE_NEEDED;
+ UpdateMark(tvPtr, x);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeSetOp --
+ *
+ * Returns the new width of the column including the resize delta.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeSetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ tvPtr->flags &= ~TV_RULE_NEEDED;
+ UpdateMark(tvPtr, tvPtr->ruleMark);
+ if (tvPtr->resizeColumnPtr != NULL) {
+ int width, delta;
+ TreeViewColumn *columnPtr;
+
+ columnPtr = tvPtr->resizeColumnPtr;
+ delta = (tvPtr->ruleMark - tvPtr->ruleAnchor);
+ width = tvPtr->resizeColumnPtr->width + delta -
+ (PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth) - 1;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(width));
+ }
+ return TCL_OK;
+}
+
+static Blt_OpSpec resizeOps[] =
+{
+ {"activate", 2, (Blt_Op)ResizeActivateOp, 5, 5, "column"},
+ {"anchor", 2, (Blt_Op)ResizeAnchorOp, 5, 5, "x"},
+ {"mark", 1, (Blt_Op)ResizeMarkOp, 5, 5, "x"},
+ {"set", 1, (Blt_Op)ResizeSetOp, 4, 4, "",},
+};
+
+static int nResizeOps = sizeof(resizeOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnResizeOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnResizeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nResizeOps, resizeOps, BLT_OP_ARG3,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+
+static Blt_OpSpec columnOps[] =
+{
+ {"activate", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+ {"bind", 1, (Blt_Op)ColumnBindOp, 4, 6, "tagName ?sequence command?",},
+ {"cget", 2, (Blt_Op)ColumnCgetOp, 5, 5, "field option",},
+ {"configure", 2, (Blt_Op)ColumnConfigureOp, 4, 0,
+ "field ?option value?...",},
+ {"current", 2, (Blt_Op)ColumnCurrentOp, 3, 3, "",},
+ {"delete", 1, (Blt_Op)ColumnDeleteOp, 3, 0, "?field...?",},
+ {"highlight", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+ {"insert", 3, (Blt_Op)ColumnInsertOp, 5, 0,
+ "position field ?field...? ?option value?...",},
+ {"invoke", 3, (Blt_Op)ColumnInvokeOp, 4, 4, "field",},
+ {"names", 2, (Blt_Op)ColumnNamesOp, 3, 3, "",},
+ {"nearest", 2, (Blt_Op)ColumnNearestOp, 4, 5, "x ?y?",},
+ {"resize", 1, (Blt_Op)ColumnResizeOp, 3, 0, "arg",},
+};
+static int nColumnOps = sizeof(columnOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewColumnOp --
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewColumnOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nColumnOps, columnOps, BLT_OP_ARG2,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+
+static int
+InvokeCompare(tvPtr, e1Ptr, e2Ptr, command)
+ TreeView *tvPtr;
+ TreeViewEntry *e1Ptr, *e2Ptr;
+ char *command;
+{
+ int result;
+ Tcl_Obj *objv[8];
+ int i;
+
+ objv[0] = Tcl_NewStringObj(command, -1);
+ objv[1] = Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1);
+ objv[2] = Tcl_NewIntObj(Blt_TreeNodeId(e1Ptr->node));
+ objv[3] = Tcl_NewIntObj(Blt_TreeNodeId(e2Ptr->node));
+ objv[4] = Tcl_NewStringObj(tvPtr->sortColumnPtr->key, -1);
+
+ if (tvPtr->flatView) {
+ objv[5] = Tcl_NewStringObj(e1Ptr->fullName, -1);
+ objv[6] = Tcl_NewStringObj(e2Ptr->fullName, -1);
+ } else {
+ objv[5] = Tcl_NewStringObj(GETLABEL(e1Ptr), -1);
+ objv[6] = Tcl_NewStringObj(GETLABEL(e2Ptr), -1);
+ }
+ objv[7] = NULL;
+ result = Tcl_EvalObjv(tvPtr->interp, 7, objv, TCL_EVAL_GLOBAL);
+ if ((result != TCL_OK) ||
+ (Tcl_GetIntFromObj(tvPtr->interp, Tcl_GetObjResult(tvPtr->interp),
+ &result) != TCL_OK)) {
+ Tcl_BackgroundError(tvPtr->interp);
+ }
+ for(i = 0; i < 7; i++) {
+ Tcl_DecrRefCount(objv[i]);
+ }
+ Tcl_ResetResult(tvPtr->interp);
+ return result;
+}
+
+static TreeView *treeViewInstance;
+
+static int
+CompareEntries(a, b)
+ CONST void *a, *b;
+{
+ TreeView *tvPtr;
+ TreeViewEntry **e1PtrPtr = (TreeViewEntry **)a;
+ TreeViewEntry **e2PtrPtr = (TreeViewEntry **)b;
+ char *s1, *s2;
+ int result;
+
+ tvPtr = (*e1PtrPtr)->tvPtr;
+ s1 = (char *)(*e1PtrPtr)->data;
+ s2 = (char *)(*e2PtrPtr)->data;
+ result = 0;
+ switch (tvPtr->sortType) {
+ case SORT_TYPE_ASCII:
+ result = strcmp(s1, s2);
+ break;
+
+ case SORT_TYPE_COMMAND:
+ {
+ char *cmd;
+
+ cmd = tvPtr->sortColumnPtr->sortCmd;
+ if (cmd == NULL) {
+ cmd = tvPtr->sortCmd;
+ }
+ if (cmd == NULL) {
+ result = Blt_DictionaryCompare(s1, s2);
+ } else {
+ result = InvokeCompare(tvPtr, *e1PtrPtr, *e2PtrPtr, cmd);
+ }
+ }
+ break;
+
+ case SORT_TYPE_DICTIONARY:
+ result = Blt_DictionaryCompare(s1, s2);
+ break;
+
+ case SORT_TYPE_INTEGER:
+ {
+ int i1, i2;
+
+ if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) {
+ if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = i1 - i2;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+
+ case SORT_TYPE_REAL:
+ {
+ double r1, r2;
+
+ if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) {
+ if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
+ } else {
+ result = -1;
+ }
+ } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+ result = 1;
+ } else {
+ result = Blt_DictionaryCompare(s1, s2);
+ }
+ }
+ break;
+ }
+ if (tvPtr->flags & TV_DECREASING) {
+ return -result;
+ }
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareNodes --
+ *
+ * Comparison routine (used by qsort) to sort a chain of subnodes.
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CompareNodes(n1Ptr, n2Ptr)
+ Blt_TreeNode *n1Ptr, *n2Ptr;
+{
+ TreeView *tvPtr = treeViewInstance;
+ TreeViewEntry *e1Ptr, *e2Ptr;
+
+ e1Ptr = NodeToEntry(tvPtr, *n1Ptr);
+ e2Ptr = NodeToEntry(tvPtr, *n2Ptr);
+
+ /* Fetch the data for sorting. */
+ if (tvPtr->sortType == SORT_TYPE_COMMAND) {
+ e1Ptr->data = (ClientData)Blt_TreeNodeId(*n1Ptr);
+ e2Ptr->data = (ClientData)Blt_TreeNodeId(*n2Ptr);
+ } else if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ if (e1Ptr->fullName == NULL) {
+ Blt_TreeViewGetFullName(tvPtr, e1Ptr, TRUE, &dString);
+ e1Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ }
+ e1Ptr->data = (ClientData)e1Ptr->fullName;
+ if (e2Ptr->fullName == NULL) {
+ Blt_TreeViewGetFullName(tvPtr, e2Ptr, TRUE, &dString);
+ e2Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ }
+ e2Ptr->data = (ClientData)e2Ptr->fullName;
+ Tcl_DStringFree(&dString);
+ } else {
+ Blt_TreeKey key;
+ Tcl_Obj *objPtr;
+
+ key = tvPtr->sortColumnPtr->key;
+ objPtr = Blt_TreeViewGetData(e1Ptr, key);
+ e1Ptr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ objPtr = Blt_TreeViewGetData(e2Ptr, key);
+ e2Ptr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ }
+ return CompareEntries(&e1Ptr, &e2Ptr);
+}
+
+static int
+SortAutoOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+
+ if (objc == 4) {
+ int bool;
+ int isAuto;
+
+ isAuto = ((tvPtr->flags & TV_AUTO_SORT) != 0);
+ if (Tcl_GetBooleanFromObj(interp, objv[3], &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (isAuto != bool) {
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ }
+ if (bool) {
+ tvPtr->flags |= TV_AUTO_SORT;
+ } else {
+ tvPtr->flags &= ~TV_AUTO_SORT;
+ }
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(tvPtr->flags & TV_AUTO_SORT));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortCgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h sort configure option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SortConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ unsigned int oldDirection;
+
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs,
+ (char *)tvPtr, objv[3], 0);
+ }
+ oldDirection = tvPtr->flags & TV_DECREASING;
+ if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, sortSpecs,
+ objc - 3, objv + 3, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((Blt_ObjConfigModified(sortSpecs, "-column", "-mode", "-command",
+ (char *)NULL)) || ((tvPtr->flags & TV_DECREASING) == oldDirection)) {
+ tvPtr->flags &= ~TV_SORTED;
+ tvPtr->flags |= TV_DIRTY;
+ }
+ tvPtr->flags |= TV_LAYOUT | TV_SORT_PENDING;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SortOnceOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ int recurse, result;
+ register int i;
+
+ recurse = FALSE;
+ if (objc > 3) {
+ char *string;
+ int length;
+
+ string = Tcl_GetString(objv[3]);
+ length = strlen(string);
+ if ((string[0] == '-') && (length > 1) &&
+ (strncmp(string, "-recurse", length) == 0)) {
+ objv++, objc--;
+ recurse = TRUE;
+ }
+ }
+ for (i = 3; i < objc; i++) {
+ if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (recurse) {
+ result = Blt_TreeApply(entryPtr->node, SortApplyProc, tvPtr);
+ } else {
+ result = SortApplyProc(entryPtr->node, tvPtr, TREE_PREORDER);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ tvPtr->flags |= TV_LAYOUT;
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortOp --
+ *
+ * Comparison routine (used by qsort) to sort a chain of subnodes.
+ * A simple string comparison is performed on each node name.
+ *
+ * .h sort auto
+ * .h sort once -recurse root
+ *
+ * Results:
+ * 1 is the first is greater, -1 is the second is greater, 0
+ * if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec sortOps[] =
+{
+ {"auto", 1, (Blt_Op)SortAutoOp, 3, 4, "?boolean?",},
+ {"cget", 2, (Blt_Op)SortCgetOp, 4, 4, "option",},
+ {"configure", 2, (Blt_Op)SortConfigureOp, 3, 0, "?option value?...",},
+ {"once", 1, (Blt_Op)SortOnceOp, 3, 0, "?-recurse? node...",},
+};
+static int nSortOps = sizeof(sortOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_TreeViewSortOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nSortOps, sortOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortApplyProc --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortApplyProc(node, clientData, order)
+ Blt_TreeNode node;
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ TreeView *tvPtr = clientData;
+
+ if (!Blt_TreeIsLeaf(node)) {
+ Blt_TreeSortNode(tvPtr->tree, node, CompareNodes);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortFlatView --
+ *
+ * Sorts the flatten array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortFlatView(tvPtr)
+ TreeView *tvPtr;
+{
+ TreeViewEntry *entryPtr, **p;
+
+ if (((tvPtr->flags & TV_AUTO_SORT) == 0) ||
+ (tvPtr->sortType == SORT_TYPE_NONE) ||
+ (tvPtr->sortColumnPtr == NULL) ||
+ (tvPtr->nEntries == 1)) {
+ return;
+ }
+ /* Prefetch the data for sorting. */
+ if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+ for(p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ if (entryPtr->fullName == NULL) {
+ Tcl_DString dString;
+
+ Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+ entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ }
+ entryPtr->data = entryPtr->fullName;
+ }
+ } else {
+ Blt_TreeKey key;
+ Tcl_Obj *objPtr;
+
+ key = tvPtr->sortColumnPtr->key;
+ for(p = tvPtr->flatArr; *p != NULL; p++) {
+ entryPtr = *p;
+ objPtr = Blt_TreeViewGetData(entryPtr, key);
+ entryPtr->data = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+ }
+ }
+ if (tvPtr->flags & TV_SORTED) {
+ int first, last;
+ TreeViewEntry *hold;
+
+ for (first = 0, last = tvPtr->nEntries - 1; last > first;
+ first++, last--) {
+ hold = tvPtr->flatArr[first];
+ tvPtr->flatArr[first] = tvPtr->flatArr[last];
+ tvPtr->flatArr[last] = hold;
+ }
+ } else {
+ qsort((char *)tvPtr->flatArr, tvPtr->nEntries, sizeof(TreeViewEntry *),
+ (QSortCompareProc *)CompareEntries);
+ tvPtr->flags |= TV_SORTED;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortTreeView --
+ *
+ * Sorts the tree array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortTreeView(tvPtr)
+ TreeView *tvPtr;
+{
+
+ if ((tvPtr->flags & TV_AUTO_SORT) &&
+ (tvPtr->sortType != SORT_TYPE_NONE) &&
+ (tvPtr->sortColumnPtr != NULL)) {
+ treeViewInstance = tvPtr;
+ Blt_TreeApply(tvPtr->rootPtr->node, SortApplyProc, tvPtr);
+ }
+}
+
+
+#endif /* NO_TREEVIEW */
diff --git a/blt/src/bltTreeViewEdit.c b/blt/src/bltTreeViewEdit.c
new file mode 100644
index 00000000000..d76e881f53a
--- /dev/null
+++ b/blt/src/bltTreeViewEdit.c
@@ -0,0 +1,1663 @@
+/*
+ * bltTreeViewEdit.c --
+ *
+ * This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define EDITOR_FOCUS (1<<0)
+#define EDITOR_REDRAW (1<<1)
+
+static Tcl_IdleProc DisplayTreeViewEditor;
+static Tcl_FreeProc DestroyTreeViewEditor;
+static Tcl_TimerProc BlinkCursorProc;
+
+/*
+ * TreeViewEditor --
+ *
+ * This structure is shared by entries when their labels are
+ * edited via the keyboard. It maintains the location of the
+ * insertion cursor and the text selection for the editted entry.
+ * The structure is shared since we need only one. The "focus"
+ * entry should be the only entry receiving KeyPress/KeyRelease
+ * events at any time. Information from the previously editted
+ * entry is overwritten.
+ *
+ * Note that all the indices internally are in terms of bytes,
+ * not characters. This is because UTF-8 strings may encode a
+ * single character into a multi-byte sequence. To find the
+ * respective character position
+ *
+ * n = Tcl_NumUtfChars(string, index);
+ *
+ * where n is the resulting character number.
+ */
+struct TreeViewEditorStruct {
+ unsigned int flags;
+ Display *display;
+ Tk_Window tkwin; /* Window representing the editing frame. */
+ int x, y; /* Position of window. */
+ int width, height; /* Dimensions of editor window. */
+
+ int active; /* Indicates that the frame is active. */
+ int exportSelection;
+
+ int insertPos; /* Position of the cursor within the
+ * array of bytes of the entry's label. */
+
+ Tk_Cursor cursor; /* X Cursor */
+ int cursorX, cursorY; /* Position of the insertion cursor in the
+ * editor window. */
+ short int cursorWidth; /* Size of the insertion cursor symbol. */
+ short int cursorHeight;
+
+ int selAnchor; /* Fixed end of selection. Used to extend
+ * the selection while maintaining the
+ * other end of the selection. */
+ int selFirst; /* Position of the first character in the
+ * selection. */
+ int selLast; /* Position of the last character in the
+ * selection. */
+
+ int cursorOn; /* Indicates if the cursor is displayed. */
+ int onTime, offTime; /* Time in milliseconds to wait before
+ * changing the cursor from off-to-on
+ * and on-to-off. Setting offTime to 0 makes
+ * the cursor steady. */
+ Tcl_TimerToken timerToken; /* Handle for a timer event called periodically
+ * to blink the cursor. */
+ /* Data-specific fields. */
+ TreeViewEntry *entryPtr; /* Selected entry */
+ TreeViewColumn *columnPtr; /* Column of entry to be edited */
+ char *string;
+ TextLayout *textPtr;
+ Tk_Font font;
+ GC gc;
+
+ Tk_3DBorder selBorder;
+ int selRelief;
+ int selBorderWidth;
+ Tk_3DBorder border;
+ int relief;
+ int borderWidth;
+ XColor *selFgColor; /* Text color of a selected entry. */
+
+
+};
+
+#define DEF_EDITOR_BACKGROUND RGB_WHITE
+#define DEF_EDITOR_BORDER_WIDTH STD_BORDERWIDTH
+#define DEF_EDITOR_CURSOR (char *)NULL
+#define DEF_EDITOR_EXPORT_SELECTION "no"
+#define DEF_EDITOR_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_EDITOR_NORMAL_FG_MONO STD_MONO_ACTIVE_FG
+#define DEF_EDITOR_RELIEF "solid"
+#define DEF_EDITOR_SELECT_BG_COLOR RGB_BISQUE1
+#define DEF_EDITOR_SELECT_BG_MONO STD_MONO_SELECT_BG
+#define DEF_EDITOR_SELECT_BORDER_WIDTH "1"
+#define DEF_EDITOR_SELECT_FG_COLOR STD_COLOR_SELECT_FG
+#define DEF_EDITOR_SELECT_FG_MONO STD_MONO_SELECT_FG
+#define DEF_EDITOR_SELECT_RELIEF "raised"
+
+/* Editor Procedures */
+static Blt_ConfigSpec editorConfigSpecs[] =
+{
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_EDITOR_BACKGROUND, Blt_Offset(TreeViewEditor, border), 0},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0,0},
+ {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_EDITOR_CURSOR, Blt_Offset(TreeViewEditor, cursor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_EDITOR_BORDER_WIDTH, Blt_Offset(TreeViewEditor, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
+ "ExportSelection", DEF_EDITOR_EXPORT_SELECTION,
+ Blt_Offset(TreeViewEditor, exportSelection),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_EDITOR_RELIEF, Blt_Offset(TreeViewEditor, relief), 0},
+ {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_EDITOR_SELECT_BG_MONO, Blt_Offset(TreeViewEditor, selBorder),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+ DEF_EDITOR_SELECT_BG_COLOR, Blt_Offset(TreeViewEditor, selBorder),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_DISTANCE, "-selectborderwidth", "selectBorderWidth",
+ "BorderWidth", DEF_EDITOR_SELECT_BORDER_WIDTH,
+ Blt_Offset(TreeViewEditor, selBorderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+
+ DEF_EDITOR_SELECT_FG_MONO, Blt_Offset(TreeViewEditor, selFgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_EDITOR_SELECT_FG_COLOR, Blt_Offset(TreeViewEditor, selFgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+ DEF_EDITOR_SELECT_RELIEF, Blt_Offset(TreeViewEditor, selRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0}
+};
+
+
+#ifdef __STDC__
+static Tk_LostSelProc TreeViewEditorLostSelectionProc;
+static Tk_SelectionProc TreeViewEditorSelectionProc;
+static Tk_EventProc TreeViewEditorEventProc;
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawEditor --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawEditor(tvPtr)
+ TreeView *tvPtr;
+{
+ if ((tvPtr->editPtr->tkwin != NULL) &&
+ ((tvPtr->editPtr->flags & EDITOR_REDRAW) == 0)) {
+ tvPtr->editPtr->flags |= EDITOR_REDRAW;
+ Tcl_DoWhenIdle(DisplayTreeViewEditor, tvPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BlinkCursorProc --
+ *
+ * This procedure is called as a timer handler to blink the
+ * insertion cursor off and on.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor gets turned on or off, redisplay gets invoked,
+ * and this procedure reschedules itself.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+BlinkCursorProc(clientData)
+ ClientData clientData; /* Pointer to record describing entry. */
+{
+ TreeView *tvPtr = clientData;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int interval;
+
+ if (!(editPtr->flags & EDITOR_FOCUS) || (editPtr->offTime == 0)) {
+ return;
+ }
+ if (editPtr->active) {
+ editPtr->cursorOn ^= 1;
+ interval = (editPtr->cursorOn) ? editPtr->onTime : editPtr->offTime;
+ editPtr->timerToken =
+ Tcl_CreateTimerHandler(interval, BlinkCursorProc, tvPtr);
+ EventuallyRedrawEditor(tvPtr);
+ }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeViewEditorEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on hierarchy widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TreeViewEditorEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ TreeView *tvPtr = clientData;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ EventuallyRedrawEditor(tvPtr);
+ }
+ } else if (eventPtr->type == ConfigureNotify) {
+ EventuallyRedrawEditor(tvPtr);
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail == NotifyInferior) {
+ return;
+ }
+ if (eventPtr->type == FocusIn) {
+ editPtr->flags |= EDITOR_FOCUS;
+ } else {
+ editPtr->flags &= ~EDITOR_FOCUS;
+ }
+ Tcl_DeleteTimerHandler(editPtr->timerToken);
+ if ((editPtr->active) && (editPtr->flags & EDITOR_FOCUS)) {
+ editPtr->cursorOn = TRUE;
+ if (editPtr->offTime != 0) {
+ editPtr->timerToken = Tcl_CreateTimerHandler(editPtr->onTime,
+ BlinkCursorProc, clientData);
+ }
+ } else {
+ editPtr->cursorOn = FALSE;
+ editPtr->timerToken = (Tcl_TimerToken) NULL;
+ }
+ EventuallyRedrawEditor(tvPtr);
+ } else if (eventPtr->type == DestroyNotify) {
+ if (editPtr->tkwin != NULL) {
+ editPtr->tkwin = NULL;
+ }
+ if (editPtr->flags & EDITOR_REDRAW) {
+ Tcl_CancelIdleCall(DisplayTreeViewEditor, tvPtr);
+ }
+ if (editPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(editPtr->timerToken);
+ }
+ Tcl_EventuallyFree(tvPtr, DestroyTreeViewEditor);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeViewEditorLostSelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is
+ * grabbed away from a Text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is
+ * marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TreeViewEditorLostSelectionProc(clientData)
+ ClientData clientData; /* Information about Text widget. */
+{
+ TreeView *tvPtr = clientData;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+
+ if ((editPtr->selFirst >= 0) && (editPtr->exportSelection)) {
+ editPtr->selFirst = editPtr->selLast = -1;
+ EventuallyRedrawEditor(tvPtr);
+ }
+}
+
+static int
+PointerToIndex(tvPtr, x, y)
+ TreeView *tvPtr;
+ int x, y;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ TextLayout *textPtr;
+ Tk_FontMetrics fontMetrics;
+ TextFragment *fragPtr;
+ int nBytes;
+ register int i;
+ int total;
+
+ if ((editPtr->string == NULL) || (editPtr->string[0] == '\0')) {
+ return 0;
+ }
+ x -= editPtr->selBorderWidth;
+ y -= editPtr->selBorderWidth;
+
+ textPtr = editPtr->textPtr;
+
+ /* Bound the y-coordinate within the window. */
+ if (y < 0) {
+ y = 0;
+ } else if (y >= textPtr->height) {
+ y = textPtr->height - 1;
+ }
+ /*
+ * Compute the line that contains the y-coordinate.
+ *
+ * FIXME: This assumes that segments are distributed
+ * line-by-line. This may change in the future.
+ */
+ Tk_GetFontMetrics(editPtr->font, &fontMetrics);
+ fragPtr = textPtr->fragArr;
+ total = 0;
+ for (i = (y / fontMetrics.linespace); i > 0; i--) {
+ total += fragPtr->count;
+ fragPtr++;
+ }
+ if (x < 0) {
+ nBytes = 0;
+ } else if (x >= textPtr->width) {
+ nBytes = fragPtr->count;
+ } else {
+ int newX;
+
+ /* Find the character underneath the pointer. */
+ nBytes = Tk_MeasureChars(editPtr->font, fragPtr->text, fragPtr->count,
+ x, 0, &newX);
+ if ((newX < x) && (nBytes < fragPtr->count)) {
+ double fract;
+ int length, charSize;
+ char *next;
+
+ next = fragPtr->text + nBytes;
+#if HAVE_UTF
+ {
+ Tcl_UniChar dummy;
+
+ length = Tcl_UtfToUniChar(next, &dummy);
+ }
+#else
+ length = 1;
+#endif
+ charSize = Tk_TextWidth(editPtr->font, next, length);
+ fract = ((double)(x - newX) / (double)charSize);
+ if (ROUND(fract)) {
+ nBytes += length;
+ }
+ }
+ }
+ return nBytes + total;
+}
+
+static int
+IndexToPointer(tvPtr)
+ TreeView *tvPtr;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int x, y;
+ int maxLines;
+ TextLayout *textPtr;
+ Tk_FontMetrics fontMetrics;
+ int nBytes;
+ int sum;
+ TextFragment *fragPtr;
+ register int i;
+
+ textPtr = editPtr->textPtr;
+ Tk_GetFontMetrics(editPtr->font, &fontMetrics);
+ maxLines = (textPtr->height / fontMetrics.linespace) - 1;
+
+ nBytes = sum = 0;
+ x = y = 0;
+ fragPtr = textPtr->fragArr;
+ for (i = 0; i <= maxLines; i++) {
+ /* Total the number of bytes on each line. Include newlines. */
+ nBytes = fragPtr->count + 1;
+ if ((sum + nBytes) > editPtr->insertPos) {
+ x += Tk_TextWidth(editPtr->font, fragPtr->text,
+ editPtr->insertPos - sum);
+ break;
+ }
+ y += fontMetrics.linespace;
+ sum += nBytes;
+ fragPtr++;
+ }
+ editPtr->cursorX = x;
+ editPtr->cursorY = y;
+ editPtr->cursorHeight = fontMetrics.linespace;
+ editPtr->cursorWidth = 3;
+ return TCL_OK;
+}
+
+static void
+UpdateLayout(tvPtr)
+ TreeView *tvPtr;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ TextStyle ts;
+ int width, height;
+ TextLayout *textPtr;
+
+ /* The layout is based upon the current font. */
+ Blt_InitTextStyle(&ts);
+ ts.anchor = TK_ANCHOR_NW;
+ ts.justify = TK_JUSTIFY_LEFT;
+ ts.font = editPtr->font;
+ textPtr = Blt_GetTextLayout(editPtr->string, &ts);
+ if (editPtr->textPtr != NULL) {
+ Blt_Free(editPtr->textPtr);
+ }
+ editPtr->textPtr = textPtr;
+
+ width = editPtr->textPtr->width;
+ if (width < editPtr->columnPtr->width) {
+ width = editPtr->columnPtr->width;
+ }
+ height = editPtr->textPtr->height;
+ if (height < 1) {
+ Tk_FontMetrics fontMetrics;
+
+ Tk_GetFontMetrics(editPtr->font, &fontMetrics);
+ height = fontMetrics.linespace;
+ }
+ editPtr->width = width + 2 * editPtr->borderWidth;
+ editPtr->height = height + 2 * editPtr->borderWidth;
+ IndexToPointer(tvPtr);
+ Tk_MoveResizeWindow(editPtr->tkwin, editPtr->x, editPtr->y, editPtr->width,
+ editPtr->height);
+}
+
+static void
+InsertText(tvPtr, insertText, insertPos, nBytes)
+ TreeView *tvPtr;
+ char *insertText;
+ int insertPos;
+ int nBytes;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int oldSize, newSize;
+ char *oldText, *newText;
+
+ oldText = editPtr->string;
+ oldSize = strlen(oldText);
+ newSize = oldSize + nBytes;
+ newText = Blt_Malloc(sizeof(char) * (newSize + 1));
+ if (insertPos == oldSize) { /* Append */
+ strcpy(newText, oldText);
+ strcat(newText, insertText);
+ } else if (insertPos == 0) {/* Prepend */
+ strcpy(newText, insertText);
+ strcat(newText, oldText);
+ } else { /* Insert into existing. */
+ char *p;
+
+ p = newText;
+ strncpy(p, oldText, insertPos);
+ p += insertPos;
+ strcpy(p, insertText);
+ p += nBytes;
+ strcpy(p, oldText + insertPos);
+ }
+
+ /*
+ * All indices from the start of the insertion to the end of the
+ * string need to be updated. Simply move the indices down by the
+ * number of characters added.
+ */
+ if (editPtr->selFirst >= insertPos) {
+ editPtr->selFirst += nBytes;
+ }
+ if (editPtr->selLast > insertPos) {
+ editPtr->selLast += nBytes;
+ }
+ if ((editPtr->selAnchor > insertPos) || (editPtr->selFirst >= insertPos)) {
+ editPtr->selAnchor += nBytes;
+ }
+ if (editPtr->string != NULL) {
+ Blt_Free(editPtr->string);
+ }
+ editPtr->string = newText;
+ editPtr->insertPos = insertPos + nBytes;
+ UpdateLayout(tvPtr);
+}
+
+static int
+DeleteText(tvPtr, firstPos, lastPos)
+ TreeView *tvPtr;
+ int firstPos, lastPos;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ char *oldText, *newText;
+ int oldSize, newSize;
+ int nBytes;
+ char *p;
+
+ oldText = editPtr->string;
+ if (firstPos > lastPos) {
+ return TCL_OK;
+ }
+ lastPos++; /* Now is the position after the last
+ * character. */
+
+ nBytes = lastPos - firstPos;
+
+ oldSize = strlen(oldText) + 1;
+ newSize = oldSize - nBytes + 1;
+ newText = Blt_Malloc(sizeof(char) * newSize);
+ p = newText;
+ if (firstPos > 0) {
+ strncpy(p, oldText, firstPos);
+ p += firstPos;
+ }
+ *p = '\0';
+ if (lastPos < oldSize) {
+ strcpy(p, oldText + lastPos);
+ }
+ Blt_Free(oldText);
+
+ /*
+ * Since deleting characters compacts the character array, we need to
+ * update the various character indices according. It depends where
+ * the index occurs in relation to range of deleted characters:
+ *
+ * before Ignore.
+ * within Move the index back to the start of the deletion.
+ * after Subtract off the deleted number of characters,
+ * since the array has been compressed by that
+ * many characters.
+ *
+ */
+ if (editPtr->selFirst >= firstPos) {
+ if (editPtr->selFirst >= lastPos) {
+ editPtr->selFirst -= nBytes;
+ } else {
+ editPtr->selFirst = firstPos;
+ }
+ }
+ if (editPtr->selLast >= firstPos) {
+ if (editPtr->selLast >= lastPos) {
+ editPtr->selLast -= nBytes;
+ } else {
+ editPtr->selLast = firstPos;
+ }
+ }
+ if (editPtr->selLast <= editPtr->selFirst) {
+ editPtr->selFirst = editPtr->selLast = -1; /* Cut away the entire
+ * selection. */
+ }
+ if (editPtr->selAnchor >= firstPos) {
+ if (editPtr->selAnchor >= lastPos) {
+ editPtr->selAnchor -= nBytes;
+ } else {
+ editPtr->selAnchor = firstPos;
+ }
+ }
+ if (editPtr->insertPos >= firstPos) {
+ if (editPtr->insertPos >= lastPos) {
+ editPtr->insertPos -= nBytes;
+ } else {
+ editPtr->insertPos = firstPos;
+ }
+ }
+ editPtr->string = newText;
+ UpdateLayout(tvPtr);
+ EventuallyRedrawEditor(tvPtr);
+ return TCL_OK;
+}
+
+static int
+AcquireText(tvPtr, entryPtr, columnPtr)
+ TreeView *tvPtr;
+ TreeViewEntry *entryPtr;
+ TreeViewColumn *columnPtr;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int x, y;
+ char *string;
+
+ string = NULL;
+ if (columnPtr == &tvPtr->treeColumn) {
+ int level;
+
+ level = DEPTH(tvPtr, entryPtr->node);
+ x = SCREENX(tvPtr, entryPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+ x += ICONWIDTH(level) + ICONWIDTH(level + 1) + 4;
+ string = GETLABEL(entryPtr);
+ } else {
+ Tcl_Obj *objPtr;
+
+ objPtr = Blt_TreeViewGetData(entryPtr, columnPtr->key);
+ if (objPtr != NULL) {
+ string = Tcl_GetString(objPtr);
+ }
+ x = SCREENX(tvPtr, columnPtr->worldX);
+ y = SCREENY(tvPtr, entryPtr->worldY);
+ }
+ if (editPtr->textPtr != NULL) {
+ Blt_Free(editPtr->textPtr);
+ editPtr->textPtr = NULL;
+ }
+ if (editPtr->string != NULL) {
+ Blt_Free(editPtr->string);
+ }
+ if (string == NULL) {
+ string = "";
+ }
+ editPtr->entryPtr = entryPtr;
+ editPtr->columnPtr = columnPtr;
+ editPtr->x = x - editPtr->borderWidth;
+ editPtr->y = y - editPtr->borderWidth;
+ editPtr->string = Blt_Strdup(string);
+ editPtr->gc = columnPtr->gc;
+ editPtr->font = CHOOSE(tvPtr->treeColumn.font, columnPtr->font);
+ editPtr->selFirst = editPtr->selLast = -1;
+ UpdateLayout(tvPtr);
+ Tk_MapWindow(editPtr->tkwin);
+ EventuallyRedrawEditor(tvPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetIndexFromObj --
+ *
+ * Parse an index into an entry and return either its value
+ * or an error.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the character index (into entryPtr) corresponding to
+ * string. The index value is guaranteed to lie between 0 and
+ * the number of characters in the string, inclusive. If an
+ * error occurs then an error message is left in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetIndexFromObj(interp, tvPtr, objPtr, indexPtr)
+ Tcl_Interp *interp;
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ int *indexPtr;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int textPos;
+ char c;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((editPtr->string == NULL) || (editPtr->string[0] == '\0')) {
+ *indexPtr = 0;
+ return TCL_OK;
+ }
+ c = string[0];
+ if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ textPos = editPtr->selAnchor;
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ textPos = strlen(editPtr->string);
+ } else if ((c == 'i') && (strcmp(string, "insert") == 0)) {
+ textPos = editPtr->insertPos;
+ } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
+ textPos = editPtr->insertPos;
+ if (textPos < strlen(editPtr->string)) {
+ textPos++;
+ }
+ } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
+ textPos = editPtr->insertPos;
+ if (textPos > 0) {
+ textPos--;
+ }
+ } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
+ if (editPtr->selFirst < 0) {
+ textPos = -1;
+ } else {
+ textPos = editPtr->selFirst;
+ }
+ } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
+ if (editPtr->selLast < 0) {
+ textPos = -1;
+ } else {
+ textPos = editPtr->selLast;
+ }
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(interp, editPtr->tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ textPos = PointerToIndex(tvPtr, x, y);
+ } else if (isdigit((int)c)) {
+ int number;
+ int maxChars;
+
+ if (Tcl_GetIntFromObj(interp, objPtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Don't allow the index to point outside the label. */
+ maxChars = Tcl_NumUtfChars(editPtr->string, -1);
+ if (number < 0) {
+ textPos = 0;
+ } else if (number > maxChars) {
+ textPos = strlen(editPtr->string);
+ } else {
+ textPos = Tcl_UtfAtIndex(editPtr->string, number) -
+ editPtr->string;
+ }
+ } else {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad label index \"", string, "\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *indexPtr = textPos;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectText --
+ *
+ * Modify the selection by moving its un-anchored end. This
+ * could make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectText(tvPtr, textPos)
+ TreeView *tvPtr; /* Information about editor. */
+ int textPos; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int selFirst, selLast;
+
+ /*
+ * Grab the selection if we don't own it already.
+ */
+ if ((editPtr->exportSelection) && (editPtr->selFirst == -1)) {
+ Tk_OwnSelection(editPtr->tkwin, XA_PRIMARY,
+ TreeViewEditorLostSelectionProc, tvPtr);
+ }
+ /* If the anchor hasn't been set yet, assume the beginning of the text*/
+ if (editPtr->selAnchor < 0) {
+ editPtr->selAnchor = 0;
+ }
+ if (editPtr->selAnchor <= textPos) {
+ selFirst = editPtr->selAnchor;
+ selLast = textPos;
+ } else {
+ selFirst = textPos;
+ selLast = editPtr->selAnchor;
+ }
+ if ((editPtr->selFirst != selFirst) || (editPtr->selLast != selLast)) {
+ editPtr->selFirst = selFirst;
+ editPtr->selLast = selLast;
+ EventuallyRedrawEditor(tvPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeViewEditorSelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is
+ * requested by someone. It returns part or all of the selection
+ * in a buffer provided by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored at
+ * buffer. Buffer is filled (or partially filled) with a
+ * NUL-terminated string containing part or all of the
+ * selection, as given by offset and maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TreeViewEditorSelectionProc(clientData, offset, buffer, maxBytes)
+ ClientData clientData; /* Information about the widget. */
+ int offset; /* Offset within selection of first
+ * character to be returned. */
+ char *buffer; /* Location in which to place
+ * selection. */
+ int maxBytes; /* Maximum number of bytes to place
+ * at buffer, not including terminating
+ * NULL character. */
+{
+ TreeView *tvPtr = clientData;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int size;
+
+ size = strlen(editPtr->string + offset);
+ /*
+ * Return the string currently in the editor.
+ */
+ strncpy(buffer, editPtr->string + offset, maxBytes);
+ buffer[maxBytes] = '\0';
+ return (size > maxBytes) ? maxBytes : size;
+}
+
+
+static void
+DestroyTreeViewEditor(data)
+ DestroyData data;
+{
+ TreeView *tvPtr = (TreeView *)data;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+
+ Blt_FreeObjOptions(editorConfigSpecs, (char *)editPtr, tvPtr->display, 0);
+
+ if (editPtr->string != NULL) {
+ Blt_Free(editPtr->string);
+ }
+ if (editPtr->textPtr != NULL) {
+ Blt_Free(editPtr->textPtr);
+ }
+ if (editPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(editPtr->timerToken);
+ }
+ if (editPtr->tkwin != NULL) {
+ Tk_DeleteSelHandler(editPtr->tkwin, XA_PRIMARY, XA_STRING);
+ }
+ Blt_Free(editPtr);
+}
+
+static void
+ConfigureTreeViewEditor(tvPtr)
+ TreeView *tvPtr;
+{
+#ifdef notdef
+ GC newGC;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ /*
+ * GC for edit window.
+ */
+ gcMask = 0;
+ newGC = Tk_GetGC(editPtr->tkwin, gcMask, &gcValues);
+ if (editPtr->gc != NULL) {
+ Tk_FreeGC(tvPtr->display, editPtr->gc);
+ }
+ editPtr->gc = newGC;
+ editPtr->width = editPtr->textPtr->width +
+ 2 * (editPtr->borderWidth + editPtr->highlightWidth);
+ editPtr->height = editPtr->textPtr->height +
+ 2 * (editPtr->borderWidth + editPtr->highlightWidth);
+
+ if (Tk_IsMapped(editPtr->tkwin)) {
+ if ((editPtr->height != Tk_Height(editPtr->tkwin)) ||
+ (editPtr->width != Tk_Width(editPtr->tkwin))) {
+ Tk_ResizeWindow(editPtr->tkwin, editPtr->width, editPtr->height);
+ }
+ }
+#endif
+}
+
+TreeViewEditor *
+Blt_TreeViewCreateEditor(tvPtr, className)
+ TreeView *tvPtr;
+ char *className; /* Class name of widget: either
+ * "Hiertable" or "TreeView". */
+{
+ TreeViewEditor *editPtr;
+ Tk_Window tkwin;
+ char editClass[20];
+
+ tkwin = Tk_CreateWindow(tvPtr->interp, tvPtr->tkwin, "edit", (char *)NULL);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ sprintf(editClass, "%sEditor", className);
+ Tk_SetClass(tkwin, editClass);
+
+ editPtr = Blt_Calloc(1, sizeof(TreeViewEditor));
+ assert(editPtr);
+
+ editPtr->tkwin = tkwin;
+ editPtr->borderWidth = 1;
+ editPtr->relief = TK_RELIEF_SOLID;
+ editPtr->selRelief = TK_RELIEF_RAISED;
+ editPtr->selBorderWidth = 1;
+ editPtr->selAnchor = -1;
+ editPtr->selFirst = editPtr->selLast = -1;
+ editPtr->onTime = 600;
+ editPtr->active = TRUE;
+ editPtr->offTime = 300;
+#if (TK_MAJOR_VERSION > 4)
+ Blt_SetWindowInstanceData(tkwin, editPtr);
+#endif
+ Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING,
+ TreeViewEditorSelectionProc, tvPtr, XA_STRING);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask |
+ FocusChangeMask, TreeViewEditorEventProc, tvPtr);
+ Tcl_CreateObjCommand(tvPtr->interp, Tk_PathName(tkwin),
+ Blt_TreeViewWidgetInstCmd, tvPtr, NULL);
+ if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tkwin, editorConfigSpecs, 0,
+ (Tcl_Obj **)NULL, (char *)editPtr, 0) != TCL_OK) {
+ Tk_DestroyWindow(tkwin);
+ return NULL;
+ }
+ return editPtr;
+}
+
+static void
+DisplayTreeViewEditor(clientData)
+ ClientData clientData;
+{
+ TreeView *tvPtr = clientData;
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ Pixmap drawable;
+ register int i;
+ int x1, x2;
+ int count, nChars;
+ int leftPos, rightPos;
+ int selStart, selEnd, selLength;
+ int x, y;
+ TextFragment *fragPtr;
+ Tk_FontMetrics fontMetrics;
+
+ editPtr->flags &= ~EDITOR_REDRAW;
+ if (!Tk_IsMapped(editPtr->tkwin)) {
+ return;
+ }
+ drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(editPtr->tkwin),
+ Tk_Width(editPtr->tkwin), Tk_Height(editPtr->tkwin),
+ Tk_Depth(editPtr->tkwin));
+
+ Tk_Fill3DRectangle(editPtr->tkwin, drawable, editPtr->border, 0, 0,
+ Tk_Width(editPtr->tkwin), Tk_Height(editPtr->tkwin),
+ editPtr->borderWidth, editPtr->relief);
+
+ Tk_GetFontMetrics(editPtr->font, &fontMetrics);
+ fragPtr = editPtr->textPtr->fragArr;
+ count = 0;
+ for (i = 0; i < editPtr->textPtr->nFrags; i++, fragPtr++) {
+ leftPos = count;
+ count += fragPtr->count;
+ rightPos = count;
+ y = fragPtr->y + editPtr->borderWidth;
+ x = editPtr->borderWidth;
+ if ((rightPos < editPtr->selFirst) || (leftPos > editPtr->selLast)) {
+ /* No selected text */
+ Tk_DrawChars(tvPtr->display, drawable, editPtr->gc, editPtr->font,
+ fragPtr->text, fragPtr->count, x, y);
+ continue;
+ }
+ /*
+ * A text segment (with selected text) may have up to 3 regions:
+ *
+ * 1. Text before the start the selection
+ * 2. Selected text itself (drawn in a raised border)
+ * 3. Text following the selection.
+ */
+
+ selStart = leftPos;
+ selEnd = rightPos;
+ /* First adjust selected region for current line. */
+ if (editPtr->selFirst > leftPos) {
+ selStart = editPtr->selFirst;
+ }
+ if (editPtr->selLast < rightPos) {
+ selEnd = editPtr->selLast;
+ }
+ selLength = (selEnd - selStart);
+ x1 = x;
+
+ if (selStart > leftPos) { /* Normal text preceding the selection */
+ nChars = (selStart - leftPos);
+ Tk_MeasureChars(editPtr->font, editPtr->string + leftPos,
+ nChars, 10000, DEF_TEXT_FLAGS, &x1);
+ x1 += x;
+ }
+ if (selLength > 0) { /* The selection itself */
+ int width;
+
+ Tk_MeasureChars(editPtr->font, fragPtr->text + selStart, selLength,
+ 10000, DEF_TEXT_FLAGS, &x2);
+ x2 += x;
+ width = (x2 - x1) + 1;
+ Tk_Fill3DRectangle(editPtr->tkwin, drawable, editPtr->selBorder,
+ x1, y - fontMetrics.ascent, width, fontMetrics.linespace,
+ editPtr->selBorderWidth, editPtr->selRelief);
+ }
+ Tk_DrawChars(Tk_Display(editPtr->tkwin), drawable, editPtr->gc,
+ editPtr->font, fragPtr->text, fragPtr->count, x, y);
+ }
+ if ((editPtr->flags & EDITOR_FOCUS) && (editPtr->cursorOn)) {
+ int left, top, right, bottom;
+
+ IndexToPointer(tvPtr);
+ left = editPtr->cursorX + editPtr->borderWidth + 1;
+ right = left + 1;
+ top = editPtr->cursorY + 2;
+ bottom = editPtr->cursorY + editPtr->cursorHeight - 2;
+ XDrawLine(tvPtr->display, drawable, editPtr->gc, left, top, left,
+ bottom);
+ XDrawLine(tvPtr->display, drawable, editPtr->gc, left - 1, top, right,
+ top);
+ XDrawLine(tvPtr->display, drawable, editPtr->gc, left - 1, bottom,
+ right, bottom);
+ }
+ Tk_Draw3DRectangle(editPtr->tkwin, drawable, editPtr->border, 0, 0,
+ Tk_Width(editPtr->tkwin), Tk_Height(editPtr->tkwin),
+ editPtr->borderWidth, editPtr->relief);
+ XCopyArea(tvPtr->display, drawable, Tk_WindowId(editPtr->tkwin),
+ editPtr->gc, 0, 0, Tk_Width(editPtr->tkwin),
+ Tk_Height(editPtr->tkwin), 0, 0);
+ Tk_FreePixmap(tvPtr->display, drawable);
+
+}
+
+/*ARGSUSED*/
+static int
+ApplyOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ TreeViewEntry *entryPtr;
+
+ entryPtr = editPtr->entryPtr;
+ if (editPtr->columnPtr == &tvPtr->treeColumn) {
+
+ if (entryPtr->labelUid != NULL) {
+ Blt_TreeViewFreeUid(tvPtr, entryPtr->labelUid);
+ }
+ if (editPtr->string == NULL) {
+ entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, "");
+ } else {
+ entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, editPtr->string);
+ }
+ } else {
+ TreeViewColumn *columnPtr;
+ Tcl_Obj *objPtr;
+
+ columnPtr = editPtr->columnPtr;
+ objPtr = Tcl_NewStringObj(editPtr->string, -1);
+ if (Blt_TreeSetValueByKey(interp, tvPtr->tree, entryPtr->node,
+ columnPtr->key, objPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ entryPtr->flags |= ENTRY_DIRTY;
+ }
+ Blt_TreeViewConfigureEntry(tvPtr, entryPtr);
+ tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+ Blt_TreeViewEventuallyRedraw(tvPtr);
+ Tk_UnmapWindow(editPtr->tkwin);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CancelOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ Tk_UnmapWindow(tvPtr->editPtr->tkwin);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ return Blt_ConfigureValueFromObj(interp, tvPtr->editPtr->tkwin,
+ editorConfigSpecs, (char *)tvPtr->editPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * This procedure is called to process a list of configuration
+ * options database, in order to reconfigure the one of more
+ * entries in the widget.
+ *
+ * .h text configure option value
+ *
+ * Results:
+ * A standard Tcl result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for tvPtr; old resources get freed, if there
+ * were any. The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->editPtr->tkwin,
+ editorConfigSpecs, (char *)tvPtr->editPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, tvPtr->editPtr->tkwin,
+ editorConfigSpecs, (char *)tvPtr->editPtr, objv[3], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, tvPtr->editPtr->tkwin,
+ editorConfigSpecs, objc - 3, objv + 3, (char *)tvPtr->editPtr,
+ BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureTreeViewEditor(tvPtr);
+ EventuallyRedrawEditor(tvPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Remove one or more characters from the label of an entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets freed, the entry gets modified and (eventually)
+ * redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int firstPos, lastPos;
+
+ if (tvPtr->editPtr->entryPtr == NULL) {
+ return TCL_OK;
+ }
+ if (GetIndexFromObj(interp, tvPtr, objv[3], &firstPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ lastPos = firstPos;
+ if ((objc == 5) &&
+ (GetIndexFromObj(interp, tvPtr, objv[4], &lastPos) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (firstPos > lastPos) {
+ return TCL_OK;
+ }
+ return DeleteText(tvPtr, firstPos, lastPos);
+}
+
+/*ARGSUSED*/
+static int
+GetOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEntry *entryPtr;
+ char *string;
+ int x, y;
+ int isRoot;
+
+ isRoot = FALSE;
+ string = Tcl_GetString(objv[3]);
+ if (strcmp("-root", string) == 0) {
+ isRoot = TRUE;
+ objv++, objc--;
+ }
+ if (objc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
+ Tcl_GetString(objv[2]), " ?-root? x y\"", (char *)NULL);
+ return TCL_ERROR;
+
+ }
+ if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (isRoot) {
+ int rootX, rootY;
+
+ Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
+ x -= rootX;
+ y -= rootY;
+ }
+ entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
+ if (entryPtr != NULL) {
+ Blt_ChainLink *linkPtr;
+ TreeViewColumn *columnPtr;
+ int worldX;
+
+ worldX = WORLDX(tvPtr, x);
+ for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ columnPtr = Blt_ChainGetValue(linkPtr);
+ if (!columnPtr->editable) {
+ continue;
+ }
+ if ((worldX >= columnPtr->worldX) &&
+ (worldX < (columnPtr->worldX + columnPtr->width))) {
+ AcquireText(tvPtr, entryPtr, columnPtr);
+ tvPtr->editPtr->insertPos = strlen(tvPtr->editPtr->string);
+ Tk_MapWindow(tvPtr->editPtr->tkwin);
+ EventuallyRedrawEditor(tvPtr);
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(Blt_TreeNodeId(entryPtr->node)));
+ return TCL_OK;
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IcursorOp --
+ *
+ * Returns the numeric index of the given string. Indices can be
+ * one of the following:
+ *
+ * "anchor" Selection anchor.
+ * "end" End of the label.
+ * "insert" Insertion cursor.
+ * "sel.first" First character selected.
+ * "sel.last" Last character selected.
+ * @x,y Index at X-Y screen coordinate.
+ * number Returns the same number.
+ *
+ * Results:
+ * A standard Tcl result. If the argument does not represent a
+ * valid label index, then TCL_ERROR is returned and the interpreter
+ * result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IcursorOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int textPos;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[3], &textPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (editPtr->columnPtr != NULL) {
+ editPtr->insertPos = textPos;
+ IndexToPointer(tvPtr);
+ EventuallyRedrawEditor(tvPtr);
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Returns the numeric index of the given string. Indices can be
+ * one of the following:
+ *
+ * "anchor" Selection anchor.
+ * "end" End of the label.
+ * "insert" Insertion cursor.
+ * "sel.first" First character selected.
+ * "sel.last" Last character selected.
+ * @x,y Index at X-Y screen coordinate.
+ * number Returns the same number.
+ *
+ * Results:
+ * A standard Tcl result. If the argument does not represent a
+ * valid label index, then TCL_ERROR is returned and the interpreter
+ * result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int textPos;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[3], &textPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((editPtr->columnPtr != NULL) && (editPtr->string != NULL)) {
+ int nChars;
+
+ nChars = Tcl_NumUtfChars(editPtr->string, textPos);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nChars));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ * Add new characters to the label of an entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * New information gets added to editPtr; it will be redisplayed
+ * soon, but not necessarily immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ TreeViewEditor *editPtr = tvPtr->editPtr;
+ int extra;
+ int insertPos;
+ char *string;
+
+ if (editPtr->entryPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (GetIndexFromObj(interp, tvPtr, objv[3], &insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[4]);
+ extra = strlen(string);
+ if (extra == 0) { /* Nothing to insert. Move the cursor anyways. */
+ editPtr->insertPos = insertPos;
+ } else {
+ InsertText(tvPtr, string, insertPos, extra);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionAdjustOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int textPos;
+ int half1, half2;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[4], &textPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ half1 = (tvPtr->editPtr->selFirst + tvPtr->editPtr->selLast) / 2;
+ half2 = (tvPtr->editPtr->selFirst + tvPtr->editPtr->selLast + 1) / 2;
+ if (textPos < half1) {
+ tvPtr->editPtr->selAnchor = tvPtr->editPtr->selLast;
+ } else if (textPos > half2) {
+ tvPtr->editPtr->selAnchor = tvPtr->editPtr->selFirst;
+ }
+ return SelectText(tvPtr, textPos);
+}
+
+/*ARGSUSED*/
+static int
+SelectionClearOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ if (tvPtr->editPtr->selFirst != -1) {
+ tvPtr->editPtr->selFirst = tvPtr->editPtr->selLast = -1;
+ EventuallyRedrawEditor(tvPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionFromOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int textPos;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[4], &textPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->editPtr->selAnchor = textPos;
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionPresentOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ int bool;
+
+ bool = (tvPtr->editPtr->selFirst != -1);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionRangeOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int selFirst, selLast;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[4], &selFirst) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetIndexFromObj(interp, tvPtr, objv[5], &selLast) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tvPtr->editPtr->selAnchor = selFirst;
+ return SelectText(tvPtr, selLast);
+}
+
+/*ARGSUSED*/
+static int
+SelectionToOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int textPos;
+
+ if (GetIndexFromObj(interp, tvPtr, objv[4], &textPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return SelectText(tvPtr, textPos);
+}
+
+
+static Blt_OpSpec textSelectionOps[] =
+{
+ {"adjust", 1, (Blt_Op)SelectionAdjustOp, 5, 5, "index",},
+ {"clear", 1, (Blt_Op)SelectionClearOp, 4, 4, "",},
+ {"from", 1, (Blt_Op)SelectionFromOp, 5, 5, "index"},
+ {"present", 1, (Blt_Op)SelectionPresentOp, 4, 4, ""},
+ {"range", 1, (Blt_Op)SelectionRangeOp, 6, 6, "start end",},
+ {"to", 1, (Blt_Op)SelectionToOp, 5, 5, "index"},
+};
+
+static int nTextSelectionOps = sizeof(textSelectionOps) / sizeof(Blt_OpSpec);
+
+/*
+ * This procedure handles the individual options for text
+ * selections. The selected text is designated by start and end
+ * indices into the text pool. The selected segment has both a
+ * anchored and unanchored ends. The following selection
+ * operations are implemented:
+ *
+ * "adjust" - resets either the first or last index
+ * of the selection.
+ * "clear" - clears the selection. Sets first/last
+ * indices to -1.
+ * "from" - sets the index of the selection anchor.
+ * "present" - return "1" if a selection is available,
+ * "0" otherwise.
+ * "range" - sets the first and last indices.
+ * "to" - sets the index of the un-anchored end.
+ */
+static int
+SelectionOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTextSelectionOps, textSelectionOps,
+ BLT_OP_ARG3, objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewTextOp --
+ *
+ * This procedure handles entry operations.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec textOps[] =
+{
+ {"apply", 1, (Blt_Op)ApplyOp, 3, 3, "",},
+ {"cancel", 2, (Blt_Op)CancelOp, 3, 3, "",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "value",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value...?",},
+ {"delete", 1, (Blt_Op)DeleteOp, 4, 5, "first ?last?"},
+ {"get", 1, (Blt_Op)GetOp, 5, 6, "?-root? x y",},
+ {"icursor", 2, (Blt_Op)IcursorOp, 4, 4, "index"},
+ {"index", 3, (Blt_Op)IndexOp, 4, 4, "index"},
+ {"insert", 3, (Blt_Op)InsertOp, 5, 5, "index string"},
+ {"selection", 3, (Blt_Op)SelectionOp, 3, 0, "args"},
+};
+static int nTextOps = sizeof(textOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_TreeViewTextOp(tvPtr, interp, objc, objv)
+ TreeView *tvPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nTextOps, textOps, BLT_OP_ARG2, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (tvPtr, interp, objc, objv);
+ return result;
+}
+
+#endif
diff --git a/blt/src/bltTreeViewStyle.c b/blt/src/bltTreeViewStyle.c
new file mode 100644
index 00000000000..94239e32314
--- /dev/null
+++ b/blt/src/bltTreeViewStyle.c
@@ -0,0 +1,423 @@
+/*
+ * bltTreeViewStyle.c --
+ *
+ * This module implements styles for treeview widget cells.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "treeview" widget was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include "bltList.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define STYLE_TEXT_ENTRY 0
+#define STYLE_OPTION_MENU 1
+#define STYLE_CHECK_BOX 2
+
+#define DEF_TEXT_ENTRY_SIDE "left"
+#define DEF_TEXT_ENTRY_KEY (char *)NULL
+#define DEF_TEXT_ENTRY_ICON (char *)NULL
+#define DEF_TEXT_ENTRY_BACKGROUND RGB_WHITE
+#define DEF_TEXT_ENTRY_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TEXT_ENTRY_FG_MONO STD_MONO_NORMAL_FG
+#define DEF_TEXT_ENTRY_FONT STD_FONT
+#define DEF_TEXT_ENTRY_HIGHLIGHT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TEXT_ENTRY_HIGHLIGHT_FG_COLOR STD_COLOR_NORMAL_FG
+#define DEF_TEXT_ENTRY_NORMAL_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TEXT_ENTRY_NORMAL_FG_COLOR STD_COLOR_NORMAL_FG
+
+extern Blt_CustomOption bltTreeViewImagesOption;
+
+static StyleCreateProc CreateTextEntry, CreateCheckBox, CreateOptionMenu;
+static StyleFreeProc FreeTextEntry, FreeCheckBox, FreeOptionMenu;
+static StyleDrawProc DrawTextEntry, DrawCheckBox, DrawOptionMenu;
+static StyleConfigProc ConfigTextEntry, ConfigCheckBox, ConfigOptionMenu;
+
+typedef struct {
+ int refCount; /* Usage reference count. */
+ unsigned int flags;
+ char *name;
+ TreeViewStyleClass *classPtr; /* Class-specific routines to manage style. */
+ Blt_HashEntry *hashPtr;
+
+ XColor *fgColor; /* Normal foreground color of cell. */
+ XColor *bgColor; /* Normal background color of cell. */
+ XColor *highlightFgColor; /* Foreground color of cell when
+ * highlighted. */
+ XColor *highlightBgColor; /* Background color of cell when
+ * highlighted. */
+ Tk_Font font;
+ TreeViewImage icon; /* If non-NULL, is a Tk_Image to be drawn
+ * in the cell. */
+ int side; /* Position of the text in relation to
+ * the icon. */
+ Blt_TreeKey key; /* Actual data resides in this data value */
+} TextEntry;
+
+static Blt_ConfigSpec textEntrySpecs[] =
+{
+ {BLT_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TEXT_ENTRY_NORMAL_BG_COLOR, Blt_Offset(TextEntry, bgColor), 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_TEXT_ENTRY_FONT, Blt_Offset(TextEntry, font), 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TEXT_ENTRY_NORMAL_FG_COLOR, Blt_Offset(TextEntry, fgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_TEXT_ENTRY_HIGHLIGHT_BG_COLOR,
+ Blt_Offset(TextEntry, highlightBgColor), BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-highlightforeground", "highlightForeground",
+ "HighlightForeground", DEF_TEXT_ENTRY_HIGHLIGHT_FG_COLOR,
+ Blt_Offset(TextEntry, highlightFgColor), 0},
+ {BLT_CONFIG_CUSTOM, "-icon", "icon", "Icon",
+ DEF_TEXT_ENTRY_ICON, Blt_Offset(TextEntry, icon),
+ BLT_CONFIG_NULL_OK, &bltTreeViewImagesOption},
+ {BLT_CONFIG_STRING, "-key", "key", "key",
+ DEF_TEXT_ENTRY_KEY, Blt_Offset(TextEntry, key),
+ BLT_CONFIG_NULL_OK, 0},
+ {BLT_CONFIG_SIDE, "-side", "side", "side",
+ DEF_TEXT_ENTRY_SIDE, Tk_Offset(TextEntry, side),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+typedef struct {
+ int refCount; /* Usage reference count. */
+ unsigned int flags;
+ char *name;
+ TreeViewStyleClass *classPtr; /* Class-specific routines to manage style. */
+ Blt_HashEntry *hashPtr;
+
+ XColor *fgColor; /* Text color of the cell. */
+ Tk_3DBorder border; /* Background color of the cell. */
+ TreeViewImage icon; /* If non-NULL, is a Tk_Image to be drawn
+ * in the cell. */
+ int anchor; /* Position of the text in relation to
+ * the icon. */
+ Blt_TreeKey dataKey; /* Actual data resides in this data value */
+
+ char *choices; /* Choices possible. */
+ char *defChoice; /* Default choice. */
+ int useScrollbar;
+ int scrollWidth;
+ int button;
+} OptionMenu;
+
+static Blt_ConfigSpec optionMenuSpecs[] =
+{
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+typedef struct {
+ int refCount; /* Usage reference count. */
+ unsigned int flags; /* Contains style type and update flags. */
+ char *name; /* Instance name. */
+ TreeViewStyleClass *classPtr; /* Class-specific routines to manage style. */
+ Blt_HashEntry *hashPtr; /* If non-NULL, points to the hash
+ * table entry for the style. A style
+ * that's been deleted, but still in
+ * use (non-zero reference count) will
+ * have no hash table entry.
+ */
+ XColor *fgColor; /* Text color of the cell. */
+ Tk_3DBorder border; /* Background color of the cell. */
+ TreeViewImage icon; /* If non-NULL, is a Tk_Image to be drawn
+ * in the cell. */
+ int anchor; /* Position of the text in relation to
+ * the icon. */
+ Blt_TreeKey dataKey; /* Actual data resides in this data value */
+
+ /* Checkbox specific fields. */
+ char *onValue;
+ char *offValue;
+} CheckBox;
+
+static Blt_ConfigSpec checkBoxSpecs[] =
+{
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static TreeViewStyleClass textEntryClass = {
+ textEntrySpecs,
+ ConfigTextEntry,
+ DrawTextEntry,
+ FreeTextEntry,
+};
+
+static TreeViewStyleClass checkBoxClass = {
+ checkBoxSpecs,
+ ConfigCheckBox,
+ DrawCheckBox,
+ FreeCheckBox,
+};
+
+static TreeViewStyleClass optionMenuClass = {
+ optionMenuSpecs,
+ ConfigOptionMenu,
+ DrawOptionMenu,
+ FreeOptionMenu,
+};
+
+static TreeViewStyle *
+CreateTextEntry(tvPtr)
+ TreeView *tvPtr;
+{
+ TextEntry *textEntryPtr;
+
+ textEntryPtr = Blt_Calloc(1, sizeof(TextEntry));
+ assert(textEntryPtr);
+ textEntryPtr->classPtr = &textEntryClass;
+ textEntryPtr->side = SIDE_LEFT;
+ return (TreeViewStyle *)textEntryPtr;
+}
+
+static void
+ConfigTextEntry(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+DrawTextEntry(tvPtr, drawable, entryPtr, stylePtr, valuePtr, x, y)
+ TreeView *tvPtr;
+ Drawable drawable;
+ TreeViewEntry *entryPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int x, y;
+{
+ TreeViewColumn *columnPtr = valuePtr->columnPtr;
+
+ if (valuePtr->image != NULL) {
+ Tk_RedrawImage(TreeViewImageData(valuePtr->image), 0, 0,
+ valuePtr->width, valuePtr->height, drawable, x, y);
+ } else {
+ TextStyle ts;
+ XColor *color;
+
+ if (entryPtr->color != NULL) {
+ XSetForeground(tvPtr->display, columnPtr->gc,
+ entryPtr->color->pixel);
+ color = entryPtr->color;
+ } else {
+ color = columnPtr->fgColor;
+ }
+ Blt_SetDrawTextStyle(&ts, columnPtr->font, columnPtr->gc, color,
+ tvPtr->selFgColor, entryPtr->shadow.color, 0.0, TK_ANCHOR_NW,
+ TK_JUSTIFY_LEFT, 0, entryPtr->shadow.offset);
+ Blt_DrawTextLayout(tvPtr->tkwin, drawable, valuePtr->textPtr,
+ &ts, x, y);
+ if (entryPtr->color != NULL) {
+ XSetForeground(tvPtr->display, valuePtr->columnPtr->gc,
+ columnPtr->fgColor->pixel);
+ }
+ }
+}
+
+static void
+FreeTextEntry(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+MeasureTextEntry(tvPtr, stylePtr, valuePtr, widthPtr, heightPtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int *widthPtr, *heightPtr;
+{
+}
+
+static TreeViewStyle *
+CreateCheckBox(tvPtr)
+ TreeView *tvPtr;
+{
+ CheckBox *checkBoxPtr;
+
+ checkBoxPtr = Blt_Malloc(sizeof(CheckBox));
+ checkBoxPtr->classPtr = &checkBoxClass;
+ return (TreeViewStyle *)checkBoxPtr;
+}
+
+static void
+ConfigCheckBox(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+DrawCheckBox(tvPtr, drawable, entryPtr, stylePtr, valuePtr, x, y)
+ TreeView *tvPtr;
+ Drawable drawable;
+ TreeViewEntry *entryPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int x, y;
+{
+}
+
+static void
+FreeCheckBox(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+MeasureCheckBox(tvPtr, stylePtr, valuePtr, widthPtr, heightPtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int *widthPtr, *heightPtr;
+{
+}
+
+static TreeViewStyle *
+CreateOptionMenu(tvPtr)
+ TreeView *tvPtr;
+{
+ OptionMenu *optionMenuPtr;
+
+ optionMenuPtr = Blt_Malloc(sizeof(OptionMenu));
+ optionMenuPtr->classPtr = &optionMenuClass;
+ return (TreeViewStyle *)optionMenuPtr;
+}
+
+static void
+ConfigOptionMenu(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+DrawOptionMenu(tvPtr, drawable, entryPtr, stylePtr, valuePtr, x, y)
+ TreeView *tvPtr;
+ Drawable drawable;
+ TreeViewEntry *entryPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int x, y;
+{
+}
+
+static void
+FreeOptionMenu(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+}
+
+static void
+MeasureOptionMenu(tvPtr, stylePtr, valuePtr, widthPtr, heightPtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+ TreeViewValue *valuePtr;
+ int *widthPtr, *heightPtr;
+{
+}
+
+TreeViewStyle *
+Blt_TreeViewGetStyle(interp, tvPtr, objPtr, stylePtrPtr)
+ Tcl_Interp *interp;
+ TreeView *tvPtr;
+ Tcl_Obj *objPtr;
+ TreeViewStyle *stylePtrPtr;
+{
+ Blt_HashEntry *hPtr;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&tvPtr->styleTable, string);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return Blt_GetHashValue(hPtr);
+}
+
+TreeViewStyle *
+Blt_TreeViewCreateStyle(tvPtr, type, name)
+ TreeView *tvPtr; /* TreeView widget. */
+ int type; /* Type of style: either
+ * STYLE_TEXT_ENTRY,
+ * STYLE_OPTION_MENU, or
+ * STYLE_CHECK_BOX */
+ char *name; /* Name of the new style. */
+{
+ TreeViewStyle *stylePtr;
+
+ /* Create the new marker based upon the given type */
+ switch (type) {
+ case STYLE_TEXT_ENTRY:
+ stylePtr = CreateTextEntry(tvPtr);
+ break;
+ case STYLE_OPTION_MENU:
+ stylePtr = CreateOptionMenu(tvPtr);
+ break;
+ case STYLE_CHECK_BOX:
+ stylePtr = CreateCheckBox(tvPtr);
+ break;
+ default:
+ return NULL;
+ }
+ assert(stylePtr);
+ stylePtr->name = Blt_Strdup(name);
+ stylePtr->flags = type;
+ stylePtr->refCount = 0;
+ return stylePtr;
+}
+
+void
+Blt_TreeViewFreeStyle(tvPtr, stylePtr)
+ TreeView *tvPtr;
+ TreeViewStyle *stylePtr;
+{
+ stylePtr->refCount--;
+ /* Remove the style from the hash table so that it's name can be used.*/
+ if (stylePtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&tvPtr->styleTable, stylePtr->hashPtr);
+ stylePtr->hashPtr = NULL;
+ }
+ /* If no cell is using the style, remove it.*/
+ if (stylePtr->refCount <= 0) {
+ (*stylePtr->classPtr->freeProc)(tvPtr, stylePtr);
+ }
+}
+
+#endif /* NO_TREEVIEW */
diff --git a/blt/src/bltUnixDnd.c b/blt/src/bltUnixDnd.c
new file mode 100644
index 00000000000..ecf70d71d0e
--- /dev/null
+++ b/blt/src/bltUnixDnd.c
@@ -0,0 +1,5140 @@
+
+/*
+ * bltUnixDnd.c --
+ *
+ * This module implements a drag-and-drop manager for the BLT
+ * Toolkit. Allows widgets to be registered as drag&drop sources
+ * and targets for handling "drag-and-drop" operations between
+ * Tcl/Tk applications.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "drag&drop" command was created by Michael J. McLennan.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_DRAGDROP
+
+#include <bltHash.h>
+#include <bltChain.h>
+
+
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+
+#define DND_THREAD_KEY "BLT Dnd Data"
+
+#define DND_SELECTED (1<<0)
+#define DND_INITIATED (1<<1)
+#define DND_ACTIVE (DND_SELECTED | DND_INITIATED)
+#define DND_IN_PACKAGE (1<<2) /* Indicates if a token package command is
+ * currently active. The user may invoke
+ * "update" or "tkwait" commands from within
+ * the package command script. This allows the
+ * "drag" operation to preempt itself. */
+#define DND_VOIDED (1<<3)
+#define DND_DELETED (1<<4)
+
+#define PACK(lo,hi) (((hi) << 16) | ((lo) & 0x0000FFFF))
+#define UNPACK(x,lo,hi) ((lo) = (x & 0x0000FFFF), (hi) = (x >> 16))
+
+#define WATCH_ENTER (1<<0)
+#define WATCH_LEAVE (1<<1)
+#define WATCH_MOTION (1<<2)
+#define WATCH_MASK (WATCH_ENTER | WATCH_LEAVE | WATCH_MOTION)
+
+/* Source-to-Target Message Types */
+
+#define ST_DRAG_ENTER 0x1001
+#define ST_DRAG_LEAVE 0x1002
+#define ST_DRAG_MOTION 0x1003
+#define ST_DROP 0x1004
+
+/* Target-to-Source Message Types */
+
+#define TS_DRAG_STATUS 0x1005
+#define TS_START_DROP 0x1006
+#define TS_DROP_RESULT 0x1007
+
+/* Indices of information fields in ClientMessage array. */
+
+#define MESG_TYPE 0 /* Message type. */
+#define MESG_WINDOW 1 /* Window id of remote. */
+#define MESG_TIMESTAMP 2 /* Transaction timestamp. */
+#define MESG_POINT 3 /* Root X-Y coordinate. */
+#define MESG_STATE 4 /* Button and key state. */
+#define MESG_RESPONSE 3 /* Response to drag/drop message. */
+#define MESG_FORMAT 3 /* Format atom. */
+#define MESG_PROPERTY 4 /* Index of button #/key state. */
+
+/* Drop Status Values (actions included) */
+
+#define DROP_CONTINUE -2
+#define DROP_FAIL -1
+#define DROP_CANCEL 0
+#define DROP_OK 1
+#define DROP_COPY 1
+#define DROP_LINK 2
+#define DROP_MOVE 3
+
+#define PROP_WATCH_FLAGS 0
+#define PROP_DATA_FORMATS 1
+#define PROP_MAX_SIZE 1000 /* Maximum size of property. */
+
+#define PROTO_BLT 0
+#define PROTO_XDND 1
+
+#define TOKEN_OFFSET 0
+#define TOKEN_REDRAW (1<<0)
+
+#define TOKEN_NORMAL 0
+#define TOKEN_REJECT -1
+#define TOKEN_ACTIVE 1
+#define TOKEN_TIMEOUT 5000 /* 5 second timeout for drop requests. */
+
+/*
+ * Each widget representing a drag & drop target is tagged with
+ * a "BltDndTarget" property in XA_STRING format. This property
+ * identifies the window as a target. It's formated as a Tcl list
+ * and contains the following information:
+ *
+ * "flags DATA_TYPE DATA_TYPE ..."
+ *
+ * "INTERP_NAME TARGET_NAME WINDOW_ID DATA_TYPE DATA_TYPE ..."
+ *
+ * INTERP_NAME Name of the target application's interpreter.
+ * TARGET_NAME Path name of widget registered as the drop target.
+ * WINDOW_ID Window Id of the target's communication window.
+ * Used to forward Enter/Leave/Motion event information
+ * to the target.
+ * DATA_TYPE One or more "types" handled by the target.
+ *
+ * When the user invokes the "drag" operation, the window hierarchy
+ * is progressively examined. Window information is cached during
+ * the operation, to minimize X server traffic. Windows carrying a
+ * "BltDndTarget" property are identified. When the token is dropped
+ * over a valid site, the drop information is sent to the application
+ * via the usual "send" command. If communication fails, the drag&drop
+ * facility automatically posts a rejection symbol on the token window.
+ */
+
+/*
+ * Drop Protocol:
+ *
+ * Source Target
+ * ------ ------
+ * ButtonRelease-? event.
+ * Invokes blt::dnd drop
+ * +
+ * Send "drop" message to target (via
+ * ClientMessage). Contains X-Y, key/ --> Gets "drop" message.
+ * button state, source window XID. Invokes LeaveCmd proc.
+ * Gets property from source of
+ * ordered matching formats.
+ * +
+ * Invokes DropCmd proc. Arguments
+ * are X-Y coordinate, key/button
+ * state, transaction timestamp,
+ * list of matching formats.
+ * +
+ * Target selects format and invokes
+ * blt::dnd pull to transfer the data
+ * in the selected format.
+ * +
+ * Sends "drop start" message to
+ * source. Contains selected format
+ * Gets "drop start" message. <-- (as atom), ?action?, target window
+ * Invokes data handler for the ID, transaction timestamp.
+ * selected format. +
+ * + Waits for property to change on
+ * Places first packet of data in its window. Time out set for
+ * property on target window. --> no response.
+ * + +
+ * Waits for response property After each packet, sets zero-length
+ * change. Time out set for no resp. <-- property on source window.
+ * If non-zero length packet, error +
+ * occurred, packet is error message. Sends "drop finished" message.
+ * Contains transaction timestamp,
+ * Gets "drop finished" message. <-- status, ?action?.
+ * Invokes FinishCmd proc.
+ */
+
+/* Configuration Parameters */
+
+#define DEF_DND_BUTTON_BG_COLOR RGB_YELLOW
+#define DEF_DND_BUTTON_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_DND_BUTTON_NUMBER "3"
+#define DEF_DND_ENTER_COMMAND (char *)NULL
+#define DEF_DND_LEAVE_COMMAND (char *)NULL
+#define DEF_DND_MOTION_COMMAND (char *)NULL
+#define DEF_DND_DROP_COMMAND (char *)NULL
+#define DEF_DND_RESULT_COMMAND (char *)NULL
+#define DEF_DND_PACKAGE_COMMAND (char *)NULL
+#define DEF_DND_SELF_TARGET "no"
+#define DEF_DND_SEND (char *)NULL
+#define DEF_DND_IS_TARGET "no"
+#define DEF_DND_IS_SOURCE "no"
+#define DEF_DND_SITE_COMMAND (char *)NULL
+
+#define DEF_DND_DRAG_THRESHOLD "0"
+#define DEF_TOKEN_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BORDERWIDTH "3"
+#define DEF_TOKEN_ACTIVE_RELIEF "sunken"
+#define DEF_TOKEN_ANCHOR "se"
+#define DEF_TOKEN_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TOKEN_BORDERWIDTH "3"
+#define DEF_TOKEN_CURSOR "top_left_arrow"
+#define DEF_TOKEN_REJECT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_REJECT_BG_MONO RGB_WHITE
+#define DEF_TOKEN_REJECT_FG_COLOR RGB_RED
+#define DEF_TOKEN_REJECT_FG_MONO RGB_BLACK
+#define DEF_TOKEN_REJECT_STIPPLE_COLOR (char *)NULL
+#define DEF_TOKEN_REJECT_STIPPLE_MONO RGB_GREY50
+#define DEF_TOKEN_RELIEF "raised"
+
+static int StringToCursors _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+ int offset));
+static char *CursorsToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+ char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption cursorsOption =
+{
+ StringToCursors, CursorsToString, (ClientData)0
+};
+
+typedef struct {
+ Blt_HashTable dndTable; /* Hash table of dnd structures keyed by
+ * the address of the reference Tk window */
+ Tk_Window mainWindow;
+ Display *display;
+ Atom mesgAtom; /* Atom signifying a drag-and-drop message. */
+ Atom formatsAtom; /* Source formats property atom. */
+ Atom targetAtom; /* Target property atom. */
+ Atom commAtom; /* Communication property atom. */
+
+#ifdef HAVE_XDND
+ Blt_HashTable handlerTable; /* Table of toplevel windows with XdndAware
+ * properties attached to them. */
+ Atom XdndActionListAtom;
+ Atom XdndAwareAtom;
+ Atom XdndEnterAtom;
+ Atom XdndFinishedAtom;
+ Atom XdndLeaveAtom;
+ Atom XdndPositionAtom;
+ Atom XdndSelectionAtom;
+ Atom XdndStatusAtom;
+ Atom XdndTypeListAtom;
+
+ Atom XdndActionCopyAtom;
+ Atom XdndActionMoveAtom;
+ Atom XdndActionLinkAtom;
+ Atom XdndActionAskAtom;
+ Atom XdndActionPrivateAtom;
+ Atom XdndActionDescriptionAtom;
+#endif
+} DndInterpData;
+
+
+typedef struct {
+ Tcl_DString dString;
+ Window window; /* Source/Target window */
+ Display *display;
+ Atom commAtom; /* Data communication property atom. */
+ int packetSize;
+ Tcl_TimerToken timerToken;
+ int status; /* Status of transaction: CONTINUE, OK, FAIL,
+ * or TIMEOUT. */
+ int timestamp; /* Timestamp of the transaction. */
+ int offset;
+ int protocol; /* Drag-and-drop protocol used by the source:
+ * either PROTO_BLT or PROTO_XDND. */
+} DropPending;
+
+/*
+ * SubstDescriptors --
+ *
+ * Structure to hold letter-value pairs for percent substitutions.
+ */
+typedef struct {
+ char letter; /* character like 'x' in "%x" */
+ char *value; /* value to be substituted in place of "%x" */
+} SubstDescriptors;
+
+/*
+ * Drag&Drop Registration Data
+ */
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the token. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up. */
+
+ Display *display; /* Display containing widget. Used, among
+ * other things, so that resources can be
+ * freed even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tk_3DBorder border; /* Structure used to draw 3-D border and
+ * background. NULL means no background
+ * or border. */
+ int borderWidth; /* Width of 3-D border (if any). */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+
+ int flags; /* Various flags; see below for
+ * definitions. */
+
+ /* Token specific fields */
+ int x, y; /* Last position of token window */
+ int startX, startY;
+
+ int status; /* Indicates the current status of the token:
+ * 0 is normal, 1 is active. */
+ int lastStatus; /* Indicates the last status of the token. */
+ Tcl_TimerToken timerToken; /* Token for routine to hide tokenwin */
+ GC fillGC; /* GC used to draw rejection fg: (\) */
+ GC outlineGC; /* GC used to draw rejection bg: (\) */
+ int width, height;
+
+ /* User-configurable fields */
+
+ Tk_Anchor anchor; /* Position of token win relative to mouse */
+ Tk_3DBorder normalBorder; /* Border/background for token window */
+ Tk_3DBorder activeBorder; /* Border/background for token window */
+ int activeRelief;
+ int activeBorderWidth; /* Border width in pixels */
+ XColor *fillColor; /* Color used to draw rejection fg: (\) */
+ XColor *outlineColor; /* Color used to draw rejection bg: (\) */
+ Pixmap rejectStipple; /* Stipple used to draw rejection: (\) */
+ int reqWidth, reqHeight;
+
+ int nSteps;
+
+} Token;
+
+/*
+ * Winfo --
+ *
+ * This structure represents a window hierarchy examined during a single
+ * "drag" operation. It's used to cache information to reduce the round
+ * trip calls to the server needed to query window geometry information
+ * and grab the target property.
+ */
+typedef struct WinfoStruct {
+ Window window; /* Window in hierarchy. */
+
+ int initialized; /* If zero, the rest of this structure's
+ * information hasn't been set. */
+
+ int x1, y1, x2, y2; /* Extents of the window (upper-left and
+ * lower-right corners). */
+
+ struct WinfoStruct *parentPtr; /* Parent node. NULL if root. Used to
+ * compute offset for X11 windows. */
+
+ Blt_Chain *chainPtr; /* List of this window's children. If NULL,
+ * there are no children. */
+
+ int isTarget; /* Indicates if this window is a drag&drop
+ * target. */
+ int lookedForProperty; /* Indicates if this window */
+
+ int eventFlags; /* Retrieved from the target's drag&drop
+ * property, indicates what kinds of pointer
+ * events should be relayed to the target via
+ * ClientMessages. Possible values are OR-ed
+ * combinations of the following bits:
+ * 001 Enter events.
+ * 010 Motion events.
+ * 100 Leave events.
+ */
+ char *matches;
+
+} Winfo;
+
+/*
+ * Dnd --
+ *
+ * This structure represents the drag&drop manager. It is associated
+ * with a widget as a drag&drop source, target, or both. It contains
+ * both the source and target components, since a widget can be both
+ * a drag source and a drop target.
+ */
+typedef struct {
+ Tcl_Interp *interp; /* Interpreter associated with the drag&drop
+ * manager. */
+
+ Tk_Window tkwin; /* Tk window representing the drag&drop
+ * manager (can be source and/or target). */
+
+ Display *display; /* Display for drag&drop widget. Saved to free
+ * resources after window has been destroyed. */
+
+ int isSource; /* Indicates if this drag&drop manager can act
+ * as a drag source. */
+ int isTarget; /* Indicates if this drag&drop manager can act
+ * as a drop target. */
+
+ int targetPropertyExists; /* Indicates is the drop target property has
+ * been set. */
+
+ unsigned int flags; /* Various flags; see below for
+ * definitions. */
+ int timestamp; /* Id of the current drag&drop transaction. */
+
+ int x, y; /* Last known location of the mouse pointer. */
+
+ Blt_HashEntry *hashPtr;
+
+ DndInterpData *dataPtr;
+
+ /* Source component. */
+
+ Blt_HashTable getDataTable; /* Table of data handlers (converters)
+ * registered for this source. */
+
+ int reqButton; /* Button used to invoke drag operation. */
+
+ int button; /* Last button press detected. */
+ int keyState; /* Last key state. */
+
+ Tk_Cursor cursor; /* Cursor restored after dragging */
+
+ int selfTarget; /* Indicated if the source should drop onto
+ * itself. */
+
+ char **reqFormats; /* List of requested data formats. The
+ * list should be ordered with the more
+ * desireable formats first. You can also
+ * temporarily turn off a source by setting
+ * the value to the empty string. */
+
+ Winfo *rootPtr; /* Cached window information: Gathered
+ * and used during the "drag" operation
+ * to see if the mouse pointer is over a
+ * valid target. */
+
+ Winfo *windowPtr; /* Points to information about the last
+ * target the pointer was over. If NULL,
+ * the pointer was not over a valid target. */
+
+ char **packageCmd; /* Tcl command executed at start of the drag
+ * operation to initialize token. */
+
+ char **resultCmd; /* Tcl command executed at the end of the
+ * "drop" operation to indicate its status. */
+
+ char **siteCmd; /* Tcl command executed to update token
+ * window. */
+
+ Token *tokenPtr; /* Token used to provide special cursor. */
+
+
+ Tcl_TimerToken timerToken;
+
+ Tk_Cursor *cursors; /* Array of drag-and-drop cursors. */
+ int cursorPos;
+
+ int dragStart; /* Minimum number of pixels movement
+ * before B1-Motion is considered to
+ * start dragging. */
+
+ /* Target component. */
+
+ Blt_HashTable setDataTable; /* Table of data handlers (converters)
+ * registered for this target. */
+ char **enterCmd; /* Tcl proc called when the mouse enters the
+ * target. */
+ char **leaveCmd; /* Tcl proc called when the mouse leaves the
+ * target. */
+ char **motionCmd; /* Tcl proc called when the mouse is moved
+ * over the target. */
+ char **dropCmd; /* Tcl proc called when the mouse button
+ * is released over the target. */
+
+ char *matchingFormats;
+ int lastId; /* The last transaction id used. This is used
+ * to cache the above formats string. */
+
+ DropPending *pendingPtr; /* Points to structure containing information
+ * about a current drop in progress. If NULL,
+ * no drop is in progress. */
+
+ short int dropX, dropY; /* Location of the current drop. */
+ short int dragX, dragY; /* Starting position of token window */
+} Dnd;
+
+
+typedef struct {
+ Tk_Window tkwin; /* Toplevel window of the drop target. */
+ int refCount; /* # of targets referencing this structure. */
+ Dnd *dndPtr; /* Last drop target selected. Used the
+ * implement Enter/Leave events for targets.
+ * If NULL, indicates that no drop target was
+ * previously selected. */
+ int lastRepsonse; /* Indicates what the last response was. */
+ Window window; /* Window id of the top-level window (ie.
+ * the wrapper). */
+ char **formatArr; /* List of formats available from source.
+ * Must be pruned down to matching list. */
+ DndInterpData *dataPtr;
+ int x, y;
+
+} XDndHandler;
+
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltDistanceOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-allowformats", "allowFormats", "AllowFormats",
+ DEF_DND_SEND, Tk_Offset(Dnd, reqFormats),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_INT, "-button", "buttonNumber", "ButtonNumber",
+ DEF_DND_BUTTON_NUMBER, Tk_Offset(Dnd, reqButton), 0},
+ {TK_CONFIG_CUSTOM, "-dragthreshold", "dragThreshold", "DragThreshold",
+ DEF_DND_DRAG_THRESHOLD, Tk_Offset(Dnd, dragStart), 0,
+ &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-cursors", "cursors", "cursors",
+ DEF_TOKEN_CURSOR, Tk_Offset(Dnd, cursors),
+ TK_CONFIG_NULL_OK, &cursorsOption },
+ {TK_CONFIG_CUSTOM, "-onenter", "onEnter", "OnEnter",
+ DEF_DND_ENTER_COMMAND, Tk_Offset(Dnd, enterCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-onmotion", "onMotion", "OnMotion",
+ DEF_DND_MOTION_COMMAND, Tk_Offset(Dnd, motionCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-onleave", "onLeave", "OnLeave",
+ DEF_DND_LEAVE_COMMAND, Tk_Offset(Dnd, leaveCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-ondrop", "onDrop", "OnDrop",
+ DEF_DND_DROP_COMMAND, Tk_Offset(Dnd, dropCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_CUSTOM, "-package", "packageCommand", "PackageCommand",
+ DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, packageCmd),
+ TK_CONFIG_NULL_OK, &bltListOption },
+ {TK_CONFIG_CUSTOM, "-result", "result", "Result",
+ DEF_DND_RESULT_COMMAND, Tk_Offset(Dnd, resultCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_BOOLEAN, "-selftarget", "selfTarget", "SelfTarget",
+ DEF_DND_SELF_TARGET, Tk_Offset(Dnd, selfTarget), 0},
+ {TK_CONFIG_CUSTOM, "-site", "siteCommand", "Command",
+ DEF_DND_SITE_COMMAND, Tk_Offset(Dnd, siteCmd),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_BOOLEAN, "-source", "source", "Source",
+ DEF_DND_IS_SOURCE, Tk_Offset(Dnd, isSource), 0},
+ {TK_CONFIG_BOOLEAN, "-target", "target", "Target",
+ DEF_DND_IS_TARGET, Tk_Offset(Dnd, isTarget), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+static Tk_ConfigSpec tokenConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "activeRelief",
+ DEF_TOKEN_ACTIVE_RELIEF, Tk_Offset(Token, activeRelief), 0},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_TOKEN_ANCHOR, Tk_Offset(Token, anchor), 0},
+ {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth",
+ "ActiveBorderWidth", DEF_TOKEN_ACTIVE_BORDERWIDTH,
+ Tk_Offset(Token, activeBorderWidth), 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_COLOR, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_MONO, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TOKEN_BORDERWIDTH, Tk_Offset(Token, borderWidth), 0},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, outlineColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Token, outlineColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-fill", "fill", "Fill",
+ DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Token, fillColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-fill", "fill", "Fill",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, fillColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TOKEN_RELIEF, Tk_Offset(Token, relief), 0},
+ {TK_CONFIG_INT, "-width", "width", "Width",
+ (char *)NULL, Tk_Offset(Token, reqWidth), 0},
+ {TK_CONFIG_INT, "-height", "height", "Height",
+ (char *)NULL, Tk_Offset(Token, reqHeight), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+
+/*
+ * Forward Declarations
+ */
+static int DndCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ int argc, char **argv));
+static void TokenEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void MoveToken _ANSI_ARGS_((Dnd *dndPtr));
+static void DisplayToken _ANSI_ARGS_((ClientData clientData));
+static void HideToken _ANSI_ARGS_((Dnd *dndPtr));
+static void DrawRejectSymbol _ANSI_ARGS_((Dnd *dndPtr));
+
+static int GetDnd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ char *name, Dnd **dndPtrPtr));
+static Dnd *CreateDnd _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin));
+static void DestroyDnd _ANSI_ARGS_((DestroyData data));
+static int DndEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
+static int ConfigureToken _ANSI_ARGS_((Tcl_Interp *interp, Dnd *dndPtr,
+ int argc, char **argv, int flags));
+
+static Winfo *OverTarget _ANSI_ARGS_((Dnd *dndPtr));
+static void AddTargetProperty _ANSI_ARGS_((Dnd *dndPtr));
+
+static Winfo *InitRoot _ANSI_ARGS_((Dnd *dndPtr));
+static void FreeWinfo _ANSI_ARGS_((Winfo *wr));
+static void GetWinfo _ANSI_ARGS_((Display *display, Winfo * windowPtr));
+
+static void CancelDrag _ANSI_ARGS_((Dnd *dndPtr));
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToCursors --
+ *
+ * Converts the resize mode into its numeric representation. Valid
+ * mode strings are "none", "expand", "shrink", or "both".
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToCursors(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing cursors. */
+ char *widgRec; /* Structure record */
+ int offset; /* Offset of field in record. */
+{
+ Tk_Cursor **cursorPtrPtr = (Tk_Cursor **)(widgRec + offset);
+ int result = TCL_OK;
+ int nElems;
+ char **elemArr;
+
+ if (*cursorPtrPtr != NULL) {
+ Blt_Free(*cursorPtrPtr);
+ *cursorPtrPtr = NULL;
+ }
+ if (string == NULL) {
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElems > 0) {
+ Tk_Cursor *cursorArr;
+ register int i;
+
+ cursorArr = Blt_Calloc(nElems + 1, sizeof(Tk_Cursor));
+ for (i = 0; i < nElems; i++) {
+ cursorArr[i] = Tk_GetCursor(interp, tkwin, Tk_GetUid(elemArr[i]));
+ if (cursorArr[i] == None) {
+ *cursorPtrPtr = cursorArr;
+ result = TCL_ERROR;
+ break;
+ }
+ }
+ Blt_Free(elemArr);
+ *cursorPtrPtr = cursorArr;
+ }
+ return result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CursorsToString --
+ *
+ * Returns resize mode string based upon the resize flags.
+ *
+ * Results:
+ * The resize mode string is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CursorsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Cursor record */
+ int offset; /* Offset of record. */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ Tk_Cursor *cursorArr = *(Tk_Cursor **)(widgRec + offset);
+ Tk_Cursor *cursorPtr;
+ Tcl_DString dString;
+ char *result;
+
+ if (cursorArr == NULL) {
+ return "";
+ }
+ Tcl_DStringInit(&dString);
+ for (cursorPtr = cursorArr; *cursorPtr != NULL; cursorPtr++) {
+ Tcl_DStringAppendElement(&dString,
+ Tk_NameOfCursor(Tk_Display(tkwin), *cursorPtr));
+ }
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+
+static char *
+PrintList(list)
+ char **list;
+{
+ int count;
+ char **p;
+
+ count = 0;
+ for(p = list; *p != NULL; p++) {
+ count++;
+ }
+ return Tcl_Merge(count, list);
+}
+
+
+/* ARGSUSED */
+static int
+XSendEventErrorProc(clientData, errEventPtr)
+ ClientData clientData;
+ XErrorEvent *errEventPtr;
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+static void
+SendClientMsg(display, window, mesgAtom, data0, data1, data2, data3, data4)
+ Display *display;
+ Window window;
+ Atom mesgAtom;
+ int data0, data1, data2, data3, data4;
+{
+ XEvent event;
+ Tk_ErrorHandler handler;
+ int result;
+ int any = -1;
+
+ event.xclient.type = ClientMessage;
+ event.xclient.serial = 0;
+ event.xclient.send_event = True;
+ event.xclient.display = display;
+ event.xclient.window = window;
+ event.xclient.message_type = mesgAtom;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = data0;
+ event.xclient.data.l[1] = data1;
+ event.xclient.data.l[2] = data2;
+ event.xclient.data.l[3] = data3;
+ event.xclient.data.l[4] = data4;
+
+ result = TCL_OK;
+ handler = Tk_CreateErrorHandler(display, any, X_SendEvent, any,
+ XSendEventErrorProc, &result);
+ if (!XSendEvent(display, window, False, ClientMessage, &event)) {
+ result = TCL_ERROR;
+ }
+ Tk_DeleteErrorHandler(handler);
+ XSync(display, False);
+ if (result != TCL_OK) {
+ fprintf(stderr, "XSendEvent response to drop: Protocol failed\n");
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowZOrder --
+ *
+ * Returns a chain of the child windows according to their stacking
+ * order. The window ids are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetWindowZOrder(display, window)
+ Display *display;
+ Window window;
+{
+ Blt_Chain *chainPtr;
+ Window *childArr;
+ unsigned int nChildren;
+ Window dummy;
+
+ chainPtr = NULL;
+ if ((XQueryTree(display, window, &dummy, &dummy, &childArr, &nChildren)) &&
+ (nChildren > 0)) {
+ register int i;
+
+ chainPtr = Blt_ChainCreate();
+ for (i = 0; i < nChildren; i++) {
+ /*
+ * XQuery returns windows in bottom to top order. We only care
+ * about the top window.
+ */
+ Blt_ChainPrepend(chainPtr, (ClientData)childArr[i]);
+ }
+ if (childArr != NULL) {
+ XFree((char *)childArr); /* done with list of kids */
+ }
+ }
+ return chainPtr;
+}
+
+static int
+GetMaxPropertySize(display)
+ Display *display;
+{
+ int size;
+
+ size = Blt_MaxRequestSize(display);
+ size *= 4; /* Convert to bytes. */
+ size -= 32;
+ return size;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetProperty --
+ *
+ * Returns the data associated with the named property on the
+ * given window. All data is assumed to be 8-bit string data.
+ *
+ * ------------------------------------------------------------------------
+ */
+static char *
+GetProperty(display, window, atom)
+ Display *display;
+ Window window;
+ Atom atom;
+{
+ char *data;
+ int result, format;
+ Atom typeAtom;
+ unsigned long nItems, bytesAfter;
+
+ if (window == None) {
+ return NULL;
+ }
+ data = NULL;
+ result = XGetWindowProperty(
+ display, /* Display of window. */
+ window, /* Window holding the property. */
+ atom, /* Name of property. */
+ 0, /* Offset of data (for multiple reads). */
+ GetMaxPropertySize(display), /* Maximum number of items to read. */
+ False, /* If true, delete the property. */
+ XA_STRING, /* Desired type of property. */
+ &typeAtom, /* (out) Actual type of the property. */
+ &format, /* (out) Actual format of the property. */
+ &nItems, /* (out) # of items in specified format. */
+ &bytesAfter, /* (out) # of bytes remaining to be read. */
+ (unsigned char **)&data);
+ if ((result != Success) || (format != 8) || (typeAtom != XA_STRING)) {
+ if (data != NULL) {
+ XFree((char *)data);
+ data = NULL;
+ }
+ }
+ return data;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SetProperty --
+ *
+ * Associates the given data with the a property on a given window.
+ * All data is assumed to be 8-bit string data.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+SetProperty(tkwin, atom, data)
+ Tk_Window tkwin;
+ Atom atom;
+ char *data;
+{
+ XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), atom, XA_STRING,
+ 8, PropModeReplace, (unsigned char *)data, strlen(data) + 1);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowRegion --
+ *
+ * Queries for the upper-left and lower-right corners of the
+ * given window.
+ *
+ * Results:
+ * Returns if the window is currently viewable. The coordinates
+ * of the window are returned via parameters.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetWindowRegion(display, windowPtr)
+ Display *display;
+ Winfo *windowPtr;
+{
+ XWindowAttributes winAttrs;
+
+ if (XGetWindowAttributes(display, windowPtr->window, &winAttrs)) {
+ windowPtr->x1 = winAttrs.x;
+ windowPtr->y1 = winAttrs.y;
+ windowPtr->x2 = winAttrs.x + winAttrs.width - 1;
+ windowPtr->y2 = winAttrs.y + winAttrs.height - 1;
+ }
+ return (winAttrs.map_state == IsViewable);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FindTopWindow --
+ *
+ * Searches for the topmost window at a given pair of X-Y coordinates.
+ *
+ * Results:
+ * Returns a pointer to the node representing the window containing
+ * the point. If one can't be found, NULL is returned.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Winfo *
+FindTopWindow(dndPtr, x, y)
+ Dnd *dndPtr;
+ int x, y;
+{
+ Winfo *rootPtr;
+ register Blt_ChainLink *linkPtr;
+ register Winfo *windowPtr;
+
+ rootPtr = dndPtr->rootPtr;
+ if (!rootPtr->initialized) {
+ GetWinfo(dndPtr->display, rootPtr);
+ }
+ if ((x < rootPtr->x1) || (x > rootPtr->x2) ||
+ (y < rootPtr->y1) || (y > rootPtr->y2)) {
+ return NULL; /* Point is not over window */
+ }
+ windowPtr = rootPtr;
+
+ /*
+ * The window list is ordered top to bottom, so stop when we find the
+ * first child that contains the X-Y coordinate. It will be the topmost
+ * window in that hierarchy. If none exists, then we already have the
+ * topmost window.
+ */
+ descend:
+ for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rootPtr = Blt_ChainGetValue(linkPtr);
+ if (!rootPtr->initialized) {
+ GetWinfo(dndPtr->display, rootPtr);
+ }
+ if (rootPtr->window == Blt_GetRealWindowId(dndPtr->tokenPtr->tkwin)) {
+ continue; /* Don't examine the token window. */
+ }
+ if ((x >= rootPtr->x1) && (x <= rootPtr->x2) &&
+ (y >= rootPtr->y1) && (y <= rootPtr->y2)) {
+ /*
+ * Remember the last window containing the pointer and descend
+ * into its window hierarchy. We'll look for a child that also
+ * contains the pointer.
+ */
+ windowPtr = rootPtr;
+ goto descend;
+ }
+ }
+ return windowPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWidgetCursor --
+ *
+ * Queries a widget for its current cursor. The given window
+ * may or may not be a Tk widget that has a -cursor option.
+ *
+ * Results:
+ * Returns the current cursor of the widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Tk_Cursor
+GetWidgetCursor(interp, tkwin)
+ Tcl_Interp *interp; /* Interpreter to evaluate widget command. */
+ Tk_Window tkwin; /* Window of drag&drop source. */
+{
+ Tk_Cursor cursor;
+ Tcl_DString dString, savedResult;
+
+ cursor = None;
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, Tk_PathName(tkwin), "cget", "-cursor",
+ (char *)NULL);
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) == TCL_OK) {
+ char *name;
+
+ name = Tcl_GetStringResult(interp);
+ if ((name != NULL) && (name[0] != '\0')) {
+ cursor = Tk_GetCursor(interp, tkwin, Tk_GetUid(name));
+ }
+ }
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_DStringFree(&dString);
+ return cursor;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * NameOfStatus --
+ *
+ * Converts a numeric drop result into its string representation.
+ *
+ * Results:
+ * Returns a static string representing the drop result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static char *
+NameOfStatus(status)
+ int status;
+{
+ switch (status) {
+ case DROP_OK:
+ return "active";
+ case DROP_CONTINUE:
+ return "normal";
+ case DROP_FAIL:
+ return "reject";
+ case DROP_CANCEL:
+ return "cancel";
+ default:
+ return "unknown status value";
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * NameOfAction --
+ *
+ * Converts a numeric drop result into its string representation.
+ *
+ * Results:
+ * Returns a static string representing the drop result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static char *
+NameOfAction(action)
+ int action;
+{
+ switch (action) {
+ case DROP_COPY:
+ return "copy";
+ case DROP_CANCEL:
+ return "cancel";
+ case DROP_MOVE:
+ return "move";
+ break;
+ case DROP_LINK:
+ return "link";
+ case DROP_FAIL:
+ return "fail";
+ default:
+ return "unknown action";
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetAction --
+ *
+ * Converts a string to its numeric drop result value.
+ *
+ * Results:
+ * Returns the drop result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetAction(string)
+ char *string;
+{
+ char c;
+
+ c = string[0];
+ if ((c == 'c') && (strcmp(string, "cancel") == 0)) {
+ return DROP_CANCEL;
+ } else if ((c == 'f') && (strcmp(string, "fail") == 0)) {
+ return DROP_FAIL;
+ } else if ((c == 'm') && (strcmp(string, "move") == 0)) {
+ return DROP_MOVE;
+ } else if ((c == 'l') && (strcmp(string, "link") == 0)) {
+ return DROP_LINK;
+ } else if ((c == 'c') && (strcmp(string, "copy") == 0)) {
+ return DROP_COPY;
+ } else {
+ return DROP_COPY;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetDragResult --
+ *
+ * Converts a string to its numeric drag result value.
+ *
+ * Results:
+ * Returns the drag result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetDragResult(interp, string)
+ Tcl_Interp *interp;
+ char *string;
+{
+ char c;
+ int bool;
+
+ c = string[0];
+ if ((c == 'c') && (strcmp(string, "cancel") == 0)) {
+ return DROP_CANCEL;
+ } else if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ return DROP_CANCEL;
+ }
+ return bool;
+}
+
+static void
+AnimateActiveCursor(clientData)
+ ClientData clientData;
+{
+ Dnd *dndPtr = clientData;
+ Tk_Cursor cursor;
+
+ dndPtr->cursorPos++;
+ cursor = dndPtr->cursors[dndPtr->cursorPos];
+ if (cursor == None) {
+ cursor = dndPtr->cursors[1];
+ dndPtr->cursorPos = 1;
+ }
+ Tk_DefineCursor(dndPtr->tkwin, cursor);
+ dndPtr->timerToken = Tcl_CreateTimerHandler(100, AnimateActiveCursor,
+ dndPtr);
+}
+
+static void
+StartActiveCursor(dndPtr)
+ Dnd *dndPtr;
+{
+ if (dndPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(dndPtr->timerToken);
+ }
+ if (dndPtr->cursors != NULL) {
+ Tk_Cursor cursor;
+
+ dndPtr->cursorPos = 1;
+ cursor = dndPtr->cursors[1];
+ if (cursor != None) {
+ Tk_DefineCursor(dndPtr->tkwin, cursor);
+ dndPtr->timerToken = Tcl_CreateTimerHandler(125,
+ AnimateActiveCursor, dndPtr);
+ }
+ }
+}
+
+static void
+StopActiveCursor(dndPtr)
+ Dnd *dndPtr;
+{
+ if (dndPtr->cursorPos > 0) {
+ dndPtr->cursorPos = 0;
+ }
+ if (dndPtr->cursors != NULL) {
+ Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursors[0]);
+ }
+ if (dndPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(dndPtr->timerToken);
+ dndPtr->timerToken = NULL;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawToken --
+ *
+ * Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawToken(dndPtr)
+ Dnd *dndPtr;
+{
+ Token *tokenPtr;
+
+ if (dndPtr->tokenPtr == NULL) {
+ return;
+ }
+ tokenPtr = dndPtr->tokenPtr;
+ if ((tokenPtr->tkwin != NULL) && (tokenPtr->tkwin != NULL) &&
+ !(tokenPtr->flags & TOKEN_REDRAW)) {
+ tokenPtr->flags |= TOKEN_REDRAW;
+ Tcl_DoWhenIdle(DisplayToken, dndPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RaiseToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RaiseToken(dndPtr)
+ Dnd *dndPtr;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ if (dndPtr->flags & DND_INITIATED) {
+ if ((Tk_Width(tokenPtr->tkwin) != Tk_ReqWidth(tokenPtr->tkwin)) ||
+ (Tk_Height(tokenPtr->tkwin) != Tk_ReqHeight(tokenPtr->tkwin))) {
+ Blt_ResizeTopLevelWindow(tokenPtr->tkwin,
+ Tk_ReqWidth(tokenPtr->tkwin),
+ Tk_ReqHeight(tokenPtr->tkwin));
+ }
+ Blt_MapTopLevelWindow(tokenPtr->tkwin);
+ Blt_RaiseTopLevelWindow(tokenPtr->tkwin);
+ }
+}
+
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DisplayToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DisplayToken(clientData)
+ ClientData clientData;
+{
+ Dnd *dndPtr = clientData;
+ Token *tokenPtr = dndPtr->tokenPtr;
+ int relief;
+ Tk_3DBorder border;
+ int borderWidth;
+
+ tokenPtr->flags &= ~TOKEN_REDRAW;
+ if (tokenPtr->status == DROP_OK) {
+ relief = tokenPtr->activeRelief;
+ border = tokenPtr->activeBorder;
+ borderWidth = tokenPtr->activeBorderWidth;
+ if ((dndPtr->cursors != NULL) && (dndPtr->cursorPos == 0)) {
+ StartActiveCursor(dndPtr);
+ }
+ } else {
+ relief = tokenPtr->relief;
+ border = tokenPtr->normalBorder;
+ borderWidth = tokenPtr->borderWidth;
+ StopActiveCursor(dndPtr);
+ }
+ Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), border,
+ 0, 0, Tk_Width(tokenPtr->tkwin), Tk_Height(tokenPtr->tkwin),
+ borderWidth, relief);
+ tokenPtr->lastStatus = tokenPtr->status;
+ if (tokenPtr->status == DROP_FAIL) {
+ DrawRejectSymbol(dndPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FadeToken --
+ *
+ * Fades the token into the target.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+FadeToken(dndPtr)
+ Dnd *dndPtr; /* Drag-and-drop manager (source). */
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+ int w, h;
+ int dx, dy;
+ Window window;
+
+ if (tokenPtr->status == DROP_FAIL) {
+ tokenPtr->nSteps = 1;
+ return;
+ }
+ if (tokenPtr->nSteps == 1) {
+ HideToken(dndPtr);
+ dndPtr->flags &= ~(DND_ACTIVE | DND_VOIDED);
+ return;
+ }
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ tokenPtr->timerToken = Tcl_CreateTimerHandler(10,
+ (Tcl_TimerProc *)FadeToken, dndPtr);
+ tokenPtr->nSteps--;
+
+ w = Tk_ReqWidth(tokenPtr->tkwin) * tokenPtr->nSteps / 10;
+ h = Tk_ReqHeight(tokenPtr->tkwin) * tokenPtr->nSteps / 10;
+ if (w < 1) {
+ w = 1;
+ }
+ if (h < 1) {
+ h = 1;
+ }
+ dx = (Tk_ReqWidth(tokenPtr->tkwin) - w) / 2;
+ dy = (Tk_ReqHeight(tokenPtr->tkwin) - h) / 2;
+ window = Blt_GetRealWindowId(tokenPtr->tkwin);
+ XMoveResizeWindow(dndPtr->display, window, tokenPtr->x + dx,
+ tokenPtr->y + dy, (unsigned int)w, (unsigned int)h);
+ tokenPtr->width = w, tokenPtr->height = h;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SnapToken --
+ *
+ * Snaps the token back to the source.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+SnapToken(dndPtr)
+ Dnd *dndPtr; /* drag&drop source window data */
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ if (tokenPtr->nSteps == 1) {
+ HideToken(dndPtr);
+ return;
+ }
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ tokenPtr->timerToken = Tcl_CreateTimerHandler(10,
+ (Tcl_TimerProc *)SnapToken, dndPtr);
+ tokenPtr->nSteps--;
+ tokenPtr->x -= (tokenPtr->x - tokenPtr->startX) / tokenPtr->nSteps;
+ tokenPtr->y -= (tokenPtr->y - tokenPtr->startY) / tokenPtr->nSteps;
+ if ((tokenPtr->x != Tk_X(tokenPtr->tkwin)) ||
+ (tokenPtr->y != Tk_Y(tokenPtr->tkwin))) {
+ Tk_MoveToplevelWindow(tokenPtr->tkwin, tokenPtr->x, tokenPtr->y);
+ }
+ RaiseToken(dndPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * HideToken --
+ *
+ * Unmaps the drag&drop token. Invoked directly at the end of a
+ * successful communication, or after a delay if the communication
+ * fails (allowing the user to see a graphical picture of failure).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+HideToken(dndPtr)
+ Dnd *dndPtr;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ tokenPtr->timerToken = NULL;
+ }
+ if (dndPtr->flags & DND_INITIATED) {
+ /* Reset the cursor back to its normal state. */
+ StopActiveCursor(dndPtr);
+ if (dndPtr->cursor == None) {
+ Tk_UndefineCursor(dndPtr->tkwin);
+ } else {
+ Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursor);
+ }
+ if (tokenPtr->tkwin != NULL) {
+ Tk_UnmapWindow(tokenPtr->tkwin);
+ Blt_ResizeTopLevelWindow(tokenPtr->tkwin,
+ Tk_ReqWidth(tokenPtr->tkwin),
+ Tk_ReqHeight(tokenPtr->tkwin));
+ }
+ }
+ if (dndPtr->rootPtr != NULL) {
+ FreeWinfo(dndPtr->rootPtr);
+ dndPtr->rootPtr = NULL;
+ }
+ dndPtr->flags &= ~(DND_ACTIVE | DND_VOIDED);
+ tokenPtr->status = DROP_CONTINUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * MorphToken --
+ *
+ * Fades the token into the target.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+MorphToken(dndPtr)
+ Dnd *dndPtr; /* Drag-and-drop manager (source). */
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ if (tokenPtr->status == DROP_FAIL) {
+ tokenPtr->nSteps = 1;
+ return;
+ }
+ if (tokenPtr->nSteps == 1) {
+ HideToken(dndPtr);
+ dndPtr->flags &= ~(DND_ACTIVE | DND_VOIDED);
+ return;
+ }
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ tokenPtr->timerToken = Tcl_CreateTimerHandler(10,
+ (Tcl_TimerProc *)MorphToken, dndPtr);
+ tokenPtr->nSteps--;
+
+ if (dndPtr->flags & DROP_CANCEL) {
+ tokenPtr->nSteps--;
+ tokenPtr->x -= (tokenPtr->x - tokenPtr->startX) / tokenPtr->nSteps;
+ tokenPtr->y -= (tokenPtr->y - tokenPtr->startY) / tokenPtr->nSteps;
+ if ((tokenPtr->x != Tk_X(tokenPtr->tkwin)) ||
+ (tokenPtr->y != Tk_Y(tokenPtr->tkwin))) {
+ Tk_MoveToplevelWindow(tokenPtr->tkwin, tokenPtr->x, tokenPtr->y);
+ }
+ } else {
+ int w, h;
+ int dx, dy;
+ Window window;
+
+ w = Tk_ReqWidth(tokenPtr->tkwin) * tokenPtr->nSteps / 10;
+ h = Tk_ReqHeight(tokenPtr->tkwin) * tokenPtr->nSteps / 10;
+ if (w < 1) {
+ w = 1;
+ }
+ if (h < 1) {
+ h = 1;
+ }
+ dx = (Tk_ReqWidth(tokenPtr->tkwin) - w) / 2;
+ dy = (Tk_ReqHeight(tokenPtr->tkwin) - h) / 2;
+ window = Blt_GetRealWindowId(tokenPtr->tkwin);
+ XMoveResizeWindow(dndPtr->display, window, tokenPtr->x + dx,
+ tokenPtr->y + dy, (unsigned int)w, (unsigned int)h);
+ tokenPtr->width = w, tokenPtr->height = h;
+ }
+ RaiseToken(dndPtr);
+}
+
+static void
+GetTokenPosition(dndPtr, x, y)
+ Dnd *dndPtr;
+ int x, y;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+ int maxX, maxY;
+ int vx, vy, dummy;
+ Screen *screenPtr;
+
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(dndPtr->tkwin, &vx, &vy, &dummy, &dummy);
+ x += vx - TOKEN_OFFSET;
+ y += vy - TOKEN_OFFSET;
+
+ screenPtr = Tk_Screen(tokenPtr->tkwin);
+ maxX = WidthOfScreen(screenPtr) - Tk_Width(tokenPtr->tkwin);
+ maxY = HeightOfScreen(screenPtr) - Tk_Height(tokenPtr->tkwin);
+ Blt_TranslateAnchor(x, y, Tk_Width(tokenPtr->tkwin),
+ Tk_Height(tokenPtr->tkwin), tokenPtr->anchor, &x, &y);
+ if (x > maxX) {
+ x = maxX;
+ } else if (x < 0) {
+ x = 0;
+ }
+ if (y > maxY) {
+ y = maxY;
+ } else if (y < 0) {
+ y = 0;
+ }
+ tokenPtr->x = x, tokenPtr->y = y;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * MoveToken --
+ *
+ * Invoked during "drag" operations to move a token window to its
+ * current "drag" coordinate.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+MoveToken(dndPtr)
+ Dnd *dndPtr; /* drag&drop source window data */
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ GetTokenPosition(dndPtr, dndPtr->x, dndPtr->y);
+ if ((tokenPtr->x != Tk_X(tokenPtr->tkwin)) ||
+ (tokenPtr->y != Tk_Y(tokenPtr->tkwin))) {
+ Tk_MoveToplevelWindow(tokenPtr->tkwin, tokenPtr->x, tokenPtr->y);
+ }
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ChangeToken --
+ *
+ * Invoked when the event loop is idle to determine whether or not
+ * the current drag&drop token position is over another drag&drop
+ * target.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+ChangeToken(dndPtr, status)
+ Dnd *dndPtr;
+ int status;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ tokenPtr->status = status;
+ EventuallyRedrawToken(dndPtr);
+
+ /*
+ * If the source has a site command, then invoke it to
+ * modify the appearance of the token window. Pass any
+ * errors onto the drag&drop error handler.
+ */
+ if (dndPtr->siteCmd) {
+ Tcl_Interp *interp = dndPtr->interp;
+ Tcl_DString dString, savedResult;
+ char **p;
+
+ Tcl_DStringInit(&dString);
+ for (p = dndPtr->siteCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(dndPtr->timestamp));
+ Tcl_DStringAppendElement(&dString, "status");
+ Tcl_DStringAppendElement(&dString, NameOfStatus(status));
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&dString);
+ Tcl_DStringResult(interp, &savedResult);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DrawRejectSymbol --
+ *
+ * Draws a rejection mark on the current drag&drop token, and arranges
+ * for the token to be unmapped after a small delay.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DrawRejectSymbol(dndPtr)
+ Dnd *dndPtr;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+ int divisor = 6; /* controls size of rejection symbol */
+ int w, h, lineWidth, x, y, margin;
+
+ margin = 2 * tokenPtr->borderWidth;
+ w = Tk_Width(tokenPtr->tkwin) - 2 * margin;
+ h = Tk_Height(tokenPtr->tkwin) - 2 * margin;
+ lineWidth = (w < h) ? w / divisor : h / divisor;
+ lineWidth = (lineWidth < 1) ? 1 : lineWidth;
+
+ w = h = lineWidth * (divisor - 1);
+ x = (Tk_Width(tokenPtr->tkwin) - w) / 2;
+ y = (Tk_Height(tokenPtr->tkwin) - h) / 2;
+
+ /*
+ * Draw the rejection symbol background (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->outlineGC,
+ lineWidth + 2, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->outlineGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->outlineGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ /*
+ * Draw the rejection symbol foreground (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->fillGC,
+ lineWidth, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->fillGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->fillGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ tokenPtr->status = DROP_FAIL;
+ /*
+ * Arrange for token window to disappear eventually.
+ */
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ tokenPtr->timerToken = Tcl_CreateTimerHandler(1000,
+ (Tcl_TimerProc *)HideToken, dndPtr);
+ RaiseToken(dndPtr);
+ dndPtr->flags &= ~(DND_ACTIVE | DND_VOIDED);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateToken --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Creates a new record if the widget name is not already
+ * registered. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroyToken(data)
+ DestroyData data;
+{
+ Dnd *dndPtr = (Dnd *)data;
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ dndPtr->tokenPtr = NULL;
+ if (tokenPtr == NULL) {
+ return;
+ }
+ if (tokenPtr->flags & TOKEN_REDRAW) {
+ Tcl_CancelIdleCall(DisplayToken, dndPtr);
+ }
+ Tk_FreeOptions(tokenConfigSpecs, (char *)tokenPtr, dndPtr->display, 0);
+ if (tokenPtr->timerToken) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ if (tokenPtr->fillGC != NULL) {
+ Tk_FreeGC(dndPtr->display, tokenPtr->fillGC);
+ }
+ if (tokenPtr->outlineGC != NULL) {
+ Tk_FreeGC(dndPtr->display, tokenPtr->outlineGC);
+ }
+ if (tokenPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(tokenPtr->tkwin,
+ ExposureMask | StructureNotifyMask, TokenEventProc, dndPtr);
+ Tk_DestroyWindow(tokenPtr->tkwin);
+ }
+ Blt_Free(tokenPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenEventProc --
+ *
+ * Invoked by the Tk dispatcher to handle widget events.
+ * Manages redraws for the drag&drop token window.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TokenEventProc(clientData, eventPtr)
+ ClientData clientData; /* data associated with widget */
+ XEvent *eventPtr; /* information about event */
+{
+ Dnd *dndPtr = clientData;
+ Token *tokenPtr = dndPtr->tokenPtr;
+
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ if (tokenPtr->tkwin != NULL) {
+ EventuallyRedrawToken(dndPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ tokenPtr->tkwin = NULL;
+ if (tokenPtr->flags & TOKEN_REDRAW) {
+ tokenPtr->flags &= ~TOKEN_REDRAW;
+ Tcl_CancelIdleCall(DisplayToken, dndPtr);
+ }
+ Tcl_EventuallyFree(dndPtr, DestroyToken);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateToken --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Creates a new record if the widget name is not already
+ * registered. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+CreateToken(interp, dndPtr)
+ Tcl_Interp *interp;
+ Dnd *dndPtr;
+{
+ XSetWindowAttributes attrs;
+ Tk_Window tkwin;
+ unsigned int mask;
+ Token *tokenPtr;
+
+ tokenPtr = Blt_Calloc(1, sizeof(Token));
+ assert(tokenPtr);
+ tokenPtr->anchor = TK_ANCHOR_SE;
+ tokenPtr->relief = TK_RELIEF_RAISED;
+ tokenPtr->activeRelief = TK_RELIEF_SUNKEN;
+ tokenPtr->borderWidth = tokenPtr->activeBorderWidth = 3;
+
+ /* Create toplevel on parent's screen. */
+ tkwin = Tk_CreateWindow(interp, dndPtr->tkwin, "dndtoken", "");
+ if (tkwin == NULL) {
+ Blt_Free(tokenPtr);
+ return TCL_ERROR;
+ }
+ tokenPtr->tkwin = tkwin;
+ Tk_SetClass(tkwin, "DndToken");
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ TokenEventProc, dndPtr);
+ attrs.override_redirect = True;
+ attrs.backing_store = WhenMapped;
+ attrs.save_under = True;
+ mask = CWOverrideRedirect | CWSaveUnder | CWBackingStore;
+ Tk_ChangeWindowAttributes(tkwin, mask, &attrs);
+ Tk_SetInternalBorder(tkwin, tokenPtr->borderWidth + 2);
+ Tk_MakeWindowExist(tkwin);
+ dndPtr->tokenPtr = tokenPtr;
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureToken --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureToken(interp, dndPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* current interpreter */
+ Dnd *dndPtr; /* Drag&drop source widget record */
+ int argc; /* number of arguments */
+ char **argv; /* argument strings */
+ int flags; /* flags controlling interpretation */
+{
+ GC newGC;
+ Token *tokenPtr = dndPtr->tokenPtr;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ Tk_MakeWindowExist(tokenPtr->tkwin);
+ if (Tk_ConfigureWidget(interp, tokenPtr->tkwin, tokenConfigSpecs, argc,
+ argv, (char *)tokenPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Set up the rejection outline GC for the token window...
+ */
+ gcValues.foreground = tokenPtr->outlineColor->pixel;
+ gcValues.subwindow_mode = IncludeInferiors;
+ gcValues.graphics_exposures = False;
+ gcValues.line_style = LineSolid;
+ gcValues.cap_style = CapButt;
+ gcValues.join_style = JoinBevel;
+
+ gcMask = GCForeground | GCSubwindowMode | GCLineStyle |
+ GCCapStyle | GCJoinStyle | GCGraphicsExposures;
+ newGC = Tk_GetGC(dndPtr->tkwin, gcMask, &gcValues);
+
+ if (tokenPtr->outlineGC != NULL) {
+ Tk_FreeGC(dndPtr->display, tokenPtr->outlineGC);
+ }
+ tokenPtr->outlineGC = newGC;
+
+ /*
+ * Set up the rejection fill GC for the token window...
+ */
+ gcValues.foreground = tokenPtr->fillColor->pixel;
+ if (tokenPtr->rejectStipple != None) {
+ gcValues.stipple = tokenPtr->rejectStipple;
+ gcValues.fill_style = FillStippled;
+ gcMask |= GCStipple | GCFillStyle;
+ }
+ newGC = Tk_GetGC(dndPtr->tkwin, gcMask, &gcValues);
+
+ if (tokenPtr->fillGC != NULL) {
+ Tk_FreeGC(dndPtr->display, tokenPtr->fillGC);
+ }
+ tokenPtr->fillGC = newGC;
+
+ if ((tokenPtr->reqWidth > 0) && (tokenPtr->reqHeight > 0)) {
+ Tk_GeometryRequest(tokenPtr->tkwin, tokenPtr->reqWidth,
+ tokenPtr->reqHeight);
+ }
+ /*
+ * Reset the border width in case it has changed...
+ */
+ Tk_SetInternalBorder(tokenPtr->tkwin, tokenPtr->borderWidth + 2);
+ return TCL_OK;
+}
+
+static int
+GetFormattedData(dndPtr, format, timestamp, resultPtr)
+ Dnd *dndPtr;
+ char *format;
+ int timestamp;
+ Tcl_DString *resultPtr;
+{
+ Tcl_Interp *interp = dndPtr->interp;
+ Blt_HashEntry *hPtr;
+ char **formatCmd;
+ Tcl_DString savedResult;
+ Tcl_DString dString;
+ char **p;
+ int x, y;
+
+ /* Find the data converter for the prescribed format. */
+ hPtr = Blt_FindHashEntry(&(dndPtr->getDataTable), format);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find format \"", format,
+ "\" in source \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ formatCmd = (char **)Blt_GetHashValue(hPtr);
+ Tcl_DStringInit(&dString);
+ for (p = formatCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ x = dndPtr->dragX - Blt_RootX(dndPtr->tkwin);
+ y = dndPtr->dragY - Blt_RootY(dndPtr->tkwin);
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ Tcl_DStringAppendElement(&dString, "x");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(x));
+ Tcl_DStringAppendElement(&dString, "y");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(y));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(timestamp));
+ Tcl_DStringAppendElement(&dString, "format");
+ Tcl_DStringAppendElement(&dString, format);
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&dString);
+ Tcl_DStringInit(resultPtr);
+ Tcl_DStringGetResult(interp, resultPtr);
+
+ /* Restore the interpreter result. */
+ Tcl_DStringResult(interp, &savedResult);
+ return TCL_OK;
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DestroyDnd --
+ *
+ * Free resources allocated for the drag&drop window.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroyDnd(data)
+ DestroyData data;
+{
+ Dnd *dndPtr = (Dnd *)data;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char **cmd;
+
+ Tk_FreeOptions(configSpecs, (char *)dndPtr, dndPtr->display, 0);
+ Tk_DeleteGenericHandler(DndEventProc, dndPtr);
+ for (hPtr = Blt_FirstHashEntry(&(dndPtr->getDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(dndPtr->getDataTable));
+
+ for (hPtr = Blt_FirstHashEntry(&(dndPtr->setDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(dndPtr->setDataTable));
+ if (dndPtr->rootPtr != NULL) {
+ FreeWinfo(dndPtr->rootPtr);
+ }
+ if (dndPtr->cursor != None) {
+ Tk_FreeCursor(dndPtr->display, dndPtr->cursor);
+ }
+ if (dndPtr->reqFormats != NULL) {
+ Blt_Free(dndPtr->reqFormats);
+ }
+ if (dndPtr->matchingFormats != NULL) {
+ Blt_Free(dndPtr->matchingFormats);
+ }
+
+ /* Now that the various commands are custom list options, we need
+ * to explicitly free them. */
+ if (dndPtr->motionCmd != NULL) {
+ Blt_Free(dndPtr->motionCmd);
+ }
+ if (dndPtr->leaveCmd != NULL) {
+ Blt_Free(dndPtr->leaveCmd);
+ }
+ if (dndPtr->enterCmd != NULL) {
+ Blt_Free(dndPtr->enterCmd);
+ }
+ if (dndPtr->dropCmd != NULL) {
+ Blt_Free(dndPtr->dropCmd);
+ }
+ if (dndPtr->resultCmd != NULL) {
+ Blt_Free(dndPtr->resultCmd);
+ }
+ if (dndPtr->packageCmd != NULL) {
+ Blt_Free(dndPtr->packageCmd);
+ }
+ if (dndPtr->siteCmd != NULL) {
+ Blt_Free(dndPtr->siteCmd);
+ }
+
+ if (dndPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(dndPtr->dataPtr->dndTable), dndPtr->hashPtr);
+ }
+ if (dndPtr->tokenPtr != NULL) {
+ DestroyToken((DestroyData)dndPtr);
+ }
+ if (dndPtr->tkwin != NULL) {
+ XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin),
+ dndPtr->dataPtr->targetAtom);
+ XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin),
+ dndPtr->dataPtr->commAtom);
+ }
+ Blt_Free(dndPtr);
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetDnd --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetDnd(clientData, interp, pathName, dndPtrPtr)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ char *pathName; /* widget pathname for desired record */
+ Dnd **dndPtrPtr;
+{
+ DndInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, pathName, dataPtr->mainWindow);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(dataPtr->dndTable), (char *)tkwin);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", pathName,
+ "\" is not a drag&drop source/target", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *dndPtrPtr = (Dnd *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateDnd --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Creates a new record if the widget name is not already
+ * registered. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Dnd *
+CreateDnd(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* widget for desired record */
+{
+ Dnd *dndPtr;
+
+ dndPtr = Blt_Calloc(1, sizeof(Dnd));
+ assert(dndPtr);
+ dndPtr->interp = interp;
+ dndPtr->display = Tk_Display(tkwin);
+ dndPtr->tkwin = tkwin;
+ Tk_MakeWindowExist(tkwin);
+ Blt_InitHashTable(&(dndPtr->setDataTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(dndPtr->getDataTable), BLT_STRING_KEYS);
+ Tk_CreateGenericHandler(DndEventProc, dndPtr);
+ return dndPtr;
+}
+
+static int
+ConfigureDnd(interp, dndPtr)
+ Tcl_Interp *interp;
+ Dnd *dndPtr;
+{
+ Tcl_CmdInfo cmdInfo;
+ Tcl_DString dString;
+ int button, result;
+
+ if (!Tcl_GetCommandInfo(interp, "blt::DndInit", &cmdInfo)) {
+ static char cmd[] = "source [file join $blt_library dnd.tcl]";
+ /*
+ * If the "DndInit" routine hasn't been sourced, do it now.
+ */
+ if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (while loading bindings for blt::drag&drop)");
+ return TCL_ERROR;
+ }
+ }
+ /*
+ * Reset the target property if it's changed state or
+ * added/subtracted one of its callback procedures.
+ */
+ if (Blt_ConfigModified(configSpecs, "-target", "-onenter", "-onmotion",
+ "-onleave", (char *)NULL)) {
+ if (dndPtr->targetPropertyExists) {
+ XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin),
+ dndPtr->dataPtr->targetAtom);
+ dndPtr->targetPropertyExists = FALSE;
+ }
+ if (dndPtr->isTarget) {
+ AddTargetProperty(dndPtr);
+ dndPtr->targetPropertyExists = TRUE;
+ }
+ }
+ if (dndPtr->isSource) {
+ /* Check the button binding for valid range (0 or 1-5) */
+ if ((dndPtr->reqButton < 0) || (dndPtr->reqButton > 5)) {
+ Tcl_AppendResult(interp,
+ "button must be 1-5, or 0 for no bindings",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ button = dndPtr->reqButton;
+ } else {
+ button = 0;
+ }
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "blt::DndInit",
+ Tk_PathName(dndPtr->tkwin), Blt_Itoa(button), (char *)NULL);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SendRestrictProc --
+ *
+ * This procedure filters incoming events when a "send" command
+ * is outstanding. It defers all events except those containing
+ * send commands and results.
+ *
+ * Results:
+ * False is returned except for property-change events on a
+ * commWindow.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* ARGSUSED */
+static Tk_RestrictAction
+SendRestrictProc(clientData, eventPtr)
+ ClientData clientData; /* Drag-and-drop manager. */
+ register XEvent *eventPtr; /* Event that just arrived. */
+{
+ Dnd *dndPtr = clientData;
+
+ if (eventPtr->xproperty.window != Tk_WindowId(dndPtr->tkwin)) {
+ return TK_PROCESS_EVENT; /* Event not in our window. */
+ }
+ if ((eventPtr->type == PropertyNotify) &&
+ (eventPtr->xproperty.state == PropertyNewValue)) {
+ return TK_PROCESS_EVENT; /* This is the one we want to process. */
+ }
+ if (eventPtr->type == Expose) {
+ return TK_PROCESS_EVENT; /* Let expose events also get
+ * handled. */
+ }
+ return TK_DEFER_EVENT; /* Defer everything else. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SendTimerProc --
+ *
+ * Procedure called when the timer event elapses. Used to wait
+ * between attempts checking for the designated window.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Sets a flag, indicating the timeout occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SendTimerProc(clientData)
+ ClientData clientData;
+{
+ int *statusPtr = clientData;
+
+ /*
+ * An unusually long amount of time has elapsed since the drag
+ * start message was sent. Assume that the other party has died
+ * and abort the operation.
+ */
+ *statusPtr = DROP_FAIL;
+}
+
+#define WAIT_INTERVAL 2000 /* Twenty seconds. */
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TargetPropertyEventProc --
+ *
+ * Invoked by the Tk dispatcher to handle widget events.
+ * Manages redraws for the drag&drop token window.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TargetPropertyEventProc(clientData, eventPtr)
+ ClientData clientData; /* Data associated with transaction. */
+ XEvent *eventPtr; /* information about event */
+{
+ DropPending *pendingPtr = clientData;
+ char *data;
+ int result, format;
+ Atom typeAtom;
+ unsigned long nItems, bytesAfter;
+
+#ifdef notdef
+ fprintf(stderr, "TargetPropertyEventProc\n");
+#endif
+ if ((eventPtr->type != PropertyNotify) ||
+ (eventPtr->xproperty.atom != pendingPtr->commAtom) ||
+ (eventPtr->xproperty.state != PropertyNewValue)) {
+ return;
+ }
+ Tcl_DeleteTimerHandler(pendingPtr->timerToken);
+ data = NULL;
+ result = XGetWindowProperty(
+ eventPtr->xproperty.display, /* Display of window. */
+ eventPtr->xproperty.window, /* Window holding the property. */
+ eventPtr->xproperty.atom, /* Name of property. */
+ 0, /* Offset of data (for multiple reads). */
+ pendingPtr->packetSize, /* Maximum number of items to read. */
+ False, /* If true, delete the property. */
+ XA_STRING, /* Desired type of property. */
+ &typeAtom, /* (out) Actual type of the property. */
+ &format, /* (out) Actual format of the property. */
+ &nItems, /* (out) # of items in specified format. */
+ &bytesAfter, /* (out) # of bytes remaining to be read. */
+ (unsigned char **)&data);
+#ifdef notdef
+ fprintf(stderr,
+ "TargetPropertyEventProc: result=%d, typeAtom=%d, format=%d, nItems=%d\n",
+ result, typeAtom, format, nItems);
+#endif
+ pendingPtr->status = DROP_FAIL;
+ if ((result == Success) && (typeAtom == XA_STRING) && (format == 8)) {
+ pendingPtr->status = DROP_OK;
+#ifdef notdef
+ fprintf(stderr, "data found is (%s)\n", data);
+#endif
+ Tcl_DStringAppend(&(pendingPtr->dString), data, -1);
+ XFree(data);
+ if (nItems == pendingPtr->packetSize) {
+ /* Normally, we'll receive the data in one chunk. But if
+ * more are required, reset the timer and go back into the
+ * wait loop again. */
+ pendingPtr->timerToken = Tcl_CreateTimerHandler(WAIT_INTERVAL,
+ SendTimerProc, &pendingPtr->status);
+ pendingPtr->status = DROP_CONTINUE;
+ }
+ }
+ /* Set an empty, zero-length value on the source's property. This
+ * acts as a handshake, indicating that the target received the
+ * latest chunk. */
+#ifdef notdef
+ fprintf(stderr, "TargetPropertyEventProc: set response property\n");
+#endif
+ XChangeProperty(pendingPtr->display, pendingPtr->window,
+ pendingPtr->commAtom, XA_STRING, 8, PropModeReplace,
+ (unsigned char *)"", 0);
+}
+
+#ifdef HAVE_XDND
+
+static int
+XDndSelectionProc(clientData, interp, portion)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ char *portion;
+{
+ DropPending *pendingPtr = clientData;
+
+ Tcl_DStringAppend(&(pendingPtr->dString), portion, -1);
+#ifdef notdef
+ fprintf(stderr, "-> XDndGetSelectionProc\n");
+#endif
+ return TCL_OK;
+}
+
+#endif /* HAVE_XDND */
+
+static void
+CompleteDataTransaction(dndPtr, format, pendingPtr)
+ Dnd *dndPtr;
+ char *format;
+ DropPending *pendingPtr;
+{
+ DndInterpData *dataPtr = dndPtr->dataPtr;
+ Tk_Window tkwin;
+ Atom formatAtom;
+
+#ifdef notdef
+ fprintf(stderr, "-> CompleteDataTransaction\n");
+#endif
+ /* Check if the source is local to the application. */
+ tkwin = Tk_IdToWindow(dndPtr->display, pendingPtr->window);
+ if (tkwin != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(dndPtr->dataPtr->dndTable), (char *)tkwin);
+ if (hPtr != NULL) {
+ Dnd *srcPtr;
+
+ srcPtr = (Dnd *)Blt_GetHashValue(hPtr);
+ GetFormattedData(srcPtr, format, pendingPtr->timestamp,
+ &(pendingPtr->dString));
+ }
+ return;
+ }
+
+ formatAtom = XInternAtom(pendingPtr->display, format, False);
+
+ if (pendingPtr->protocol == PROTO_XDND) {
+#ifdef HAVE_XDND
+ if (Tk_GetSelection(dndPtr->interp, dndPtr->tkwin,
+ dataPtr->XdndSelectionAtom, formatAtom, XDndSelectionProc,
+ pendingPtr) != TCL_OK) {
+ pendingPtr->status = DROP_FAIL;
+ }
+#endif
+ pendingPtr->status = DROP_OK;
+ } else {
+ Tk_RestrictProc *proc;
+ ClientData arg;
+
+ SendClientMsg(pendingPtr->display, pendingPtr->window,
+ dataPtr->mesgAtom,
+ TS_START_DROP,
+ (int)Tk_WindowId(dndPtr->tkwin),
+ pendingPtr->timestamp,
+ (int)formatAtom,
+ (int)pendingPtr->commAtom);
+
+ pendingPtr->commAtom = dndPtr->dataPtr->commAtom;
+ pendingPtr->status = DROP_CONTINUE;
+ pendingPtr->display = dndPtr->display;
+ proc = Tk_RestrictEvents(SendRestrictProc, dndPtr, &arg);
+ Tk_CreateEventHandler(dndPtr->tkwin, PropertyChangeMask,
+ TargetPropertyEventProc, pendingPtr);
+ pendingPtr->timerToken = Tcl_CreateTimerHandler(WAIT_INTERVAL,
+ SendTimerProc, &pendingPtr->status);
+ /*
+ * ----------------------------------------------------------
+ *
+ * Enter a loop processing X events until the all the data is
+ * received or the source is declared to be dead (i.e. we
+ * timeout). While waiting for a result, restrict handling to
+ * just property-related events so that the transfer is
+ * synchronous with respect to other events in the widget.
+ *
+ * ----------------------------------------------------------
+ */
+ while (pendingPtr->status == DROP_CONTINUE) {
+ /* Wait for property event. */
+ Tcl_DoOneEvent(TCL_ALL_EVENTS);
+ }
+ Tk_RestrictEvents(proc, arg, &arg);
+ Tcl_DeleteTimerHandler(pendingPtr->timerToken);
+ Tk_DeleteEventHandler(dndPtr->tkwin, PropertyChangeMask,
+ TargetPropertyEventProc, pendingPtr);
+ }
+#ifdef notdef
+ fprintf(stderr, "<- CompleteDataTransaction\n");
+#endif
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SourcePropertyEventProc --
+ *
+ * Invoked by the Tk dispatcher when a PropertyNotify event occurs
+ * on the source window. The event acts as a handshake between the
+ * target and the source. The source acknowledges the target has
+ * received the last packet of data and sends the next packet.
+ *
+ * Note a special case. If the data is divisible by the packetsize,
+ * then an extra zero-length packet is sent to mark the end of the
+ * data. A packetsize length packet indicates more is to follow.
+ *
+ * Normally the property is empty (zero-length). But if an
+ * errored occurred on the target, it will contain the error
+ * message.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+SourcePropertyEventProc(clientData, eventPtr)
+ ClientData clientData; /* data associated with widget */
+ XEvent *eventPtr; /* information about event */
+{
+ DropPending *pendingPtr = clientData;
+ char *data;
+ int result, format;
+ Atom typeAtom;
+ unsigned long nItems, bytesAfter;
+ int size, bytesLeft;
+ unsigned char *p;
+
+#ifdef notdef
+ fprintf(stderr, "-> SourcePropertyEventProc\n");
+#endif
+ if ((eventPtr->xproperty.atom != pendingPtr->commAtom)
+ || (eventPtr->xproperty.state != PropertyNewValue)) {
+ return;
+ }
+ Tcl_DeleteTimerHandler(pendingPtr->timerToken);
+ data = NULL;
+ result = XGetWindowProperty(
+ eventPtr->xproperty.display, /* Display of window. */
+ eventPtr->xproperty.window, /* Window holding the property. */
+ eventPtr->xproperty.atom, /* Name of property. */
+ 0, /* Offset of data (for multiple reads). */
+ pendingPtr->packetSize, /* Maximum number of items to read. */
+ True, /* If true, delete the property. */
+ XA_STRING, /* Desired type of property. */
+ &typeAtom, /* (out) Actual type of the property. */
+ &format, /* (out) Actual format of the property. */
+ &nItems, /* (out) # of items in specified format. */
+ &bytesAfter, /* (out) # of bytes remaining to be read. */
+ (unsigned char **)&data);
+
+ if ((result != Success) || (typeAtom != XA_STRING) || (format != 8)) {
+ pendingPtr->status = DROP_FAIL;
+#ifdef notdef
+ fprintf(stderr, "<- SourcePropertyEventProc: wrong format\n");
+#endif
+ return; /* Wrong data format. */
+ }
+ if (nItems > 0) {
+ pendingPtr->status = DROP_FAIL;
+ Tcl_DStringFree(&(pendingPtr->dString));
+ Tcl_DStringAppend(&(pendingPtr->dString), data, -1);
+ XFree(data);
+#ifdef notdef
+ fprintf(stderr, "<- SourcePropertyEventProc: error\n");
+#endif
+ return; /* Error occurred on target. */
+ }
+ bytesLeft = Tcl_DStringLength(&(pendingPtr->dString)) - pendingPtr->offset;
+ if (bytesLeft <= 0) {
+#ifdef notdef
+ fprintf(stderr, "<- SourcePropertyEventProc: done\n");
+#endif
+ pendingPtr->status = DROP_OK;
+ size = 0;
+ } else {
+ size = MIN(bytesLeft, pendingPtr->packetSize);
+ pendingPtr->status = DROP_CONTINUE;
+ }
+ p = (unsigned char *)Tcl_DStringValue(&(pendingPtr->dString)) +
+ pendingPtr->offset;
+ XChangeProperty(pendingPtr->display, pendingPtr->window,
+ pendingPtr->commAtom, XA_STRING, 8, PropModeReplace, p, size);
+ pendingPtr->offset += size;
+ pendingPtr->timerToken = Tcl_CreateTimerHandler(WAIT_INTERVAL,
+ SendTimerProc, &pendingPtr->status);
+#ifdef notdef
+ fprintf(stderr, "<- SourcePropertyEventProc\n");
+#endif
+}
+
+
+static void
+SendDataToTarget(dndPtr, pendingPtr)
+ Dnd *dndPtr;
+ DropPending *pendingPtr;
+{
+ int size;
+ Tk_RestrictProc *proc;
+ ClientData arg;
+
+#ifdef notdef
+ fprintf(stderr, "-> SendDataToTarget\n");
+#endif
+ Tk_CreateEventHandler(dndPtr->tkwin, PropertyChangeMask,
+ SourcePropertyEventProc, pendingPtr);
+ pendingPtr->timerToken = Tcl_CreateTimerHandler(WAIT_INTERVAL,
+ SendTimerProc, &pendingPtr->status);
+ size = MIN(Tcl_DStringLength(&pendingPtr->dString), pendingPtr->packetSize);
+
+ proc = Tk_RestrictEvents(SendRestrictProc, dndPtr, &arg);
+
+ /*
+ * Setting the property starts the process. The target will
+ * see the PropertyChange event and respond accordingly.
+ */
+ XChangeProperty(dndPtr->display, pendingPtr->window,
+ pendingPtr->commAtom, XA_STRING, 8, PropModeReplace,
+ (unsigned char *)Tcl_DStringValue(&(pendingPtr->dString)), size);
+ pendingPtr->offset += size;
+
+ /*
+ * Enter a loop processing X events until the result comes
+ * in or the target is declared to be dead. While waiting
+ * for a result, look only at property-related events so that
+ * the handshake is synchronous with respect to other events in
+ * the application.
+ */
+ pendingPtr->status = DROP_CONTINUE;
+ while (pendingPtr->status == DROP_CONTINUE) {
+ /* Wait for the property change event. */
+ Tcl_DoOneEvent(TCL_ALL_EVENTS);
+ }
+ Tk_RestrictEvents(proc, arg, &arg);
+ Tcl_DeleteTimerHandler(pendingPtr->timerToken);
+ Tk_DeleteEventHandler(dndPtr->tkwin, PropertyChangeMask,
+ SourcePropertyEventProc, pendingPtr);
+#ifdef notdef
+ fprintf(stderr, "<- SendDataToTarget\n");
+#endif
+}
+
+static void
+DoDrop(dndPtr, eventPtr)
+ Dnd *dndPtr;
+ XEvent *eventPtr;
+{
+ DndInterpData *dataPtr = dndPtr->dataPtr;
+ Token *tokenPtr = dndPtr->tokenPtr;
+ Tcl_Interp *interp = dndPtr->interp;
+ struct DropRequest {
+ int mesg; /* TS_DROP_RESULT message. */
+ Window window; /* Target window. */
+ int timestamp; /* Transaction timestamp. */
+ Atom formatAtom; /* Format requested. */
+ } *dropPtr;
+ char *format;
+ DropPending pending;
+
+ if (tokenPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(tokenPtr->timerToken);
+ }
+ dropPtr = (struct DropRequest *)eventPtr->xclient.data.l;
+ format = XGetAtomName(dndPtr->display, dropPtr->formatAtom);
+#ifdef notdef
+ fprintf(stderr, "DoDrop %s 0x%x\n", Tk_PathName(dndPtr->tkwin),
+ dropPtr->window);
+#endif
+ if (GetFormattedData(dndPtr, format, dropPtr->timestamp, &(pending.dString))
+ != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ /* Send empty string to break target's wait loop. */
+ XChangeProperty(dndPtr->display, dropPtr->window, dataPtr->commAtom,
+ XA_STRING, 8, PropModeReplace, (unsigned char *)"", 0);
+ return;
+ }
+ pending.window = dropPtr->window;
+ pending.display = dndPtr->display;
+ pending.commAtom = dataPtr->commAtom;
+ pending.offset = 0;
+ pending.packetSize = GetMaxPropertySize(dndPtr->display);
+ SendDataToTarget(dndPtr, &pending);
+ Tcl_DStringFree(&(pending.dString));
+#ifdef notdef
+ fprintf(stderr, "<- DoDrop\n");
+#endif
+}
+
+static void
+DropFinished(dndPtr, eventPtr)
+ Dnd *dndPtr;
+ XEvent *eventPtr;
+{
+ Tcl_Interp *interp = dndPtr->interp;
+ Tcl_DString dString, savedResult;
+ int result;
+ char **p;
+ struct DropResult {
+ int mesg; /* TS_DROP_RESULT message. */
+ Window window; /* Target window. */
+ int timestamp; /* Transaction timestamp. */
+ int result; /* Result of transaction. */
+ } *dropPtr;
+
+#ifdef notdef
+ fprintf(stderr, "DropFinished\n");
+#endif
+ dropPtr = (struct DropResult *)eventPtr->xclient.data.l;
+
+ Tcl_DStringInit(&dString);
+ for (p = dndPtr->resultCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ Tcl_DStringAppendElement(&dString, "action");
+ Tcl_DStringAppendElement(&dString, NameOfAction(dropPtr->result));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(dropPtr->timestamp));
+
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringResult(interp, &savedResult);
+}
+
+
+static void
+FreeFormats(dndPtr)
+ Dnd *dndPtr;
+{
+ if (dndPtr->matchingFormats != NULL) {
+ Blt_Free(dndPtr->matchingFormats);
+ dndPtr->matchingFormats = NULL;
+ }
+ dndPtr->lastId = None;
+}
+
+static char *
+GetSourceFormats(dndPtr, window, timestamp)
+ Dnd *dndPtr;
+ Window window;
+ int timestamp;
+{
+ if (dndPtr->lastId != timestamp) {
+ char *data;
+
+ FreeFormats(dndPtr);
+ data = GetProperty(dndPtr->display, window,
+ dndPtr->dataPtr->formatsAtom);
+ if (data != NULL) {
+ dndPtr->matchingFormats = Blt_Strdup(data);
+ XFree(data);
+ }
+ dndPtr->lastId = timestamp;
+ }
+ if (dndPtr->matchingFormats == NULL) {
+ return "";
+ }
+ return dndPtr->matchingFormats;
+}
+
+
+static int
+InvokeCallback(dndPtr, cmd, x, y, formats, button, keyState, timestamp)
+ Dnd *dndPtr;
+ char **cmd;
+ int x, y;
+ char *formats;
+ int button, keyState, timestamp;
+{
+ Tcl_DString dString, savedResult;
+ Tcl_Interp *interp = dndPtr->interp;
+ int result;
+ char **p;
+
+ Tcl_DStringInit(&dString);
+ for (p = cmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ x -= Blt_RootX(dndPtr->tkwin); /* Send coordinates relative to target. */
+ y -= Blt_RootY(dndPtr->tkwin);
+ Tcl_DStringAppendElement(&dString, "x");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(x));
+ Tcl_DStringAppendElement(&dString, "y");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(y));
+ Tcl_DStringAppendElement(&dString, "formats");
+ if (formats == NULL) {
+ formats = "";
+ }
+ Tcl_DStringAppendElement(&dString, formats);
+ Tcl_DStringAppendElement(&dString, "button");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(button));
+ Tcl_DStringAppendElement(&dString, "state");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(keyState));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(timestamp));
+ Tcl_Preserve(interp);
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result == TCL_OK) {
+ result = GetDragResult(interp, Tcl_GetStringResult(interp));
+ } else {
+ result = DROP_CANCEL;
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_Release(interp);
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * AcceptDrop --
+ *
+ * Invokes a Tcl procedure to handle the target's side of the
+ * drop. A Tcl procedure is invoked, either one designated for
+ * this target by the user (-ondrop) or a default Tcl procedure.
+ * It is passed the following arguments:
+ *
+ * widget The path name of the target.
+ * x X-coordinate of the mouse relative to the
+ * widget.
+ * y Y-coordinate of the mouse relative to the
+ * widget.
+ * formats A list of data formats acceptable to both
+ * the source and target.
+ * button Button pressed.
+ * state Key state.
+ * timestamp Timestamp of transaction.
+ * action Requested action from source.
+ *
+ * If the Tcl procedure returns "cancel", this indicates that the drop was
+ * not accepted by the target and the reject symbol should be displayed.
+ * Otherwise one of the following strings may be recognized:
+ *
+ * "cancel" Drop was canceled.
+ * "copy" Source data has been successfully copied.
+ * "link" Target has made a link to the data. It's
+ * Ok for the source to remove it's association
+ * with the data, but not to delete the data
+ * itself.
+ * "move" Source data has been successfully copied,
+ * it's Ok for the source to delete its
+ * association with the data and the data itself.
+ *
+ * The result is relayed back to the source via another client message.
+ * The source may or may not be waiting for the result.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A Tcl procedure is invoked in the target to handle the drop event.
+ * The result of the drop is sent (via another ClientMessage) to the
+ * source.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+AcceptDrop(dndPtr, x, y, formats, button, keyState, timestamp)
+ Dnd *dndPtr; /* Target where the drop event occurred. */
+ int x, y;
+ char *formats;
+ int button, keyState, timestamp;
+{
+ Tcl_Interp *interp = dndPtr->interp;
+ char **cmd;
+ Tcl_DString dString, savedResult;
+ int result;
+
+ if (dndPtr->motionCmd != NULL) {
+ result = InvokeCallback(dndPtr, dndPtr->motionCmd, x, y, formats,
+ button, keyState, timestamp);
+ if (result != DROP_OK) {
+ return result;
+ }
+ }
+ if (dndPtr->leaveCmd != NULL) {
+ InvokeCallback(dndPtr, dndPtr->leaveCmd, x, y, formats, button,
+ keyState, timestamp);
+ }
+ Tcl_DStringInit(&dString);
+ cmd = dndPtr->dropCmd;
+ if (cmd != NULL) {
+ char **p;
+
+ for (p = cmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ } else {
+ Tcl_DStringAppendElement(&dString, "blt::DndStdDrop");
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ dndPtr->dropX = x - Blt_RootX(dndPtr->tkwin);
+ dndPtr->dropY = y - Blt_RootY(dndPtr->tkwin);
+ Tcl_DStringAppendElement(&dString, "x");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->dropX));
+ Tcl_DStringAppendElement(&dString, "y");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->dropY));
+ Tcl_DStringAppendElement(&dString, "formats");
+ Tcl_DStringAppendElement(&dString, formats);
+ Tcl_DStringAppendElement(&dString, "button");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(button));
+ Tcl_DStringAppendElement(&dString, "state");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(keyState));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(timestamp));
+ Tcl_Preserve(interp);
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (result == TCL_OK) {
+ result = GetAction(Tcl_GetStringResult(interp));
+ } else {
+ result = DROP_CANCEL;
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_Release(interp);
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * HandleDropEvent --
+ *
+ * Invokes a Tcl procedure to handle the target's side of the
+ * drop. This routine is triggered via a client message from the
+ * drag source indicating that the token was dropped over this
+ * target. The fields of the incoming message are:
+ *
+ * data.l[0] Message type.
+ * data.l[1] Window Id of the source.
+ * data.l[2] Screen X-coordinate of the pointer.
+ * data.l[3] Screen Y-coordinate of the pointer.
+ * data.l[4] Id of the drag&drop transaction.
+ *
+ * A Tcl procedure is invoked, either one designated for this
+ * target by the user (-ondrop) or a default Tcl procedure. It
+ * is passed the following arguments:
+ *
+ * widget The path name of the target.
+ * x X-coordinate of the mouse relative to the
+ * widget.
+ * y Y-coordinate of the mouse relative to the
+ * widget.
+ * formats A list of data formats acceptable to both
+ * the source and target.
+ *
+ * If the Tcl procedure returns "cancel", this indicates that the drop was
+ * not accepted by the target and the reject symbol should be displayed.
+ * Otherwise one of the following strings may be recognized:
+ *
+ * "cancel" Drop was canceled.
+ * "copy" Source data has been successfully copied.
+ * "link" Target has made a link to the data. It's
+ * Ok for the source to remove it's association
+ * with the data, but not to delete the data
+ * itself.
+ * "move" Source data has been successfully copied,
+ * it's Ok for the source to delete its
+ * association with the data and the data itself.
+ *
+ * The result is relayed back to the source via another client message.
+ * The source may or may not be waiting for the result.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A Tcl procedure is invoked in the target to handle the drop event.
+ * The result of the drop is sent (via another ClientMessage) to the
+ * source.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+HandleDropEvent(dndPtr, eventPtr)
+ Dnd *dndPtr; /* Target where the drop event occurred. */
+ XEvent *eventPtr; /* Message sent from the drag source. */
+{
+ int button, keyState;
+ int x, y;
+ char *formats;
+ int result;
+ struct DropInfo {
+ int mesg; /* TS_DROP message. */
+ Window window; /* Source window. */
+ int timestamp; /* Transaction timestamp. */
+ int point; /* Root X-Y coordinate of pointer. */
+ int flags; /* Button/keystate information. */
+ } *dropPtr;
+ DropPending pending;
+
+ dropPtr = (struct DropInfo *)eventPtr->xclient.data.l;
+ UNPACK(dropPtr->point, x, y);
+ UNPACK(dropPtr->flags, button, keyState);
+
+ /* Set up temporary bookkeeping for the drop transaction */
+ memset (&pending, 0, sizeof(pending));
+ pending.window = dropPtr->window;
+ pending.display = eventPtr->xclient.display;
+ pending.timestamp = dropPtr->timestamp;
+ pending.protocol = PROTO_BLT;
+ pending.packetSize = GetMaxPropertySize(pending.display);
+ Tcl_DStringInit(&(pending.dString));
+
+ formats = GetSourceFormats(dndPtr, dropPtr->window, dropPtr->timestamp);
+
+ dndPtr->pendingPtr = &pending;
+ result = AcceptDrop(dndPtr, x, y, formats, button, keyState,
+ dropPtr->timestamp);
+ dndPtr->pendingPtr = NULL;
+
+ /* Target-to-Source: Drop result message. */
+ SendClientMsg(dndPtr->display, dropPtr->window, dndPtr->dataPtr->mesgAtom,
+ TS_DROP_RESULT, (int)Tk_WindowId(dndPtr->tkwin), dropPtr->timestamp,
+ result, 0);
+ FreeFormats(dndPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * HandleDragEvent --
+ *
+ * Invokes one of 3 Tcl procedures to handle the target's side of
+ * the drag operation. This routine is triggered via a ClientMessage
+ * from the drag source indicating that the token as either entered,
+ * moved, or left this target. The source sends messages only if
+ * Tcl procedures on the target have been defined to watch the
+ * events. The message_type field can be either
+ *
+ * ST_DRAG_ENTER The mouse has entered the target.
+ * ST_DRAG_MOTION The mouse has moved within the target.
+ * ST_DRAG_LEAVE The mouse has left the target.
+ *
+ * The data fields are as follows:
+ * data.l[0] Message type.
+ * data.l[1] Window Id of the source.
+ * data.l[2] Timestamp of the drag&drop transaction.
+ * data.l[3] Root X-Y coordinate of the pointer.
+ * data.l[4] Button and key state information.
+ *
+ * For any of the 3 Tcl procedures, the following arguments
+ * are passed:
+ *
+ * widget The path name of the target.
+ * x X-coordinate of the mouse in the widget.
+ * y Y-coordinate of the mouse in the widget.
+ * formats A list of data formats acceptable to both
+ * the source and target.
+ *
+ * If the Tcl procedure returns "cancel", this indicates that the drag
+ * operation has been canceled and the reject symbol should be displayed.
+ * Otherwise it should return a boolean:
+ *
+ * true Target will accept drop.
+ * false Target will not accept the drop.
+ *
+ * The purpose of the Enter and Leave procedure is to allow the
+ * target to provide visual feedback that the drop can occur or not.
+ * The Motion procedure is for cases where the drop area is a smaller
+ * area within the target, such as a canvas item on a canvas. The
+ * procedure can determine (based upon the X-Y coordinates) whether
+ * the pointer is over the canvas item and return a value accordingly.
+ *
+ * The result of the Tcl procedure is then relayed back to the
+ * source by a ClientMessage.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A Tcl procedure is invoked in the target to handle the drag event.
+ * The result of the drag is sent (via another ClientMessage) to the
+ * source.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+HandleDragEvent(dndPtr, eventPtr)
+ Dnd *dndPtr; /* Target where the drag event occurred. */
+ XEvent *eventPtr; /* Message sent from the drag source. */
+{
+ char **cmd;
+ int resp;
+ int x, y;
+ int button, keyState;
+ char *formats;
+ struct DragInfo {
+ int mesg; /* Drag-and-drop message type. */
+ Window window; /* Source window. */
+ int timestamp; /* Transaction timestamp. */
+ int point; /* Root X-Y coordinate of pointer. */
+ int flags; /* Button/keystate information. */
+ } *dragPtr;
+
+ dragPtr = (struct DragInfo *)eventPtr->xclient.data.l;
+
+ cmd = NULL;
+ switch (dragPtr->mesg) {
+ case ST_DRAG_ENTER:
+ cmd = dndPtr->enterCmd;
+ break;
+ case ST_DRAG_MOTION:
+ cmd = dndPtr->motionCmd;
+ break;
+ case ST_DRAG_LEAVE:
+ cmd = dndPtr->leaveCmd;
+ break;
+ }
+ if (cmd == NULL) {
+ return; /* Nothing to do. */
+ }
+ UNPACK(dragPtr->point, x, y);
+ UNPACK(dragPtr->flags, button, keyState);
+ formats = GetSourceFormats(dndPtr, dragPtr->window, dragPtr->timestamp);
+ resp = InvokeCallback(dndPtr, cmd, x, y, formats, button, keyState,
+ dragPtr->timestamp);
+
+ /* Target-to-Source: Drag result message. */
+ SendClientMsg(dndPtr->display, dragPtr->window, dndPtr->dataPtr->mesgAtom,
+ TS_DRAG_STATUS, (int)Tk_WindowId(dndPtr->tkwin), dragPtr->timestamp,
+ resp, 0);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DndEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+DndEventProc(clientData, eventPtr)
+ ClientData clientData; /* Drag&drop record. */
+ XEvent *eventPtr; /* Event description. */
+{
+ Dnd *dndPtr = clientData;
+
+ if (eventPtr->xany.window != Tk_WindowId(dndPtr->tkwin)) {
+ return 0;
+ }
+ if (eventPtr->type == DestroyNotify) {
+ dndPtr->tkwin = NULL;
+ dndPtr->flags |= DND_DELETED;
+ Tcl_EventuallyFree(dndPtr, DestroyDnd);
+ return 0; /* Other handlers have to see this event too.*/
+ } else if (eventPtr->type == ButtonPress) {
+ dndPtr->keyState = eventPtr->xbutton.state;
+ dndPtr->button = eventPtr->xbutton.button;
+ return 0;
+ } else if (eventPtr->type == ButtonRelease) {
+ dndPtr->keyState = eventPtr->xbutton.state;
+ dndPtr->button = eventPtr->xbutton.button;
+ return 0;
+ } else if (eventPtr->type == MotionNotify) {
+ dndPtr->keyState = eventPtr->xmotion.state;
+ return 0;
+ } else if ((eventPtr->type == ClientMessage) &&
+ (eventPtr->xclient.message_type == dndPtr->dataPtr->mesgAtom)) {
+ int result;
+
+ switch((unsigned int)eventPtr->xclient.data.l[0]) {
+ case TS_START_DROP:
+ DoDrop(dndPtr, eventPtr);
+ return 1;
+
+ case TS_DROP_RESULT:
+ result = eventPtr->xclient.data.l[MESG_RESPONSE];
+ dndPtr->tokenPtr->status = result;
+ if (result == DROP_CANCEL) {
+ CancelDrag(dndPtr);
+ } else if (result == DROP_FAIL) {
+ EventuallyRedrawToken(dndPtr);
+ } else {
+ dndPtr->tokenPtr->nSteps = 10;
+ FadeToken(dndPtr);
+ }
+ if (dndPtr->resultCmd != NULL) {
+ DropFinished(dndPtr, eventPtr);
+ }
+ return 1;
+
+ case TS_DRAG_STATUS:
+ result = eventPtr->xclient.data.l[MESG_RESPONSE];
+ ChangeToken(dndPtr, result);
+ return 1;
+
+ case ST_DROP:
+ HandleDropEvent(dndPtr, eventPtr);
+ return 1;
+
+ case ST_DRAG_ENTER:
+ case ST_DRAG_MOTION:
+ case ST_DRAG_LEAVE:
+ HandleDragEvent(dndPtr, eventPtr);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+SendPointerMessage(dndPtr, eventType, windowPtr, x, y)
+ Dnd *dndPtr; /* Source drag&drop manager. */
+ int eventType; /* Type of event to relay. */
+ Winfo *windowPtr; /* Generic window information. */
+ int x, y; /* Root coordinates of mouse. */
+{
+ /* Source-to-Target: Pointer event messages. */
+ SendClientMsg(
+ dndPtr->display, /* Display of recipient window. */
+ windowPtr->window, /* Recipient window. */
+ dndPtr->dataPtr->mesgAtom, /* Message type. */
+ eventType, /* Data 1 */
+ (int)Tk_WindowId(dndPtr->tkwin), /* Data 2 */
+ dndPtr->timestamp, /* Data 3 */
+ PACK(x, y), /* Data 4 */
+ PACK(dndPtr->button, dndPtr->keyState)); /* Data 5 */
+ /* Don't wait the response. */
+}
+
+static void
+RelayEnterEvent(dndPtr, windowPtr, x, y)
+ Dnd *dndPtr;
+ Winfo *windowPtr;
+ int x, y;
+{
+ if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_ENTER)) {
+ SendPointerMessage(dndPtr, ST_DRAG_ENTER, windowPtr, x, y);
+ }
+}
+
+static void
+RelayLeaveEvent(dndPtr, windowPtr, x, y)
+ Dnd *dndPtr;
+ Winfo *windowPtr;
+ int x, y;
+{
+ if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_LEAVE)) {
+ SendPointerMessage(dndPtr, ST_DRAG_LEAVE, windowPtr, x, y);
+ }
+}
+
+static void
+RelayMotionEvent(dndPtr, windowPtr, x, y)
+ Dnd *dndPtr;
+ Winfo *windowPtr;
+ int x, y;
+{
+ if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_MOTION)) {
+ SendPointerMessage(dndPtr, ST_DRAG_MOTION, windowPtr, x, y);
+ }
+}
+
+static void
+RelayDropEvent(dndPtr, windowPtr, x, y)
+ Dnd *dndPtr;
+ Winfo *windowPtr;
+ int x, y;
+{
+ SendPointerMessage(dndPtr, ST_DROP, windowPtr, x, y);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FreeWinfo --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+FreeWinfo(windowPtr)
+ Winfo *windowPtr; /* window rep to be freed */
+{
+ Winfo *childPtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(windowPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_ChainGetValue(linkPtr);
+ FreeWinfo(childPtr); /* Recursively free children. */
+ }
+ if (windowPtr->matches != NULL) {
+ Blt_Free(windowPtr->matches);
+ }
+ Blt_ChainDestroy(windowPtr->chainPtr);
+ Blt_Free(windowPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWinfo --
+ *
+ * Invoked during "drag" operations. Digs into the root window
+ * hierarchy and caches the window-related information.
+ * If the current point lies over an uninitialized window (i.e.
+ * one that already has an allocated Winfo structure, but has
+ * not been filled in yet), this routine is called to query
+ * window coordinates. If the window has any children, more
+ * uninitialized Winfo structures are allocated. Further queries
+ * will cause these structures to be initialized in turn.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+GetWinfo(display, windowPtr)
+ Display *display;
+ Winfo *windowPtr; /* window rep to be initialized */
+{
+ int visible;
+
+ if (windowPtr->initialized) {
+ return;
+ }
+ /* Query for the window coordinates. */
+ visible = GetWindowRegion(display, windowPtr);
+ if (visible) {
+ Blt_ChainLink *linkPtr;
+ Blt_Chain *chainPtr;
+ Winfo *childPtr;
+
+ /* Add offset from parent's origin to coordinates */
+ if (windowPtr->parentPtr != NULL) {
+ windowPtr->x1 += windowPtr->parentPtr->x1;
+ windowPtr->y1 += windowPtr->parentPtr->y1;
+ windowPtr->x2 += windowPtr->parentPtr->x1;
+ windowPtr->y2 += windowPtr->parentPtr->y1;
+ }
+ /*
+ * Collect a list of child windows, sorted in z-order. The
+ * topmost window will be first in the list.
+ */
+ chainPtr = GetWindowZOrder(display, windowPtr->window);
+
+ /* Add and initialize extra slots if needed. */
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_Calloc(1, sizeof(Winfo));
+ assert(childPtr);
+ childPtr->initialized = FALSE;
+ childPtr->window = (Window)Blt_ChainGetValue(linkPtr);
+ childPtr->parentPtr = windowPtr;
+ Blt_ChainSetValue(linkPtr, childPtr);
+ }
+ windowPtr->chainPtr = chainPtr;
+ } else {
+ /* If it's not viewable don't bother doing anything else. */
+ windowPtr->x1 = windowPtr->y1 = windowPtr->x2 = windowPtr->y2 = -1;
+ windowPtr->chainPtr = NULL;
+ }
+ windowPtr->initialized = TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * InitRoot --
+ *
+ * Invoked at the start of a "drag" operation to capture the
+ * positions of all windows on the current root. Queries the
+ * entire window hierarchy and determines the placement of each
+ * window. Queries the "BltDndTarget" property info where
+ * appropriate. This information is used during the drag
+ * operation to determine when the drag&drop token is over a
+ * valid drag&drop target.
+ *
+ * Results:
+ * Returns the record for the root window, which contains records
+ * for all other windows as children.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Winfo *
+InitRoot(dndPtr)
+ Dnd *dndPtr;
+{
+ Winfo *rootPtr;
+
+ rootPtr = Blt_Calloc(1, sizeof(Winfo));
+ assert(rootPtr);
+ rootPtr->window = DefaultRootWindow(dndPtr->display);
+ dndPtr->windowPtr = NULL;
+ GetWinfo(dndPtr->display, rootPtr);
+ return rootPtr;
+}
+
+
+static int
+ParseProperty(interp, dndPtr, windowPtr, data)
+ Tcl_Interp *interp;
+ Dnd *dndPtr;
+ Winfo *windowPtr;
+ char *data;
+{
+ int nElems;
+ char **elemArr;
+ int eventFlags;
+ Tcl_DString dString;
+ int count;
+ register int i;
+
+ if (Tcl_SplitList(interp, data, &nElems, &elemArr) != TCL_OK) {
+ return TCL_ERROR; /* Malformed property list. */
+ }
+ if (nElems < 1) {
+ Tcl_AppendResult(interp, "Malformed property \"", data, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ if (Tcl_GetInt(interp, elemArr[PROP_WATCH_FLAGS], &eventFlags) != TCL_OK) {
+ goto error;
+ }
+
+ /* target flags, type1, type2, ... */
+ /*
+ * The target property contains a list of possible formats.
+ * Compare this with what formats the source is willing to
+ * convert and compress the list down to just the matching
+ * formats. It's up to the target to request the specific
+ * type (or types) that it wants.
+ */
+ count = 0;
+ Tcl_DStringInit(&dString);
+ if (dndPtr->reqFormats == NULL) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *fmt;
+
+ for (i = 1; i < nElems; i++) {
+ for(hPtr = Blt_FirstHashEntry(&(dndPtr->getDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ fmt = Blt_GetHashKey(&(dndPtr->getDataTable), hPtr);
+ if ((*fmt == elemArr[i][0]) && (strcmp(fmt, elemArr[i]) == 0)) {
+ Tcl_DStringAppendElement(&dString, elemArr[i]);
+ count++;
+ break;
+ }
+ }
+ }
+ } else {
+ register char **s;
+
+ for (i = 1; i < nElems; i++) {
+ for (s = dndPtr->reqFormats; *s != NULL; s++) {
+ if ((**s == elemArr[i][0]) && (strcmp(*s, elemArr[i]) == 0)) {
+ Tcl_DStringAppendElement(&dString, elemArr[i]);
+ count++;
+ }
+ }
+ }
+ }
+ if (count == 0) {
+#ifdef notdef
+ fprintf(stderr, "source/target mismatch: No matching types\n");
+#endif
+ return TCL_BREAK;
+ }
+ if (eventFlags != 0) {
+ SetProperty(dndPtr->tkwin, dndPtr->dataPtr->formatsAtom,
+ Tcl_DStringValue(&dString));
+ windowPtr->matches = NULL;
+ } else {
+ windowPtr->matches = Blt_Strdup(Tcl_DStringValue(&dString));
+ }
+ Tcl_DStringFree(&dString);
+ windowPtr->eventFlags = eventFlags;
+ return TCL_OK;
+ error:
+ Blt_Free(elemArr);
+ return TCL_ERROR;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * OverTarget --
+ *
+ * Checks to see if a compatible drag&drop target exists at the
+ * given position. A target is "compatible" if it is a drag&drop
+ * window, and if it has a handler that is compatible with the
+ * current source window.
+ *
+ * Results:
+ * Returns a pointer to a structure describing the target, or NULL
+ * if no compatible target is found.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Winfo *
+OverTarget(dndPtr)
+ Dnd *dndPtr; /* drag&drop source window */
+{
+ Tcl_Interp *interp = dndPtr->interp;
+ int x, y;
+ int vx, vy;
+ int dummy;
+ Winfo *windowPtr;
+
+ /*
+ * If no window info has been been gathered yet for this target,
+ * then abort this call. This probably means that the token is
+ * moved before it has been properly built.
+ */
+ if (dndPtr->rootPtr == NULL) {
+ fprintf(stderr, "rootPtr not initialized\n");
+ return NULL;
+ }
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(dndPtr->tkwin, &vx, &vy, &dummy, &dummy);
+ x = dndPtr->x + vx;
+ y = dndPtr->y + vy;
+
+ windowPtr = FindTopWindow(dndPtr, x, y);
+ if (windowPtr == NULL) {
+ return NULL; /* Not over a window. */
+ }
+ if ((!dndPtr->selfTarget) &&
+ (Tk_WindowId(dndPtr->tkwin) == windowPtr->window)) {
+ return NULL; /* If the self-target flag isn't set,
+ * don't allow the source window to
+ * drop onto itself. */
+ }
+ if (!windowPtr->lookedForProperty) {
+ char *data;
+ int result;
+
+ windowPtr->lookedForProperty = TRUE;
+ /* See if this window has a "BltDndTarget" property. */
+ data = GetProperty(dndPtr->display, windowPtr->window,
+ dndPtr->dataPtr->targetAtom);
+ if (data == NULL) {
+#ifdef notdef
+ fprintf(stderr, "No property on 0x%x\n", windowPtr->window);
+#endif
+ return NULL; /* No such property on window. */
+ }
+ result = ParseProperty(interp, dndPtr, windowPtr, data);
+ XFree(data);
+ if (result == TCL_BREAK) {
+#ifdef notdef
+ fprintf(stderr, "No matching formats\n");
+#endif
+ return NULL;
+ }
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ return NULL; /* Malformed property list. */
+ }
+ windowPtr->isTarget = TRUE;
+ }
+ if (!windowPtr->isTarget) {
+ return NULL;
+ }
+ return windowPtr;
+
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * AddTargetProperty --
+ *
+ * Attaches a drag&drop property to the given target window.
+ * This property allows us to recognize the window later as a
+ * valid target. It also stores important information including
+ * the interpreter managing the target and the pathname of the
+ * target window. Usually this routine is called when the target
+ * is first registered or first exposed (so that the X-window
+ * really exists).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+AddTargetProperty(dndPtr)
+ Dnd *dndPtr; /* drag&drop target window data */
+{
+ Tcl_DString dString;
+ Blt_HashEntry *hPtr;
+ unsigned int eventFlags;
+ Blt_HashSearch cursor;
+ char *fmt;
+ char string[200];
+
+ Tcl_DStringInit(&dString);
+ /*
+ * Each target window's dnd property contains
+ *
+ * 1. Mouse event flags.
+ * 2. List of all the data types that can be handled. If none
+ * are listed, then all can be handled.
+ */
+ eventFlags = 0;
+ if (dndPtr->enterCmd != NULL) {
+ eventFlags |= WATCH_ENTER;
+ }
+ if (dndPtr->leaveCmd != NULL) {
+ eventFlags |= WATCH_LEAVE;
+ }
+ if (dndPtr->motionCmd != NULL) {
+ eventFlags |= WATCH_MOTION;
+ }
+ sprintf(string, "0x%x", eventFlags);
+ Tcl_DStringAppendElement(&dString, string);
+ for (hPtr = Blt_FirstHashEntry(&(dndPtr->setDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ fmt = Blt_GetHashKey(&(dndPtr->setDataTable), hPtr);
+ Tcl_DStringAppendElement(&dString, fmt);
+ }
+ SetProperty(dndPtr->tkwin, dndPtr->dataPtr->targetAtom,
+ Tcl_DStringValue(&dString));
+ dndPtr->targetPropertyExists = TRUE;
+ Tcl_DStringFree(&dString);
+}
+
+static void
+CancelDrag(dndPtr)
+ Dnd *dndPtr;
+{
+ if (dndPtr->flags & DND_INITIATED) {
+ dndPtr->tokenPtr->nSteps = 10;
+ SnapToken(dndPtr);
+ StopActiveCursor(dndPtr);
+ if (dndPtr->cursor == None) {
+ Tk_UndefineCursor(dndPtr->tkwin);
+ } else {
+ Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursor);
+ }
+ }
+ if (dndPtr->rootPtr != NULL) {
+ FreeWinfo(dndPtr->rootPtr);
+ dndPtr->rootPtr = NULL;
+ }
+}
+
+static int
+DragInit(dndPtr, x, y)
+ Dnd *dndPtr;
+ int x, y;
+{
+ Token *tokenPtr = dndPtr->tokenPtr;
+ int result;
+ Winfo *newPtr;
+
+ assert((dndPtr->flags & DND_ACTIVE) == DND_SELECTED);
+
+ if (dndPtr->rootPtr != NULL) {
+ FreeWinfo(dndPtr->rootPtr);
+ }
+ dndPtr->rootPtr = InitRoot(dndPtr); /* Reset information cache. */
+ dndPtr->flags &= ~DND_VOIDED;
+
+ dndPtr->x = x; /* Save current location. */
+ dndPtr->y = y;
+ result = TRUE;
+ Tcl_Preserve(dndPtr);
+ if (dndPtr->packageCmd != NULL) {
+ Tcl_DString dString, savedResult;
+ Tcl_Interp *interp = dndPtr->interp;
+ int status;
+ char **p;
+ int rx, ry;
+
+ Tcl_DStringInit(&dString);
+ for (p = dndPtr->packageCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ rx = dndPtr->dragX - Blt_RootX(dndPtr->tkwin);
+ ry = dndPtr->dragY - Blt_RootY(dndPtr->tkwin);
+ Tcl_DStringAppendElement(&dString, "x");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(rx));
+ Tcl_DStringAppendElement(&dString, "y");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(ry));
+ Tcl_DStringAppendElement(&dString, "button");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->button));
+ Tcl_DStringAppendElement(&dString, "state");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->keyState));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString, Blt_Utoa(dndPtr->timestamp));
+ Tcl_DStringAppendElement(&dString, "token");
+ Tcl_DStringAppendElement(&dString, Tk_PathName(tokenPtr->tkwin));
+
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ dndPtr->flags |= DND_IN_PACKAGE;
+ status = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+ dndPtr->flags &= ~DND_IN_PACKAGE;
+ if (status == TCL_OK) {
+ result = GetDragResult(interp, Tcl_GetStringResult(interp));
+ } else {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&dString);
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_DStringFree(&dString);
+ if (status != TCL_OK) {
+ HideToken(dndPtr);
+ Tcl_Release(dndPtr);
+ return TCL_ERROR;
+ }
+ }
+ if (dndPtr->flags & DND_VOIDED) {
+ HideToken(dndPtr);
+ Tcl_Release(dndPtr);
+ return TCL_RETURN;
+ }
+ if ((!result) || (dndPtr->flags & DND_DELETED)) {
+ HideToken(dndPtr);
+ Tcl_Release(dndPtr);
+ return TCL_RETURN;
+ }
+ Tcl_Release(dndPtr);
+
+ if (dndPtr->cursor != None) {
+ Tk_Cursor cursor;
+
+ /* Save the old cursor */
+ cursor = GetWidgetCursor(dndPtr->interp, dndPtr->tkwin);
+ if (dndPtr->cursor != None) {
+ Tk_FreeCursor(dndPtr->display, dndPtr->cursor);
+ }
+ dndPtr->cursor = cursor;
+ if (dndPtr->cursors != NULL) {
+ /* Temporarily install the drag-and-drop cursor. */
+ Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursors[0]);
+ }
+ }
+ if (Tk_WindowId(tokenPtr->tkwin) == None) {
+ Tk_MakeWindowExist(tokenPtr->tkwin);
+ }
+ if (!Tk_IsMapped(tokenPtr->tkwin)) {
+ Tk_MapWindow(tokenPtr->tkwin);
+ }
+ dndPtr->flags |= DND_INITIATED;
+ newPtr = OverTarget(dndPtr);
+ RelayEnterEvent(dndPtr, newPtr, x, y);
+ dndPtr->windowPtr = newPtr;
+ tokenPtr->status = (newPtr != NULL) ? DROP_OK : DROP_CONTINUE;
+ if (tokenPtr->lastStatus != tokenPtr->status) {
+ EventuallyRedrawToken(dndPtr);
+ }
+ MoveToken(dndPtr); /* Move token to current drag point. */
+ RaiseToken(dndPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CancelOp --
+ *
+ * Cancels the current drag&drop operation for the source. Calling
+ * this operation does not affect the transfer of data from the
+ * source to the target, once the drop has been made. From the
+ * source's point of view, the drag&drop operation is already over.
+ *
+ * Example: dnd cancel .widget
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Hides the token and sets a flag indicating that further "drag"
+ * and "drop" operations should be ignored.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CancelOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Dnd *dndPtr;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!dndPtr->isSource) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin),
+ "\" is not a registered drag&drop source.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Send the target a Leave message so it can change back. */
+ RelayLeaveEvent(dndPtr, dndPtr->windowPtr, 0, 0);
+ CancelDrag(dndPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+CgetOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Dnd *dndPtr;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, dndPtr->tkwin, configSpecs, (char *)dndPtr,
+ argv[3], 0);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp; /* current interpreter */
+ int argc; /* number of arguments */
+ char **argv; /* argument strings */
+{
+ Dnd *dndPtr;
+ int flags;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = TK_CONFIG_ARGV_ONLY;
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, dndPtr->tkwin, configSpecs,
+ (char *)dndPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, dndPtr->tkwin, configSpecs,
+ (char *)dndPtr, argv[3], flags);
+ }
+ if (Tk_ConfigureWidget(interp, dndPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)dndPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureDnd(interp, dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the drag&drop manager from the widget. If a "-source"
+ * or "-target" switch is present, only that component of the
+ * drag&drop manager is shutdown. The manager is not deleted
+ * unless both the target and source components are shutdown.
+ *
+ * Example: dnd delete .widget
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Deletes the drag&drop manager. Also the source and target window
+ * properties are removed from the widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+DeleteOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Dnd *dndPtr;
+ register int i;
+
+ for(i = 3; i < argc; i++) {
+ if (GetDnd(clientData, interp, argv[i], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ dndPtr->flags |= DND_DELETED;
+ Tcl_EventuallyFree(dndPtr, DestroyDnd);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * Initializes a drag&drop transaction. Typically this operation
+ * is called from a ButtonPress event on a source widget. The
+ * window information cache is initialized, and the token is
+ * initialized and displayed.
+ *
+ * Example: dnd pickup .widget x y
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * The token is initialized and displayed. This may require invoking
+ * a user-defined package command. The window information cache is
+ * also initialized.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Dnd *dndPtr;
+ int x, y, timestamp;
+ Token *tokenPtr;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!dndPtr->isSource) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin),
+ "\" is not a registered drag&drop source.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tokenPtr = dndPtr->tokenPtr;
+ if (tokenPtr == NULL) {
+ Tcl_AppendResult(interp, "no drag&drop token created for \"",
+ argv[2], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[5], &timestamp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (dndPtr->flags & (DND_IN_PACKAGE | DND_ACTIVE | DND_VOIDED)) {
+ return TCL_OK;
+ }
+
+ if (tokenPtr->timerToken != NULL) {
+ HideToken(dndPtr); /* If the user selected again before the
+ * token snap/melt has completed, first
+ * disable the token timer callback. */
+ }
+ /* At this point, simply save the starting pointer location. */
+ dndPtr->dragX = x, dndPtr->dragY = y;
+ GetTokenPosition(dndPtr, x, y);
+ tokenPtr->startX = tokenPtr->x;
+ tokenPtr->startY = tokenPtr->y;
+ dndPtr->timestamp = timestamp;
+ dndPtr->flags |= DND_SELECTED;
+
+ if (dndPtr->dragStart == 0) {
+ if (DragInit(dndPtr, x, y) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DragOp --
+ *
+ * Continues the drag&drop transaction. Typically this operation
+ * is called from a button Motion event on a source widget. Pointer
+ * event messages are possibly sent to the target, indicating Enter,
+ * Leave, and Motion events.
+ *
+ * Example: dnd drag .widget x y
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Pointer events are relayed to the target (if the mouse is over
+ * one).
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DragOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Winfo *newPtr, *oldPtr;
+ Dnd *dndPtr;
+ int x, y;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!dndPtr->isSource) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin),
+ "\" is not a registered drag&drop source.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (dndPtr->tokenPtr == NULL) {
+ Tcl_AppendResult(interp, "no drag&drop token created for \"",
+ argv[2], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+
+ if ((dndPtr->flags & DND_SELECTED) == 0) {
+ return TCL_OK; /* Re-entered this routine. */
+ }
+ /*
+ * The following code gets tricky because the package command may
+ * call "update" or "tkwait". A motion event may then trigger
+ * this routine, before the token has been initialized. Until the
+ * package command finishes, no target messages are sent and drops
+ * are silently ignored. Note that we do still track mouse
+ * movements, so that when the package command completes, it will
+ * have the latest pointer position.
+ */
+ dndPtr->x = x; /* Save current location. */
+ dndPtr->y = y;
+
+ if (dndPtr->flags & DND_IN_PACKAGE) {
+ return TCL_OK; /* Re-entered this routine. */
+ }
+ if ((dndPtr->flags & DND_INITIATED) == 0) {
+ int dx, dy;
+ int result;
+
+ dx = dndPtr->dragX - x;
+ dy = dndPtr->dragY - y;
+ if ((ABS(dx) < dndPtr->dragStart) && (ABS(dy) < dndPtr->dragStart)) {
+ return TCL_OK;
+ }
+ result = DragInit(dndPtr, x, y);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (result == TCL_RETURN) {
+ return TCL_OK;
+ }
+ }
+ if (dndPtr->flags & DND_VOIDED) {
+ return TCL_OK;
+ }
+ oldPtr = dndPtr->windowPtr;
+ newPtr = OverTarget(dndPtr);
+ if (newPtr == oldPtr) {
+ RelayMotionEvent(dndPtr, oldPtr, x, y);
+ dndPtr->windowPtr = oldPtr;
+ } else {
+ RelayLeaveEvent(dndPtr, oldPtr, x, y);
+ RelayEnterEvent(dndPtr, newPtr, x, y);
+ dndPtr->windowPtr = newPtr;
+ }
+ dndPtr->tokenPtr->status = (newPtr != NULL) ? DROP_OK : DROP_CONTINUE;
+ if (dndPtr->tokenPtr->lastStatus != dndPtr->tokenPtr->status) {
+ EventuallyRedrawToken(dndPtr);
+ }
+ MoveToken(dndPtr); /* Move token to current drag point. */
+ RaiseToken(dndPtr);
+ return TCL_OK;
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DropOp --
+ *
+ * Finishes the drag&drop transaction by dropping the data on the
+ * selected target. Typically this operation is called from a
+ * ButtonRelease event on a source widget. Note that a Leave message
+ * is always sent to the target so that is can un-highlight itself.
+ * The token is hidden and a drop message is sent to the target.
+ *
+ * Example: dnd drop .widget x y
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * The token is hidden and a drop message is sent to the target.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DropOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Winfo *windowPtr;
+ Dnd *dndPtr;
+ int x, y;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!dndPtr->isSource) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin),
+ "\" is not a registered drag&drop source.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ dndPtr->x = x; /* Save drag&drop location */
+ dndPtr->y = y;
+ if ((dndPtr->flags & DND_INITIATED) == 0) {
+ return TCL_OK; /* Never initiated any drag operation. */
+ }
+ if (dndPtr->flags & DND_VOIDED) {
+ HideToken(dndPtr);
+ return TCL_OK;
+ }
+ windowPtr = OverTarget(dndPtr);
+ if (windowPtr != NULL) {
+ if (windowPtr->matches != NULL) {
+ SetProperty(dndPtr->tkwin, dndPtr->dataPtr->formatsAtom,
+ windowPtr->matches);
+ }
+ MoveToken(dndPtr); /* Move token to current drag point. */
+ RaiseToken(dndPtr);
+ RelayDropEvent(dndPtr, windowPtr, x, y);
+#ifdef notdef
+ tokenPtr->nSteps = 10;
+ FadeToken(dndPtr);
+#endif
+ } else {
+ CancelDrag(dndPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetdataOp --
+ *
+ * Registers one or more data formats with a drag&drop source.
+ * Each format has a Tcl command associated with it. This command
+ * is automatically invoked whenever data is pulled from the source
+ * to a target requesting the data in that particular format. The
+ * purpose of the Tcl command is to get the data from in the
+ * application. When the Tcl command is invoked, special percent
+ * substitutions are made:
+ *
+ * %# Drag&drop transaction timestamp.
+ * %W Source widget.
+ *
+ * If a converter (command) already exists for a format, it
+ * overwrites the existing command.
+ *
+ * Example: dnd getdata .widget "color" { %W cget -bg }
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetdataOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Dnd *dndPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ int isNew, nElem;
+ char **cmd;
+ register int i;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ /* Return list of source data formats */
+ for (hPtr = Blt_FirstHashEntry(&(dndPtr->getDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(dndPtr->getDataTable), hPtr));
+ }
+ return TCL_OK;
+ }
+
+ if (argc == 4) {
+ hPtr = Blt_FindHashEntry(&(dndPtr->getDataTable), argv[3]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find handler for format \"",
+ argv[3], "\" for source \"", Tk_PathName(dndPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ if (cmd == NULL) {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, PrintList(cmd), TCL_DYNAMIC);
+ }
+ return TCL_OK;
+ }
+
+ for (i = 3; i < argc; i += 2) {
+ hPtr = Blt_CreateHashEntry(&(dndPtr->getDataTable), argv[i], &isNew);
+ if (!isNew) {
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ Blt_Free(cmd);
+ }
+ if (Tcl_SplitList(interp, argv[i + 1], &nElem, &cmd) != TCL_OK) {
+ Blt_DeleteHashEntry(&(dndPtr->getDataTable), hPtr);
+ return TCL_ERROR;
+ }
+ Blt_SetHashValue(hPtr, cmd);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns the names of all the drag&drop managers. If either
+ * a "-source" or "-target" switch is present, only the names of
+ * managers acting as sources or targets respectively are returned.
+ * A pattern argument may also be given. Only those managers
+ * matching the pattern are returned.
+ *
+ * Examples: dnd names
+ * dnd names -source
+ * dnd names -target
+ * dnd names .*label
+ * Results:
+ * A standard Tcl result. A Tcl list of drag&drop manager
+ * names is returned.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+NamesOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ DndInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Dnd *dndPtr;
+ int findSources, findTargets;
+
+ findSources = findTargets = TRUE;
+ if (argc > 2) {
+ if ((argv[2][0] == '-') && (strcmp(argv[2], "-source") == 0)) {
+ findTargets = FALSE;
+ argc--, argv++;
+ } else if ((argv[2][0] == '-') && (strcmp(argv[2], "-target") == 0)) {
+ findSources = FALSE;
+ argc--, argv++;
+ }
+ }
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->dndTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ dndPtr = (Dnd *)Blt_GetHashValue(hPtr);
+ if ((argc > 3) &&
+ (!Tcl_StringMatch(Tk_PathName(dndPtr->tkwin), argv[3]))) {
+ continue;
+ }
+ if (((findSources) && (dndPtr->isSource)) ||
+ ((findTargets) && (dndPtr->isTarget))) {
+ Tcl_AppendElement(interp, Tk_PathName(dndPtr->tkwin));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * PullOp --
+ *
+ * Pulls the current data from the source in the given format.
+ * application.
+ *
+ * dnd pull .widget format data
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Invokes the target's data converter to store the data.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PullOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Dnd *dndPtr; /* drag&drop source record */
+ int result;
+ char **formatCmd;
+ Blt_HashEntry *hPtr;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!dndPtr->isTarget) {
+ Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin),
+ "\" is not a registered drag&drop target.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(dndPtr->setDataTable), argv[3]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find format \"", argv[3],
+ "\" in target \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ formatCmd = (char **)Blt_GetHashValue(hPtr);
+ if (dndPtr->pendingPtr == NULL) {
+ Tcl_AppendResult(interp, "no drop in progress", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ CompleteDataTransaction(dndPtr, argv[3], dndPtr->pendingPtr);
+ result = TCL_OK;
+ if (Tcl_DStringLength(&(dndPtr->pendingPtr->dString)) > 0) {
+ Tcl_DString dString, savedResult;
+ char **p;
+
+ Tcl_DStringInit(&dString);
+ for (p = formatCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&dString, *p);
+ }
+ Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin));
+ Tcl_DStringAppendElement(&dString, "x");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->dropX));
+ Tcl_DStringAppendElement(&dString, "y");
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(dndPtr->dropY));
+ Tcl_DStringAppendElement(&dString, "timestamp");
+ Tcl_DStringAppendElement(&dString,
+ Blt_Utoa(dndPtr->pendingPtr->timestamp));
+ Tcl_DStringAppendElement(&dString, "format");
+ Tcl_DStringAppendElement(&dString, argv[3]);
+ Tcl_DStringAppendElement(&dString, "value");
+ Tcl_DStringAppendElement(&dString,
+ Tcl_DStringValue(&(dndPtr->pendingPtr->dString)));
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_DStringFree(&dString);
+ }
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SetdataOp --
+ *
+ * Registers one or more data formats with a drag&drop target.
+ * Each format has a Tcl command associated with it. This command
+ * is automatically invoked whenever data arrives from a source
+ * to be converted to that particular format. The purpose of the
+ * command is to set the data somewhere in the application (either
+ * using a Tcl command or variable). When the Tcl command is invoked,
+ * special percent substitutions are made:
+ *
+ * %# Drag&drop transaction timestamp.
+ * %W Target widget.
+ * %v Data value transfered from the source to
+ * be converted into the correct format.
+ *
+ * If a converter (command) already exists for a format, it
+ * overwrites the existing command.
+ *
+ * Example: dnd setdata .widget color { . configure -bg %v }
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+SetdataOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Dnd *dndPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ int isNew, nElem;
+ char **cmd;
+ int i;
+
+ if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ /* Show target handler data formats */
+ for (hPtr = Blt_FirstHashEntry(&(dndPtr->setDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(dndPtr->setDataTable), hPtr));
+ }
+ return TCL_OK;
+ }
+ if (argc == 4) {
+ hPtr = Blt_FindHashEntry(&(dndPtr->setDataTable), argv[3]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find handler for format \"",
+ argv[3], "\" for target \"", Tk_PathName(dndPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ if (cmd == NULL) {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, PrintList(cmd), TCL_DYNAMIC);
+ }
+ return TCL_OK;
+ }
+ for (i = 3; i < argc; i += 2) {
+ hPtr = Blt_CreateHashEntry(&(dndPtr->setDataTable), argv[i], &isNew);
+ if (!isNew) {
+ cmd = (char **)Blt_GetHashValue(hPtr);
+ Blt_Free(cmd);
+ }
+ if (Tcl_SplitList(interp, argv[i + 1], &nElem, &cmd) != TCL_OK) {
+ Blt_DeleteHashEntry(&(dndPtr->setDataTable), hPtr);
+ return TCL_ERROR;
+ }
+ Blt_SetHashValue(hPtr, cmd);
+ }
+ AddTargetProperty(dndPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RegisterOp --
+ *
+ * dnd register .window
+ * ------------------------------------------------------------------------
+ */
+static int
+RegisterOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ DndInterpData *dataPtr = clientData;
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ Dnd *dndPtr;
+ int isNew;
+
+ tkwin = Tk_NameToWindow(interp, argv[2], dataPtr->mainWindow);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&(dataPtr->dndTable), (char *)tkwin, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
+ "\" is already registered as a drag&drop manager", (char *)NULL);
+ return TCL_ERROR;
+ }
+ dndPtr = CreateDnd(interp, tkwin);
+ dndPtr->hashPtr = hPtr;
+ dndPtr->dataPtr = dataPtr;
+ Blt_SetHashValue(hPtr, dndPtr);
+ if (Tk_ConfigureWidget(interp, dndPtr->tkwin, configSpecs, argc - 3,
+ argv + 3, (char *)dndPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureDnd(interp, dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenWindowOp --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+TokenWindowOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Dnd *dndPtr;
+ int flags;
+
+ if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = 0;
+ if (dndPtr->tokenPtr == NULL) {
+ if (CreateToken(interp, dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ flags = TK_CONFIG_ARGV_ONLY;
+ }
+ if (ConfigureToken(interp, dndPtr, argc - 4, argv + 4, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(dndPtr->tokenPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenCgetOp --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+TokenCgetOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Dnd *dndPtr;
+
+ if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (dndPtr->tokenPtr == NULL) {
+ Tcl_AppendResult(interp, "no token created for \"", argv[3], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, dndPtr->tokenPtr->tkwin, tokenConfigSpecs,
+ (char *)dndPtr->tokenPtr, argv[4], TK_CONFIG_ARGV_ONLY);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenConfigureOp --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+TokenConfigureOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Token *tokenPtr;
+ Dnd *dndPtr;
+ int flags;
+
+ if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = TK_CONFIG_ARGV_ONLY;
+ if (dndPtr->tokenPtr == NULL) {
+ Tcl_AppendResult(interp, "no token created for \"", argv[3], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ tokenPtr = dndPtr->tokenPtr;
+ if (argc == 3) {
+ return Tk_ConfigureInfo(interp, tokenPtr->tkwin, tokenConfigSpecs,
+ (char *)tokenPtr, (char *)NULL, flags);
+ } else if (argc == 4) {
+ return Tk_ConfigureInfo(interp, tokenPtr->tkwin, tokenConfigSpecs,
+ (char *)tokenPtr, argv[3], flags);
+ }
+ return ConfigureToken(interp, dndPtr, argc - 4, argv + 4, flags);
+}
+
+static Blt_OpSpec tokenOps[] =
+{
+ {"cget", 2, (Blt_Op)TokenCgetOp, 5, 5, "widget option",},
+ {"configure", 2, (Blt_Op)TokenConfigureOp, 4, 0,
+ "widget ?option value?...",},
+ {"window", 5, (Blt_Op)TokenWindowOp, 4, 0,
+ "widget ?option value?...",},
+};
+
+static int nTokenOps = sizeof(tokenOps) / sizeof(Blt_OpSpec);
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenOp --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+TokenOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nTokenOps, tokenOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+static Blt_OpSpec dndOps[] =
+{
+ {"cancel", 2, (Blt_Op)CancelOp, 3, 3, "widget",},
+ {"cget", 2, (Blt_Op)CgetOp, 4, 4, "widget option",},
+ {"configure", 4, (Blt_Op)ConfigureOp, 3, 0,
+ "widget ?option value?...",},
+#ifdef notdef
+ {"convert", 4, (Blt_Op)ConvertOp, 5, 5, "widget data format",},
+#endif
+ {"delete", 2, (Blt_Op)DeleteOp, 3, 0,"?-source|-target? widget...",},
+ {"drag", 3, (Blt_Op)DragOp, 3, 0, "widget x y ?token?",},
+ {"drop", 3, (Blt_Op)DropOp, 3, 0, "widget x y ?token?",},
+ {"getdata", 1, (Blt_Op)GetdataOp, 3, 0, "widget ?format command?",},
+ {"names", 1, (Blt_Op)NamesOp, 2, 4, "?-source|-target? ?pattern?",},
+ {"pull", 1, (Blt_Op)PullOp, 4, 4, "widget format",},
+ {"register", 1, (Blt_Op)RegisterOp, 3, 0, "widget ?option value?...",},
+ {"select", 3, (Blt_Op)SelectOp, 6, 6, "widget x y timestamp",},
+ {"setdata", 3, (Blt_Op)SetdataOp, 3, 0, "widget ?format command?",},
+ {"token", 5, (Blt_Op)TokenOp, 3, 0, "args...",},
+};
+
+static int nDndOps = sizeof(dndOps) / sizeof(Blt_OpSpec);
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DndCmd --
+ *
+ * Invoked by TCL whenever the user issues a drag&drop command.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+DndCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp; /* current interpreter */
+ int argc; /* number of arguments */
+ char **argv; /* argument strings */
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nDndOps, dndOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DndInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the "dnd" command is
+ * destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys the hash table containing the drag&drop managers.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+DndInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Thread-specific data. */
+ Tcl_Interp *interp;
+{
+ DndInterpData *dataPtr = clientData;
+ Dnd *dndPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->dndTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ dndPtr = (Dnd *)Blt_GetHashValue(hPtr);
+ dndPtr->hashPtr = NULL;
+ DestroyDnd((DestroyData)dndPtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->dndTable));
+ Tcl_DeleteAssocData(interp, DND_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+static DndInterpData *
+GetDndInterpData(interp)
+ Tcl_Interp *interp;
+{
+ DndInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (DndInterpData *)Tcl_GetAssocData(interp, DND_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ Display *display;
+ Tk_Window tkwin;
+
+ dataPtr = Blt_Malloc(sizeof(DndInterpData));
+ assert(dataPtr);
+ tkwin = Tk_MainWindow(interp);
+ display = Tk_Display(tkwin);
+ dataPtr->mainWindow = tkwin;
+ dataPtr->display = display;
+ Tcl_SetAssocData(interp, DND_THREAD_KEY, DndInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->dndTable), BLT_ONE_WORD_KEYS);
+ dataPtr->mesgAtom = XInternAtom(display, "BLT Dnd Message", False);
+ dataPtr->targetAtom = XInternAtom(display, "BLT Dnd Target", False);
+ dataPtr->formatsAtom = XInternAtom(display, "BLT Dnd Formats",False);
+ dataPtr->commAtom = XInternAtom(display, "BLT Dnd CommData", False);
+
+#ifdef HAVE_XDND
+ dataPtr->XdndActionListAtom = XInternAtom(display, "XdndActionList",
+ False);
+ dataPtr->XdndAwareAtom = XInternAtom(display, "XdndAware", False);
+ dataPtr->XdndEnterAtom = XInternAtom(display, "XdndEnter", False);
+ dataPtr->XdndFinishedAtom = XInternAtom(display, "XdndFinished", False);
+ dataPtr->XdndLeaveAtom = XInternAtom(display, "XdndLeave", False);
+ dataPtr->XdndPositionAtom = XInternAtom(display, "XdndPosition", False);
+ dataPtr->XdndSelectionAtom = XInternAtom(display, "XdndSelection",
+ False);
+ dataPtr->XdndStatusAtom = XInternAtom(display, "XdndStatus", False);
+ dataPtr->XdndTypeListAtom = XInternAtom(display, "XdndTypeList", False);
+#endif /* HAVE_XDND */
+ }
+ return dataPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * Blt_DndInit --
+ *
+ * Adds the drag&drop command to the given interpreter. Should
+ * be invoked to properly install the command whenever a new
+ * interpreter is created.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_DndInit(interp)
+ Tcl_Interp *interp; /* interpreter to be updated */
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "dnd", DndCmd
+ };
+ DndInterpData *dataPtr;
+
+ dataPtr = GetDndInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#ifdef notdef
+/*
+ * Registers bitmap outline of dragged data, used to indicate
+ * what is being dragged by source. Bitmap is XOR-ed as cursor/token
+ * is moved around the screen.
+ */
+static void
+Blt_DndSetOutlineBitmap(tkwin, bitmap, x, y)
+ Tk_Window tkwin;
+ Pixmap bitmap;
+ int x, y;
+{
+
+}
+#endif
+
+#ifdef HAVE_XDND
+
+static void
+XDndFreeFormats(handlerPtr)
+ XDndHandler *handlerPtr;
+{
+ if (handlerPtr->formatArr != NULL) {
+ char **p;
+
+ for (p = handlerPtr->formatArr; *p != NULL; p++) {
+ XFree(*p);
+ }
+ Blt_Free(handlerPtr->formatArr);
+ handlerPtr->formatArr = NULL;
+ }
+}
+
+static char **
+XDndGetFormats(handlerPtr, eventPtr)
+ XDndHandler *handlerPtr;
+ XEvent *eventPtr;
+{
+ int flags;
+ Window window;
+ unsigned char *data;
+ Atom typeAtom;
+ Atom format;
+ int nItems, bytesAfter;
+ Atom *atomArr;
+ char *nameArr[XDND_MAX_TYPES + 1];
+ Display *display;
+
+ XDndFreeFormats(handlerPtr);
+ display = eventPtr->xclient.display;
+ window = eventPtr->xclient.data.l[0];
+ flags = eventPtr->xclient.data.l[1];
+ data = NULL;
+ if (flags & 0x01) {
+ result = XGetWindowProperty(
+ display, /* Display of window. */
+ window, /* Window holding the property. */
+ handlerPtr->dataPtr->XdndTypeListAtom, /* Name of property. */
+ 0, /* Offset of data (for multiple reads). */
+ XDND_MAX_TYPES, /* Maximum number of items to read. */
+ False, /* If true, delete the property. */
+ XA_ATOM, /* Desired type of property. */
+ &typeAtom, /* (out) Actual type of the property. */
+ &format, /* (out) Actual format of the property. */
+ &nItems, /* (out) # of items in specified format. */
+ &bytesAfter, /* (out) # of bytes remaining to be read. */
+ (unsigned char **)&data);
+ if ((result != Success) || (format != 32) || (typeAtom != XA_ATOM)) {
+ if (data != NULL) {
+ XFree((char *)data);
+ return (char **)NULL;
+ }
+ }
+ atomArr = (Atom *)data;
+ nAtoms = nItems;
+ } else {
+ atomArr = &(eventPtr->xclient.data.l[2]);
+ nAtoms = 3;
+ }
+ formatArr = Blt_Calloc(nAtoms + 1, sizeof(char *));
+ for (i = 0; (i < nAtoms) && (atomArr[i] != None); i++) {
+ formatArr[i] = XGetAtomName(display, atomArr[i]);
+ }
+ if (data != NULL) {
+ XFree((char *)data);
+ }
+ handlerPtr->formatArr = formatArr;
+}
+
+static char *
+GetMatchingFormats(dndPtr, formatArr)
+ Dnd *dndPtr;
+ char **formatArr;
+{
+ int nMatches;
+
+ nMatches = 0;
+ Tcl_DStringInit(&dString);
+ for (p = formatArr; *p != NULL; p++) {
+ for(hPtr = Blt_FirstHashEntry(&(dndPtr->setDataTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ fmt = Blt_GetHashKey(&(dndPtr->setDataTable), hPtr);
+ if ((*fmt == **p) && (strcmp(fmt, *p) == 0)) {
+ Tcl_DStringAppendElement(&dString, *p);
+ nMatches++;
+ break;
+ }
+ }
+ }
+ if (nMatches > 0) {
+ char *string;
+
+ string = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return string;
+ }
+ return NULL;
+}
+
+static void
+XDndPointerEvent(handlerPtr, eventPtr)
+ XDndHandler *handlerPtr;
+ XEvent *eventPtr;
+{
+ Tk_Window tkwin;
+ int flags;
+ Atom action;
+ Window window;
+
+ flags = 0;
+ action = None;
+ window = eventPtr->xclient.data.l[MESG_XDND_WINDOW];
+ /*
+ * If the XDND source has no formats specified, don't process any further.
+ * Simply send a "no accept" action with the message.
+ */
+ if (handlerPtr->formatArr != NULL) {
+ Dnd *newPtr, *oldPtr;
+ int point;
+ int button, keyState;
+ int x, y;
+ char *formats;
+
+ point = (int)eventPtr->xclient.data.l[MESG_XDND_POINT];
+ UNPACK(point, x, y);
+
+ /*
+ * See if the mouse pointer currently over a drop target. We first
+ * determine what Tk window is under the mouse, and then check if
+ * that window is registered as a drop target.
+ */
+ newPtr = NULL;
+ tkwin = Tk_CoordsToWindow(x, y, handlerPtr->tkwin);
+ if (tkwin != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(handlerPtr->dataPtr->dndTable),
+ (char *)tkwin);
+ if (hPtr != NULL) {
+ newPtr = (Dnd *)Blt_GetHashValue(hPtr);
+ if (!newPtr->isTarget) {
+ newPtr = NULL; /* Not a DND target. */
+ }
+ formats = GetMatchingFormats(newPtr, handlerPtr->formatArr);
+ if (formats == NULL) {
+ newPtr = NULL; /* Source has no matching formats. */
+ }
+ }
+ }
+ button = keyState = 0;
+ oldPtr = handlerPtr->dndPtr;
+ resp = DROP_CANCEL;
+ if (newPtr == oldPtr) {
+ if ((oldPtr != NULL) && (oldPtr->motionCmd != NULL)) {
+ resp = InvokeCallback(oldPtr, oldPtr->motionCmd, x, y, formats,
+ button, keyState, dndPtr->timestamp);
+ }
+ } else {
+ if ((oldPtr != NULL) && (oldPtr->leaveCmd != NULL)) {
+ InvokeCallback(oldPtr, oldPtr->leaveCmd, x, y, formats, button,
+ keyState, dndPtr->timestamp);
+ }
+ if ((newPtr != NULL) && (newPtr->enterCmd != NULL)) {
+ resp = InvokeCallback(newPtr, newPtr->enterCmd, x, y, formats,
+ button, keyState, dndPtr->timestamp);
+ }
+ handlerPtr->dndPtr = newPtr;
+ /*
+ * Save the current mouse position, since we get them from the
+ * drop message.
+ */
+ newPtr->x = x;
+ newPtr->y = y;
+ }
+ if (formats != NULL) {
+ Blt_Free(formats);
+ }
+ flags = XDND_FLAGS_WANT_POSITION_MSGS;
+ if (resp) {
+ flags |= XDND_FLAGS_ACCEPT_DROP;
+ action = handlerPtr->dataPtr->XdndActionCopyAtom;
+ }
+ }
+ /* Target-to-Source: Drag result message. */
+ SendClientMsg(handlerPtr->display, window,
+ handlerPtr->dataPtr->XdndStatusAtom, handlerPtr->window,
+ flags, 0, 0, action);
+}
+
+static void
+XDndDropEvent(handlerPtr, eventPtr)
+ XDndHandler *handlerPtr;
+ XEvent *eventPtr;
+{
+ Tk_Window tkwin;
+ int flags;
+ Atom action;
+ Window window;
+ int timestamp;
+
+ flags = 0;
+ action = None;
+ window = eventPtr->xclient.data.l[MESG_XDND_WINDOW];
+ timestamp = eventPtr->xclient.data.l[MESG_XDND_TIMESTAMP];
+
+ /*
+ * If no formats were specified for the XDND source or if the last
+ * motion event did not place the mouse over a valid drop target,
+ * don't process any further. Simply send a "no accept" action with
+ * the message.
+ */
+ if ((handlerPtr->formatArr != NULL) && (handlerPtr->dndPtr != NULL)) {
+ int button, keyState;
+ Dnd *dndPtr = handlerPtr->dndPtr;
+ DropPending pending;
+ int resp;
+
+ button = keyState = 0; /* Protocol doesn't supply this information. */
+
+ /* Set up temporary bookkeeping for the drop transaction */
+ memset (&pending, 0, sizeof(pending));
+ pending.window = window;
+ pending.display = eventPtr->xclient.display;
+ pending.timestamp = timestamp;
+ pending.protocol = PROTO_XDND;
+ pending.packetSize = GetMaxPropertySize(pending.display);
+ Tcl_DStringInit(&(pending.dString));
+
+ formats = GetMatchingFormats(handlerPtr->dndPtr, handlerPtr->formatArr);
+ if (formats == NULL) {
+ }
+ dndPtr->pendingPtr = &pending;
+ resp = AcceptDrop(dndPtr, dndPtr->x, dndPtr->y, formats, button,
+ keyState, action, timestamp);
+ dndPtr->pendingPtr = NULL;
+ if (resp) {
+ flags |= XDND_FLAGS_ACCEPT_DROP;
+ action = handlerPtr->dataPtr->XdndActionCopyAtom;
+ }
+ }
+ /* Target-to-Source: Drag result message. */
+ SendClientMsg(handlerPtr->display, window,
+ handlerPtr->dataPtr->XdndStatusAtom, handlerPtr->window,
+ flags, 0, 0, action);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * XDndProtoEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+XDndProtoEventProc(clientData, eventPtr)
+ ClientData clientData; /* Drag&drop record. */
+ XEvent *eventPtr; /* Event description. */
+{
+ DndInterpData *dataPtr = clientData;
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ XDndHandler *handlerPtr;
+ int point;
+ int x, y;
+ Atom mesg;
+
+ if (eventPtr->type != ClientMessage) {
+ return 0; /* Not a ClientMessage event. */
+ }
+ /* Was the recipient a registered toplevel window? */
+ hPtr = Blt_FindHashEntry(&(dataPtr->handlerTable),
+ (char *)eventPtr->xclient.window);
+ if (hPtr == NULL) {
+ return 0; /* No handler registered with window. */
+ }
+ handlerPtr = (XDndHandler *)Blt_GetHashValue(hPtr);
+ mesg = eventPtr->xclient.message_type;
+ if (mesg == dataPtr->XdndEnterAtom) {
+ XDndGetFormats(handlerPtr, eventPtr);
+ handlerPtr->dndPtr = NULL;
+ } else if (mesg == dataPtr->XdndPositionAtom) {
+ XDndPointerEvent(handlerPtr, eventPtr);
+ } else if (mesg == dataPtr->XdndLeaveAtom) {
+ XDndFreeFormats(handlerPtr); /* Free up any collected formats. */
+ if (handlerPtr->dndPtr != NULL) {
+ InvokeCallback(handlerPtr->dndPtr, handlerPtr->dndPtr->leaveCmd,
+ -1, -1, NULL, 0, 0);
+ /* Send leave event to drop target. */
+ }
+ } else if (mesg == dataPtr->XdndDropAtom) {
+ XDndDropEvent(handlerPtr, eventPtr);
+ } else {
+ fprintf(stderr, "Unknown client message type = 0x%x\n", mesg);
+ return 0; /* Unknown message type. */
+ }
+ return 1;
+}
+
+static XDndHandler *
+XDndCreateHandler(dndPtr)
+ Dnd *dndPtr;
+{
+ Tk_Window tkwin;
+ Window window;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ XDndHandler *handlerPtr;
+
+ /*
+ * Find the containing toplevel of this window. See if an XDND
+ * handler is already registered for it.
+ */
+ tkwin = Blt_GetToplevel(dndPtr->tkwin);
+ window = Blt_GetRealWindowId(tkwin); /* Use the wrapper window as
+ * the real toplevel window. */
+ hPtr = Blt_CreateHashEntry(&(dataPtr->XDndHandlerTable), (char *)window,
+ &isNew);
+ if (!isNew) {
+ handlerPtr = (XDndHandler *)Blt_GetHashEntry(hPtr);
+ handlerPtr->refCount++;
+ } else {
+ handlerPtr = Blt_Malloc(sizeof(XDndHandler));
+ handlerPtr->tkwin = tkwin;
+ handlerPtr->dndPtr = NULL;
+ handlerPtr->refCount = 1;
+ handlerPtr->dataPtr = dataPtr;
+ /* FIXME */
+ SetProperty(window, dataPtr->XdndAwareAtom, "3");
+ Blt_SetHashValue(hPtr, handlerPtr);
+ }
+ return handlerPtr;
+}
+
+static void
+XDndDeleteHandler(dndPtr)
+ Dnd *dndPtr;
+{
+ Tk_Window tkwin;
+ Window window;
+ Blt_HashEntry *hPtr;
+
+ tkwin = Blt_GetToplevel(dndPtr->tkwin);
+ window = Blt_GetRealWindowId(tkwin); /* Use the wrapper window as the real
+ * toplevel window. */
+ hPtr = Blt_FindHashEntry(&(dataPtr->XDndHandlerTable), (char *)window);
+ if (hPtr != NULL) {
+ XDndHandler *handlerPtr;
+
+ handlerPtr = (XDndHandler *)Blt_GetHashEntry(hPtr);
+ handlerPtr->refCount--;
+ if (handlerPtr->refCount == 0) {
+ XDndFreeFormats(handlerPtr);
+ XDeleteProperty(dndPtr->display, window,
+ dndPtr->dataPtr->XdndAwareAtom);
+ Blt_DeleteHashEntry(&(dataPtr->XDndHandlerTable), hPtr);
+ Blt_Free(handlerPtr);
+ }
+ }
+}
+
+#endif /* HAVE_XDND */
+
+#endif /* NO_DRAGDROP */
diff --git a/blt/src/bltUnixImage.c b/blt/src/bltUnixImage.c
new file mode 100644
index 00000000000..5cb3d24167b
--- /dev/null
+++ b/blt/src/bltUnixImage.c
@@ -0,0 +1,885 @@
+
+/*
+ * bltUnixImage.c --
+ *
+ * This module implements image processing procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+
+#define CLAMP(c) ((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+int redAdjust, greenAdjust, blueAdjust;
+int redMaskShift, greenMaskShift, blueMaskShift;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShiftCount --
+ *
+ * Returns the position of the least significant (low) bit in
+ * the given mask.
+ *
+ * For TrueColor and DirectColor visuals, a pixel value is
+ * formed by OR-ing the red, green, and blue colormap indices
+ * into a single 32-bit word. The visual's color masks tell
+ * you where in the word the indices are supposed to be. The
+ * masks contain bits only where the index is found. By counting
+ * the leading zeros in the mask, we know how many bits to shift
+ * to the individual red, green, and blue values to form a pixel.
+ *
+ * Results:
+ * The number of the least significant bit.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShiftCount(mask)
+ register unsigned int mask;
+{
+ register int count;
+
+ for (count = 0; count < 32; count++) {
+ if (mask & 0x01) {
+ break;
+ }
+ mask >>= 1;
+ }
+ return count;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CountBits --
+ *
+ * Returns the number of bits set in the given mask.
+ *
+ * Reference: Graphics Gems Volume 2.
+ *
+ * Results:
+ * The number of bits to set in the mask.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CountBits(mask)
+ register unsigned long mask; /* 32 1-bit tallies */
+{
+ /* 16 2-bit tallies */
+ mask = (mask & 0x55555555) + ((mask >> 1) & (0x55555555));
+ /* 8 4-bit tallies */
+ mask = (mask & 0x33333333) + ((mask >> 2) & (0x33333333));
+ /* 4 8-bit tallies */
+ mask = (mask & 0x07070707) + ((mask >> 4) & (0x07070707));
+ /* 2 16-bit tallies */
+ mask = (mask & 0x000F000F) + ((mask >> 8) & (0x000F000F));
+ /* 1 32-bit tally */
+ mask = (mask & 0x0000001F) + ((mask >> 16) & (0x0000001F));
+ return mask;
+}
+
+static void
+ComputeMasks(visualPtr)
+ Visual *visualPtr;
+{
+ int count;
+
+ redMaskShift = ShiftCount((unsigned int)visualPtr->red_mask);
+ greenMaskShift = ShiftCount((unsigned int)visualPtr->green_mask);
+ blueMaskShift = ShiftCount((unsigned int)visualPtr->blue_mask);
+
+ redAdjust = greenAdjust = blueAdjust = 0;
+ count = CountBits((unsigned long)visualPtr->red_mask);
+ if (count < 8) {
+ redAdjust = 8 - count;
+ }
+ count = CountBits((unsigned long)visualPtr->green_mask);
+ if (count < 8) {
+ greenAdjust = 8 - count;
+ }
+ count = CountBits((unsigned long)visualPtr->blue_mask);
+ if (count < 8) {
+ blueAdjust = 8 - count;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TrueColorPixel --
+ *
+ * Computes a pixel index from the 3 component RGB values.
+ *
+ * Results:
+ * The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+TrueColorPixel(visualPtr, pixelPtr)
+ Visual *visualPtr;
+ Pix32 *pixelPtr;
+{
+ unsigned int red, green, blue;
+
+ /*
+ * The number of bits per color may be less than eight. For example,
+ * 15/16 bit displays (hi-color) use only 5 bits, 8-bit displays
+ * use 2 or 3 bits (don't ask me why you'd have an 8-bit TrueColor
+ * display). So shift off the least significant bits.
+ */
+ red = ((unsigned int)pixelPtr->Red >> redAdjust);
+ green = ((unsigned int)pixelPtr->Green >> greenAdjust);
+ blue = ((unsigned int)pixelPtr->Blue >> blueAdjust);
+
+ /* Shift each color into the proper location of the pixel index. */
+ red = (red << redMaskShift) & visualPtr->red_mask;
+ green = (green << greenMaskShift) & visualPtr->green_mask;
+ blue = (blue << blueMaskShift) & visualPtr->blue_mask;
+ return (red | green | blue);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DirectColorPixel --
+ *
+ * Translates the 3 component RGB values into a pixel index.
+ * This differs from TrueColor only in that it first translates
+ * the RGB values through a color table.
+ *
+ * Results:
+ * The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+DirectColorPixel(colorTabPtr, pixelPtr)
+ struct ColorTableStruct *colorTabPtr;
+ Pix32 *pixelPtr;
+{
+ unsigned int red, green, blue;
+
+ red = colorTabPtr->red[pixelPtr->Red];
+ green = colorTabPtr->green[pixelPtr->Green];
+ blue = colorTabPtr->blue[pixelPtr->Blue];
+ return (red | green | blue);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PseudoColorPixel --
+ *
+ * Translates the 3 component RGB values into a pixel index.
+ * This differs from TrueColor only in that it first translates
+ * the RGB values through a color table.
+ *
+ * Results:
+ * The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+PseudoColorPixel(pixelPtr, lut)
+ Pix32 *pixelPtr;
+ unsigned int *lut;
+{
+ int red, green, blue;
+ int pixel;
+
+ red = (pixelPtr->Red >> 3) + 1;
+ green = (pixelPtr->Green >> 3) + 1;
+ blue = (pixelPtr->Blue >> 3) + 1;
+ pixel = RGBIndex(red, green, blue);
+ return lut[pixel];
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPixmap --
+ *
+ * Converts a color image into a pixmap.
+ *
+ * Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ * The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorimageToPixmap(interp, tkwin, image, colorTablePtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Blt_Colorimage image;
+ ColorTable *colorTablePtr; /* Points to array of colormap indices */
+{
+ Display *display;
+ int width, height;
+ Pixmap pixmap;
+ GC pixmapGC;
+ Visual *visualPtr;
+ XImage *imagePtr;
+ int nPixels;
+
+ visualPtr = Tk_Visual(tkwin);
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+ display = Tk_Display(tkwin);
+
+ ComputeMasks(visualPtr);
+
+ *colorTablePtr = NULL;
+ imagePtr = XCreateImage(Tk_Display(tkwin), visualPtr, Tk_Depth(tkwin),
+ ZPixmap, 0, (char *)NULL, width, height, 32, 0);
+ assert(imagePtr);
+
+ nPixels = width * height;
+ imagePtr->data = Blt_Malloc(sizeof(Pix32) * nPixels);
+ assert(imagePtr->data);
+
+ imagePtr->byte_order = MSBFirst; /* Force the byte order */
+ imagePtr->bitmap_bit_order = imagePtr->byte_order;
+ imagePtr->bytes_per_line = width * sizeof(Pix32);
+
+ switch (visualPtr->class) {
+ case TrueColor:
+ {
+ register int x, y;
+ register Pix32 *srcPtr;
+ register char *destPtr;
+ unsigned int pixel;
+ int rowOffset;
+
+ /*
+ * Compute the colormap locations directly from pixel RGB values.
+ */
+ srcPtr = Blt_ColorimageBits(image);
+ rowOffset = 0;
+ for (y = 0; y < height; y++) {
+ destPtr = imagePtr->data + rowOffset;
+ for (x = 0; x < width; x++, srcPtr++) {
+ pixel = TrueColorPixel(visualPtr, srcPtr);
+ switch (imagePtr->bits_per_pixel) {
+ case 32:
+ *destPtr++ = (pixel >> 24) & 0xFF;
+ /*FALLTHRU*/
+ case 24:
+ *destPtr++ = (pixel >> 16) & 0xFF;
+ /*FALLTHRU*/
+ case 16:
+ *destPtr++ = (pixel >> 8) & 0xFF;
+ /*FALLTHRU*/
+ case 8:
+ *destPtr++ = pixel & 0xFF;
+ /*FALLTHRU*/
+ }
+ }
+ rowOffset += imagePtr->bytes_per_line;
+ }
+ }
+ break;
+
+ case DirectColor:
+ {
+ register int x, y;
+ register Pix32 *srcPtr;
+ register char *destPtr;
+ unsigned int pixel;
+ int rowOffset;
+ struct ColorTableStruct *colorTabPtr;
+
+ /* Build a color table first */
+ colorTabPtr = Blt_DirectColorTable(interp, tkwin, image);
+
+ /*
+ * Compute the colormap locations directly from pixel RGB values.
+ */
+ srcPtr = Blt_ColorimageBits(image);
+ rowOffset = 0;
+ for (y = 0; y < height; y++) {
+ destPtr = imagePtr->data + rowOffset;
+ for (x = 0; x < width; x++, srcPtr++) {
+ pixel = DirectColorPixel(colorTabPtr, srcPtr);
+ switch (imagePtr->bits_per_pixel) {
+ case 32:
+ *destPtr++ = (pixel >> 24) & 0xFF;
+ /*FALLTHRU*/
+ case 24:
+ *destPtr++ = (pixel >> 16) & 0xFF;
+ /*FALLTHRU*/
+ case 16:
+ *destPtr++ = (pixel >> 8) & 0xFF;
+ /*FALLTHRU*/
+ case 8:
+ *destPtr++ = pixel & 0xFF;
+ /*FALLTHRU*/
+ }
+ }
+ rowOffset += imagePtr->bytes_per_line;
+ }
+ *colorTablePtr = colorTabPtr;
+ }
+ break;
+
+ case GrayScale:
+ case StaticGray:
+ case PseudoColor:
+ case StaticColor:
+ {
+ register int x, y;
+ register Pix32 *srcPtr;
+ register char *destPtr;
+ unsigned int pixel;
+ int rowOffset;
+ struct ColorTableStruct *colorTabPtr;
+
+ colorTabPtr = Blt_PseudoColorTable(interp, tkwin, image);
+
+ srcPtr = Blt_ColorimageBits(image);
+ rowOffset = 0;
+ for (y = 0; y < height; y++) {
+ destPtr = imagePtr->data + rowOffset;
+ for (x = 0; x < width; x++, srcPtr++) {
+ pixel = PseudoColorPixel(srcPtr, colorTabPtr->lut);
+ switch (imagePtr->bits_per_pixel) {
+ case 32:
+ *destPtr++ = (pixel >> 24) & 0xFF;
+ /*FALLTHRU*/
+ case 24:
+ *destPtr++ = (pixel >> 16) & 0xFF;
+ /*FALLTHRU*/
+ case 16:
+ *destPtr++ = (pixel >> 8) & 0xFF;
+ /*FALLTHRU*/
+ case 8:
+ *destPtr++ = pixel & 0xFF;
+ /*FALLTHRU*/
+ }
+ }
+ rowOffset += imagePtr->bytes_per_line;
+ }
+ Blt_Free(colorTabPtr->lut);
+ *colorTablePtr = colorTabPtr;
+ }
+ break;
+ default:
+ return None; /* Bad or unknown visual class. */
+ }
+ pixmapGC = Tk_GetGC(tkwin, 0L, (XGCValues *)NULL);
+ pixmap = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height,
+ Tk_Depth(tkwin));
+ XPutImage(display, pixmap, pixmapGC, imagePtr, 0, 0, 0, 0, width, height);
+ XDestroyImage(imagePtr);
+ Tk_FreeGC(display, pixmapGC);
+ return pixmap;
+}
+
+/* ARGSUSED */
+static int
+XGetImageErrorProc(clientData, errEventPtr)
+ ClientData clientData;
+ XErrorEvent *errEventPtr;
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DrawableToColorimage --
+ *
+ * Takes a snapshot of an X drawable (pixmap or window) and
+ * converts it to a color image.
+ *
+ * The trick here is to efficiently convert the pixel values
+ * (indices into the color table) into RGB color values. In the
+ * days of 8-bit displays, it was simpler to get RGB values for
+ * all 256 indices into the colormap. Instead we'll build a
+ * hashtable of unique pixels and from that an array of pixels to
+ * pass to XQueryColors. For TrueColor visuals, we'll simple
+ * compute the colors from the pixel.
+ *
+ * [I don't know how much faster it would be to take advantage
+ * of all the different visual types. This pretty much depends
+ * on the size of the image and the number of colors it uses.]
+ *
+ * Results:
+ * Returns a color image of the drawable. If an error occurred,
+ * NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_DrawableToColorimage(tkwin, drawable, x, y, width, height, inputGamma)
+ Tk_Window tkwin;
+ Drawable drawable;
+ register int x, y; /* Offset of image from the drawable's
+ * origin. */
+ int width, height; /* Dimension of the image. Image must
+ * be completely contained by the
+ * drawable. */
+ double inputGamma;
+{
+ XImage *imagePtr;
+ Blt_Colorimage image;
+ register Pix32 *destPtr;
+ unsigned long pixel;
+ int result = TCL_OK;
+ Tk_ErrorHandler errHandler;
+ Visual *visualPtr;
+ unsigned char lut[256];
+
+ errHandler = Tk_CreateErrorHandler(Tk_Display(tkwin), BadMatch,
+ X_GetImage, -1, XGetImageErrorProc, &result);
+ imagePtr = XGetImage(Tk_Display(tkwin), drawable, x, y, width, height,
+ AllPlanes, ZPixmap);
+ Tk_DeleteErrorHandler(errHandler);
+ XSync(Tk_Display(tkwin), False);
+ if (result != TCL_OK) {
+ return NULL;
+ }
+
+ {
+ register int i;
+ double value;
+
+ for (i = 0; i < 256; i++) {
+ value = pow(i / 255.0, inputGamma) * 255.0 + 0.5;
+ lut[i] = (unsigned char)CLAMP(value);
+ }
+ }
+ /*
+ * First allocate a color image to hold the screen snapshot.
+ */
+ image = Blt_CreateColorimage(width, height);
+ visualPtr = Tk_Visual(tkwin);
+ if (visualPtr->class == TrueColor) {
+ unsigned int red, green, blue;
+ /*
+ * Directly compute the RGB color values from the pixel index
+ * rather than of going through XQueryColors.
+ */
+ ComputeMasks(visualPtr);
+ destPtr = Blt_ColorimageBits(image);
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ pixel = XGetPixel(imagePtr, x, y);
+
+ red = ((pixel & visualPtr->red_mask) >> redMaskShift) << redAdjust;
+ green = ((pixel & visualPtr->green_mask) >> greenMaskShift) << greenAdjust;
+ blue = ((pixel & visualPtr->blue_mask) >> blueMaskShift) << blueAdjust;
+
+ /*
+ * The number of bits per color in the pixel may be
+ * less than eight. For example, 15/16 bit displays
+ * (hi-color) use only 5 bits, 8-bit displays use 2 or
+ * 3 bits (don't ask me why you'd have an 8-bit
+ * TrueColor display). So shift back the least
+ * significant bits.
+ */
+ destPtr->Red = lut[red];
+ destPtr->Green = lut[green];
+ destPtr->Blue = lut[blue];
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ XDestroyImage(imagePtr);
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable pixelTable;
+ XColor *colorPtr, *colorArr;
+ Pix32 *endPtr;
+ int nPixels;
+ int nColors;
+ int isNew;
+
+ /*
+ * Fill the array with each pixel of the image. At the same time, build
+ * up a hashtable of the pixels used.
+ */
+ nPixels = width * height;
+ Blt_InitHashTable(&pixelTable, BLT_ONE_WORD_KEYS);
+ destPtr = Blt_ColorimageBits(image);
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ pixel = XGetPixel(imagePtr, x, y);
+ hPtr = Blt_CreateHashEntry(&pixelTable, (char *)pixel, &isNew);
+ if (isNew) {
+ Blt_SetHashValue(hPtr, (char *)pixel);
+ }
+ destPtr->value = pixel;
+ destPtr++;
+ }
+ }
+ XDestroyImage(imagePtr);
+
+ /*
+ * Convert the hashtable of pixels into an array of XColors so
+ * that we can call XQueryColors with it. XQueryColors will
+ * convert the pixels into their RGB values.
+ */
+ nColors = pixelTable.numEntries;
+ colorArr = Blt_Malloc(sizeof(XColor) * nColors);
+ assert(colorArr);
+
+ colorPtr = colorArr;
+ for (hPtr = Blt_FirstHashEntry(&pixelTable, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ colorPtr->pixel = (unsigned long)Blt_GetHashValue(hPtr);
+ Blt_SetHashValue(hPtr, (char *)colorPtr);
+ colorPtr++;
+ }
+ XQueryColors(Tk_Display(tkwin), Tk_Colormap(tkwin), colorArr, nColors);
+
+ /*
+ * Go again through the array of pixels, replacing each pixel
+ * of the image with its RGB value.
+ */
+ destPtr = Blt_ColorimageBits(image);
+ endPtr = destPtr + nPixels;
+ for (/* empty */; destPtr < endPtr; destPtr++) {
+ hPtr = Blt_FindHashEntry(&pixelTable, (char *)destPtr->value);
+ colorPtr = (XColor *)Blt_GetHashValue(hPtr);
+ destPtr->Red = lut[colorPtr->red >> 8];
+ destPtr->Green = lut[colorPtr->green >> 8];
+ destPtr->Blue = lut[colorPtr->blue >> 8];
+ destPtr->Alpha = (unsigned char)-1;
+ }
+ Blt_Free(colorArr);
+ Blt_DeleteHashTable(&pixelTable);
+ }
+ return image;
+}
+
+
+Pixmap
+Blt_PhotoImageMask(tkwin, src)
+ Tk_Window tkwin;
+ Tk_PhotoImageBlock src;
+{
+ Pixmap bitmap;
+ int arraySize, bytes_per_line;
+ int offset, count;
+ int value, bitMask;
+ register int x, y;
+ unsigned char *bits;
+ unsigned char *srcPtr;
+ unsigned char *destPtr;
+ unsigned long pixel;
+
+ bytes_per_line = (src.width + 7) / 8;
+ arraySize = src.height * bytes_per_line;
+ bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+ assert(bits);
+ destPtr = bits;
+ offset = count = 0;
+ for (y = 0; y < src.height; y++) {
+ value = 0, bitMask = 1;
+ srcPtr = src.pixelPtr + offset;
+ for (x = 0; x < src.width; /*empty*/ ) {
+ pixel = (srcPtr[src.offset[3]] != 0x00);
+ if (pixel) {
+ value |= bitMask;
+ } else {
+ count++; /* Count the number of transparent pixels. */
+ }
+ bitMask <<= 1;
+ x++;
+ if (!(x & 7)) {
+ *destPtr++ = (unsigned char)value;
+ value = 0, bitMask = 1;
+ }
+ srcPtr += src.pixelSize;
+ }
+ if (x & 7) {
+ *destPtr++ = (unsigned char)value;
+ }
+ offset += src.pitch;
+ }
+ if (count > 0) {
+ Tk_MakeWindowExist(tkwin);
+ bitmap = XCreateBitmapFromData(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ (char *)bits, (unsigned int)src.width, (unsigned int)src.height);
+ } else {
+ bitmap = None; /* Image is opaque. */
+ }
+ Blt_Free(bits);
+ return bitmap;
+}
+
+Pixmap
+Blt_ColorimageMask(tkwin, image)
+ Tk_Window tkwin;
+ Blt_Colorimage image;
+{
+ Pixmap bitmap;
+ int arraySize, bytes_per_line;
+ int count;
+ int value, bitMask;
+ register int x, y;
+ unsigned char *bits;
+ Pix32 *srcPtr;
+ unsigned char *destPtr;
+ unsigned long pixel;
+ int width, height;
+
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+ bytes_per_line = (width + 7) / 8;
+ arraySize = height * bytes_per_line;
+ bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+ assert(bits);
+ destPtr = bits;
+ count = 0;
+ srcPtr = Blt_ColorimageBits(image);
+ for (y = 0; y < height; y++) {
+ value = 0, bitMask = 1;
+ for (x = 0; x < width; /*empty*/ ) {
+ pixel = (srcPtr->Alpha != 0x00);
+ if (pixel) {
+ value |= bitMask;
+ } else {
+ count++; /* Count the number of transparent pixels. */
+ }
+ bitMask <<= 1;
+ x++;
+ if (!(x & 7)) {
+ *destPtr++ = (unsigned char)value;
+ value = 0, bitMask = 1;
+ }
+ srcPtr++;
+ }
+ if (x & 7) {
+ *destPtr++ = (unsigned char)value;
+ }
+ }
+ if (count > 0) {
+ Tk_MakeWindowExist(tkwin);
+ bitmap = XCreateBitmapFromData(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ (char *)bits, (unsigned int)width, (unsigned int)height);
+ } else {
+ bitmap = None; /* Image is opaque. */
+ }
+ Blt_Free(bits);
+ return bitmap;
+}
+
+#if HAVE_JPEGLIB_H
+
+#undef HAVE_STDLIB_H
+#undef EXTERN
+#ifdef WIN32
+#define XMD_H 1
+#endif
+#include "jpeglib.h"
+#include <setjmp.h>
+
+typedef struct {
+ struct jpeg_error_mgr pub; /* "public" fields */
+ jmp_buf jmpBuf;
+ Tcl_DString dString;
+} ReaderHandler;
+
+static void ErrorProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+static void MessageProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+static void
+ErrorProc(jpgPtr)
+ j_common_ptr jpgPtr;
+{
+ ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+
+ (*handlerPtr->pub.output_message) (jpgPtr);
+ longjmp(handlerPtr->jmpBuf, 1);
+}
+
+static void
+MessageProc(jpgPtr)
+ j_common_ptr jpgPtr;
+{
+ ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message and append it into the dynamic string. */
+ (*handlerPtr->pub.format_message) (jpgPtr, buffer);
+ Tcl_DStringAppend(&(handlerPtr->dString), " ", -1);
+ Tcl_DStringAppend(&(handlerPtr->dString), buffer, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToColorimage --
+ *
+ * Reads a JPEG file and converts it into a color image.
+ *
+ * Results:
+ * The color image is returned. If an error occured, such
+ * as the designated file could not be opened, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_JPEGToColorimage(interp, fileName)
+ Tcl_Interp *interp;
+ char *fileName;
+{
+ struct jpeg_decompress_struct jpg;
+ Blt_Colorimage image;
+ unsigned int imageWidth, imageHeight;
+ register Pix32 *destPtr;
+ ReaderHandler handler;
+ FILE *f;
+ JSAMPLE **readBuffer;
+ int row_stride;
+ register int i;
+ register JSAMPLE *bufPtr;
+
+ f = fopen(fileName, "rb");
+ if (f == NULL) {
+ Tcl_AppendResult(interp, "can't open \"", fileName, "\":",
+ Tcl_PosixError(interp), (char *)NULL);
+ return NULL;
+ }
+ image = NULL;
+
+ /* Step 1: allocate and initialize JPEG decompression object */
+
+ /* We set up the normal JPEG error routines, then override error_exit. */
+ jpg.dct_method = JDCT_IFAST;
+ jpg.err = jpeg_std_error(&handler.pub);
+ handler.pub.error_exit = ErrorProc;
+ handler.pub.output_message = MessageProc;
+
+ Tcl_DStringInit(&handler.dString);
+ Tcl_DStringAppend(&handler.dString, "error reading \"", -1);
+ Tcl_DStringAppend(&handler.dString, fileName, -1);
+ Tcl_DStringAppend(&handler.dString, "\": ", -1);
+
+ if (setjmp(handler.jmpBuf)) {
+ jpeg_destroy_decompress(&jpg);
+ fclose(f);
+ Tcl_DStringResult(interp, &(handler.dString));
+ return NULL;
+ }
+ jpeg_create_decompress(&jpg);
+ jpeg_stdio_src(&jpg, f);
+
+ jpeg_read_header(&jpg, TRUE); /* Step 3: read file parameters */
+
+ jpeg_start_decompress(&jpg); /* Step 5: Start decompressor */
+ imageWidth = jpg.output_width;
+ imageHeight = jpg.output_height;
+ if ((imageWidth < 1) || (imageHeight < 1)) {
+ Tcl_AppendResult(interp, "bad JPEG image size", (char *)NULL);
+ fclose(f);
+ return NULL;
+ }
+ /* JSAMPLEs per row in output buffer */
+ row_stride = imageWidth * jpg.output_components;
+
+ /* Make a one-row-high sample array that will go away when done
+ * with image */
+ readBuffer = (*jpg.mem->alloc_sarray) ((j_common_ptr)&jpg, JPOOL_IMAGE,
+ row_stride, 1);
+ image = Blt_CreateColorimage(imageWidth, imageHeight);
+ destPtr = Blt_ColorimageBits(image);
+
+ if (jpg.output_components == 1) {
+ while (jpg.output_scanline < imageHeight) {
+ jpeg_read_scanlines(&jpg, readBuffer, 1);
+ bufPtr = readBuffer[0];
+ for (i = 0; i < (int)imageWidth; i++) {
+ destPtr->Red = destPtr->Green = destPtr->Blue = *bufPtr++;
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else {
+ while (jpg.output_scanline < imageHeight) {
+ jpeg_read_scanlines(&jpg, readBuffer, 1);
+ bufPtr = readBuffer[0];
+ for (i = 0; i < (int)imageWidth; i++) {
+ destPtr->Red = *bufPtr++;
+ destPtr->Green = *bufPtr++;
+ destPtr->Blue = *bufPtr++;
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ }
+ jpeg_finish_decompress(&jpg); /* We can ignore the return value
+ * since suspension is not
+ * possible with the stdio data
+ * source. */
+ jpeg_destroy_decompress(&jpg);
+
+
+ /*
+ * After finish_decompress, we can close the input file. Here we
+ * postpone it until after no more JPEG errors are possible, so as
+ * to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume
+ * anything...)
+ */
+ fclose(f);
+
+ /*
+ * At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+ */
+ if (handler.pub.num_warnings > 0) {
+ Tcl_SetErrorCode(interp, "IMAGE", "JPEG",
+ Tcl_DStringValue(&(handler.dString)), (char *)NULL);
+ } else {
+ Tcl_SetErrorCode(interp, "NONE", (char *)NULL);
+ }
+ /*
+ * We're ready to call the Tk_Photo routines. They'll take the RGB
+ * array we've processed to build the Tk image of the JPEG.
+ */
+ Tcl_DStringFree(&(handler.dString));
+ return image;
+}
+
+#endif /* HAVE_JPEGLIB_H */
+
diff --git a/blt/src/bltUnixMain.c b/blt/src/bltUnixMain.c
new file mode 100644
index 00000000000..94ec10431ac
--- /dev/null
+++ b/blt/src/bltUnixMain.c
@@ -0,0 +1,174 @@
+/*
+ * bltUnixMain.c --
+ *
+ * Provides a default version of the Tcl_AppInit procedure for
+ * use in wish and similar Tk-based applications.
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Copyright 1991-1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include <tcl.h>
+#ifndef TCL_ONLY
+#include <tk.h>
+#endif
+
+#if HAVE_ITCL_H
+#include "itcl.h"
+#endif
+#if HAVE_ITK_H
+#include "itk.h"
+#endif
+
+extern int Blt_Init _ANSI_ARGS_((Tcl_Interp *interp));
+extern int Blt_SafeInit _ANSI_ARGS_((Tcl_Interp *interp));
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+#ifdef NEED_MATHERR
+extern int matherr();
+int *tclDummyMathPtr = (int *)matherr;
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tk_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Vector of command-line arguments. */
+{
+
+#ifdef TCL_ONLY
+ Tcl_Main(argc, argv, Tcl_AppInit);
+#else
+ Tk_Main(argc, argv, Tcl_AppInit);
+#endif
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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. */
+{
+#ifdef TCLLIBPATH
+ /*
+ * It seems that some distributions of Tcl don't compile-in a
+ * default location of the library. This causes Tcl_Init to fail
+ * if bltwish and bltsh are moved to another directory. The
+ * workaround is to set the magic variable "tclDefaultLibrary".
+ */
+ Tcl_SetVar(interp, "tclDefaultLibrary", TCLLIBPATH, TCL_GLOBAL_ONLY);
+#endif
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+#ifndef TCL_ONLY
+ if (Tk_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+#endif
+ /*
+ * Call the init procedures for included packages. Each call should
+ * look like this:
+ *
+ * if (Mod_Init(interp) == TCL_ERROR) {
+ * return TCL_ERROR;
+ * }
+ *
+ * where "Mod" is the name of the module.
+ */
+ if (Blt_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "BLT", Blt_Init, Blt_SafeInit);
+ /*
+
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ */
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
diff --git a/blt/src/bltUnixPipe.c b/blt/src/bltUnixPipe.c
new file mode 100644
index 00000000000..7eea26f97d1
--- /dev/null
+++ b/blt/src/bltUnixPipe.c
@@ -0,0 +1,1071 @@
+/*
+ * bltUnixPipe.c --
+ *
+ * Originally taken from tclPipe.c and tclUnixPipe.c in the Tcl
+ * distribution, implements the former Tcl_CreatePipeline API.
+ * This file contains the generic portion of the command channel
+ * driver as well as various utility routines used in managing
+ * subprocesses.
+ *
+ * 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.
+ *
+ */
+
+#include "bltInt.h"
+#include <fcntl.h>
+#include <signal.h>
+
+#include "bltWait.h"
+
+#if (TCL_MAJOR_VERSION == 7)
+typedef pid_t Tcl_Pid;
+
+#define FILEHANDLER_USES_TCLFILES 1
+
+static int
+Tcl_GetChannelHandle(channel, direction, clientDataPtr)
+ Tcl_Channel channel;
+ int direction;
+ ClientData *clientDataPtr;
+{
+ Tcl_File file;
+
+ file = Tcl_GetChannelFile(channel, direction);
+ if (file == NULL) {
+ return TCL_ERROR;
+ }
+ *clientDataPtr = (ClientData)Tcl_GetFileInfo(file, NULL);
+ return TCL_OK;
+}
+
+#else
+typedef int Tcl_File;
+#endif /* TCL_MAJOR_VERSION == 7 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OpenFile --
+ *
+ * Open a file for use in a pipeline.
+ *
+ * Results:
+ * Returns a new TclFile handle or NULL on failure.
+ *
+ * Side effects:
+ * May cause a file to be created on the file system.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+OpenFile(fname, mode)
+ char *fname; /* The name of the file to open. */
+ int mode; /* In what mode to open the file? */
+{
+ int fd;
+
+ fd = open(fname, mode, 0666);
+ if (fd != -1) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ /*
+ * If the file is being opened for writing, seek to the end
+ * so we can append to any data already in the file.
+ */
+
+ if (mode & O_WRONLY) {
+ lseek(fd, 0, SEEK_END);
+ }
+ return fd;
+ }
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateTempFile --
+ *
+ * This function creates a temporary file initialized with an
+ * optional string, and returns a file handle with the file pointer
+ * at the beginning of the file.
+ *
+ * Results:
+ * A handle to a file.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CreateTempFile(contents)
+ char *contents; /* String to write into temp file, or NULL. */
+{
+ char fileName[L_tmpnam];
+ int fd;
+ size_t length = (contents == NULL) ? 0 : strlen(contents);
+
+ mkstemp(fileName);
+ fd = OpenFile(fileName, O_RDWR | O_CREAT | O_TRUNC);
+ unlink(fileName);
+
+ if ((fd >= 0) && (length > 0)) {
+ for (;;) {
+ if (write(fd, contents, length) != -1) {
+ break;
+ } else if (errno != EINTR) {
+ close(fd);
+ return -1;
+ }
+ }
+ lseek(fd, 0, SEEK_SET);
+ }
+ return fd;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreatePipe --
+ *
+ * Creates a pipe - simply calls the pipe() function.
+ *
+ * Results:
+ * Returns 1 on success, 0 on failure.
+ *
+ * Side effects:
+ * Creates a pipe.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CreatePipe(inFilePtr, outFilePtr)
+ int *inFilePtr; /* (out) Descriptor for read side of pipe. */
+ int *outFilePtr; /* (out) Descriptor for write side of pipe. */
+{
+ int pipeIds[2];
+
+ if (pipe(pipeIds) != 0) {
+ return 0;
+ }
+ fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
+ fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
+
+ *inFilePtr = pipeIds[0];
+ *outFilePtr = pipeIds[1];
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CloseFile --
+ *
+ * Implements a mechanism to close a UNIX file.
+ *
+ * Results:
+ * Returns 0 on success, or -1 on error, setting errno.
+ *
+ * Side effects:
+ * The file is closed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CloseFile(fd)
+ int fd; /* File descriptor to be closed. */
+{
+ if ((fd == 0) || (fd == 1) || (fd == 2)) {
+ return 0; /* Don't close stdin, stdout or stderr. */
+ }
+#if (TCL_MAJOR_VERSION > 7)
+ Tcl_DeleteFileHandler(fd);
+#endif
+ return close(fd);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestoreSignals --
+ *
+ * This procedure is invoked in a forked child process just before
+ * exec-ing a new program to restore all signals to their default
+ * settings.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Signal settings get changed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RestoreSignals()
+{
+#ifdef SIGABRT
+ signal(SIGABRT, SIG_DFL);
+#endif
+#ifdef SIGALRM
+ signal(SIGALRM, SIG_DFL);
+#endif
+#ifdef SIGFPE
+ signal(SIGFPE, SIG_DFL);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_DFL);
+#endif
+#ifdef SIGILL
+ signal(SIGILL, SIG_DFL);
+#endif
+#ifdef SIGINT
+ signal(SIGINT, SIG_DFL);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_DFL);
+#endif
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+#endif
+#ifdef SIGSEGV
+ signal(SIGSEGV, SIG_DFL);
+#endif
+#ifdef SIGTERM
+ signal(SIGTERM, SIG_DFL);
+#endif
+#ifdef SIGUSR1
+ signal(SIGUSR1, SIG_DFL);
+#endif
+#ifdef SIGUSR2
+ signal(SIGUSR2, SIG_DFL);
+#endif
+#ifdef SIGCHLD
+ signal(SIGCHLD, SIG_DFL);
+#endif
+#ifdef SIGCONT
+ signal(SIGCONT, SIG_DFL);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_DFL);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_DFL);
+#endif
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_DFL);
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetupStdFile --
+ *
+ * Set up stdio file handles for the child process, using the
+ * current standard channels if no other files are specified.
+ * If no standard channel is defined, or if no file is associated
+ * with the channel, then the corresponding standard fd is closed.
+ *
+ * Results:
+ * Returns 1 on success, or 0 on failure.
+ *
+ * Side effects:
+ * Replaces stdio fds.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetupStdFile(fd, type)
+ int fd; /* File descriptor to dup, or -1. */
+ int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
+{
+ int targetFd = 0; /* Initializations here needed only to */
+ int direction = 0; /* prevent warnings about using uninitialized
+ * variables. */
+
+ switch (type) {
+ case TCL_STDIN:
+ targetFd = 0;
+ direction = TCL_READABLE;
+ break;
+ case TCL_STDOUT:
+ targetFd = 1;
+ direction = TCL_WRITABLE;
+ break;
+ case TCL_STDERR:
+ targetFd = 2;
+ direction = TCL_WRITABLE;
+ break;
+ }
+ if (fd < 0) {
+ Tcl_Channel channel;
+
+ channel = Tcl_GetStdChannel(type);
+ if (channel) {
+ Tcl_GetChannelHandle(channel, direction, (ClientData *)&fd);
+ }
+ }
+ if (fd >= 0) {
+ if (fd != targetFd) {
+ if (dup2(fd, targetFd) == -1) {
+ return 0;
+ }
+ /*
+ * Must clear the close-on-exec flag for the target FD, since
+ * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on
+ * the target FD.
+ */
+
+ fcntl(targetFd, F_SETFD, 0);
+ } else {
+ /*
+ * Since we aren't dup'ing the file, we need to explicitly clear
+ * the close-on-exec flag.
+ */
+ fcntl(fd, F_SETFD, 0);
+ }
+ } else {
+ close(targetFd);
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateProcess --
+ *
+ * Create a child process that has the specified files as its
+ * standard input, output, and error. The child process runs
+ * asynchronously and runs with the same environment variables
+ * as the creating process.
+ *
+ * The path is searched to find the specified executable.
+ *
+ * Results:
+ * The return value is TCL_ERROR and an error message is left in
+ * interp->result if there was a problem creating the child
+ * process. Otherwise, the return value is TCL_OK and *pidPtr is
+ * filled with the process id of the child process.
+ *
+ * Side effects:
+ * A process is created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* ARGSUSED */
+static int
+CreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, pidPtr)
+ Tcl_Interp *interp; /* Interpreter in which to leave errors that
+ * occurred when creating the child process.
+ * Error messages from the child process
+ * itself are sent to errorFile. */
+ int argc; /* Number of arguments in following array. */
+ char **argv; /* Array of argument strings. argv[0]
+ * contains the name of the executable
+ * converted to native format (using the
+ * Tcl_TranslateFileName call). Additional
+ * arguments have not been converted. */
+ int inputFile; /* If non-NULL, gives the file to use as
+ * input for the child process. If inputFile
+ * file is not readable or is NULL, the child
+ * will receive no standard input. */
+ int outputFile; /* If non-NULL, gives the file that
+ * receives output from the child process. If
+ * outputFile file is not writeable or is
+ * NULL, output from the child will be
+ * discarded. */
+ int errorFile; /* If non-NULL, gives the file that
+ * receives errors from the child process. If
+ * errorFile file is not writeable or is NULL,
+ * errors from the child will be discarded.
+ * errorFile may be the same as outputFile. */
+ int *pidPtr; /* If this procedure is successful, pidPtr
+ * is filled with the process id of the child
+ * process. */
+{
+ int errPipeIn, errPipeOut;
+ int joinThisError, count, status, fd;
+ char errSpace[200];
+ int pid;
+
+ errPipeIn = errPipeOut = -1;
+ pid = -1;
+
+ /*
+ * Create a pipe that the child can use to return error
+ * information if anything goes wrong.
+ */
+
+ if (CreatePipe(&errPipeIn, &errPipeOut) == 0) {
+ Tcl_AppendResult(interp, "can't create pipe: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ joinThisError = (errorFile == outputFile);
+ pid = fork();
+ if (pid == 0) {
+ fd = errPipeOut;
+
+ /*
+ * Set up stdio file handles for the child process.
+ */
+
+ if (!SetupStdFile(inputFile, TCL_STDIN) ||
+ !SetupStdFile(outputFile, TCL_STDOUT) ||
+ (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR)) ||
+ (joinThisError &&
+ ((dup2(1, 2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) {
+ sprintf(errSpace, "%dforked process can't set up input/output: ",
+ errno);
+ write(fd, errSpace, (size_t) strlen(errSpace));
+ _exit(1);
+ }
+ /*
+ * Close the input side of the error pipe.
+ */
+
+ RestoreSignals();
+ execvp(argv[0], &argv[0]);
+ sprintf(errSpace, "%dcan't execute \"%.150s\": ", errno, argv[0]);
+ write(fd, errSpace, (size_t) strlen(errSpace));
+ _exit(1);
+ }
+ if (pid == -1) {
+ Tcl_AppendResult(interp, "can't fork child process: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ /*
+ * Read back from the error pipe to see if the child started
+ * up OK. The info in the pipe (if any) consists of a decimal
+ * errno value followed by an error message.
+ */
+
+ CloseFile(errPipeOut);
+ errPipeOut = -1;
+
+ fd = errPipeIn;
+ count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
+ if (count > 0) {
+ char *end;
+
+ errSpace[count] = 0;
+ errno = strtol(errSpace, &end, 10);
+ Tcl_AppendResult(interp, end, Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ CloseFile(errPipeIn);
+ *pidPtr = pid;
+ return TCL_OK;
+
+ error:
+ if (pid != -1) {
+ /*
+ * Reap the child process now if an error occurred during its
+ * startup.
+ */
+ Tcl_WaitPid((Tcl_Pid)pid, &status, WNOHANG);
+ }
+ if (errPipeIn >= 0) {
+ CloseFile(errPipeIn);
+ }
+ if (errPipeOut >= 0) {
+ CloseFile(errPipeOut);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileForRedirect --
+ *
+ * This procedure does much of the work of parsing redirection
+ * operators. It handles "@" if specified and allowed, and a file
+ * name, and opens the file if necessary.
+ *
+ * Results:
+ * The return value is the descriptor number for the file. If an
+ * error occurs then NULL is returned and an error message is left
+ * in interp->result. Several arguments are side-effected; see
+ * the argument list below for details.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr)
+ Tcl_Interp *interp; /* Intepreter to use for error reporting. */
+ char *spec; /* Points to character just after
+ * redirection character. */
+ char *arg; /* Pointer to entire argument containing
+ * spec: used for error reporting. */
+ int atOK; /* Non-zero means that '@' notation can be
+ * used to specify a channel, zero means that
+ * it isn't. */
+ char *nextArg; /* Next argument in argc/argv array, if needed
+ * for file name or channel name. May be
+ * NULL. */
+ int flags; /* Flags to use for opening file or to
+ * specify mode for channel. */
+ int *skipPtr; /* Filled with 1 if redirection target was
+ * in spec, 2 if it was in nextArg. */
+ int *closePtr; /* Filled with one if the caller should
+ * close the file when done with it, zero
+ * otherwise. */
+{
+ int writing = (flags & O_WRONLY);
+ Tcl_Channel chan;
+ int fd;
+ int direction;
+
+ *skipPtr = 1;
+ if ((atOK != 0) && (*spec == '@')) {
+ spec++;
+ if (*spec == '\0') {
+ spec = nextArg;
+ if (spec == NULL) {
+ goto badLastArg;
+ }
+ *skipPtr = 2;
+ }
+ chan = Tcl_GetChannel(interp, spec, NULL);
+ if (chan == NULL) {
+ return -1;
+ }
+ direction = (writing) ? TCL_WRITABLE : TCL_READABLE;
+ if (Tcl_GetChannelHandle(chan, direction, (ClientData *)&fd) != TCL_OK) {
+ fd = -1;
+ }
+ if (fd < 0) {
+ Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
+ "\" wasn't opened for ",
+ ((writing) ? "writing" : "reading"), (char *)NULL);
+ return -1;
+ }
+ if (writing) {
+ /*
+ * Be sure to flush output to the file, so that anything
+ * written by the child appears after stuff we've already
+ * written.
+ */
+ Tcl_Flush(chan);
+ }
+ } else {
+ char *name;
+ Tcl_DString nameString;
+
+ if (*spec == '\0') {
+ spec = nextArg;
+ if (spec == NULL) {
+ goto badLastArg;
+ }
+ *skipPtr = 2;
+ }
+ name = Tcl_TranslateFileName(interp, spec, &nameString);
+
+ if (name != NULL) {
+ fd = OpenFile(name, flags);
+ } else {
+ fd = -1;
+ }
+ Tcl_DStringFree(&nameString);
+ if (fd < 0) {
+ Tcl_AppendResult(interp, "can't ",
+ ((writing) ? "write" : "read"), " file \"", spec, "\": ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return -1;
+ }
+ *closePtr = 1;
+ }
+ return fd;
+
+ badLastArg:
+ Tcl_AppendResult(interp, "can't specify \"", arg,
+ "\" as last word in command", (char *)NULL);
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreatePipeline --
+ *
+ * Given an argc/argv array, instantiate a pipeline of processes
+ * as described by the argv.
+ *
+ * Results:
+ * The return value is a count of the number of new processes
+ * created, or -1 if an error occurred while creating the pipeline.
+ * *pidArrayPtr is filled in with the address of a dynamically
+ * allocated array giving the ids of all of the processes. It
+ * is up to the caller to free this array when it isn't needed
+ * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
+ * with the file id for the input pipe for the pipeline (if any):
+ * the caller must eventually close this file. If outPipePtr
+ * isn't NULL, then *outPipePtr is filled in with the file id
+ * for the output pipe from the pipeline: the caller must close
+ * this file. If errPipePtr isn't NULL, then *errPipePtr is filled
+ * with a file id that may be used to read error output after the
+ * pipeline completes.
+ *
+ * Side effects:
+ * Processes and pipes are created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Blt_CreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
+ outPipePtr, errPipePtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ int argc; /* Number of entries in argv. */
+ char **argv; /* Array of strings describing commands in
+ * pipeline plus I/O redirection with <,
+ * <<, >, etc. Argv[argc] must be NULL. */
+ int **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with
+ * address of array of pids for processes
+ * in pipeline (first pid is first process
+ * in pipeline). */
+ int *inPipePtr; /* If non-NULL, input to the pipeline comes
+ * from a pipe (unless overridden by
+ * redirection in the command). The file
+ * id with which to write to this pipe is
+ * stored at *inPipePtr. NULL means command
+ * specified its own input source. */
+ int *outPipePtr; /* If non-NULL, output to the pipeline goes
+ * to a pipe, unless overriden by redirection
+ * in the command. The file id with which to
+ * read frome this pipe is stored at
+ * *outPipePtr. NULL means command specified
+ * its own output sink. */
+ int *errPipePtr; /* If non-NULL, all stderr output from the
+ * pipeline will go to a temporary file
+ * created here, and a descriptor to read
+ * the file will be left at *errPipePtr.
+ * The file will be removed already, so
+ * closing this descriptor will be the end
+ * of the file. If this is NULL, then
+ * all stderr output goes to our stderr.
+ * If the pipeline specifies redirection
+ * then the file will still be created
+ * but it will never get any data. */
+{
+ int *pidPtr = NULL; /* Points to malloc-ed array holding all
+ * the pids of child processes. */
+ int nPids; /* Actual number of processes that exist
+ * at *pidPtr right now. */
+ int cmdCount; /* Count of number of distinct commands
+ * found in argc/argv. */
+ char *inputLiteral = NULL; /* If non-null, then this points to a
+ * string containing input data (specified
+ * via <<) to be piped to the first process
+ * in the pipeline. */
+ int inputFd = -1; /* If != NULL, gives file to use as input for
+ * first process in pipeline (specified via <
+ * or <@). */
+ int inputClose = 0; /* If non-zero, then inputFd should be
+ * closed when cleaning up. */
+ int outputFd = -1; /* Writable file for output from last command
+ * in pipeline (could be file or pipe). NULL
+ * means use stdout. */
+ int outputClose = 0; /* If non-zero, then outputFd should be
+ * closed when cleaning up. */
+ int errorFd = -1; /* Writable file for error output from all
+ * commands in pipeline. NULL means use
+ * stderr. */
+ int errorClose = 0; /* If non-zero, then errorFd should be
+ * closed when cleaning up. */
+ char *p;
+ int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;
+ Tcl_DString execBuffer;
+ int pipeIn;
+ int curInFd, curOutFd, curErrFd;
+
+ if (inPipePtr != NULL) {
+ *inPipePtr = -1;
+ }
+ if (outPipePtr != NULL) {
+ *outPipePtr = -1;
+ }
+ if (errPipePtr != NULL) {
+ *errPipePtr = -1;
+ }
+ Tcl_DStringInit(&execBuffer);
+
+ pipeIn = curInFd = curOutFd = -1;
+ nPids = 0;
+
+ /*
+ * First, scan through all the arguments to figure out the structure
+ * of the pipeline. Process all of the input and output redirection
+ * arguments and remove them from the argument list in the pipeline.
+ * Count the number of distinct processes (it's the number of "|"
+ * arguments plus one) but don't remove the "|" arguments because
+ * they'll be used in the second pass to seperate the individual
+ * child processes. Cannot start the child processes in this pass
+ * because the redirection symbols may appear anywhere in the
+ * command line -- e.g., the '<' that specifies the input to the
+ * entire pipe may appear at the very end of the argument list.
+ */
+
+ lastBar = -1;
+ cmdCount = 1;
+ for (i = 0; i < argc; i++) {
+ skip = 0;
+ p = argv[i];
+ switch (*p++) {
+ case '|':
+ if (*p == '&') {
+ p++;
+ }
+ if (*p == '\0') {
+ if ((i == (lastBar + 1)) || (i == (argc - 1))) {
+ Tcl_AppendResult(interp,
+ "illegal use of | or |& in command",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ lastBar = i;
+ cmdCount++;
+ break;
+
+ case '<':
+ if (inputClose != 0) {
+ inputClose = 0;
+ CloseFile(inputFd);
+ }
+ if (*p == '<') {
+ inputFd = -1;
+ inputLiteral = p + 1;
+ skip = 1;
+ if (*inputLiteral == '\0') {
+ inputLiteral = argv[i + 1];
+ if (inputLiteral == NULL) {
+ Tcl_AppendResult(interp, "can't specify \"", argv[i],
+ "\" as last word in command", (char *)NULL);
+ goto error;
+ }
+ skip = 2;
+ }
+ } else {
+ inputLiteral = NULL;
+ inputFd = FileForRedirect(interp, p, 1, argv[i], argv[i + 1],
+ O_RDONLY, &skip, &inputClose);
+ if (inputFd < 0) {
+ goto error;
+ }
+ }
+ break;
+
+ case '>':
+ atOK = 1;
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ errorToOutput = 0;
+ if (*p == '>') {
+ p++;
+ atOK = 0;
+ flags = O_WRONLY | O_CREAT;
+ }
+ if (*p == '&') {
+ if (errorClose != 0) {
+ errorClose = 0;
+ CloseFile(errorFd);
+ }
+ errorToOutput = 1;
+ p++;
+ }
+ if (outputClose != 0) {
+ outputClose = 0;
+ CloseFile(outputFd);
+ }
+ outputFd = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1],
+ flags, &skip, &outputClose);
+ if (outputFd < 0) {
+ goto error;
+ }
+ if (errorToOutput) {
+ errorClose = 0;
+ errorFd = outputFd;
+ }
+ break;
+
+ case '2':
+ if (*p != '>') {
+ break;
+ }
+ p++;
+ atOK = 1;
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ if (*p == '>') {
+ p++;
+ atOK = 0;
+ flags = O_WRONLY | O_CREAT;
+ }
+ if (errorClose != 0) {
+ errorClose = 0;
+ CloseFile(errorFd);
+ }
+ errorFd = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1],
+ flags, &skip, &errorClose);
+ if (errorFd < 0) {
+ goto error;
+ }
+ break;
+ }
+
+ if (skip != 0) {
+ for (j = i + skip; j < argc; j++) {
+ argv[j - skip] = argv[j];
+ }
+ argc -= skip;
+ i -= 1;
+ }
+ }
+
+ if (inputFd == -1) {
+ if (inputLiteral != NULL) {
+ /*
+ * The input for the first process is immediate data coming from
+ * Tcl. Create a temporary file for it and put the data into the
+ * file.
+ */
+ inputFd = CreateTempFile(inputLiteral);
+ if (inputFd < 0) {
+ Tcl_AppendResult(interp,
+ "can't create input file for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ inputClose = 1;
+ } else if (inPipePtr != NULL) {
+ /*
+ * The input for the first process in the pipeline is to
+ * come from a pipe that can be written from by the caller.
+ */
+
+ if (CreatePipe(&inputFd, inPipePtr) == 0) {
+ Tcl_AppendResult(interp,
+ "can't create input pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ inputClose = 1;
+ } else {
+ /*
+ * The input for the first process comes from stdin.
+ */
+
+ inputFd = 0;
+ }
+ }
+ if (outputFd == -1) {
+ if (outPipePtr != NULL) {
+ /*
+ * Output from the last process in the pipeline is to go to a
+ * pipe that can be read by the caller.
+ */
+
+ if (CreatePipe(outPipePtr, &outputFd) == 0) {
+ Tcl_AppendResult(interp,
+ "can't create output pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ outputClose = 1;
+ } else {
+ /*
+ * The output for the last process goes to stdout.
+ */
+ outputFd = 1;
+ }
+ }
+ if (errorFd == -1) {
+ if (errPipePtr != NULL) {
+ /*
+ * Stderr from the last process in the pipeline is to go to a
+ * pipe that can be read by the caller.
+ */
+ if (CreatePipe(errPipePtr, &errorFd) == 0) {
+ Tcl_AppendResult(interp,
+ "can't create error pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ errorClose = 1;
+ } else {
+ /*
+ * Errors from the pipeline go to stderr.
+ */
+ errorFd = 2;
+ }
+ }
+ /*
+ * Scan through the argc array, creating a process for each
+ * group of arguments between the "|" characters.
+ */
+
+ Tcl_ReapDetachedProcs();
+ pidPtr = Blt_Malloc((unsigned)(cmdCount * sizeof(int)));
+
+ curInFd = inputFd;
+
+ lastArg = 0; /* Suppress compiler warning */
+ for (i = 0; i < argc; i = lastArg + 1) {
+ int joinThisError;
+ int pid;
+
+ /*
+ * Convert the program name into native form.
+ */
+
+ argv[i] = Tcl_TranslateFileName(interp, argv[i], &execBuffer);
+ if (argv[i] == NULL) {
+ goto error;
+ }
+ /*
+ * Find the end of the current segment of the pipeline.
+ */
+ joinThisError = 0;
+ for (lastArg = i; lastArg < argc; lastArg++) {
+ if (argv[lastArg][0] == '|') {
+ if (argv[lastArg][1] == '\0') {
+ break;
+ }
+ if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
+ joinThisError = 1;
+ break;
+ }
+ }
+ }
+ argv[lastArg] = NULL;
+
+ /*
+ * If this is the last segment, use the specified outputFile.
+ * Otherwise create an intermediate pipe. pipeIn will become the
+ * curInFile for the next segment of the pipe.
+ */
+
+ if (lastArg == argc) {
+ curOutFd = outputFd;
+ } else {
+ if (CreatePipe(&pipeIn, &curOutFd) == 0) {
+ Tcl_AppendResult(interp, "can't create pipe: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ }
+
+ if (joinThisError != 0) {
+ curErrFd = curOutFd;
+ } else {
+ curErrFd = errorFd;
+ }
+
+ if (CreateProcess(interp, lastArg - i, argv + i,
+ curInFd, curOutFd, curErrFd, &pid) != TCL_OK) {
+ goto error;
+ }
+ Tcl_DStringFree(&execBuffer);
+
+ pidPtr[nPids] = pid;
+ nPids++;
+
+
+ /*
+ * Close off our copies of file descriptors that were set up for
+ * this child, then set up the input for the next child.
+ */
+
+ if ((curInFd >= 0) && (curInFd != inputFd)) {
+ CloseFile(curInFd);
+ }
+ curInFd = pipeIn;
+ pipeIn = -1;
+
+ if ((curOutFd >= 0) && (curOutFd != outputFd)) {
+ CloseFile(curOutFd);
+ }
+ curOutFd = -1;
+ }
+
+ *pidArrayPtr = pidPtr;
+
+ /*
+ * All done. Cleanup open files lying around and then return.
+ */
+
+ cleanup:
+ Tcl_DStringFree(&execBuffer);
+
+ if (inputClose) {
+ CloseFile(inputFd);
+ }
+ if (outputClose) {
+ CloseFile(outputFd);
+ }
+ if (errorClose) {
+ CloseFile(errorFd);
+ }
+ return nPids;
+
+ /*
+ * An error occurred. There could have been extra files open, such
+ * as pipes between children. Clean them all up. Detach any child
+ * processes that have been created.
+ */
+
+ error:
+ if (pipeIn >= 0) {
+ CloseFile(pipeIn);
+ }
+ if ((curOutFd >= 0) && (curOutFd != outputFd)) {
+ CloseFile(curOutFd);
+ }
+ if ((curInFd >= 0) && (curInFd != inputFd)) {
+ CloseFile(curInFd);
+ }
+ if ((inPipePtr != NULL) && (*inPipePtr >= 0)) {
+ CloseFile(*inPipePtr);
+ *inPipePtr = -1;
+ }
+ if ((outPipePtr != NULL) && (*outPipePtr >= 0)) {
+ CloseFile(*outPipePtr);
+ *outPipePtr = -1;
+ }
+ if ((errPipePtr != NULL) && (*errPipePtr >= 0)) {
+ CloseFile(*errPipePtr);
+ *errPipePtr = -1;
+ }
+ if (pidPtr != NULL) {
+ for (i = 0; i < nPids; i++) {
+ if (pidPtr[i] != -1) {
+#if (TCL_MAJOR_VERSION == 7)
+ Tcl_DetachPids(1, &pidPtr[i]);
+#else
+ Tcl_DetachPids(1, (Tcl_Pid *)&pidPtr[i]);
+#endif
+ }
+ }
+ Blt_Free(pidPtr);
+ }
+ nPids = -1;
+ goto cleanup;
+}
diff --git a/blt/src/bltUtil.c b/blt/src/bltUtil.c
new file mode 100644
index 00000000000..deef0423632
--- /dev/null
+++ b/blt/src/bltUtil.c
@@ -0,0 +1,1178 @@
+/*
+ * bltUtil.c --
+ *
+ * This module implements utility procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <bltHash.h>
+
+#ifndef HAVE_STRTOLOWER
+void
+strtolower(s)
+ register char *s;
+{
+ while (*s != '\0') {
+ *s = tolower(UCHAR(*s));
+ s++;
+ }
+}
+#endif /* !HAVE_STRTOLOWER */
+
+#ifndef HAVE_STRCASECMP
+
+static unsigned char caseTable[] =
+{
+ (unsigned char)'\000', (unsigned char)'\001',
+ (unsigned char)'\002', (unsigned char)'\003',
+ (unsigned char)'\004', (unsigned char)'\005',
+ (unsigned char)'\006', (unsigned char)'\007',
+ (unsigned char)'\010', (unsigned char)'\011',
+ (unsigned char)'\012', (unsigned char)'\013',
+ (unsigned char)'\014', (unsigned char)'\015',
+ (unsigned char)'\016', (unsigned char)'\017',
+ (unsigned char)'\020', (unsigned char)'\021',
+ (unsigned char)'\022', (unsigned char)'\023',
+ (unsigned char)'\024', (unsigned char)'\025',
+ (unsigned char)'\026', (unsigned char)'\027',
+ (unsigned char)'\030', (unsigned char)'\031',
+ (unsigned char)'\032', (unsigned char)'\033',
+ (unsigned char)'\034', (unsigned char)'\035',
+ (unsigned char)'\036', (unsigned char)'\037',
+ (unsigned char)'\040', (unsigned char)'\041',
+ (unsigned char)'\042', (unsigned char)'\043',
+ (unsigned char)'\044', (unsigned char)'\045',
+ (unsigned char)'\046', (unsigned char)'\047',
+ (unsigned char)'\050', (unsigned char)'\051',
+ (unsigned char)'\052', (unsigned char)'\053',
+ (unsigned char)'\054', (unsigned char)'\055',
+ (unsigned char)'\056', (unsigned char)'\057',
+ (unsigned char)'\060', (unsigned char)'\061',
+ (unsigned char)'\062', (unsigned char)'\063',
+ (unsigned char)'\064', (unsigned char)'\065',
+ (unsigned char)'\066', (unsigned char)'\067',
+ (unsigned char)'\070', (unsigned char)'\071',
+ (unsigned char)'\072', (unsigned char)'\073',
+ (unsigned char)'\074', (unsigned char)'\075',
+ (unsigned char)'\076', (unsigned char)'\077',
+ (unsigned char)'\100', (unsigned char)'\141',
+ (unsigned char)'\142', (unsigned char)'\143',
+ (unsigned char)'\144', (unsigned char)'\145',
+ (unsigned char)'\146', (unsigned char)'\147',
+ (unsigned char)'\150', (unsigned char)'\151',
+ (unsigned char)'\152', (unsigned char)'\153',
+ (unsigned char)'\154', (unsigned char)'\155',
+ (unsigned char)'\156', (unsigned char)'\157',
+ (unsigned char)'\160', (unsigned char)'\161',
+ (unsigned char)'\162', (unsigned char)'\163',
+ (unsigned char)'\164', (unsigned char)'\165',
+ (unsigned char)'\166', (unsigned char)'\167',
+ (unsigned char)'\170', (unsigned char)'\171',
+ (unsigned char)'\172', (unsigned char)'\133',
+ (unsigned char)'\134', (unsigned char)'\135',
+ (unsigned char)'\136', (unsigned char)'\137',
+ (unsigned char)'\140', (unsigned char)'\141',
+ (unsigned char)'\142', (unsigned char)'\143',
+ (unsigned char)'\144', (unsigned char)'\145',
+ (unsigned char)'\146', (unsigned char)'\147',
+ (unsigned char)'\150', (unsigned char)'\151',
+ (unsigned char)'\152', (unsigned char)'\153',
+ (unsigned char)'\154', (unsigned char)'\155',
+ (unsigned char)'\156', (unsigned char)'\157',
+ (unsigned char)'\160', (unsigned char)'\161',
+ (unsigned char)'\162', (unsigned char)'\163',
+ (unsigned char)'\164', (unsigned char)'\165',
+ (unsigned char)'\166', (unsigned char)'\167',
+ (unsigned char)'\170', (unsigned char)'\171',
+ (unsigned char)'\172', (unsigned char)'\173',
+ (unsigned char)'\174', (unsigned char)'\175',
+ (unsigned char)'\176', (unsigned char)'\177',
+ (unsigned char)'\200', (unsigned char)'\201',
+ (unsigned char)'\202', (unsigned char)'\203',
+ (unsigned char)'\204', (unsigned char)'\205',
+ (unsigned char)'\206', (unsigned char)'\207',
+ (unsigned char)'\210', (unsigned char)'\211',
+ (unsigned char)'\212', (unsigned char)'\213',
+ (unsigned char)'\214', (unsigned char)'\215',
+ (unsigned char)'\216', (unsigned char)'\217',
+ (unsigned char)'\220', (unsigned char)'\221',
+ (unsigned char)'\222', (unsigned char)'\223',
+ (unsigned char)'\224', (unsigned char)'\225',
+ (unsigned char)'\226', (unsigned char)'\227',
+ (unsigned char)'\230', (unsigned char)'\231',
+ (unsigned char)'\232', (unsigned char)'\233',
+ (unsigned char)'\234', (unsigned char)'\235',
+ (unsigned char)'\236', (unsigned char)'\237',
+ (unsigned char)'\240', (unsigned char)'\241',
+ (unsigned char)'\242', (unsigned char)'\243',
+ (unsigned char)'\244', (unsigned char)'\245',
+ (unsigned char)'\246', (unsigned char)'\247',
+ (unsigned char)'\250', (unsigned char)'\251',
+ (unsigned char)'\252', (unsigned char)'\253',
+ (unsigned char)'\254', (unsigned char)'\255',
+ (unsigned char)'\256', (unsigned char)'\257',
+ (unsigned char)'\260', (unsigned char)'\261',
+ (unsigned char)'\262', (unsigned char)'\263',
+ (unsigned char)'\264', (unsigned char)'\265',
+ (unsigned char)'\266', (unsigned char)'\267',
+ (unsigned char)'\270', (unsigned char)'\271',
+ (unsigned char)'\272', (unsigned char)'\273',
+ (unsigned char)'\274', (unsigned char)'\275',
+ (unsigned char)'\276', (unsigned char)'\277',
+ (unsigned char)'\300', (unsigned char)'\341',
+ (unsigned char)'\342', (unsigned char)'\343',
+ (unsigned char)'\344', (unsigned char)'\345',
+ (unsigned char)'\346', (unsigned char)'\347',
+ (unsigned char)'\350', (unsigned char)'\351',
+ (unsigned char)'\352', (unsigned char)'\353',
+ (unsigned char)'\354', (unsigned char)'\355',
+ (unsigned char)'\356', (unsigned char)'\357',
+ (unsigned char)'\360', (unsigned char)'\361',
+ (unsigned char)'\362', (unsigned char)'\363',
+ (unsigned char)'\364', (unsigned char)'\365',
+ (unsigned char)'\366', (unsigned char)'\367',
+ (unsigned char)'\370', (unsigned char)'\371',
+ (unsigned char)'\372', (unsigned char)'\333',
+ (unsigned char)'\334', (unsigned char)'\335',
+ (unsigned char)'\336', (unsigned char)'\337',
+ (unsigned char)'\340', (unsigned char)'\341',
+ (unsigned char)'\342', (unsigned char)'\343',
+ (unsigned char)'\344', (unsigned char)'\345',
+ (unsigned char)'\346', (unsigned char)'\347',
+ (unsigned char)'\350', (unsigned char)'\351',
+ (unsigned char)'\352', (unsigned char)'\353',
+ (unsigned char)'\354', (unsigned char)'\355',
+ (unsigned char)'\356', (unsigned char)'\357',
+ (unsigned char)'\360', (unsigned char)'\361',
+ (unsigned char)'\362', (unsigned char)'\363',
+ (unsigned char)'\364', (unsigned char)'\365',
+ (unsigned char)'\366', (unsigned char)'\367',
+ (unsigned char)'\370', (unsigned char)'\371',
+ (unsigned char)'\372', (unsigned char)'\373',
+ (unsigned char)'\374', (unsigned char)'\375',
+ (unsigned char)'\376', (unsigned char)'\377',
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strcasecmp --
+ *
+ * Compare two strings, disregarding case.
+ *
+ * Results:
+ * Returns a signed integer representing the following:
+ *
+ * zero - two strings are equal
+ * negative - first string is less than second
+ * positive - first string is greater than second
+ *
+ *----------------------------------------------------------------------
+ */
+int
+strcasecmp(s1, s2)
+ CONST char *s1;
+ CONST char *s2;
+{
+ unsigned char *s = (unsigned char *)s1;
+ unsigned char *t = (unsigned char *)s2;
+
+ for ( /* empty */ ; (caseTable[*s] == caseTable[*t]); s++, t++) {
+ if (*s == '\0') {
+ return 0;
+ }
+ }
+ return (caseTable[*s] - caseTable[*t]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strncasecmp --
+ *
+ * Compare two strings, disregarding case, up to a given length.
+ *
+ * Results:
+ * Returns a signed integer representing the following:
+ *
+ * zero - two strings are equal
+ * negative - first string is less than second
+ * positive - first string is greater than second
+ *
+ *----------------------------------------------------------------------
+ */
+int
+strncasecmp(s1, s2, length)
+ CONST char *s1;
+ CONST char *s2;
+ size_t length;
+{
+ register unsigned char *s = (unsigned char *)s1;
+ register unsigned char *t = (unsigned char *)s2;
+
+ for ( /* empty */ ; (length > 0); s++, t++, length--) {
+ if (caseTable[*s] != caseTable[*t]) {
+ return (caseTable[*s] - caseTable[*t]);
+ }
+ if (*s == '\0') {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+#endif /* !HAVE_STRCASECMP */
+
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0)) && (TCL_MAJOR_VERSION > 7)
+
+char *
+Tcl_GetString(objPtr)
+ Tcl_Obj *objPtr;
+{
+ unsigned int nBytes;
+
+ return Tcl_GetStringFromObj(objPtr, &nBytes);
+}
+
+int
+Tcl_EvalObjv(interp, objc, objv, flags)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj **objv;
+ int flags;
+{
+ Tcl_DString dString;
+ register int i;
+ int result;
+
+ Tcl_DStringInit(&dString);
+ for (i = 0; i < objc; i++) {
+ Tcl_DStringAppendElement(&dString, Tcl_GetString(objv[i]));
+ }
+ result = Tcl_Eval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return result;
+}
+
+int
+Tcl_WriteObj(channel, objPtr)
+ Tcl_Channel channel;
+ Tcl_Obj *objPtr;
+{
+ char *data;
+ int nBytes;
+
+ data = Tcl_GetStringFromObj(objPtr, &nBytes);
+ return Tcl_Write(channel, data, nBytes);
+}
+
+char *
+Tcl_SetVar2Ex(interp, part1, part2, objPtr, flags)
+ Tcl_Interp *interp;
+ char *part1, *part2;
+ Tcl_Obj *objPtr;
+ int flags;
+{
+ return Tcl_SetVar2(interp, part1, part2, Tcl_GetString(objPtr), flags);
+}
+
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareByDictionary
+ *
+ * This function compares two strings as if they were being used in
+ * an index or card catalog. The case of alphabetic characters is
+ * ignored, except to break ties. Thus "B" comes before "b" but
+ * after "a". Also, integers embedded in the strings compare in
+ * numerical order. In other words, "x10y" comes after "x9y", not
+ * before it as it would when using strcmp().
+ *
+ * Results:
+ * A negative result means that the first element comes before the
+ * second, and a positive result means that the second element
+ * should come first. A result of zero means the two elements
+ * are equal and it doesn't matter which comes first.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if HAVE_UTF
+int
+Blt_DictionaryCompare(left, right)
+ char *left, *right;
+{
+ Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
+ int diff, zeros;
+ int secondaryDiff = 0;
+
+ for(;;) {
+ if ((isdigit(UCHAR(*right))) && (isdigit(UCHAR(*left)))) {
+ /*
+ * There are decimal numbers embedded in the two
+ * strings. Compare them as numbers, rather than
+ * strings. If one number has more leading zeros than
+ * the other, the number with more leading zeros sorts
+ * later, but only as a secondary choice.
+ */
+
+ zeros = 0;
+ while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
+ right++;
+ zeros--;
+ }
+ while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
+ left++;
+ zeros++;
+ }
+ if (secondaryDiff == 0) {
+ secondaryDiff = zeros;
+ }
+
+ /*
+ * The code below compares the numbers in the two
+ * strings without ever converting them to integers. It
+ * does this by first comparing the lengths of the
+ * numbers and then comparing the digit values.
+ */
+
+ diff = 0;
+ for (;;) {
+ if (diff == 0) {
+ diff = UCHAR(*left) - UCHAR(*right);
+ }
+ right++;
+ left++;
+
+ /* Ignore commas in numbers. */
+ if (*left == ',') {
+ left++;
+ }
+ if (*right == ',') {
+ right++;
+ }
+
+ if (!isdigit(UCHAR(*right))) { /* INTL: digit */
+ if (isdigit(UCHAR(*left))) { /* INTL: digit */
+ return 1;
+ } else {
+ /*
+ * The two numbers have the same length. See
+ * if their values are different.
+ */
+
+ if (diff != 0) {
+ return diff;
+ }
+ break;
+ }
+ } else if (!isdigit(UCHAR(*left))) { /* INTL: digit */
+ return -1;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Convert character to Unicode for comparison purposes. If either
+ * string is at the terminating null, do a byte-wise comparison and
+ * bail out immediately.
+ */
+ if ((*left != '\0') && (*right != '\0')) {
+ left += Tcl_UtfToUniChar(left, &uniLeft);
+ right += Tcl_UtfToUniChar(right, &uniRight);
+ /*
+ * Convert both chars to lower for the comparison, because
+ * dictionary sorts are case insensitve. Convert to lower, not
+ * upper, so chars between Z and a will sort before A (where most
+ * other interesting punctuations occur)
+ */
+ uniLeftLower = Tcl_UniCharToLower(uniLeft);
+ uniRightLower = Tcl_UniCharToLower(uniRight);
+ } else {
+ diff = UCHAR(*left) - UCHAR(*right);
+ break;
+ }
+
+ diff = uniLeftLower - uniRightLower;
+ if (diff) {
+ return diff;
+ } else if (secondaryDiff == 0) {
+ if (Tcl_UniCharIsUpper(uniLeft) &&
+ Tcl_UniCharIsLower(uniRight)) {
+ secondaryDiff = -1;
+ } else if (Tcl_UniCharIsUpper(uniRight)
+ && Tcl_UniCharIsLower(uniLeft)) {
+ secondaryDiff = 1;
+ }
+ }
+ }
+ if (diff == 0) {
+ diff = secondaryDiff;
+ }
+ return diff;
+}
+
+#else
+
+int
+Blt_DictionaryCompare(left, right)
+ char *left, *right; /* The strings to compare */
+{
+ int diff, zeros;
+ int secondaryDiff = 0;
+
+ while (1) {
+ if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
+ /*
+ * There are decimal numbers embedded in the two
+ * strings. Compare them as numbers, rather than
+ * strings. If one number has more leading zeros than
+ * the other, the number with more leading zeros sorts
+ * later, but only as a secondary choice.
+ */
+
+ zeros = 0;
+ while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
+ right++;
+ zeros--;
+ }
+ while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
+ left++;
+ zeros++;
+ }
+ if (secondaryDiff == 0) {
+ secondaryDiff = zeros;
+ }
+
+ /*
+ * The code below compares the numbers in the two
+ * strings without ever converting them to integers. It
+ * does this by first comparing the lengths of the
+ * numbers and then comparing the digit values.
+ */
+
+ diff = 0;
+ while (1) {
+ if (diff == 0) {
+ diff = UCHAR(*left) - UCHAR(*right);
+ }
+ right++;
+ left++;
+ /* Ignore commas in numbers. */
+ if (*left == ',') {
+ left++;
+ }
+ if (*right == ',') {
+ right++;
+ }
+ if (!isdigit(UCHAR(*right))) {
+ if (isdigit(UCHAR(*left))) {
+ return 1;
+ } else {
+ /*
+ * The two numbers have the same length. See
+ * if their values are different.
+ */
+
+ if (diff != 0) {
+ return diff;
+ }
+ break;
+ }
+ } else if (!isdigit(UCHAR(*left))) {
+ return -1;
+ }
+ }
+ continue;
+ }
+ diff = UCHAR(*left) - UCHAR(*right);
+ if (diff) {
+ if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
+ diff = UCHAR(tolower(*left)) - UCHAR(*right);
+ if (diff) {
+ return diff;
+ } else if (secondaryDiff == 0) {
+ secondaryDiff = -1;
+ }
+ } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
+ diff = UCHAR(*left) - UCHAR(tolower(UCHAR(*right)));
+ if (diff) {
+ return diff;
+ } else if (secondaryDiff == 0) {
+ secondaryDiff = 1;
+ }
+ } else {
+ return diff;
+ }
+ }
+ if (*left == 0) {
+ break;
+ }
+ left++;
+ right++;
+ }
+ if (diff == 0) {
+ diff = secondaryDiff;
+ }
+ return diff;
+}
+#endif
+
+#ifndef NDEBUG
+void
+Blt_Assert(testExpr, fileName, lineNumber)
+ char *testExpr;
+ char *fileName;
+ int lineNumber;
+{
+#ifdef WINDEBUG
+ PurifyPrintf("line %d of %s: Assert \"%s\" failed", lineNumber,
+ fileName, testExpr);
+#endif
+ fprintf(stderr, "line %d of %s: Assert \"%s\" failed",
+ lineNumber, fileName, testExpr);
+ fflush(stderr);
+ abort();
+}
+#endif
+
+/*ARGSUSED*/
+void
+Blt_Panic TCL_VARARGS_DEF(char *, arg1)
+{
+ va_list argList;
+ char *format;
+
+ format = TCL_VARARGS_START(char *, arg1, argList);
+ vfprintf(stderr, format, argList);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ abort();
+}
+
+void
+Blt_DStringAppendElements
+TCL_VARARGS_DEF(Tcl_DString *, arg1)
+{
+ va_list argList;
+ Tcl_DString *dsPtr;
+ register char *elem;
+
+ dsPtr = TCL_VARARGS_START(Tcl_DString *, arg1, argList);
+ while ((elem = va_arg(argList, char *)) != NULL) {
+ Tcl_DStringAppendElement(dsPtr, elem);
+ }
+ va_end(argList);
+}
+
+static char stringRep[200];
+
+char *
+Blt_Itoa(value)
+ int value;
+{
+ sprintf(stringRep, "%d", value);
+ return stringRep;
+}
+
+char *
+Blt_Utoa(value)
+ unsigned int value;
+{
+ sprintf(stringRep, "%u", value);
+ return stringRep;
+}
+
+char *
+Blt_Dtoa(interp, value)
+ Tcl_Interp *interp;
+ double value;
+{
+ Tcl_PrintDouble(interp, value, stringRep);
+ return stringRep;
+}
+
+#if HAVE_UTF
+
+#undef fopen
+FILE *
+Blt_OpenUtfFile(fileName, mode)
+ char *fileName, *mode;
+{
+ Tcl_DString dString;
+ FILE *f;
+
+ fileName = Tcl_UtfToExternalDString(NULL, fileName, -1, &dString);
+ f = fopen(fileName, mode);
+ Tcl_DStringFree(&dString);
+ return f;
+}
+
+#endif /* HAVE_UTF */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_InitHexTable --
+ *
+ * Table index for the hex values. Initialized once, first time.
+ * Used for translation value or delimiter significance lookup.
+ *
+ * We build the table at run time for several reasons:
+ *
+ * 1. portable to non-ASCII machines.
+ * 2. still reentrant since we set the init flag after setting
+ * table.
+ * 3. easier to extend.
+ * 4. less prone to bugs.
+ *
+ * Results:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_InitHexTable(hexTable)
+ char hexTable[];
+{
+ hexTable['0'] = 0;
+ hexTable['1'] = 1;
+ hexTable['2'] = 2;
+ hexTable['3'] = 3;
+ hexTable['4'] = 4;
+ hexTable['5'] = 5;
+ hexTable['6'] = 6;
+ hexTable['7'] = 7;
+ hexTable['8'] = 8;
+ hexTable['9'] = 9;
+ hexTable['a'] = hexTable['A'] = 10;
+ hexTable['b'] = hexTable['B'] = 11;
+ hexTable['c'] = hexTable['C'] = 12;
+ hexTable['d'] = hexTable['D'] = 13;
+ hexTable['e'] = hexTable['E'] = 14;
+ hexTable['f'] = hexTable['F'] = 15;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_GetPosition --
+ *
+ * Convert a string representing a numeric position.
+ * A position can be in one of the following forms.
+ *
+ * number - number of the item in the hierarchy, indexed
+ * from zero.
+ * "end" - last position in the hierarchy.
+ *
+ * Results:
+ * A standard Tcl result. If "string" is a valid index, then
+ * *indexPtr is filled with the corresponding numeric index.
+ * If "end" was selected then *indexPtr is set to -1.
+ * Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_GetPosition(interp, string, indexPtr)
+ Tcl_Interp *interp; /* Interpreter to report results back
+ * to. */
+ char *string; /* String representation of the index.
+ * Can be an integer or "end" to refer
+ * to the last index. */
+ int *indexPtr; /* Holds the converted index. */
+{
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ *indexPtr = -1; /* Indicates last position in hierarchy. */
+ } else {
+ int position;
+
+ if (Tcl_GetInt(interp, string, &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position < 0) {
+ Tcl_AppendResult(interp, "bad position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = position;
+ }
+ return TCL_OK;
+}
+
+/*
+ * The hash table below is used to keep track of all the Blt_Uids created
+ * so far.
+ */
+static Blt_HashTable uidTable;
+static int uidInitialized = 0;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetUid --
+ *
+ * Given a string, returns a unique identifier for the string.
+ * A reference count is maintained, so that the identifier
+ * can be freed when it is not needed any more. This can be used
+ * in many places to replace Tcl_GetUid.
+ *
+ * Results:
+ * This procedure returns a Blt_Uid corresponding to the "string"
+ * argument. The Blt_Uid has a string value identical to string
+ * (strcmp will return 0), but it's guaranteed that any other
+ * calls to this procedure with a string equal to "string" will
+ * return exactly the same result (i.e. can compare Blt_Uid
+ * *values* directly, without having to call strcmp on what they
+ * point to).
+ *
+ * Side effects:
+ * New information may be entered into the identifier table.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Uid
+Blt_GetUid(string)
+ char *string; /* String to convert. */
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+ int refCount;
+
+ if (!uidInitialized) {
+ Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+ uidInitialized = 1;
+ }
+ hPtr = Blt_CreateHashEntry(&uidTable, string, &isNew);
+ if (isNew) {
+ refCount = 0;
+ } else {
+ refCount = (int)Blt_GetHashValue(hPtr);
+ }
+ refCount++;
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ return (Blt_Uid)Blt_GetHashKey(&uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeUid --
+ *
+ * Frees the Blt_Uid if there are no more clients using this
+ * identifier.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The identifier may be deleted from the identifier table.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeUid(uid)
+ Blt_Uid uid; /* Identifier to release. */
+{
+ Blt_HashEntry *hPtr;
+
+ if (!uidInitialized) {
+ Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+ uidInitialized = 1;
+ }
+ hPtr = Blt_FindHashEntry(&uidTable, uid);
+ if (hPtr) {
+ int refCount;
+
+ refCount = (int)Blt_GetHashValue(hPtr);
+ refCount--;
+ if (refCount == 0) {
+ Blt_DeleteHashEntry(&uidTable, hPtr);
+ } else {
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ }
+ } else {
+ fprintf(stderr, "tried to release unknown identifier \"%s\"\n", uid);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindUid --
+ *
+ * Returns a Blt_Uid associated with a given string, if one exists.
+ *
+ * Results:
+ * A Blt_Uid for the string if one exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Uid
+Blt_FindUid(string)
+ char *string; /* String to find. */
+{
+ Blt_HashEntry *hPtr;
+
+ if (!uidInitialized) {
+ Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+ uidInitialized = 1;
+ }
+ hPtr = Blt_FindHashEntry(&uidTable, string);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return (Blt_Uid) Blt_GetHashKey(&uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BinaryOpSearch --
+ *
+ * Performs a binary search on the array of command operation
+ * specifications to find a partial, anchored match for the
+ * given operation string.
+ *
+ * Results:
+ * If the string matches unambiguously the index of the specification
+ * in the array is returned. If the string does not match, even
+ * as an abbreviation, any operation, -1 is returned. If the string
+ * matches, but ambiguously -2 is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BinaryOpSearch(specArr, nSpecs, string)
+ Blt_OpSpec specArr[];
+ int nSpecs;
+ char *string; /* Name of minor operation to search for */
+{
+ Blt_OpSpec *specPtr;
+ char c;
+ register int high, low, median;
+ register int compare, length;
+
+ low = 0;
+ high = nSpecs - 1;
+ c = string[0];
+ length = strlen(string);
+ while (low <= high) {
+ median = (low + high) >> 1;
+ specPtr = specArr + median;
+
+ /* Test the first character */
+ compare = c - specPtr->name[0];
+ if (compare == 0) {
+ /* Now test the entire string */
+ compare = strncmp(string, specPtr->name, length);
+ if (compare == 0) {
+ if (length < specPtr->minChars) {
+ return -2; /* Ambiguous operation name */
+ }
+ }
+ }
+ if (compare < 0) {
+ high = median - 1;
+ } else if (compare > 0) {
+ low = median + 1;
+ } else {
+ return median; /* Op found. */
+ }
+ }
+ return -1; /* Can't find operation */
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinearOpSearch --
+ *
+ * Performs a binary search on the array of command operation
+ * specifications to find a partial, anchored match for the
+ * given operation string.
+ *
+ * Results:
+ * If the string matches unambiguously the index of the specification
+ * in the array is returned. If the string does not match, even
+ * as an abbreviation, any operation, -1 is returned. If the string
+ * matches, but ambiguously -2 is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+LinearOpSearch(specArr, nSpecs, string)
+ Blt_OpSpec specArr[];
+ int nSpecs;
+ char *string; /* Name of minor operation to search for */
+{
+ Blt_OpSpec *specPtr;
+ char c;
+ int length, nMatches, last;
+ register int i;
+
+ c = string[0];
+ length = strlen(string);
+ nMatches = 0;
+ last = -1;
+ for (specPtr = specArr, i = 0; i < nSpecs; i++, specPtr++) {
+ if ((c == specPtr->name[0]) &&
+ (strncmp(string, specPtr->name, length) == 0)) {
+ last = i;
+ nMatches++;
+ if (length == specPtr->minChars) {
+ break;
+ }
+ }
+ }
+ if (nMatches > 1) {
+ return -2; /* Ambiguous operation name */
+ }
+ if (nMatches == 0) {
+ return -1; /* Can't find operation */
+ }
+ return last; /* Op found. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetOp --
+ *
+ * Find the command operation given a string name. This is useful
+ * where a group of command operations have the same argument
+ * signature.
+ *
+ * Results:
+ * If found, a pointer to the procedure (function pointer) is
+ * returned. Otherwise NULL is returned and an error message
+ * containing a list of the possible commands is returned in
+ * interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Op
+Blt_GetOp(interp, nSpecs, specArr, operPos, argc, argv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int nSpecs; /* Number of specifications in array */
+ Blt_OpSpec specArr[]; /* Op specification array */
+ int operPos; /* Index of the operation name argument */
+ int argc; /* Number of arguments in the argument vector.
+ * This includes any prefixed arguments */
+ char *argv[]; /* Argument vector */
+ int flags; /* */
+{
+ Blt_OpSpec *specPtr;
+ char *string;
+ register int i;
+ register int n;
+
+ if (argc <= operPos) { /* No operation argument */
+ Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
+ usage:
+ Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
+ for (n = 0; n < nSpecs; n++) {
+ Tcl_AppendResult(interp, "\n ", (char *)NULL);
+ for (i = 0; i < operPos; i++) {
+ Tcl_AppendResult(interp, argv[i], " ", (char *)NULL);
+ }
+ specPtr = specArr + n;
+ Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
+ (char *)NULL);
+ }
+ return NULL;
+ }
+ string = argv[operPos];
+ if (flags & BLT_OP_LINEAR_SEARCH) {
+ n = LinearOpSearch(specArr, nSpecs, string);
+ } else {
+ n = BinaryOpSearch(specArr, nSpecs, string);
+ }
+ if (n == -2) {
+ char c;
+ int length;
+
+ Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
+ if (operPos > 2) {
+ Tcl_AppendResult(interp, " ", argv[operPos - 1], (char *)NULL);
+ }
+ Tcl_AppendResult(interp, " operation \"", string, "\" matches:",
+ (char *)NULL);
+
+ c = string[0];
+ length = strlen(string);
+ for (n = 0; n < nSpecs; n++) {
+ specPtr = specArr + n;
+ if ((c == specPtr->name[0]) &&
+ (strncmp(string, specPtr->name, length) == 0)) {
+ Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
+ }
+ }
+ return NULL;
+
+ } else if (n == -1) { /* Can't find operation, display help */
+ Tcl_AppendResult(interp, "bad", (char *)NULL);
+ if (operPos > 2) {
+ Tcl_AppendResult(interp, " ", argv[operPos - 1], (char *)NULL);
+ }
+ Tcl_AppendResult(interp, " operation \"", string, "\": ",
+ (char *)NULL);
+ goto usage;
+ }
+ specPtr = specArr + n;
+ if ((argc < specPtr->minArgs) || ((specPtr->maxArgs > 0) &&
+ (argc > specPtr->maxArgs))) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
+ for (i = 0; i < operPos; i++) {
+ Tcl_AppendResult(interp, argv[i], " ", (char *)NULL);
+ }
+ Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ return specPtr->proc;
+}
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetOpFromObj --
+ *
+ * Find the command operation given a string name. This is useful
+ * where a group of command operations have the same argument
+ * signature.
+ *
+ * Results:
+ * If found, a pointer to the procedure (function pointer) is
+ * returned. Otherwise NULL is returned and an error message
+ * containing a list of the possible commands is returned in
+ * interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Op
+Blt_GetOpFromObj(interp, nSpecs, specArr, operPos, objc, objv, flags)
+ Tcl_Interp *interp; /* Interpreter to report errors to */
+ int nSpecs; /* Number of specifications in array */
+ Blt_OpSpec specArr[]; /* Op specification array */
+ int operPos; /* Position of operation in argument list. */
+ int objc; /* Number of arguments in the argument vector.
+ * This includes any prefixed arguments */
+ Tcl_Obj *CONST objv[]; /* Argument vector */
+ int flags;
+{
+ Blt_OpSpec *specPtr;
+ char *string;
+ register int i;
+ register int n;
+
+ if (objc <= operPos) { /* No operation argument */
+ Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
+ usage:
+ Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
+ for (n = 0; n < nSpecs; n++) {
+ Tcl_AppendResult(interp, "\n ", (char *)NULL);
+ for (i = 0; i < operPos; i++) {
+ Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ",
+ (char *)NULL);
+ }
+ specPtr = specArr + n;
+ Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
+ (char *)NULL);
+ }
+ return NULL;
+ }
+ string = Tcl_GetString(objv[operPos]);
+ if (flags & BLT_OP_LINEAR_SEARCH) {
+ n = LinearOpSearch(specArr, nSpecs, string);
+ } else {
+ n = BinaryOpSearch(specArr, nSpecs, string);
+ }
+ if (n == -2) {
+ char c;
+ int length;
+
+ Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
+ if (operPos > 2) {
+ Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]),
+ (char *)NULL);
+ }
+ Tcl_AppendResult(interp, " operation \"", string, "\" matches:",
+ (char *)NULL);
+
+ c = string[0];
+ length = strlen(string);
+ for (n = 0; n < nSpecs; n++) {
+ specPtr = specArr + n;
+ if ((c == specPtr->name[0]) &&
+ (strncmp(string, specPtr->name, length) == 0)) {
+ Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
+ }
+ }
+ return NULL;
+
+ } else if (n == -1) { /* Can't find operation, display help */
+ Tcl_AppendResult(interp, "bad", (char *)NULL);
+ if (operPos > 2) {
+ Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]),
+ (char *)NULL);
+ }
+ Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL);
+ goto usage;
+ }
+ specPtr = specArr + n;
+ if ((objc < specPtr->minArgs) ||
+ ((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
+ for (i = 0; i < operPos; i++) {
+ Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ",
+ (char *)NULL);
+ }
+ Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ return specPtr->proc;
+}
+
+#endif
diff --git a/blt/src/bltVecCmd.c b/blt/src/bltVecCmd.c
new file mode 100644
index 00000000000..df1c1297f5d
--- /dev/null
+++ b/blt/src/bltVecCmd.c
@@ -0,0 +1,1978 @@
+/*
+ * bltVecCmd.c --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ * o Add H. Kirsch's vector binary read operation
+ * x binread file0
+ * x binread -file file0
+ *
+ * o Add ASCII/binary file reader
+ * x read fileName
+ *
+ * o Allow Tcl-based client notifications.
+ * vector x
+ * x notify call Display
+ * x notify delete Display
+ * x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+
+#if (TCL_MAJOR_VERSION == 7)
+
+static void
+GetValues(vPtr, first, last, resultPtr)
+ VectorObject *vPtr;
+ int first, last;
+ Tcl_DString *resultPtr;
+{
+ register int i;
+ char valueString[TCL_DOUBLE_SPACE + 1];
+
+ for (i = first; i <= last; i++) {
+ Tcl_PrintDouble(vPtr->interp, vPtr->valueArr[i], valueString);
+ Tcl_DStringAppendElement(resultPtr, valueString);
+ }
+}
+
+static void
+ReplicateValue(vPtr, first, last, value)
+ VectorObject *vPtr;
+ int first, last;
+ double value;
+{
+ register int i;
+ for (i = first; i <= last; i++) {
+ vPtr->valueArr[i] = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+}
+
+static int
+CopyList(vPtr, nElem, elemArr)
+ VectorObject *vPtr;
+ int nElem;
+ char **elemArr;
+{
+ register int i;
+ double value;
+
+ if (Blt_VectorChangeLength(vPtr, nElem) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nElem; i++) {
+ if (Tcl_GetDouble(vPtr->interp, elemArr[i], &value)!= TCL_OK) {
+ vPtr->length = i;
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[i] = value;
+ }
+ return TCL_OK;
+}
+
+static int
+AppendVector(destPtr, srcPtr)
+ VectorObject *destPtr, *srcPtr;
+{
+ int nBytes;
+ int oldSize, newSize;
+
+ oldSize = destPtr->length;
+ newSize = oldSize + srcPtr->last - srcPtr->first + 1;
+ if (Blt_VectorChangeLength(destPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nBytes = (newSize - oldSize) * sizeof(double);
+ memcpy((char *)(destPtr->valueArr + oldSize),
+ (srcPtr->valueArr + srcPtr->first), nBytes);
+ destPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+static int
+AppendList(vPtr, nElem, elemArr)
+ VectorObject *vPtr;
+ int nElem;
+ char **elemArr;
+{
+ int count;
+ register int i;
+ double value;
+ int oldSize;
+
+ oldSize = vPtr->length;
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + nElem) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = oldSize;
+ for (i = 0; i < nElem; i++) {
+ if (Tcl_ExprDouble(vPtr->interp, elemArr[i], &value)
+ != TCL_OK) {
+ vPtr->length = count;
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[count++] = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+/* Vector instance option commands */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ * Appends one of more Tcl lists of values, or vector objects
+ * onto the end of the current vector object.
+ *
+ * Results:
+ * A standard Tcl result. If a current vector can't be created,
+ * resized, any of the named vectors can't be found, or one of
+ * lists of values is invalid, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of current vector will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AppendOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register int i;
+ int result;
+ VectorObject *v2Ptr;
+
+ for (i = 2; i < argc; i++) {
+ v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ argv[i], (char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ result = AppendVector(vPtr, v2Ptr);
+ } else {
+ int nElem;
+ char **elemArr;
+
+ if (Tcl_SplitList(interp, argv[i], &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = AppendList(vPtr, nElem, elemArr);
+ Blt_Free(elemArr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (argc > 2) {
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ClearOp --
+ *
+ * Deletes all the accumulated array indices for the Tcl array
+ * associated will the vector. This routine can be used to
+ * free excess memory from a large vector.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side Effects:
+ * Memory used for the entries of the Tcl array variable is freed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_VectorFlushCache(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the given indices from the vector. If no indices are
+ * provided the entire vector is deleted.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given indices is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * The clients of the vector will be notified of the vector
+ * deletions.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ unsigned char *unsetArr;
+ register int i, j;
+ register int count;
+
+ if (argc == 2) {
+ Blt_VectorFree(vPtr);
+ return TCL_OK;
+ }
+ /*
+ * Allocate an "unset" bitmap the size of the vector. We should
+ * try to use bit fields instead of a character array, since
+ * memory may be an issue if the vector is large.
+ */
+ unsetArr = Blt_Calloc(sizeof(unsigned char), vPtr->length);
+ assert(unsetArr);
+ for (i = 2; i < argc; i++) {
+ if (Blt_VectorGetIndexRange(interp, vPtr, argv[i],
+ (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL)
+ != TCL_OK) {
+ Blt_Free(unsetArr);
+ return TCL_ERROR;
+ }
+ for (j = vPtr->first; j <= vPtr->last; j++) {
+ unsetArr[j] = TRUE;
+ }
+ }
+ count = 0;
+ for (i = 0; i < vPtr->length; i++) {
+ if (unsetArr[i]) {
+ continue;
+ }
+ if (count < i) {
+ vPtr->valueArr[count] = vPtr->valueArr[i];
+ }
+ count++;
+ }
+ Blt_Free(unsetArr);
+ vPtr->length = count;
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DupOp --
+ *
+ * Creates one or more duplicates of the vector object.
+ *
+ * Results:
+ * A standard Tcl result. If a new vector can't be created,
+ * or and existing vector resized, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of existing vectors will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DupOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int argc;
+ char **argv;
+{
+ VectorObject *v2Ptr;
+ int isNew;
+ register int i;
+
+ for (i = 2; i < argc; i++) {
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[i], argv[i], argv[i],
+ &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (v2Ptr == vPtr) {
+ continue;
+ }
+ if (Blt_VectorDuplicate(v2Ptr, vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Sets or reads the value of the index. This simulates what the
+ * vector's variable does.
+ *
+ * Results:
+ * A standard Tcl result. If the index is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the values.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+IndexOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int first, last;
+
+ if (Blt_VectorGetIndexRange(interp, vPtr, argv[2], INDEX_ALL_FLAGS,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ first = vPtr->first, last = vPtr->last;
+ if (argc == 3) {
+ Tcl_DString dString;
+
+ if (first == vPtr->length) {
+ Tcl_AppendResult(interp, "can't get index \"", argv[2], "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Can't read from index "++end" */
+ }
+ Tcl_DStringInit(&dString);
+ GetValues(vPtr, first, last, &dString);
+ Tcl_DStringResult(interp, &dString);
+ Tcl_DStringFree(&dString);
+ } else {
+ char string[TCL_DOUBLE_SPACE + 1];
+ double value;
+
+ if (first == SPECIAL_INDEX) {
+ Tcl_AppendResult(interp, "can't set index \"", argv[2], "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Tried to set "min" or "max" */
+ }
+ if (Tcl_ExprDouble(interp, argv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first == vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ ReplicateValue(vPtr, first, last, value);
+
+ Tcl_PrintDouble(interp, value, string);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * LengthOp --
+ *
+ * Returns the length of the vector. If a new size is given, the
+ * vector is resized to the new vector.
+ *
+ * Results:
+ * A standard Tcl result. If the new length is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the length of the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+LengthOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ int size;
+
+ if (Tcl_GetInt(interp, argv[2], &size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (size < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"", argv[3], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ Tcl_SetResult(interp, Blt_Itoa(vPtr->length), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Queries or sets the offset of the array index from the base
+ * address of the data array of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ if (argc > 2) {
+ if (Blt_VectorMapVariable(interp, vPtr, argv[2]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (vPtr->arrayName != NULL) {
+ Tcl_SetResult(interp, vPtr->arrayName, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MergeOp --
+ *
+ * Merges the values from the given vectors to the current vector.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MergeOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ VectorObject *v2Ptr;
+ VectorObject **vecArr;
+ register VectorObject **vPtrPtr;
+ int refSize, length, nElem;
+ register int i;
+ double *valuePtr, *valueArr;
+
+ /* Allocate an array of vector pointers of each vector to be
+ * merged in the current vector. */
+ vecArr = Blt_Malloc(sizeof(VectorObject *) * argc);
+ assert(vecArr);
+ vPtrPtr = vecArr;
+
+ refSize = -1;
+ nElem = 0;
+ for (i = 2; i < argc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ /* Check that all the vectors are the same length */
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (refSize < 0) {
+ refSize = length;
+ } else if (length != refSize) {
+ Tcl_AppendResult(vPtr->interp, "vector \"", v2Ptr->name,
+ "\" has inconsistent length", (char *)NULL);
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ *vPtrPtr++ = v2Ptr;
+ nElem += refSize;
+ }
+ *vPtrPtr = NULL;
+ valueArr = Blt_Malloc(sizeof(double) * nElem);
+ if (valueArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ",
+ Blt_Itoa(nElem), " vector elements", (char *)NULL);
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ /* Merge the values from each of the vectors into the current vector */
+ valuePtr = valueArr;
+ for (i = 0; i < refSize; i++) {
+ for (vPtrPtr = vecArr; *vPtrPtr != NULL; vPtrPtr++) {
+ *valuePtr++ = (*vPtrPtr)->valueArr[i + (*vPtrPtr)->first];
+ }
+ }
+ Blt_Free(vecArr);
+ Blt_VectorReset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NormalizeOp --
+ *
+ * Normalizes the vector.
+ *
+ * Results:
+ * A standard Tcl result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NormalizeOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register int i;
+ double range;
+
+ Blt_VectorUpdateRange(vPtr);
+ range = vPtr->max - vPtr->min;
+ if (argc > 2) {
+ VectorObject *v2Ptr;
+ int isNew;
+
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2],
+ &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_VectorChangeLength(v2Ptr, vPtr->length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < vPtr->length; i++) {
+ v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range;
+ }
+ Blt_VectorUpdateRange(v2Ptr);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ } else {
+ double norm;
+
+ for (i = 0; i < vPtr->length; i++) {
+ norm = (vPtr->valueArr[i] - vPtr->min) / range;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, norm));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ * Notify clients of vector.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ * x vector notify now
+ * x vector notify always
+ * x vector notify whenidle
+ * x vector notify update {}
+ * x vector notify delete {}
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ char c;
+ int length;
+
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'a') && (length > 1)
+ && (strncmp(argv[2], "always", length) == 0)) {
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_ALWAYS;
+ } else if ((c == 'n') && (length > 2)
+ && (strncmp(argv[2], "never", length) == 0)) {
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_NEVER;
+ } else if ((c == 'w') && (length > 1)
+ && (strncmp(argv[2], "whenidle", length) == 0)) {
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_WHENIDLE;
+ } else if ((c == 'n') && (length > 2)
+ && (strncmp(argv[2], "now", length) == 0)) {
+ /* How does this play when an update is pending? */
+ Blt_VectorNotifyClients(vPtr);
+ } else if ((c == 'c') && (length > 1)
+ && (strncmp(argv[2], "cancel", length) == 0)) {
+ if (vPtr->notifyFlags & NOTIFY_PENDING) {
+ vPtr->notifyFlags &= ~NOTIFY_PENDING;
+ Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr);
+ }
+ } else if ((c == 'p') && (length > 1)
+ && (strncmp(argv[2], "pending", length) == 0)) {
+ Blt_SetBooleanResult(interp, (vPtr->notifyFlags & NOTIFY_PENDING));
+ } else {
+ Tcl_AppendResult(interp, "bad qualifier \"", argv[2], "\": should be \
+\"always\", \"never\", \"whenidle\", \"now\", \"cancel\", or \"pending\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PopulateOp --
+ *
+ * Creates or resizes a new vector based upon the density specified.
+ *
+ * Results:
+ * A standard Tcl result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PopulateOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ VectorObject *v2Ptr;
+ int size, density;
+ int isNew;
+ register int i, j;
+ double slice, range;
+ register double *valuePtr;
+ int count;
+
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2],
+ &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (vPtr->length == 0) {
+ return TCL_OK; /* Source vector is empty. */
+ }
+ if (Tcl_GetInt(interp, argv[3], &density) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (density < 1) {
+ Tcl_AppendResult(interp, "bad density \"", argv[3], "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ size = (vPtr->length - 1) * (density + 1) + 1;
+ if (Blt_VectorChangeLength(v2Ptr, size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = 0;
+ valuePtr = v2Ptr->valueArr;
+ for (i = 0; i < (vPtr->length - 1); i++) {
+ range = vPtr->valueArr[i + 1] - vPtr->valueArr[i];
+ slice = range / (double)(density + 1);
+ for (j = 0; j <= density; j++) {
+ *valuePtr = vPtr->valueArr[i] + (slice * (double)j);
+ valuePtr++;
+ count++;
+ }
+ }
+ count++;
+ *valuePtr = vPtr->valueArr[i];
+ assert(count == v2Ptr->length);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns a Tcl list of the range of vector values specified.
+ *
+ * Results:
+ * A standard Tcl result. If the given range is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned and interp->result
+ * will contain the list of values.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RangeOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int first, last;
+ register int i;
+
+ if ((Blt_VectorGetIndex(interp, vPtr, argv[2], &first, INDEX_CHECK,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) ||
+ (Blt_VectorGetIndex(interp, vPtr, argv[3], &last, INDEX_CHECK,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (first > last) {
+ /* Return the list reversed */
+ for (i = last; i <= first; i++) {
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+ }
+ } else {
+ for (i = first; i <= last; i++) {
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ * Determines if a value lies within a given range.
+ *
+ * The value is normalized and compared against the interval
+ * [0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ * DBL_EPSILON is the smallest number that can be represented
+ * on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ * Please note, min can't be greater than max.
+ *
+ * Results:
+ * If the value is within of the interval [min..max], 1 is
+ * returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(value, min, max)
+ register double value, min, max;
+{
+ double range;
+
+ range = max - min;
+ if (range < DBL_EPSILON) {
+ return (FABS(max - value) < DBL_EPSILON);
+ } else {
+ double norm;
+
+ norm = (value - min) / range;
+ return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+ }
+}
+
+enum NativeFormats {
+ FMT_UNKNOWN = -1,
+ FMT_UCHAR, FMT_CHAR,
+ FMT_USHORT, FMT_SHORT,
+ FMT_UINT, FMT_INT,
+ FMT_ULONG, FMT_LONG,
+ FMT_FLOAT, FMT_DOUBLE
+};
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetBinaryFormat
+ *
+ * Translates a format string into a native type. Formats may be
+ * as follows.
+ *
+ * signed i1, i2, i4, i8
+ * unsigned u1, u2, u4, u8
+ * real r4, r8, r16
+ *
+ * But there must be a corresponding native type. For example,
+ * this for reading 2-byte binary integers from an instrument and
+ * converting them to unsigned shorts or ints.
+ *
+ * -----------------------------------------------------------------------
+ */
+static enum NativeFormats
+GetBinaryFormat(interp, string, sizePtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *sizePtr;
+{
+ char c;
+
+ c = tolower(string[0]);
+ if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) {
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": incorrect byte size", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (c) {
+ case 'r':
+ if (*sizePtr == sizeof(double)) {
+ return FMT_DOUBLE;
+ } else if (*sizePtr == sizeof(float)) {
+ return FMT_FLOAT;
+ }
+ break;
+
+ case 'i':
+ if (*sizePtr == sizeof(char)) {
+ return FMT_CHAR;
+ } else if (*sizePtr == sizeof(int)) {
+ return FMT_INT;
+ } else if (*sizePtr == sizeof(long)) {
+ return FMT_LONG;
+ } else if (*sizePtr == sizeof(short)) {
+ return FMT_SHORT;
+ }
+ break;
+
+ case 'u':
+ if (*sizePtr == sizeof(unsigned char)) {
+ return FMT_UCHAR;
+ } else if (*sizePtr == sizeof(unsigned int)) {
+ return FMT_UINT;
+ } else if (*sizePtr == sizeof(unsigned long)) {
+ return FMT_ULONG;
+ } else if (*sizePtr == sizeof(unsigned short)) {
+ return FMT_USHORT;
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": should be either i#, r#, u# (where # is size in bytes)",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+ }
+ Tcl_AppendResult(interp, "can't handle format \"", string, "\"",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+}
+
+static int
+CopyValues(vPtr, byteArr, fmt, size, length, swap, indexPtr)
+ VectorObject *vPtr;
+ char *byteArr;
+ enum NativeFormats fmt;
+ int size;
+ int length;
+ int swap;
+ int *indexPtr;
+{
+ register int i, n;
+ int newSize;
+
+ if ((swap) && (size > 1)) {
+ int nBytes = size * length;
+ register unsigned char *p;
+ register int left, right;
+
+ for (i = 0; i < nBytes; i += size) {
+ p = (unsigned char *)(byteArr + i);
+ for (left = 0, right = size - 1; left < right; left++, right--) {
+ p[left] ^= p[right];
+ p[right] ^= p[left];
+ p[left] ^= p[right];
+ }
+
+ }
+ }
+ newSize = *indexPtr + length;
+ if (newSize > vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+#define CopyArrayToVector(vPtr, arr) \
+ for (i = 0, n = *indexPtr; i < length; i++, n++) { \
+ (vPtr)->valueArr[n] = (double)(arr)[i]; \
+ }
+
+ switch (fmt) {
+ case FMT_CHAR:
+ CopyArrayToVector(vPtr, (char *)byteArr);
+ break;
+
+ case FMT_UCHAR:
+ CopyArrayToVector(vPtr, (unsigned char *)byteArr);
+ break;
+
+ case FMT_INT:
+ CopyArrayToVector(vPtr, (int *)byteArr);
+ break;
+
+ case FMT_UINT:
+ CopyArrayToVector(vPtr, (unsigned int *)byteArr);
+ break;
+
+ case FMT_LONG:
+ CopyArrayToVector(vPtr, (long *)byteArr);
+ break;
+
+ case FMT_ULONG:
+ CopyArrayToVector(vPtr, (unsigned long *)byteArr);
+ break;
+
+ case FMT_SHORT:
+ CopyArrayToVector(vPtr, (short int *)byteArr);
+ break;
+
+ case FMT_USHORT:
+ CopyArrayToVector(vPtr, (unsigned short int *)byteArr);
+ break;
+
+ case FMT_FLOAT:
+ CopyArrayToVector(vPtr, (float *)byteArr);
+ break;
+
+ case FMT_DOUBLE:
+ CopyArrayToVector(vPtr, (double *)byteArr);
+ break;
+
+ case FMT_UNKNOWN:
+ break;
+ }
+ *indexPtr += length;
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BinreadOp --
+ *
+ * Reads binary values from a Tcl channel. Values are either appended
+ * to the end of the vector or placed at a given index (using the
+ * "-at" option), overwriting existing values. Data is read until EOF
+ * is found on the channel or a specified number of values are read.
+ * (note that this is not necessarily the same as the number of bytes).
+ *
+ * The following flags are supported:
+ * -swap Swap bytes
+ * -at index Start writing data at the index.
+ * -format fmt Specifies the format of the data.
+ *
+ * This binary reader was created by Harald Kirsch (kir@iitb.fhg.de).
+ *
+ * Results:
+ * Returns a standard Tcl result. The interpreter result will contain
+ * the number of values (not the number of bytes) read.
+ *
+ * Caveats:
+ * Channel reads must end on an element boundary.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BinreadOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ char *byteArr;
+ enum NativeFormats fmt;
+ int size, length, mode;
+ Tcl_Channel channel;
+ int arraySize, bytesRead;
+ int count, total;
+ int first;
+ int swap;
+ register int i;
+
+ channel = Tcl_GetChannel(interp, argv[2], &mode);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ if ((mode & TCL_READABLE) == 0) {
+ Tcl_AppendResult(interp, "channel \"", argv[2],
+ "\" wasn't opened for reading", (char *)NULL);
+ return TCL_ERROR;
+ }
+ first = vPtr->length;
+ fmt = FMT_DOUBLE;
+ size = sizeof(double);
+ swap = FALSE;
+ count = 0;
+
+ if ((argc > 3) && (argv[3][0] != '-')) {
+ long int value;
+ /* Get the number of values to read. */
+ if (Tcl_ExprLong(interp, argv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_AppendResult(interp, "count can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ count = (int)value;
+ argc--, argv++;
+ }
+ /* Process any option-value pairs that remain. */
+ for (i = 3; i < argc; i++) {
+ if (strcmp(argv[i], "-swap") == 0) {
+ swap = TRUE;
+ } else if (strcmp(argv[i], "-format") == 0) {
+ i += 1;
+ if (i >= argc) {
+ Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ fmt = GetBinaryFormat(interp, argv[i], &size);
+ if (fmt == FMT_UNKNOWN) {
+ return TCL_ERROR;
+ }
+ } else if (strcmp(argv[i], "-at") == 0) {
+ i += 1;
+ if (i >= argc) {
+ Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_VectorGetIndex(interp, vPtr, argv[i], &first, 0,
+ (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first > vPtr->length) {
+ Tcl_AppendResult(interp, "index \"", argv[i],
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+
+#define BUFFER_SIZE 1024
+ if (count == 0) {
+ arraySize = BUFFER_SIZE * size;
+ } else {
+ arraySize = count * size;
+ }
+
+ byteArr = Blt_Malloc(arraySize);
+ assert(byteArr);
+
+ /* FIXME: restore old channel translation later? */
+ if (Tcl_SetChannelOption(interp, channel, "-translation",
+ "binary") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total = 0;
+ while (!Tcl_Eof(channel)) {
+ bytesRead = Tcl_Read(channel, byteArr, arraySize);
+ if (bytesRead < 0) {
+ Tcl_AppendResult(interp, "error reading channel: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((bytesRead % size) != 0) {
+ Tcl_AppendResult(interp, "error reading channel: short read",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ length = bytesRead / size;
+ if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total += length;
+ if (count > 0) {
+ break;
+ }
+ }
+ Blt_Free(byteArr);
+
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+
+ /* Set the result as the number of values read. */
+ Tcl_SetResult(interp, Blt_Itoa(total), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ * Searchs for a value in the vector. Returns the indices of all
+ * vector elements matching a particular value.
+ *
+ * Results:
+ * Always returns TCL_OK. interp->result will contain a list of
+ * the indices of array elements matching value. If no elements
+ * match, interp->result will contain the empty string.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ double min, max;
+ register int i;
+ int wantValue;
+
+ wantValue = FALSE;
+ if ((argv[2][0] == '-') && (strcmp(argv[2], "-value") == 0)) {
+ wantValue = TRUE;
+ argv++, argc--;
+ }
+ if (Tcl_ExprDouble(interp, argv[2], &min) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ max = min;
+ if ((argc > 3) && (Tcl_ExprDouble(interp, argv[3], &max) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((min - max) >= DBL_EPSILON) {
+ return TCL_OK; /* Bogus range. Don't bother looking. */
+ }
+ if (wantValue) {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+ }
+ }
+ } else {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_AppendElement(interp, Blt_Itoa(i + vPtr->offset));
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * OffsetOp --
+ *
+ * Queries or sets the offset of the array index from the base
+ * address of the data array of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffsetOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ if (argc == 3) {
+ int newOffset;
+
+ if (Tcl_GetInt(interp, argv[2], &newOffset) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->offset = newOffset;
+ }
+ Tcl_SetResult(interp, Blt_Itoa(vPtr->offset), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RandomOp --
+ *
+ * Generates random values for the length of the vector.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RandomOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+#ifdef HAVE_DRAND48
+ register int i;
+
+ for (i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = drand48();
+ }
+#endif /* HAVE_DRAND48 */
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SequenceOp --
+ *
+ * Generates a sequence of values in the vector.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SequenceOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ double start, finish, step;
+ int fillVector;
+ int nSteps;
+
+ if (Tcl_ExprDouble(interp, argv[2], &start) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fillVector = FALSE;
+ if ((argv[3][0] == 'e') && (strcmp(argv[3], "end") == 0)) {
+ fillVector = TRUE;
+ } else if (Tcl_ExprDouble(interp, argv[3], &finish) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ step = 1.0;
+ if ((argc > 4) && (Tcl_ExprDouble(interp, argv[4], &step) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (fillVector) {
+ nSteps = vPtr->length;
+ } else {
+ nSteps = (int)((finish - start) / step) + 1;
+ }
+ if (nSteps > 0) {
+ if (Blt_VectorChangeLength(vPtr, nSteps) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nSteps; i++) {
+ vPtr->valueArr[i] = start + (step * (double)i);
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ * Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified.
+ * Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SetOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ int result;
+ VectorObject *v2Ptr;
+ int nElem;
+ char **elemArr;
+
+ /* The source can be either a list of expressions of another
+ * vector. */
+ if (Tcl_SplitList(interp, argv[2], &nElem, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* If there's only one element, see whether it's the name of a
+ * vector. Otherwise, treat it as a single numeric expression. */
+
+ if ((nElem == 1) && ((v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL,
+ vPtr->dataPtr, argv[2], (char **)NULL, NS_SEARCH_BOTH)) != NULL)) {
+ if (vPtr == v2Ptr) {
+ VectorObject *tmpPtr;
+
+ /*
+ * Source and destination vectors are the same. Copy the
+ * source first into a temporary vector to avoid memory
+ * overlaps.
+ */
+ tmpPtr = Blt_VectorNew(vPtr->dataPtr);
+ result = Blt_VectorDuplicate(tmpPtr, v2Ptr);
+ if (result == TCL_OK) {
+ result = Blt_VectorDuplicate(vPtr, tmpPtr);
+ }
+ Blt_VectorFree(tmpPtr);
+ } else {
+ result = Blt_VectorDuplicate(vPtr, v2Ptr);
+ }
+ } else {
+ result = CopyList(vPtr, nElem, elemArr);
+ }
+ Blt_Free(elemArr);
+
+ if (result == TCL_OK) {
+ /*
+ * The vector has changed; so flush the array indices (they're
+ * wrong now), find the new range of the data, and notify
+ * the vector's clients that it's been modified.
+ */
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return result;
+}
+
+static VectorObject **sortVectorArr; /* Pointer to the array of values currently
+ * being sorted. */
+static int nSortVectors;
+static int reverse; /* Indicates the ordering of the sort. If
+ * non-zero, the vectors are sorted in
+ * decreasing order */
+
+static int
+CompareVectors(a, b)
+ void *a;
+ void *b;
+{
+ double delta;
+ int i;
+ int sign;
+ register VectorObject *vPtr;
+
+ sign = (reverse) ? -1 : 1;
+ for (i = 0; i < nSortVectors; i++) {
+ vPtr = sortVectorArr[i];
+ delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b];
+ if (delta < 0.0) {
+ return (-1 * sign);
+ } else if (delta > 0.0) {
+ return (1 * sign);
+ }
+ }
+ return 0;
+}
+
+int *
+Blt_VectorSortIndex(vPtrPtr, nVectors)
+ VectorObject **vPtrPtr;
+ int nVectors;
+{
+ int *indexArr;
+ register int i;
+ VectorObject *vPtr = *vPtrPtr;
+
+ indexArr = Blt_Malloc(sizeof(int) * vPtr->length);
+ assert(indexArr);
+ for (i = 0; i < vPtr->length; i++) {
+ indexArr[i] = i;
+ }
+ sortVectorArr = vPtrPtr;
+ nSortVectors = nVectors;
+ qsort((char *)indexArr, vPtr->length, sizeof(int),
+ (QSortCompareProc *)CompareVectors);
+ return indexArr;
+}
+
+static int *
+SortVectors(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ VectorObject **vPtrArray, *v2Ptr;
+ int *iArr;
+ register int i;
+
+ vPtrArray = Blt_Malloc(sizeof(VectorObject *) * (argc + 1));
+ assert(vPtrArray);
+ vPtrArray[0] = vPtr;
+ iArr = NULL;
+ for (i = 0; i < argc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+ goto error;
+ }
+ if (v2Ptr->length != vPtr->length) {
+ Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+ "\" is not the same size as \"", vPtr->name, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ vPtrArray[i + 1] = v2Ptr;
+ }
+ iArr = Blt_VectorSortIndex(vPtrArray, argc + 1);
+ error:
+ Blt_Free(vPtrArray);
+ return iArr;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ * Sorts the vector object and any other vectors according to
+ * sorting order of the vector object.
+ *
+ * Results:
+ * A standard Tcl result. If any of the auxiliary vectors are
+ * a different size than the sorted vector object, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vectors are sorted.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+static int
+SortOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int *iArr;
+ double *mergeArr;
+ VectorObject *v2Ptr;
+ int refSize, nBytes;
+ int result;
+ register int i, n;
+
+ reverse = FALSE;
+ if ((argc > 2) && (argv[2][0] == '-')) {
+ int length;
+
+ length = strlen(argv[2]);
+ if ((length > 1) && (strncmp(argv[2], "-reverse", length) == 0)) {
+ reverse = TRUE;
+ } else {
+ Tcl_AppendResult(interp, "unknown flag \"", argv[2],
+ "\": should be \"-reverse\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ argc--, argv++;
+ }
+ if (argc > 2) {
+ iArr = SortVectors(vPtr, interp, argc - 2, argv + 2);
+ } else {
+ iArr = Blt_VectorSortIndex(&vPtr, 1);
+ }
+ if (iArr == NULL) {
+ return TCL_ERROR;
+ }
+ refSize = vPtr->length;
+
+ /*
+ * Create an array to store a copy of the current values of the
+ * vector. We'll merge the values back into the vector based upon
+ * the indices found in the index array.
+ */
+ nBytes = sizeof(double) * refSize;
+ mergeArr = Blt_Malloc(nBytes);
+ assert(mergeArr);
+ memcpy((char *)mergeArr, (char *)vPtr->valueArr, nBytes);
+ for (n = 0; n < refSize; n++) {
+ vPtr->valueArr[n] = mergeArr[iArr[n]];
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+
+ /* Now sort any other vectors in the same fashion. The vectors
+ * must be the same size as the iArr though. */
+ result = TCL_ERROR;
+ for (i = 2; i < argc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+ goto error;
+ }
+ if (v2Ptr->length != refSize) {
+ Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+ "\" is not the same size as \"", vPtr->name, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ memcpy((char *)mergeArr, (char *)v2Ptr->valueArr, nBytes);
+ for (n = 0; n < refSize; n++) {
+ v2Ptr->valueArr[n] = mergeArr[iArr[n]];
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ }
+ result = TCL_OK;
+ error:
+ Blt_Free(mergeArr);
+ Blt_Free(iArr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InstExprOp --
+ *
+ * Computes the result of the expression which may be
+ * either a scalar (single value) or vector (list of values).
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InstExprOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (Blt_ExprVector(interp, argv[2], (Blt_Vector *) vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ArithOp --
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified.
+ * Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArithOp(vPtr, interp, argc, argv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register double value;
+ register int i;
+ VectorObject *v2Ptr;
+
+ v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, argv[2],
+ (char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ register int j;
+ int length;
+
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (length != vPtr->length) {
+ Tcl_AppendResult(interp, "vectors \"", argv[0], "\" and \"",
+ argv[2], "\" are not the same length", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (argv[1][0]) {
+ case '*':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] * v2Ptr->valueArr[j];
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '/':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] / v2Ptr->valueArr[j];
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '-':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] - v2Ptr->valueArr[j];
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '+':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] + v2Ptr->valueArr[j];
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+ }
+ } else {
+ double scalar;
+
+ if (Tcl_ExprDouble(interp, argv[2], &scalar) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (argv[1][0]) {
+ case '*':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] * scalar;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '/':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] / scalar;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '-':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] - scalar;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+
+ case '+':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] + scalar;
+ Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+ }
+ break;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorInstCmd --
+ *
+ * Parses and invokes the appropriate vector instance command
+ * option.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec vectorInstOps[] =
+{
+ {"*", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"+", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"-", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"/", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"append", 1, (Blt_Op)AppendOp, 3, 0, "item ?item...?",},
+ {"binread", 1, (Blt_Op)BinreadOp, 3, 0, "channel ?numValues? ?flags?",},
+ {"clear", 1, (Blt_Op)ClearOp, 2, 2, "",},
+ {"delete", 2, (Blt_Op)DeleteOp, 2, 0, "index ?index...?",},
+ {"dup", 2, (Blt_Op)DupOp, 3, 0, "vecName",},
+ {"expr", 1, (Blt_Op)InstExprOp, 3, 3, "expression",},
+ {"index", 1, (Blt_Op)IndexOp, 3, 4, "index ?value?",},
+ {"length", 1, (Blt_Op)LengthOp, 2, 3, "?newSize?",},
+ {"merge", 1, (Blt_Op)MergeOp, 3, 0, "vecName ?vecName...?",},
+ {"normalize", 3, (Blt_Op)NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/
+ {"notify", 3, (Blt_Op)NotifyOp, 3, 3, "keyword",},
+ {"offset", 2, (Blt_Op)OffsetOp, 2, 3, "?offset?",},
+ {"populate", 1, (Blt_Op)PopulateOp, 4, 4, "vecName density",},
+ {"random", 4, (Blt_Op)RandomOp, 2, 2, "",}, /*Deprecated*/
+ {"range", 4, (Blt_Op)RangeOp, 4, 4, "first last",},
+ {"search", 3, (Blt_Op)SearchOp, 3, 4, "?-value? value ?value?",},
+ {"seq", 3, (Blt_Op)SequenceOp, 4, 5, "start end ?step?",},
+ {"set", 3, (Blt_Op)SetOp, 3, 3, "list",},
+ {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-reverse? ?vecName...?",},
+ {"variable", 1, (Blt_Op)MapOp, 2, 3, "?varName?",},
+};
+
+static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_VectorInstCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ VectorObject *vPtr = clientData;
+
+ vPtr->first = 0;
+ vPtr->last = vPtr->length - 1;
+ proc = Blt_GetOp(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, argc, argv,
+ 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (vPtr, interp, argc, argv);
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorVarTrace --
+ *
+ * Results:
+ * Returns NULL on success. Only called from a variable trace.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+char *
+Blt_VectorVarTrace(clientData, interp, part1, part2, flags)
+ ClientData clientData; /* File output information. */
+ Tcl_Interp *interp;
+ char *part1, *part2;
+ int flags;
+{
+ VectorObject *vPtr = clientData;
+ char string[TCL_DOUBLE_SPACE + 1];
+#define MAX_ERR_MSG 1023
+ static char message[MAX_ERR_MSG + 1];
+ Blt_VectorIndexProc *indexProc;
+ int varFlags;
+ int first, last;
+
+ if (part2 == NULL) {
+ if (flags & TCL_TRACE_UNSETS) {
+ Blt_Free(vPtr->arrayName);
+ vPtr->arrayName = NULL;
+ vPtr->varNsPtr = NULL;
+ if (vPtr->freeOnUnset) {
+ Blt_VectorFree(vPtr);
+ }
+ }
+ return NULL;
+ }
+ if (Blt_VectorGetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS,
+ &indexProc) != TCL_OK) {
+ goto error;
+ }
+ first = vPtr->first, last = vPtr->last;
+ varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags);
+ if (flags & TCL_TRACE_WRITES) {
+ double value;
+ char *newValue;
+
+ if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */
+ return "read-only index";
+ }
+ newValue = Tcl_GetVar2(interp, part1, part2, varFlags);
+ if (newValue == NULL) {
+ goto error;
+ }
+ if (Tcl_ExprDouble(interp, newValue, &value) != TCL_OK) {
+ if ((last == first) && (first >= 0)) {
+ /* Single numeric index. Reset the array element to
+ * its old value on errors */
+ Tcl_PrintDouble(interp, vPtr->valueArr[first], string);
+ Tcl_SetVar2(interp, part1, part2, string, varFlags);
+ }
+ goto error;
+ }
+ if (first == vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+ return "error resizing vector";
+ }
+ }
+ /* Set possibly an entire range of values */
+ ReplicateValue(vPtr, first, last, value);
+ } else if (flags & TCL_TRACE_READS) {
+ double value;
+
+ if (vPtr->length == 0) {
+ if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) {
+ goto error;
+ }
+ return NULL;
+ }
+ if (first == vPtr->length) {
+ return "write-only index";
+ }
+ if (first == last) {
+ if (first >= 0) {
+ value = vPtr->valueArr[first];
+ } else {
+ vPtr->first = 0, vPtr->last = vPtr->length - 1;
+ value = (*indexProc) ((Blt_Vector *) vPtr);
+ }
+ Tcl_PrintDouble(interp, value, string);
+ if (Tcl_SetVar2(interp, part1, part2, string, varFlags)
+ == NULL) {
+ goto error;
+ }
+ } else {
+ Tcl_DString dString;
+ char *result;
+
+ Tcl_DStringInit(&dString);
+ GetValues(vPtr, first, last, &dString);
+ result = Tcl_SetVar2(interp, part1, part2,
+ Tcl_DStringValue(&dString), varFlags);
+ Tcl_DStringFree(&dString);
+ if (result == NULL) {
+ goto error;
+ }
+ }
+ } else if (flags & TCL_TRACE_UNSETS) {
+ register int i, j;
+
+ if ((first == vPtr->length) || (first == SPECIAL_INDEX)) {
+ return "special vector index";
+ }
+ /*
+ * Collapse the vector from the point of the first unset element.
+ * Also flush any array variable entries so that the shift is
+ * reflected when the array variable is read.
+ */
+ for (i = first, j = last + 1; j < vPtr->length; i++, j++) {
+ vPtr->valueArr[i] = vPtr->valueArr[j];
+ }
+ vPtr->length -= ((last - first) + 1);
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ } else {
+ return "unknown variable trace flag";
+ }
+ if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) {
+ Blt_VectorUpdateClients(vPtr);
+ }
+ Tcl_ResetResult(interp);
+ return NULL;
+
+ error:
+ strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG);
+ message[MAX_ERR_MSG] = '\0';
+ return message;
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 */
diff --git a/blt/src/bltVecInt.h b/blt/src/bltVecInt.h
new file mode 100644
index 00000000000..08b6844db9d
--- /dev/null
+++ b/blt/src/bltVecInt.h
@@ -0,0 +1,248 @@
+/*
+ * bltVecInt.h --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+
+#include "bltInt.h"
+#include <bltHash.h>
+#include <bltChain.h>
+
+#define VECTOR_THREAD_KEY "BLT Vector Data"
+#define VECTOR_MAGIC ((unsigned int) 0x46170277)
+
+/* These defines allow parsing of different types of indices */
+
+#define INDEX_SPECIAL (1<<0) /* Recognize "min", "max", and "++end" as
+ * valid indices */
+#define INDEX_COLON (1<<1) /* Also recognize a range of indices
+ * separated by a colon */
+#define INDEX_CHECK (1<<2) /* Verify that the specified index or
+ * range of indices are within limits */
+#define INDEX_ALL_FLAGS (INDEX_SPECIAL | INDEX_COLON | INDEX_CHECK)
+
+#define SPECIAL_INDEX -2
+
+
+typedef struct {
+ Blt_HashTable vectorTable; /* Table of vectors */
+ Blt_HashTable mathProcTable; /* Table of vector math functions */
+ Blt_HashTable indexProcTable;
+ Tcl_Interp *interp;
+ unsigned int nextId;
+} VectorInterpData;
+
+/*
+ * VectorObject --
+ *
+ * A vector is an array of double precision values. It can be
+ * accessed through a Tcl command, a Tcl array variable, or C
+ * API. The storage for the array points initially to a
+ * statically allocated buffer, but to malloc-ed memory if more
+ * is necessary.
+ *
+ * Vectors can be shared by several clients (for example, two
+ * different graph widgets). The data is shared. When a client
+ * wants to use a vector, it allocates a vector identifier, which
+ * identifies the client. Clients use this ID to specify a
+ * callback routine to be invoked whenever the vector is modified
+ * or destroyed. Whenever the vector is updated or destroyed,
+ * each client is notified of the change by their callback
+ * routine.
+ */
+
+typedef struct {
+
+ /*
+ * If you change these fields, make sure you change the definition
+ * of Blt_Vector in bltInt.h and blt.h too.
+ */
+
+ double *valueArr; /* Array of values (malloc-ed) */
+
+ int length; /* Current number of values in the array. */
+
+ int size; /* Maximum number of values that can be stored
+ * in the value array. */
+
+ double min, max; /* Minimum and maximum values in the vector */
+
+ int dirty; /* Indicates if the vector has been updated */
+
+ int reserved;
+
+ /* The following fields are local to this module */
+
+ char *name; /* The namespace-qualified name of the vector.
+ * It points to the hash key allocated for the
+ * entry in the vector hash table. */
+
+ VectorInterpData *dataPtr;
+ Tcl_Interp *interp; /* Interpreter associated with the
+ * vector */
+
+ Blt_HashEntry *hashPtr; /* If non-NULL, pointer in a hash table to
+ * track the vectors in use. */
+
+ Tcl_FreeProc *freeProc; /* Address of procedure to call to
+ * release storage for the value
+ * array, Optionally can be one of the
+ * following: TCL_STATIC, TCL_DYNAMIC,
+ * or TCL_VOLATILE. */
+
+ char *arrayName; /* The name of the Tcl array variable
+ * mapped to the vector
+ * (malloc'ed). If NULL, indicates
+ * that the vector isn't mapped to any
+ * variable */
+
+ Tcl_Namespace *varNsPtr; /* Namespace context of the Tcl variable
+ * associated with the vector. This is
+ * needed to reset the indices of the array
+ * variable. */
+
+ Tcl_Namespace *nsPtr; /* Namespace context of the vector itself. */
+
+ int offset; /* Offset from zero of the vector's
+ * starting index */
+
+ Tcl_Command cmdToken; /* Token for vector's Tcl command. */
+
+ Blt_Chain *chainPtr; /* List of clients using this vector */
+
+ int notifyFlags; /* Notification flags. See definitions
+ * below */
+
+ int varFlags; /* Indicate if the variable is global,
+ * namespace, or local */
+
+ int freeOnUnset; /* For backward compatibility only: If
+ * non-zero, free the vector when its
+ * variable is unset. */
+ int flush;
+
+ int first, last; /* Selected region of vector. This is used
+ * mostly for the math routines */
+} VectorObject;
+
+#define NOTIFY_UPDATED ((int)BLT_VECTOR_NOTIFY_UPDATE)
+#define NOTIFY_DESTROYED ((int)BLT_VECTOR_NOTIFY_DESTROY)
+
+#define NOTIFY_NEVER (1<<3) /* Never notify clients of updates to
+ * the vector */
+#define NOTIFY_ALWAYS (1<<4) /* Notify clients after each update
+ * of the vector is made */
+#define NOTIFY_WHENIDLE (1<<5) /* Notify clients at the next idle point
+ * that the vector has been updated. */
+
+#define NOTIFY_PENDING (1<<6) /* A do-when-idle notification of the
+ * vector's clients is pending. */
+#define NOTIFY_NOW (1<<7) /* Notify clients of changes once
+ * immediately */
+
+#define NOTIFY_WHEN_MASK (NOTIFY_NEVER|NOTIFY_ALWAYS|NOTIFY_WHENIDLE)
+
+#define UPDATE_RANGE (1<<9) /* The data of the vector has changed.
+ * Update the min and max limits when
+ * they are needed */
+
+#define FindRange(array, first, last, min, max) \
+{ \
+ min = max = 0.0; \
+ if (first <= last) { \
+ register int i; \
+ min = max = array[first]; \
+ for (i = first + 1; i <= last; i++) { \
+ if (min > array[i]) { \
+ min = array[i]; \
+ } else if (max < array[i]) { \
+ max = array[i]; \
+ } \
+ } \
+ } \
+}
+
+extern void Blt_VectorInstallSpecialIndices
+ _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern void Blt_VectorInstallMathFunctions
+ _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern void Blt_VectorUninstallMathFunctions
+ _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern VectorInterpData *Blt_VectorGetInterpData
+ _ANSI_ARGS_((Tcl_Interp *interp));
+
+extern VectorObject *Blt_VectorNew _ANSI_ARGS_((VectorInterpData *dataPtr));
+
+extern int Blt_VectorDuplicate _ANSI_ARGS_((VectorObject *destPtr,
+ VectorObject *srcPtr));
+
+extern int Blt_VectorChangeLength _ANSI_ARGS_((VectorObject *vPtr,
+ int length));
+
+extern VectorObject *Blt_VectorParseElement _ANSI_ARGS_((Tcl_Interp *interp,
+ VectorInterpData *dataPtr, char *start, char **endPtr, int flags));
+
+extern void Blt_VectorFree _ANSI_ARGS_((VectorObject *vPtr));
+
+extern int *Blt_VectorSortIndex _ANSI_ARGS_((VectorObject **vPtrPtr,
+ int nVectors));
+
+extern int Blt_VectorLookupName _ANSI_ARGS_((VectorInterpData *dataPtr,
+ char *vecName, VectorObject **vPtrPtr));
+
+extern VectorObject *Blt_VectorCreate _ANSI_ARGS_((VectorInterpData *dataPtr,
+ char *name, char *cmdName, char *varName, int *newPtr));
+
+extern void Blt_VectorUpdateRange _ANSI_ARGS_((VectorObject *vPtr));
+
+extern void Blt_VectorUpdateClients _ANSI_ARGS_((VectorObject *vPtr));
+
+extern void Blt_VectorFlushCache _ANSI_ARGS_((VectorObject *vPtr));
+
+extern int Blt_VectorReset _ANSI_ARGS_((VectorObject *vPtr, double *dataArr,
+ int nValues, int arraySize, Tcl_FreeProc *freeProc));
+
+extern int Blt_VectorGetIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ VectorObject *vPtr, char *string, int *indexPtr, int flags,
+ Blt_VectorIndexProc **procPtrPtr));
+
+extern int Blt_VectorGetIndexRange _ANSI_ARGS_((Tcl_Interp *interp,
+ VectorObject *vPtr, char *string, int flags,
+ Blt_VectorIndexProc **procPtrPtr));
+
+extern int Blt_VectorMapVariable _ANSI_ARGS_((Tcl_Interp *interp,
+ VectorObject *vPtr, char *name));
+
+#if (TCL_MAJOR_VERSION == 7)
+extern Tcl_CmdProc Blt_VectorInstCmd;
+#else
+extern Tcl_ObjCmdProc Blt_VectorInstCmd;
+#endif
+
+extern Tcl_VarTraceProc Blt_VectorVarTrace;
+
+extern Tcl_IdleProc Blt_VectorNotifyClients;
diff --git a/blt/src/bltVecMath.c b/blt/src/bltVecMath.c
new file mode 100644
index 00000000000..679e31151b3
--- /dev/null
+++ b/blt/src/bltVecMath.c
@@ -0,0 +1,2041 @@
+/*
+ * bltVecMath.c --
+ *
+ * This module implements mathematical expressions with vector
+ * data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltVecInt.h"
+
+/*
+ * Three types of math functions:
+ *
+ * ComponentProc Function is applied in multiple calls to
+ * each component of the vector.
+ * VectorProc Entire vector is passed, each component is
+ * modified.
+ * ScalarProc Entire vector is passed, single scalar value
+ * is returned.
+ */
+
+typedef double (ComponentProc) _ANSI_ARGS_((double value));
+typedef int (VectorProc) _ANSI_ARGS_((VectorObject *vPtr));
+typedef double (ScalarProc) _ANSI_ARGS_((VectorObject *vPtr));
+
+/*
+ * Built-in math functions:
+ */
+typedef int (GenericMathProc) _ANSI_ARGS_(ANYARGS);
+
+/*
+ * MathFunction --
+ *
+ * Contains information about math functions that can be called
+ * for vectors. The table of math functions is global within the
+ * application. So you can't define two different "sqrt"
+ * functions.
+ */
+typedef struct {
+ char *name; /* Name of built-in math function. If
+ * NULL, indicates that the function
+ * was user-defined and dynamically
+ * allocated. Function names are
+ * global across all interpreters. */
+
+ GenericMathProc *proc; /* Procedure that implements this math
+ * function. */
+
+ ClientData clientData; /* Argument to pass when invoking the
+ * function. */
+
+} MathFunction;
+
+
+/*
+ * Macros for testing floating-point values for certain special cases:
+ *
+ * IS_NAN Test for not-a-number by comparing a value against itself
+ * IF_INF Test for infinity by comparing against the largest floating
+ * point value.
+ */
+
+#define IS_NAN(v) ((v) != (v))
+
+#ifdef DBL_MAX
+# define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
+#else
+# define IS_INF(v) 0
+#endif
+
+/* The data structure below is used to describe an expression value,
+ * which can be either a double-precision floating-point value, or a
+ * string. A given number has only one value at a time. */
+
+#define STATIC_STRING_SPACE 150
+
+/*
+ * Tokens --
+ *
+ * The token types are defined below. In addition, there is a
+ * table associating a precedence with each operator. The order
+ * of types is important. Consult the code before changing it.
+ */
+enum Tokens {
+ VALUE, OPEN_PAREN, CLOSE_PAREN, COMMA, END, UNKNOWN,
+ MULT = 8, DIVIDE, MOD, PLUS, MINUS,
+ LEFT_SHIFT, RIGHT_SHIFT,
+ LESS, GREATER, LEQ, GEQ, EQUAL, NEQ,
+ OLD_BIT_AND, EXPONENT, OLD_BIT_OR, OLD_QUESTY, OLD_COLON,
+ AND, OR, UNARY_MINUS, OLD_UNARY_PLUS, NOT, OLD_BIT_NOT
+};
+
+/*
+ * ParseValue --
+ *
+ * The following data structure is used by various parsing
+ * procedures to hold information about where to store the
+ * results of parsing (e.g. the substituted contents of a quoted
+ * argument, or the result of a nested command). At any given
+ * time, the space available for output is fixed, but a procedure
+ * may be called to expand the space available if the current
+ * space runs out.
+ */
+
+typedef struct ParseValueStruct ParseValue;
+
+struct ParseValueStruct {
+ char *buffer; /* Address of first character in
+ * output buffer. */
+ char *next; /* Place to store next character in
+ * output buffer. */
+ char *end; /* Address of the last usable character
+ * in the buffer. */
+ void (*expandProc) _ANSI_ARGS_((ParseValue * pvPtr, int needed));
+ /* Procedure to call when space runs out;
+ * it will make more space. */
+ ClientData clientData; /* Arbitrary information for use of
+ * expandProc. */
+};
+
+typedef struct {
+ VectorObject *vPtr;
+ char staticSpace[STATIC_STRING_SPACE];
+ ParseValue pv; /* Used to hold a string value, if any. */
+} Value;
+
+/*
+ * ParseInfo --
+ *
+ * The data structure below describes the state of parsing an
+ * expression. It's passed among the routines in this module.
+ */
+typedef struct {
+ char *expr; /* The entire right-hand side of the
+ * expression, as originally passed to
+ * Blt_ExprVector. */
+
+ char *nextPtr; /* Position of the next character to
+ * be scanned from the expression
+ * string. */
+
+ enum Tokens token; /* Type of the last token to be parsed
+ * from nextPtr. See below for
+ * definitions. Corresponds to the
+ * characters just before nextPtr. */
+
+} ParseInfo;
+
+/*
+ * Precedence table. The values for non-operator token types are ignored.
+ */
+static int precTable[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 12, 12, /* MULT, DIVIDE, MOD */
+ 11, 11, /* PLUS, MINUS */
+ 10, 10, /* LEFT_SHIFT, RIGHT_SHIFT */
+ 9, 9, 9, 9, /* LESS, GREATER, LEQ, GEQ */
+ 8, 8, /* EQUAL, NEQ */
+ 7, /* OLD_BIT_AND */
+ 13, /* EXPONENTIATION */
+ 5, /* OLD_BIT_OR */
+ 4, /* AND */
+ 3, /* OR */
+ 2, /* OLD_QUESTY */
+ 1, /* OLD_COLON */
+ 14, 14, 14, 14 /* UNARY_MINUS, OLD_UNARY_PLUS, NOT,
+ * OLD_BIT_NOT */
+};
+
+
+/*
+ * Forward declarations.
+ */
+
+static int NextValue _ANSI_ARGS_((Tcl_Interp *interp, ParseInfo *parsePtr,
+ int prec, Value * valuePtr));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+#define TclParseBraces Blt_ParseBraces
+#define TclParseNestedCmd Blt_ParseNestedCmd
+#define TclParseQuotes Blt_ParseQuotes
+#define TclExpandParseValue Blt_ExpandParseValue
+#endif /* TCL_VERSION_NUMBER >= _VERSION(8,1,0) */
+
+extern int TclParseBraces _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ char **termPtr, ParseValue * pvPtr));
+
+extern int TclParseNestedCmd _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ int flags, char **termPtr, ParseValue * pvPtr));
+
+extern int TclParseQuotes _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ int termChar, int flags, char **termPtr, ParseValue * pvPtr));
+
+extern void TclExpandParseValue _ANSI_ARGS_((ParseValue * pvPtr, int needed));
+
+#if defined(HAVE_DRAND48) && defined(NO_DECL_DRAND48)
+extern double drand48 _ANSI_ARGS_((void));
+#endif
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * First --
+ *
+ * Gets the first index of the designated interval. The interval
+ * is between vPtr->first and vPtr->last. But the range may
+ * NaN or Inf values that should be ignored.
+ *
+ * Results:
+ * Returns the index of the first finite value in the designated
+ * interval. If no finite values exists in the range, then -1 is
+ * returned.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+First(vPtr)
+ VectorObject *vPtr;
+{
+ register int i;
+
+ for(i = vPtr->first; i <= vPtr->last; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Next --
+ *
+ * Gets the next index of the designated interval. The interval
+ * is between vPtr->first and vPtr->last. Ignore NaN or Inf
+ * values.
+ *
+ * Results:
+ * Returns the index of the next finite value in the designated
+ * interval. If no more finite values exists in the range,
+ * then -1 is returned.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+Next(vPtr, current)
+ VectorObject *vPtr;
+ int current;
+{
+ register int i;
+
+ for(i = current + 1; i <= vPtr->last; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Sort --
+ *
+ * A vector math function. Sorts the values of the given
+ * vector.
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ * Side Effects:
+ * The vector is sorted.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+Sort(vPtr)
+ VectorObject *vPtr;
+{
+ int *indexArr;
+ double *tempArr;
+ register int i;
+
+ indexArr = Blt_VectorSortIndex(&vPtr, 1);
+ tempArr = Blt_Malloc(sizeof(double) * vPtr->length);
+ assert(tempArr);
+ for(i = vPtr->first; i <= vPtr->last; i++) {
+ tempArr[i] = vPtr->valueArr[indexArr[i]];
+ }
+ Blt_Free(indexArr);
+ for(i = vPtr->first; i <= vPtr->last; i++) {
+ vPtr->valueArr[i] = tempArr[i];
+ }
+ Blt_Free(tempArr);
+ return TCL_OK;
+}
+
+static double
+Length(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ int count;
+ register int i;
+
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ count++;
+ }
+ return (double) count;
+}
+
+/* ARGSUSED */
+double
+Blt_VecMin(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ double min;
+ register int i;
+
+ min = DBL_MAX;
+ for (i = 0; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ min = vPtr->valueArr[i];
+ break;
+ }
+ }
+ for (/* empty */; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ if (min > vPtr->valueArr[i]) {
+ min = vPtr->valueArr[i];
+ }
+ }
+ }
+ return min;
+}
+
+double
+Blt_VecMax(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ double max;
+ register int i;
+
+ max = -DBL_MAX;
+ for (i = 0; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ max = vPtr->valueArr[i];
+ break;
+ }
+ }
+ for (/* empty */; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ if (max < vPtr->valueArr[i]) {
+ max = vPtr->valueArr[i];
+ }
+ }
+ }
+ return max;
+}
+
+static double
+Mean(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register int i;
+ int count;
+ double sum;
+
+ sum = 0.0;
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ sum += vPtr->valueArr[i];
+ count++;
+ }
+ return sum / (double)count;
+}
+
+/*
+ * var = 1/N Sum( (x[i] - mean)^2 )
+ */
+static double
+Variance(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register double dx, var, mean;
+ register int i;
+ int count;
+
+ mean = Mean(vecPtr);
+ var = 0.0;
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ dx = vPtr->valueArr[i] - mean;
+ var += dx * dx;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ return var;
+}
+
+/*
+ * skew = Sum( (x[i] - mean)^3 ) / (var^3/2)
+ */
+static double
+Skew(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register double diff, var, skew, mean, diffsq;
+ register int i;
+ int count;
+
+ mean = Mean(vecPtr);
+ var = skew = 0.0;
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ diff = vPtr->valueArr[i] - mean;
+ diff = FABS(diff);
+ diffsq = diff * diff;
+ var += diffsq;
+ skew += diffsq * diff;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ skew /= count * var * sqrt(var);
+ return skew;
+}
+
+static double
+StdDeviation(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ double var;
+
+ var = Variance(vecPtr);
+ if (var > 0.0) {
+ return sqrt(var);
+ }
+ return 0.0;
+}
+
+
+static double
+AvgDeviation(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register double diff, avg, mean;
+ register int i;
+ int count;
+
+ mean = Mean(vecPtr);
+ avg = 0.0;
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ diff = vPtr->valueArr[i] - mean;
+ avg += FABS(diff);
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ avg /= (double)count;
+ return avg;
+}
+
+
+static double
+Kurtosis(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register double diff, diffsq, kurt, var, mean;
+ register int i;
+ int count;
+
+ mean = Mean(vecPtr);
+ var = kurt = 0.0;
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ diff = vPtr->valueArr[i] - mean;
+ diffsq = diff * diff;
+ var += diffsq;
+ kurt += diffsq * diffsq;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ if (var == 0.0) {
+ return 0.0;
+ }
+ kurt /= (count * var * var);
+ return kurt - 3.0; /* Fisher Kurtosis */
+}
+
+
+static double
+Median(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ int *iArr;
+ double q2;
+ int mid;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+ iArr = Blt_VectorSortIndex(&vPtr, 1);
+ mid = (vPtr->length - 1) / 2;
+
+ /*
+ * Determine Q2 by checking if the number of elements [0..n-1] is
+ * odd or even. If even, we must take the average of the two
+ * middle values.
+ */
+ if (vPtr->length & 1) { /* Odd */
+ q2 = vPtr->valueArr[iArr[mid]];
+ } else { /* Even */
+ q2 = (vPtr->valueArr[iArr[mid]] + vPtr->valueArr[iArr[mid + 1]]) * 0.5;
+ }
+ Blt_Free(iArr);
+ return q2;
+}
+
+static double
+Q1(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ double q1;
+ int *iArr;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+ iArr = Blt_VectorSortIndex(&vPtr, 1);
+
+ if (vPtr->length < 4) {
+ q1 = vPtr->valueArr[iArr[0]];
+ } else {
+ int mid, q;
+
+ mid = (vPtr->length - 1) / 2;
+ q = mid / 2;
+
+ /*
+ * Determine Q1 by checking if the number of elements in the
+ * bottom half [0..mid) is odd or even. If even, we must
+ * take the average of the two middle values.
+ */
+ if (mid & 1) { /* Odd */
+ q1 = vPtr->valueArr[iArr[q]];
+ } else { /* Even */
+ q1 = (vPtr->valueArr[iArr[q]] + vPtr->valueArr[iArr[q + 1]]) * 0.5;
+ }
+ }
+ Blt_Free(iArr);
+ return q1;
+}
+
+static double
+Q3(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ double q3;
+ int *iArr;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+
+ iArr = Blt_VectorSortIndex(&vPtr, 1);
+
+ if (vPtr->length < 4) {
+ q3 = vPtr->valueArr[iArr[vPtr->length - 1]];
+ } else {
+ int mid, q;
+
+ mid = (vPtr->length - 1) / 2;
+ q = (vPtr->length + mid) / 2;
+
+ /*
+ * Determine Q3 by checking if the number of elements in the
+ * upper half (mid..n-1] is odd or even. If even, we must
+ * take the average of the two middle values.
+ */
+ if (mid & 1) { /* Odd */
+ q3 = vPtr->valueArr[iArr[q]];
+ } else { /* Even */
+ q3 = (vPtr->valueArr[iArr[q]] + vPtr->valueArr[iArr[q + 1]]) * 0.5;
+ }
+ }
+ Blt_Free(iArr);
+ return q3;
+}
+
+
+static int
+Norm(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ double norm, range, min, max;
+ register int i;
+
+ min = Blt_VecMin(vecPtr);
+ max = Blt_VecMax(vecPtr);
+ range = max - min;
+ for(i = 0; i < vPtr->length; i++) {
+ norm = (vPtr->valueArr[i] - min) / range;
+ vPtr->valueArr[i] = norm;
+ }
+ return TCL_OK;
+}
+
+static double
+Product(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register int i;
+ register double prod;
+
+ prod = 1.0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ prod *= vPtr->valueArr[i];
+ }
+ return prod;
+}
+
+static double
+Sum(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register int i;
+ double sum;
+
+ sum = 0.0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ sum += vPtr->valueArr[i];
+ }
+ return sum;
+}
+
+static double
+Nonzeros(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ register int i;
+ int count;
+
+ count = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ if (vPtr->valueArr[i] == 0.0) {
+ count++;
+ }
+ }
+ return (double) count;
+}
+
+static double
+Fabs(value)
+ double value;
+{
+ if (value < 0.0) {
+ return -value;
+ }
+ return value;
+}
+
+static double
+Round(value)
+ double value;
+{
+ if (value < 0.0) {
+ return ceil(value - 0.5);
+ } else {
+ return floor(value + 0.5);
+ }
+}
+
+static double
+Fmod(x, y)
+ double x, y;
+{
+ if (y == 0.0) {
+ return 0.0;
+ }
+ return x - (floor(x / y) * y);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MathError --
+ *
+ * This procedure is called when an error occurs during a
+ * floating-point operation. It reads errno and sets
+ * interp->result accordingly.
+ *
+ * Results:
+ * Interp->result is set to hold an error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MathError(interp, value)
+ Tcl_Interp *interp; /* Where to store error message. */
+ double value; /* Value returned after error; used to
+ * distinguish underflows from overflows. */
+{
+ if ((errno == EDOM) || (value != value)) {
+ Tcl_AppendResult(interp, "domain error: argument not in valid range",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", interp->result,
+ (char *)NULL);
+ } else if ((errno == ERANGE) || IS_INF(value)) {
+ if (value == 0.0) {
+ Tcl_AppendResult(interp,
+ "floating-point value too small to represent",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", interp->result,
+ (char *)NULL);
+ } else {
+ Tcl_AppendResult(interp,
+ "floating-point value too large to represent",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", interp->result,
+ (char *)NULL);
+ }
+ } else {
+ char buf[20];
+
+ sprintf(buf, "%d", errno);
+ Tcl_AppendResult(interp, "unknown floating-point error, ",
+ "errno = ", buf, (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", interp->result,
+ (char *)NULL);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseString --
+ *
+ * Given a string (such as one coming from command or variable
+ * substitution), make a Value based on the string. The value
+ * will be a floating-point or integer, if possible, or else it
+ * will just be a copy of the string.
+ *
+ * Results:
+ * TCL_OK is returned under normal circumstances, and TCL_ERROR
+ * is returned if a floating-point overflow or underflow occurred
+ * while reading in a number. The value at *valuePtr is modified
+ * to hold a number, if possible.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ParseString(interp, string, valuePtr)
+ Tcl_Interp *interp; /* Where to store error message. */
+ char *string; /* String to turn into value. */
+ Value *valuePtr; /* Where to store value information.
+ * Caller must have initialized pv field. */
+{
+ char *endPtr;
+ double value;
+
+ errno = 0;
+
+ /*
+ * The string can be either a number or a vector. First try to
+ * convert the string to a number. If that fails then see if
+ * we can find a vector by that name.
+ */
+
+ value = strtod(string, &endPtr);
+ if ((endPtr != string) && (*endPtr == '\0')) {
+ if (errno != 0) {
+ Tcl_ResetResult(interp);
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ /* Numbers are stored as single element vectors. */
+ if (Blt_VectorChangeLength(valuePtr->vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ valuePtr->vPtr->valueArr[0] = value;
+ return TCL_OK;
+ } else {
+ VectorObject *vPtr;
+
+ while (isspace(UCHAR(*string))) {
+ string++; /* Skip spaces leading the vector name. */
+ }
+ vPtr = Blt_VectorParseElement(interp, valuePtr->vPtr->dataPtr, string,
+ &endPtr, NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (*endPtr != '\0') {
+ Tcl_AppendResult(interp, "extra characters after vector",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Copy the designated vector to our temporary. */
+ Blt_VectorDuplicate(valuePtr->vPtr, vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseMathFunction --
+ *
+ * This procedure is invoked to parse a math function from an
+ * expression string, carry out the function, and return the
+ * value computed.
+ *
+ * Results:
+ * TCL_OK is returned if all went well and the function's value
+ * was computed successfully. If the name doesn't match any
+ * known math function, returns TCL_RETURN. And if a format error
+ * was found, TCL_ERROR is returned and an error message is left
+ * in interp->result.
+ *
+ * After a successful return parsePtr will be updated to point to
+ * the character just after the function call, the token is set
+ * to VALUE, and the value is stored in valuePtr.
+ *
+ * Side effects:
+ * Embedded commands could have arbitrary side-effects.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ParseMathFunction(interp, start, parsePtr, valuePtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ char *start; /* Start of string to parse */
+ ParseInfo *parsePtr; /* Describes the state of the parse.
+ * parsePtr->nextPtr must point to the
+ * first character of the function's
+ * name. */
+ Value *valuePtr; /* Where to store value, if that is
+ * what's parsed from string. Caller
+ * must have initialized pv field
+ * correctly. */
+{
+ Blt_HashEntry *hPtr;
+ MathFunction *mathPtr; /* Info about math function. */
+ register char *p;
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+
+ /*
+ * Find the end of the math function's name and lookup the
+ * record for the function.
+ */
+ p = start;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ parsePtr->nextPtr = p;
+ while (isalnum(UCHAR(*p)) || (*p == '_')) {
+ p++;
+ }
+ if (*p != '(') {
+ return TCL_RETURN; /* Must start with open parenthesis */
+ }
+ dataPtr = valuePtr->vPtr->dataPtr;
+ *p = '\0';
+ hPtr = Blt_FindHashEntry(&(dataPtr->mathProcTable), parsePtr->nextPtr);
+ *p = '(';
+ if (hPtr == NULL) {
+ return TCL_RETURN; /* Name doesn't match any known function */
+ }
+ /* Pick up the single value as the argument to the function */
+ parsePtr->token = OPEN_PAREN;
+ parsePtr->nextPtr = p + 1;
+ valuePtr->pv.next = valuePtr->pv.buffer;
+ if (NextValue(interp, parsePtr, -1, valuePtr) != TCL_OK) {
+ return TCL_ERROR; /* Parse error */
+ }
+ if (parsePtr->token != CLOSE_PAREN) {
+ Tcl_AppendResult(interp, "unmatched parentheses in expression \"",
+ parsePtr->expr, "\"", (char *)NULL);
+ return TCL_ERROR; /* Missing right parenthesis */
+ }
+ mathPtr = (MathFunction *) Blt_GetHashValue(hPtr);
+ if ((*mathPtr->proc) (mathPtr->clientData, interp, valuePtr->vPtr)
+ != TCL_OK) {
+ return TCL_ERROR; /* Function invocation error */
+ }
+ parsePtr->token = VALUE;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextToken --
+ *
+ * Lexical analyzer for expression parser: parses a single value,
+ * operator, or other syntactic element from an expression string.
+ *
+ * Results:
+ * TCL_OK is returned unless an error occurred while doing lexical
+ * analysis or executing an embedded command. In that case a
+ * standard Tcl error is returned, using interp->result to hold
+ * an error message. In the event of a successful return, the token
+ * and field in parsePtr is updated to refer to the next symbol in
+ * the expression string, and the expr field is advanced past that
+ * token; if the token is a value, then the value is stored at
+ * valuePtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NextToken(interp, parsePtr, valuePtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ ParseInfo *parsePtr; /* Describes the state of the parse. */
+ Value *valuePtr; /* Where to store value, if that is
+ * what's parsed from string. Caller
+ * must have initialized pv field
+ * correctly. */
+{
+ register char *p;
+ char *endPtr;
+ char *var;
+ int result;
+
+ p = parsePtr->nextPtr;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p == '\0') {
+ parsePtr->token = END;
+ parsePtr->nextPtr = p;
+ return TCL_OK;
+ }
+ /*
+ * Try to parse the token as a floating-point number. But check
+ * that the first character isn't a "-" or "+", which "strtod"
+ * will happily accept as an unary operator. Otherwise, we might
+ * accidently treat a binary operator as unary by mistake, which
+ * will eventually cause a syntax error.
+ */
+ if ((*p != '-') && (*p != '+')) {
+ double value;
+
+ errno = 0;
+ value = strtod(p, &endPtr);
+ if (endPtr != p) {
+ if (errno != 0) {
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ parsePtr->token = VALUE;
+ parsePtr->nextPtr = endPtr;
+
+ /*
+ * Save the single floating-point value as an 1-component vector.
+ */
+ if (Blt_VectorChangeLength(valuePtr->vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ valuePtr->vPtr->valueArr[0] = value;
+ return TCL_OK;
+ }
+ }
+ parsePtr->nextPtr = p + 1;
+ switch (*p) {
+ case '$':
+ parsePtr->token = VALUE;
+ var = Tcl_ParseVar(interp, p, &endPtr);
+ if (var == NULL) {
+ return TCL_ERROR;
+ }
+ parsePtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, var, valuePtr);
+ return result;
+
+ case '[':
+ parsePtr->token = VALUE;
+ result = TclParseNestedCmd(interp, p + 1, 0, &endPtr, &(valuePtr->pv));
+ if (result != TCL_OK) {
+ return result;
+ }
+ parsePtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '"':
+ parsePtr->token = VALUE;
+ result = TclParseQuotes(interp, p + 1, '"', 0, &endPtr,
+ &(valuePtr->pv));
+ if (result != TCL_OK) {
+ return result;
+ }
+ parsePtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '{':
+ parsePtr->token = VALUE;
+ result = TclParseBraces(interp, p + 1, &endPtr, &valuePtr->pv);
+ if (result != TCL_OK) {
+ return result;
+ }
+ parsePtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '(':
+ parsePtr->token = OPEN_PAREN;
+ break;
+
+ case ')':
+ parsePtr->token = CLOSE_PAREN;
+ break;
+
+ case ',':
+ parsePtr->token = COMMA;
+ break;
+
+ case '*':
+ parsePtr->token = MULT;
+ break;
+
+ case '/':
+ parsePtr->token = DIVIDE;
+ break;
+
+ case '%':
+ parsePtr->token = MOD;
+ break;
+
+ case '+':
+ parsePtr->token = PLUS;
+ break;
+
+ case '-':
+ parsePtr->token = MINUS;
+ break;
+
+ case '^':
+ parsePtr->token = EXPONENT;
+ break;
+
+ case '<':
+ switch (*(p + 1)) {
+ case '<':
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = LEFT_SHIFT;
+ break;
+ case '=':
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = LEQ;
+ break;
+ default:
+ parsePtr->token = LESS;
+ break;
+ }
+ break;
+
+ case '>':
+ switch (*(p + 1)) {
+ case '>':
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = RIGHT_SHIFT;
+ break;
+ case '=':
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = GEQ;
+ break;
+ default:
+ parsePtr->token = GREATER;
+ break;
+ }
+ break;
+
+ case '=':
+ if (*(p + 1) == '=') {
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = EQUAL;
+ } else {
+ parsePtr->token = UNKNOWN;
+ }
+ break;
+
+ case '&':
+ if (*(p + 1) == '&') {
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = AND;
+ } else {
+ parsePtr->token = UNKNOWN;
+ }
+ break;
+
+ case '|':
+ if (*(p + 1) == '|') {
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = OR;
+ } else {
+ parsePtr->token = UNKNOWN;
+ }
+ break;
+
+ case '!':
+ if (*(p + 1) == '=') {
+ parsePtr->nextPtr = p + 2;
+ parsePtr->token = NEQ;
+ } else {
+ parsePtr->token = NOT;
+ }
+ break;
+
+ default:
+ parsePtr->token = VALUE;
+ result = ParseMathFunction(interp, p, parsePtr, valuePtr);
+ if ((result == TCL_OK) || (result == TCL_ERROR)) {
+ return result;
+ } else {
+ VectorObject *vPtr;
+
+ while (isspace(UCHAR(*p))) {
+ p++; /* Skip spaces leading the vector name. */
+ }
+ vPtr = Blt_VectorParseElement(interp, valuePtr->vPtr->dataPtr, p,
+ &endPtr, NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_VectorDuplicate(valuePtr->vPtr, vPtr);
+ parsePtr->nextPtr = endPtr;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextValue --
+ *
+ * Parse a "value" from the remainder of the expression in parsePtr.
+ *
+ * Results:
+ * Normally TCL_OK is returned. The value of the expression is
+ * returned in *valuePtr. If an error occurred, then interp->result
+ * contains an error message and TCL_ERROR is returned.
+ * InfoPtr->token will be left pointing to the token AFTER the
+ * expression, and parsePtr->nextPtr will point to the character just
+ * after the terminating token.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NextValue(interp, parsePtr, prec, valuePtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ ParseInfo *parsePtr; /* Describes the state of the parse
+ * just before the value (i.e. NextToken will
+ * be called to get first token of value). */
+ int prec; /* Treat any un-parenthesized operator
+ * with precedence <= this as the end
+ * of the expression. */
+ Value *valuePtr; /* Where to store the value of the expression.
+ * Caller must have initialized pv field. */
+{
+ Value value2; /* Second operand for current operator. */
+ int operator; /* Current operator (either unary or binary). */
+ int gotOp; /* Non-zero means already lexed the operator
+ * (while picking up value for unary operator).
+ * Don't lex again. */
+ int result;
+ VectorObject *vPtr, *v2Ptr;
+ register int i;
+
+ /*
+ * There are two phases to this procedure. First, pick off an initial
+ * value. Then, parse (binary operator, value) pairs until done.
+ */
+
+ vPtr = valuePtr->vPtr;
+ v2Ptr = Blt_VectorNew(vPtr->dataPtr);
+ gotOp = FALSE;
+ value2.vPtr = v2Ptr;
+ value2.pv.buffer = value2.pv.next = value2.staticSpace;
+ value2.pv.end = value2.pv.buffer + STATIC_STRING_SPACE - 1;
+ value2.pv.expandProc = TclExpandParseValue;
+ value2.pv.clientData = NULL;
+
+ result = NextToken(interp, parsePtr, valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if (parsePtr->token == OPEN_PAREN) {
+
+ /* Parenthesized sub-expression. */
+
+ result = NextValue(interp, parsePtr, -1, valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if (parsePtr->token != CLOSE_PAREN) {
+ Tcl_AppendResult(interp, "unmatched parentheses in expression \"",
+ parsePtr->expr, "\"", (char *)NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else {
+ if (parsePtr->token == MINUS) {
+ parsePtr->token = UNARY_MINUS;
+ }
+ if (parsePtr->token >= UNARY_MINUS) {
+ operator = parsePtr->token;
+ result = NextValue(interp, parsePtr, precTable[operator], valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ gotOp = TRUE;
+ /* Process unary operators. */
+ switch (operator) {
+ case UNARY_MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = -(vPtr->valueArr[i]);
+ }
+ break;
+
+ case NOT:
+ for(i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = (double)(!vPtr->valueArr[i]);
+ }
+ break;
+ default:
+ Tcl_AppendResult(interp, "unknown operator", (char *)NULL);
+ goto error;
+ }
+ } else if (parsePtr->token != VALUE) {
+ Tcl_AppendResult(interp, "missing operand", (char *)NULL);
+ goto error;
+ }
+ }
+ if (!gotOp) {
+ result = NextToken(interp, parsePtr, &value2);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ }
+ /*
+ * Got the first operand. Now fetch (operator, operand) pairs.
+ */
+ for (;;) {
+ operator = parsePtr->token;
+
+ value2.pv.next = value2.pv.buffer;
+ if ((operator < MULT) || (operator >= UNARY_MINUS)) {
+ if ((operator == END) || (operator == CLOSE_PAREN)) {
+ result = TCL_OK;
+ goto done;
+ } else {
+ Tcl_AppendResult(interp, "bad operator", (char *)NULL);
+ goto error;
+ }
+ }
+ if (precTable[operator] <= prec) {
+ result = TCL_OK;
+ goto done;
+ }
+ result = NextValue(interp, parsePtr, precTable[operator], &value2);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if ((parsePtr->token < MULT) && (parsePtr->token != VALUE) &&
+ (parsePtr->token != END) && (parsePtr->token != CLOSE_PAREN)) {
+ Tcl_AppendResult(interp, "unexpected token in expression",
+ (char *)NULL);
+ goto error;
+ }
+ /*
+ * At this point we've got two vectors and an operator.
+ */
+
+ if (v2Ptr->length == 1) {
+ register double *opnd;
+ register double scalar;
+
+ /*
+ * 2nd operand is a scalar.
+ */
+ scalar = v2Ptr->valueArr[0];
+ opnd = vPtr->valueArr;
+ switch (operator) {
+ case MULT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] *= scalar;
+ }
+ break;
+
+ case DIVIDE:
+ if (scalar == 0.0) {
+ Tcl_AppendResult(interp, "divide by zero", (char *)NULL);
+ goto error;
+ }
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] /= scalar;
+ }
+ break;
+
+ case PLUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] += scalar;
+ }
+ break;
+
+ case MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] -= scalar;
+ }
+ break;
+
+ case EXPONENT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = pow(opnd[i], scalar);
+ }
+ break;
+
+ case MOD:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = Fmod(opnd[i], scalar);
+ }
+ break;
+
+ case LESS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] < scalar);
+ }
+ break;
+
+ case GREATER:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] > scalar);
+ }
+ break;
+
+ case LEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] <= scalar);
+ }
+ break;
+
+ case GEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] >= scalar);
+ }
+ break;
+
+ case EQUAL:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] == scalar);
+ }
+ break;
+
+ case NEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] != scalar);
+ }
+ break;
+
+ case AND:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] && scalar);
+ }
+ break;
+
+ case OR:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] || scalar);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ {
+ int offset;
+
+ offset = (int)scalar % vPtr->length;
+ if (offset > 0) {
+ double *hold;
+ register int j;
+
+ hold = Blt_Malloc(sizeof(double) * offset);
+ for (i = 0; i < offset; i++) {
+ hold[i] = opnd[i];
+ }
+ for (i = offset, j = 0; i < vPtr->length; i++, j++) {
+ opnd[j] = opnd[i];
+ }
+ for (i = 0, j = vPtr->length - offset;
+ j < vPtr->length; i++, j++) {
+ opnd[j] = hold[i];
+ }
+ Blt_Free(hold);
+ }
+ }
+ break;
+
+ case RIGHT_SHIFT:
+ {
+ int offset;
+
+ offset = (int)scalar % vPtr->length;
+ if (offset > 0) {
+ double *hold;
+ register int j;
+
+ hold = Blt_Malloc(sizeof(double) * offset);
+ for (i = vPtr->length - offset, j = 0;
+ i < vPtr->length; i++, j++) {
+ hold[j] = opnd[i];
+ }
+ for (i = vPtr->length - offset - 1,
+ j = vPtr->length - 1; i >= 0; i--, j--) {
+ opnd[j] = opnd[i];
+ }
+ for (i = 0; i < offset; i++) {
+ opnd[i] = hold[i];
+ }
+ Blt_Free(hold);
+ }
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+
+ } else if (vPtr->length == 1) {
+ register double *opnd;
+ register double scalar;
+
+ /*
+ * 1st operand is a scalar.
+ */
+ scalar = vPtr->valueArr[0];
+ Blt_VectorDuplicate(vPtr, v2Ptr);
+ opnd = vPtr->valueArr;
+ switch (operator) {
+ case MULT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] *= scalar;
+ }
+ break;
+
+ case PLUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] += scalar;
+ }
+ break;
+
+ case DIVIDE:
+ for(i = 0; i < vPtr->length; i++) {
+ if (opnd[i] == 0.0) {
+ Tcl_AppendResult(interp, "divide by zero",
+ (char *)NULL);
+ goto error;
+ }
+ opnd[i] = (scalar / opnd[i]);
+ }
+ break;
+
+ case MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = scalar - opnd[i];
+ }
+ break;
+
+ case EXPONENT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = pow(scalar, opnd[i]);
+ }
+ break;
+
+ case MOD:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = Fmod(scalar, opnd[i]);
+ }
+ break;
+
+ case LESS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar < opnd[i]);
+ }
+ break;
+
+ case GREATER:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar > opnd[i]);
+ }
+ break;
+
+ case LEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar >= opnd[i]);
+ }
+ break;
+
+ case GEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar <= opnd[i]);
+ }
+ break;
+
+ case EQUAL:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] == scalar);
+ }
+ break;
+
+ case NEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] != scalar);
+ }
+ break;
+
+ case AND:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] && scalar);
+ }
+ break;
+
+ case OR:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] || scalar);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ case RIGHT_SHIFT:
+ Tcl_AppendResult(interp, "second shift operand must be scalar",
+ (char *)NULL);
+ goto error;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+ } else {
+ register double *opnd1, *opnd2;
+ /*
+ * Carry out the function of the specified operator.
+ */
+ if (vPtr->length != v2Ptr->length) {
+ Tcl_AppendResult(interp, "vectors are different lengths",
+ (char *)NULL);
+ goto error;
+ }
+ opnd1 = vPtr->valueArr, opnd2 = v2Ptr->valueArr;
+ switch (operator) {
+ case MULT:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] *= opnd2[i];
+ }
+ break;
+
+ case DIVIDE:
+ for (i = 0; i < vPtr->length; i++) {
+ if (opnd2[i] == 0.0) {
+ Tcl_AppendResult(interp,
+ "can't divide by 0.0 vector component",
+ (char *)NULL);
+ goto error;
+ }
+ opnd1[i] /= opnd2[i];
+ }
+ break;
+
+ case PLUS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] += opnd2[i];
+ }
+ break;
+
+ case MINUS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] -= opnd2[i];
+ }
+ break;
+
+ case MOD:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = Fmod(opnd1[i], opnd2[i]);
+ }
+ break;
+
+ case EXPONENT:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = pow(opnd1[i], opnd2[i]);
+ }
+ break;
+
+ case LESS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] < opnd2[i]);
+ }
+ break;
+
+ case GREATER:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] > opnd2[i]);
+ }
+ break;
+
+ case LEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] <= opnd2[i]);
+ }
+ break;
+
+ case GEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] >= opnd2[i]);
+ }
+ break;
+
+ case EQUAL:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] == opnd2[i]);
+ }
+ break;
+
+ case NEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] != opnd2[i]);
+ }
+ break;
+
+ case AND:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] && opnd2[i]);
+ }
+ break;
+
+ case OR:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] || opnd2[i]);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ case RIGHT_SHIFT:
+ Tcl_AppendResult(interp, "second shift operand must be scalar",
+ (char *)NULL);
+ goto error;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ }
+ done:
+ if (value2.pv.buffer != value2.staticSpace) {
+ Blt_Free(value2.pv.buffer);
+ }
+ Blt_VectorFree(v2Ptr);
+ return result;
+
+ error:
+ if (value2.pv.buffer != value2.staticSpace) {
+ Blt_Free(value2.pv.buffer);
+ }
+ Blt_VectorFree(v2Ptr);
+ return TCL_ERROR;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EvaluateExpression --
+ *
+ * This procedure provides top-level functionality shared by
+ * procedures like Tcl_ExprInt, Tcl_ExprDouble, etc.
+ *
+ * Results:
+ * The result is a standard Tcl return value. If an error
+ * occurs then an error message is left in interp->result.
+ * The value of the expression is returned in *valuePtr, in
+ * whatever form it ends up in (could be string or integer
+ * or double). Caller may need to convert result. Caller
+ * is also responsible for freeing string memory in *valuePtr,
+ * if any was allocated.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+EvaluateExpression(interp, string, valuePtr)
+ Tcl_Interp *interp; /* Context in which to evaluate the
+ * expression. */
+ char *string; /* Expression to evaluate. */
+ Value *valuePtr; /* Where to store result. Should
+ * not be initialized by caller. */
+{
+ ParseInfo info;
+ int result;
+ VectorObject *vPtr;
+ register int i;
+
+ info.expr = info.nextPtr = string;
+ valuePtr->pv.buffer = valuePtr->pv.next = valuePtr->staticSpace;
+ valuePtr->pv.end = valuePtr->pv.buffer + STATIC_STRING_SPACE - 1;
+ valuePtr->pv.expandProc = TclExpandParseValue;
+ valuePtr->pv.clientData = NULL;
+
+ result = NextValue(interp, &info, -1, valuePtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (info.token != END) {
+ Tcl_AppendResult(interp, ": syntax error in expression \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ vPtr = valuePtr->vPtr;
+
+ /* Check for NaN's and overflows. */
+ for (i = 0; i < vPtr->length; i++) {
+ if (!finite(vPtr->valueArr[i])) {
+ /*
+ * IEEE floating-point error.
+ */
+ MathError(interp, vPtr->valueArr[i]);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Math Functions --
+ *
+ * This page contains the procedures that implement all of the
+ * built-in math functions for expressions.
+ *
+ * Results:
+ * Each procedure returns TCL_OK if it succeeds and places result
+ * information at *resultPtr. If it fails it returns TCL_ERROR
+ * and leaves an error message in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ComponentFunc(clientData, interp, vPtr)
+ ClientData clientData; /* Contains address of procedure that
+ * takes one double argument and
+ * returns a double result. */
+ Tcl_Interp *interp;
+ VectorObject *vPtr;
+{
+ ComponentProc *procPtr = (ComponentProc *) clientData;
+ register int i;
+
+ errno = 0;
+ for(i = First(vPtr); i >= 0; i = Next(vPtr, i)) {
+ vPtr->valueArr[i] = (*procPtr) (vPtr->valueArr[i]);
+ if (errno != 0) {
+ MathError(interp, vPtr->valueArr[i]);
+ return TCL_ERROR;
+ }
+ if (!finite(vPtr->valueArr[i])) {
+ /*
+ * IEEE floating-point error.
+ */
+ MathError(interp, vPtr->valueArr[i]);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+static int
+ScalarFunc(clientData, interp, vPtr)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ VectorObject *vPtr;
+{
+ double value;
+ ScalarProc *procPtr = (ScalarProc *) clientData;
+
+ errno = 0;
+ value = (*procPtr) (vPtr);
+ if (errno != 0) {
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ if (Blt_VectorChangeLength(vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[0] = value;
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+VectorFunc(clientData, interp, vPtr)
+ ClientData clientData;
+ Tcl_Interp *interp; /* Not used. */
+ VectorObject *vPtr;
+{
+ VectorProc *procPtr = (VectorProc *) clientData;
+
+ return (*procPtr) (vPtr);
+}
+
+
+static MathFunction mathFunctions[] =
+{
+ {"abs", (GenericMathProc *) ComponentFunc, (ClientData)Fabs},
+ {"acos", (GenericMathProc *) ComponentFunc, (ClientData)acos},
+ {"asin", (GenericMathProc *) ComponentFunc, (ClientData)asin},
+ {"atan", (GenericMathProc *) ComponentFunc, (ClientData)atan},
+ {"adev", (GenericMathProc *) ScalarFunc, (ClientData)AvgDeviation},
+ {"ceil", (GenericMathProc *) ComponentFunc, (ClientData)ceil},
+ {"cos", (GenericMathProc *) ComponentFunc, (ClientData)cos},
+ {"cosh", (GenericMathProc *) ComponentFunc, (ClientData)cosh},
+ {"exp", (GenericMathProc *) ComponentFunc, (ClientData)exp},
+ {"floor", (GenericMathProc *) ComponentFunc, (ClientData)floor},
+ {"kurtosis", (GenericMathProc *) ScalarFunc, (ClientData)Kurtosis},
+ {"length", (GenericMathProc *) ScalarFunc, (ClientData)Length},
+ {"log", (GenericMathProc *) ComponentFunc, (ClientData)log},
+ {"log10", (GenericMathProc *) ComponentFunc, (ClientData)log10},
+ {"max", (GenericMathProc *) ScalarFunc, (ClientData)Blt_VecMax},
+ {"mean", (GenericMathProc *) ScalarFunc, (ClientData)Mean},
+ {"median", (GenericMathProc *) ScalarFunc, (ClientData)Median},
+ {"min", (GenericMathProc *) ScalarFunc, (ClientData)Blt_VecMin},
+ {"norm", (GenericMathProc *) VectorFunc, (ClientData)Norm},
+ {"nz", (GenericMathProc *) ScalarFunc, (ClientData)Nonzeros},
+ {"q1", (GenericMathProc *) ScalarFunc, (ClientData)Q1},
+ {"q3", (GenericMathProc *) ScalarFunc, (ClientData)Q3},
+ {"prod", (GenericMathProc *) ScalarFunc, (ClientData)Product},
+#ifdef HAVE_DRAND48
+ {"random", (GenericMathProc *) ComponentFunc, (ClientData)drand48},
+#endif
+ {"round", (GenericMathProc *) ComponentFunc, (ClientData)Round},
+ {"sdev", (GenericMathProc *) ScalarFunc, (ClientData)StdDeviation},
+ {"sin", (GenericMathProc *) ComponentFunc, (ClientData)sin},
+ {"sinh", (GenericMathProc *) ComponentFunc, (ClientData)sinh},
+ {"skew", (GenericMathProc *) ScalarFunc, (ClientData)Skew},
+ {"sort", (GenericMathProc *) VectorFunc, (ClientData)Sort},
+ {"sqrt", (GenericMathProc *) ComponentFunc, (ClientData)sqrt},
+ {"sum", (GenericMathProc *) ScalarFunc, (ClientData)Sum},
+ {"tan", (GenericMathProc *) ComponentFunc, (ClientData)tan},
+ {"tanh", (GenericMathProc *) ComponentFunc, (ClientData)tanh},
+ {"var", (GenericMathProc *) ScalarFunc, (ClientData)Variance},
+ {(char *)NULL,},
+};
+
+void
+Blt_VectorInstallMathFunctions(tablePtr)
+ Blt_HashTable *tablePtr;
+{
+ Blt_HashEntry *hPtr;
+ register MathFunction *mathPtr;
+ int isNew;
+
+ for (mathPtr = mathFunctions; mathPtr->name != NULL; mathPtr++) {
+ hPtr = Blt_CreateHashEntry(tablePtr, mathPtr->name, &isNew);
+ Blt_SetHashValue(hPtr, (ClientData)mathPtr);
+ }
+}
+
+void
+Blt_VectorUninstallMathFunctions(tablePtr)
+ Blt_HashTable *tablePtr;
+{
+ MathFunction *mathPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ mathPtr = (MathFunction *) Blt_GetHashValue(hPtr);
+ if (mathPtr->name == NULL) {
+ Blt_Free(mathPtr);
+ }
+ }
+}
+
+
+static void
+InstallIndexProc(tablePtr, string, procPtr)
+ Blt_HashTable *tablePtr;
+ char *string;
+ Blt_VectorIndexProc *procPtr; /* Pointer to function to be called
+ * when the vector finds the named index.
+ * If NULL, this indicates to remove
+ * the index from the table.
+ */
+{
+ Blt_HashEntry *hPtr;
+ int dummy;
+
+ hPtr = Blt_CreateHashEntry(tablePtr, string, &dummy);
+ if (procPtr == NULL) {
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+ } else {
+ Blt_SetHashValue(hPtr, (ClientData)procPtr);
+ }
+}
+
+void
+Blt_VectorInstallSpecialIndices(tablePtr)
+ Blt_HashTable *tablePtr;
+{
+ InstallIndexProc(tablePtr, "min", Blt_VecMin);
+ InstallIndexProc(tablePtr, "max", Blt_VecMax);
+ InstallIndexProc(tablePtr, "mean", Mean);
+ InstallIndexProc(tablePtr, "sum", Sum);
+ InstallIndexProc(tablePtr, "prod", Product);
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ExprVector --
+ *
+ * Evaluates an vector expression and returns its value(s).
+ *
+ * Results:
+ * Each of the procedures below returns a standard Tcl result.
+ * If an error occurs then an error message is left in
+ * interp->result. Otherwise the value of the expression,
+ * in the appropriate form, is stored at *resultPtr. If
+ * the expression had a result that was incompatible with the
+ * desired form then an error is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ExprVector(interp, string, vecPtr)
+ Tcl_Interp *interp; /* Context in which to evaluate the
+ * expression. */
+ char *string; /* Expression to evaluate. */
+ Blt_Vector *vecPtr; /* Where to store result. */
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ Value value;
+
+ dataPtr = (vecPtr != NULL)
+ ? vPtr->dataPtr : Blt_VectorGetInterpData(interp);
+ value.vPtr = Blt_VectorNew(dataPtr);
+ if (EvaluateExpression(interp, string, &value) != TCL_OK) {
+ Blt_VectorFree(value.vPtr);
+ return TCL_ERROR;
+ }
+ if (vPtr != NULL) {
+ Blt_VectorDuplicate(vPtr, value.vPtr);
+ } else {
+ register int i;
+ /* No result vector. Put values in interp->result. */
+ for (i = 0; i < value.vPtr->length; i++) {
+ string = Blt_Dtoa(interp, value.vPtr->valueArr[i]);
+ Tcl_AppendElement(interp, string);
+ }
+ }
+ Blt_VectorFree(value.vPtr);
+ return TCL_OK;
+}
diff --git a/blt/src/bltVecObjCmd.c b/blt/src/bltVecObjCmd.c
new file mode 100644
index 00000000000..9a54a22587c
--- /dev/null
+++ b/blt/src/bltVecObjCmd.c
@@ -0,0 +1,2068 @@
+/*
+ * bltVecCmd.c --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ * o Add H. Kirsch's vector binary read operation
+ * x binread file0
+ * x binread -file file0
+ *
+ * o Add ASCII/binary file reader
+ * x read fileName
+ *
+ * o Allow Tcl-based client notifications.
+ * vector x
+ * x notify call Display
+ * x notify delete Display
+ * x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+
+#if (TCL_MAJOR_VERSION > 7)
+
+static
+int GetDouble(interp, objPtr, valuePtr)
+ Tcl_Interp *interp;
+ Tcl_Obj *objPtr;
+ double *valuePtr;
+{
+ /* First try to extract the value as a double precision number. */
+ if (Tcl_GetDoubleFromObj(interp, objPtr, valuePtr) == TCL_OK) {
+ return TCL_OK;
+ }
+ Tcl_ResetResult(interp);
+
+ /* Then try to parse it as an expression. */
+ if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) {
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+static void
+GetValues(vPtr, first, last, listObjPtr)
+ VectorObject *vPtr;
+ int first, last;
+ Tcl_Obj *listObjPtr;
+{
+ register int i;
+
+ for (i = first; i <= last; i++) {
+ Tcl_ListObjAppendElement(vPtr->interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+}
+
+static void
+ReplicateValue(vPtr, first, last, value)
+ VectorObject *vPtr;
+ int first, last;
+ double value;
+{
+ register int i;
+
+ for (i = first; i <= last; i++) {
+ vPtr->valueArr[i] = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+}
+
+static int
+CopyList(vPtr, objc, objv)
+ VectorObject *vPtr;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ double value;
+
+ if (Blt_VectorChangeLength(vPtr, objc) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ if (GetDouble(vPtr->interp, objv[i], &value) != TCL_OK) {
+ Blt_VectorChangeLength(vPtr, i);
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[i] = value;
+ }
+ return TCL_OK;
+}
+
+static int
+AppendVector(destPtr, srcPtr)
+ VectorObject *destPtr, *srcPtr;
+{
+ int nBytes;
+ int oldSize, newSize;
+
+ oldSize = destPtr->length;
+ newSize = oldSize + srcPtr->last - srcPtr->first + 1;
+ if (Blt_VectorChangeLength(destPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nBytes = (newSize - oldSize) * sizeof(double);
+ memcpy((char *)(destPtr->valueArr + oldSize),
+ (srcPtr->valueArr + srcPtr->first), nBytes);
+ destPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+static int
+AppendList(vPtr, objc, objv)
+ VectorObject *vPtr;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int count;
+ register int i;
+ double value;
+ int oldSize;
+
+ oldSize = vPtr->length;
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + objc) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = oldSize;
+ for (i = 0; i < objc; i++) {
+ if (GetDouble(vPtr->interp, objv[i], &value) != TCL_OK) {
+ Blt_VectorChangeLength(vPtr, count);
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[count++] = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+/* Vector instance option commands */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ * Appends one of more Tcl lists of values, or vector objects
+ * onto the end of the current vector object.
+ *
+ * Results:
+ * A standard Tcl result. If a current vector can't be created,
+ * resized, any of the named vectors can't be found, or one of
+ * lists of values is invalid, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of current vector will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AppendOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ int result;
+ VectorObject *v2Ptr;
+
+ for (i = 2; i < objc; i++) {
+ v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[i]), (char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ result = AppendVector(vPtr, v2Ptr);
+ } else {
+ int nElem;
+ Tcl_Obj **elemObjArr;
+
+ if (Tcl_ListObjGetElements(interp, objv[i], &nElem, &elemObjArr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = AppendList(vPtr, nElem, elemObjArr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (objc > 2) {
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ClearOp --
+ *
+ * Deletes all the accumulated array indices for the Tcl array
+ * associated will the vector. This routine can be used to
+ * free excess memory from a large vector.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side Effects:
+ * Memory used for the entries of the Tcl array variable is freed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+ Blt_VectorFlushCache(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the given indices from the vector. If no indices are
+ * provided the entire vector is deleted.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given indices is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * The clients of the vector will be notified of the vector
+ * deletions.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ unsigned char *unsetArr;
+ register int i, j;
+ register int count;
+ char *string;
+
+ /* FIXME: Don't delete vector with no indices. */
+ if (objc == 2) {
+ Blt_VectorFree(vPtr);
+ return TCL_OK;
+ }
+ /*
+ * Allocate an "unset" bitmap the size of the vector.
+ */
+ unsetArr = Blt_Calloc(sizeof(unsigned char), (vPtr->length + 7) / 8);
+ assert(unsetArr);
+
+#define SetBit(i) \
+ unsetArr[(i) >> 3] |= (1 << ((i) & 0x07))
+#define GetBit(i) \
+ (unsetArr[(i) >> 3] & (1 << ((i) & 0x07)))
+
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (Blt_VectorGetIndexRange(interp, vPtr, string,
+ (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL)
+ != TCL_OK) {
+ Blt_Free(unsetArr);
+ return TCL_ERROR;
+ }
+ for (j = vPtr->first; j <= vPtr->last; j++) {
+ SetBit(j); /* Mark the range of elements for deletion. */
+ }
+ }
+ count = 0;
+ for (i = 0; i < vPtr->length; i++) {
+ if (GetBit(i)) {
+ continue; /* Skip elements marked for deletion. */
+ }
+ if (count < i) {
+ vPtr->valueArr[count] = vPtr->valueArr[i];
+ }
+ count++;
+ }
+ Blt_Free(unsetArr);
+ vPtr->length = count;
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DupOp --
+ *
+ * Creates one or more duplicates of the vector object.
+ *
+ * Results:
+ * A standard Tcl result. If a new vector can't be created,
+ * or and existing vector resized, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of existing vectors will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DupOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp; /* Not used. */
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ VectorObject *v2Ptr;
+ int isNew;
+ register int i;
+ char *string;
+
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string,&isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (v2Ptr == vPtr) {
+ continue;
+ }
+ if (Blt_VectorDuplicate(v2Ptr, vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Sets or reads the value of the index. This simulates what the
+ * vector's variable does.
+ *
+ * Results:
+ * A standard Tcl result. If the index is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the values.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+IndexOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int first, last;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ if (Blt_VectorGetIndexRange(interp, vPtr, string, INDEX_ALL_FLAGS,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ first = vPtr->first, last = vPtr->last;
+ if (objc == 3) {
+ Tcl_Obj *listObjPtr;
+
+ if (first == vPtr->length) {
+ Tcl_AppendResult(interp, "can't get index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Can't read from index "++end" */
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ GetValues(vPtr, first, last, listObjPtr);
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else {
+ double value;
+
+ /* FIXME: huh? Why set values here?. */
+ if (first == SPECIAL_INDEX) {
+ Tcl_AppendResult(interp, "can't set index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Tried to set "min" or "max" */
+ }
+ if (GetDouble(vPtr->interp, objv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first == vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ ReplicateValue(vPtr, first, last, value);
+ Tcl_SetObjResult(interp, objv[3]);
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * LengthOp --
+ *
+ * Returns the length of the vector. If a new size is given, the
+ * vector is resized to the new vector.
+ *
+ * Results:
+ * A standard Tcl result. If the new length is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the length of the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+LengthOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ int size;
+
+ if (Tcl_GetIntFromObj(interp, objv[2], &size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (size < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"",
+ Tcl_GetString(objv[2]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(vPtr->length));
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Queries or sets the offset of the array index from the base
+ * address of the data array of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ if (objc > 2) {
+ if (Blt_VectorMapVariable(interp, vPtr, Tcl_GetString(objv[2]))
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (vPtr->arrayName != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(vPtr->arrayName, -1));
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MergeOp --
+ *
+ * Merges the values from the given vectors to the current vector.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MergeOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ VectorObject *v2Ptr;
+ VectorObject **vecArr;
+ register VectorObject **vPtrPtr;
+ int refSize, length, nElem;
+ register int i;
+ double *valuePtr, *valueArr;
+
+ /* Allocate an array of vector pointers of each vector to be
+ * merged in the current vector. */
+ vecArr = Blt_Malloc(sizeof(VectorObject *) * objc);
+ assert(vecArr);
+ vPtrPtr = vecArr;
+
+ refSize = -1;
+ nElem = 0;
+ for (i = 2; i < objc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+ != TCL_OK) {
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ /* Check that all the vectors are the same length */
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (refSize < 0) {
+ refSize = length;
+ } else if (length != refSize) {
+ Tcl_AppendResult(vPtr->interp, "vectors \"", vPtr->name,
+ "\" and \"", v2Ptr->name, "\" differ in length",
+ (char *)NULL);
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ *vPtrPtr++ = v2Ptr;
+ nElem += refSize;
+ }
+ *vPtrPtr = NULL;
+
+ valueArr = Blt_Malloc(sizeof(double) * nElem);
+ if (valueArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ",
+ Blt_Itoa(nElem), " vector elements", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Merge the values from each of the vectors into the current vector */
+ valuePtr = valueArr;
+ for (i = 0; i < refSize; i++) {
+ for (vPtrPtr = vecArr; *vPtrPtr != NULL; vPtrPtr++) {
+ *valuePtr++ = (*vPtrPtr)->valueArr[i + (*vPtrPtr)->first];
+ }
+ }
+ Blt_Free(vecArr);
+ Blt_VectorReset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NormalizeOp --
+ *
+ * Normalizes the vector.
+ *
+ * Results:
+ * A standard Tcl result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NormalizeOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ double range;
+
+ Blt_VectorUpdateRange(vPtr);
+ range = vPtr->max - vPtr->min;
+ if (objc > 2) {
+ VectorObject *v2Ptr;
+ int isNew;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string,
+ &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_VectorChangeLength(v2Ptr, vPtr->length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < vPtr->length; i++) {
+ v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range;
+ }
+ Blt_VectorUpdateRange(v2Ptr);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ } else {
+ double norm;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = 0; i < vPtr->length; i++) {
+ norm = (vPtr->valueArr[i] - vPtr->min) / range;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(norm));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ * Notify clients of vector.
+ *
+ * Results:
+ * A standard Tcl result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ * x vector notify now
+ * x vector notify always
+ * x vector notify whenidle
+ * x vector notify update {}
+ * x vector notify delete {}
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ int option;
+ int bool;
+ enum optionIndices {
+ OPTION_ALWAYS, OPTION_NEVER, OPTION_WHENIDLE,
+ OPTION_NOW, OPTION_CANCEL, OPTION_PENDING
+ };
+ static char *optionArr[] = {
+ "always", "never", "whenidle", "now", "cancel", "pending", NULL
+ };
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], optionArr, "qualifier", TCL_EXACT,
+ &option) != TCL_OK) {
+ return TCL_OK;
+ }
+ switch (option) {
+ case OPTION_ALWAYS:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_ALWAYS;
+ break;
+ case OPTION_NEVER:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_NEVER;
+ break;
+ case OPTION_WHENIDLE:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_WHENIDLE;
+ break;
+ case OPTION_NOW:
+ /* FIXME: How does this play when an update is pending? */
+ Blt_VectorNotifyClients(vPtr);
+ break;
+ case OPTION_CANCEL:
+ if (vPtr->notifyFlags & NOTIFY_PENDING) {
+ vPtr->notifyFlags &= ~NOTIFY_PENDING;
+ Tcl_CancelIdleCall(Blt_VectorNotifyClients, (ClientData)vPtr);
+ }
+ break;
+ case OPTION_PENDING:
+ bool = (vPtr->notifyFlags & NOTIFY_PENDING);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+ break;
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PopulateOp --
+ *
+ * Creates or resizes a new vector based upon the density specified.
+ *
+ * Results:
+ * A standard Tcl result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PopulateOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ VectorObject *v2Ptr;
+ int size, density;
+ int isNew;
+ register int i, j;
+ double slice, range;
+ register double *valuePtr;
+ int count;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string, &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (vPtr->length == 0) {
+ return TCL_OK; /* Source vector is empty. */
+ }
+ if (Tcl_GetIntFromObj(interp, objv[3], &density) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (density < 1) {
+ Tcl_AppendResult(interp, "bad density \"", Tcl_GetString(objv[3]),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ size = (vPtr->length - 1) * (density + 1) + 1;
+ if (Blt_VectorChangeLength(v2Ptr, size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = 0;
+ valuePtr = v2Ptr->valueArr;
+ for (i = 0; i < (vPtr->length - 1); i++) {
+ range = vPtr->valueArr[i + 1] - vPtr->valueArr[i];
+ slice = range / (double)(density + 1);
+ for (j = 0; j <= density; j++) {
+ *valuePtr = vPtr->valueArr[i] + (slice * (double)j);
+ valuePtr++;
+ count++;
+ }
+ }
+ count++;
+ *valuePtr = vPtr->valueArr[i];
+ assert(count == v2Ptr->length);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns a Tcl list of the range of vector values specified.
+ *
+ * Results:
+ * A standard Tcl result. If the given range is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned and interp->result
+ * will contain the list of values.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RangeOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ Tcl_Obj *listObjPtr;
+ int first, last;
+ register int i;
+
+ if ((Blt_VectorGetIndex(interp, vPtr, Tcl_GetString(objv[2]), &first,
+ INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK) ||
+ (Blt_VectorGetIndex(interp, vPtr, Tcl_GetString(objv[3]), &last,
+ INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (first > last) {
+ /* Return the list reversed */
+ for (i = last; i <= first; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ } else {
+ for (i = first; i <= last; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ * Determines if a value lies within a given range.
+ *
+ * The value is normalized and compared against the interval
+ * [0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ * DBL_EPSILON is the smallest number that can be represented
+ * on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ * Please note, min cannot be greater than max.
+ *
+ * Results:
+ * If the value is within of the interval [min..max], 1 is
+ * returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(value, min, max)
+ double value, min, max;
+{
+ double range;
+
+ range = max - min;
+ if (range < DBL_EPSILON) {
+ return (FABS(max - value) < DBL_EPSILON);
+ } else {
+ double norm;
+
+ norm = (value - min) / range;
+ return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+ }
+}
+
+enum NativeFormats {
+ FMT_UNKNOWN = -1,
+ FMT_UCHAR, FMT_CHAR,
+ FMT_USHORT, FMT_SHORT,
+ FMT_UINT, FMT_INT,
+ FMT_ULONG, FMT_LONG,
+ FMT_FLOAT, FMT_DOUBLE
+};
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetBinaryFormat
+ *
+ * Translates a format string into a native type. Formats may be
+ * as follows.
+ *
+ * signed i1, i2, i4, i8
+ * unsigned u1, u2, u4, u8
+ * real r4, r8, r16
+ *
+ * But there must be a corresponding native type. For example,
+ * this for reading 2-byte binary integers from an instrument and
+ * converting them to unsigned shorts or ints.
+ *
+ * -----------------------------------------------------------------------
+ */
+static enum NativeFormats
+GetBinaryFormat(interp, string, sizePtr)
+ Tcl_Interp *interp;
+ char *string;
+ int *sizePtr;
+{
+ char c;
+
+ c = tolower(string[0]);
+ if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) {
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": incorrect byte size", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (c) {
+ case 'r':
+ if (*sizePtr == sizeof(double)) {
+ return FMT_DOUBLE;
+ } else if (*sizePtr == sizeof(float)) {
+ return FMT_FLOAT;
+ }
+ break;
+
+ case 'i':
+ if (*sizePtr == sizeof(char)) {
+ return FMT_CHAR;
+ } else if (*sizePtr == sizeof(int)) {
+ return FMT_INT;
+ } else if (*sizePtr == sizeof(long)) {
+ return FMT_LONG;
+ } else if (*sizePtr == sizeof(short)) {
+ return FMT_SHORT;
+ }
+ break;
+
+ case 'u':
+ if (*sizePtr == sizeof(unsigned char)) {
+ return FMT_UCHAR;
+ } else if (*sizePtr == sizeof(unsigned int)) {
+ return FMT_UINT;
+ } else if (*sizePtr == sizeof(unsigned long)) {
+ return FMT_ULONG;
+ } else if (*sizePtr == sizeof(unsigned short)) {
+ return FMT_USHORT;
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": should be either i#, r#, u# (where # is size in bytes)",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+ }
+ Tcl_AppendResult(interp, "can't handle format \"", string, "\"",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+}
+
+static int
+CopyValues(vPtr, byteArr, fmt, size, length, swap, indexPtr)
+ VectorObject *vPtr;
+ char *byteArr;
+ enum NativeFormats fmt;
+ int size;
+ int length;
+ int swap;
+ int *indexPtr;
+{
+ register int i, n;
+ int newSize;
+
+ if ((swap) && (size > 1)) {
+ int nBytes = size * length;
+ register unsigned char *p;
+ register int left, right;
+
+ for (i = 0; i < nBytes; i += size) {
+ p = (unsigned char *)(byteArr + i);
+ for (left = 0, right = size - 1; left < right; left++, right--) {
+ p[left] ^= p[right];
+ p[right] ^= p[left];
+ p[left] ^= p[right];
+ }
+
+ }
+ }
+ newSize = *indexPtr + length;
+ if (newSize > vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+#define CopyArrayToVector(vPtr, arr) \
+ for (i = 0, n = *indexPtr; i < length; i++, n++) { \
+ (vPtr)->valueArr[n] = (double)(arr)[i]; \
+ }
+
+ switch (fmt) {
+ case FMT_CHAR:
+ CopyArrayToVector(vPtr, (char *)byteArr);
+ break;
+
+ case FMT_UCHAR:
+ CopyArrayToVector(vPtr, (unsigned char *)byteArr);
+ break;
+
+ case FMT_INT:
+ CopyArrayToVector(vPtr, (int *)byteArr);
+ break;
+
+ case FMT_UINT:
+ CopyArrayToVector(vPtr, (unsigned int *)byteArr);
+ break;
+
+ case FMT_LONG:
+ CopyArrayToVector(vPtr, (long *)byteArr);
+ break;
+
+ case FMT_ULONG:
+ CopyArrayToVector(vPtr, (unsigned long *)byteArr);
+ break;
+
+ case FMT_SHORT:
+ CopyArrayToVector(vPtr, (short int *)byteArr);
+ break;
+
+ case FMT_USHORT:
+ CopyArrayToVector(vPtr, (unsigned short int *)byteArr);
+ break;
+
+ case FMT_FLOAT:
+ CopyArrayToVector(vPtr, (float *)byteArr);
+ break;
+
+ case FMT_DOUBLE:
+ CopyArrayToVector(vPtr, (double *)byteArr);
+ break;
+
+ case FMT_UNKNOWN:
+ break;
+ }
+ *indexPtr += length;
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BinreadOp --
+ *
+ * Reads binary values from a Tcl channel. Values are either appended
+ * to the end of the vector or placed at a given index (using the
+ * "-at" option), overwriting existing values. Data is read until EOF
+ * is found on the channel or a specified number of values are read.
+ * (note that this is not necessarily the same as the number of bytes).
+ *
+ * The following flags are supported:
+ * -swap Swap bytes
+ * -at index Start writing data at the index.
+ * -format fmt Specifies the format of the data.
+ *
+ * This binary reader was created by Harald Kirsch (kir@iitb.fhg.de).
+ * Anything that's wrong is due to my munging of his code.
+ *
+ * Results:
+ * Returns a standard Tcl result. The interpreter result will contain
+ * the number of values (not the number of bytes) read.
+ *
+ * Caveats:
+ * Channel reads must end on an element boundary.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BinreadOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Tcl_Channel channel;
+ char *byteArr;
+ char *string;
+ enum NativeFormats fmt;
+ int arraySize, bytesRead;
+ int count, total;
+ int first;
+ int size, length, mode;
+ int swap;
+ register int i;
+
+ string = Tcl_GetString(objv[2]);
+ channel = Tcl_GetChannel(interp, string, &mode);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ if ((mode & TCL_READABLE) == 0) {
+ Tcl_AppendResult(interp, "channel \"", string,
+ "\" wasn't opened for reading", (char *)NULL);
+ return TCL_ERROR;
+ }
+ first = vPtr->length;
+ fmt = FMT_DOUBLE;
+ size = sizeof(double);
+ swap = FALSE;
+ count = 0;
+
+ if (objc > 3) {
+ string = Tcl_GetString(objv[3]);
+ if (string[0] != '-') {
+ long int value;
+ /* Get the number of values to read. */
+ if (Tcl_GetLongFromObj(interp, objv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_AppendResult(interp, "count can't be negative",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ count = (int)value;
+ objc--, objv++;
+ }
+ }
+ /* Process any option-value pairs that remain. */
+ for (i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (strcmp(string, "-swap") == 0) {
+ swap = TRUE;
+ } else if (strcmp(string, "-format") == 0) {
+ i++;
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "missing arg after \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[i]);
+ fmt = GetBinaryFormat(interp, string, &size);
+ if (fmt == FMT_UNKNOWN) {
+ return TCL_ERROR;
+ }
+ } else if (strcmp(string, "-at") == 0) {
+ i++;
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "missing arg after \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[i]);
+ if (Blt_VectorGetIndex(interp, vPtr, string, &first, 0,
+ (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first > vPtr->length) {
+ Tcl_AppendResult(interp, "index \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+
+#define BUFFER_SIZE 1024
+ if (count == 0) {
+ arraySize = BUFFER_SIZE * size;
+ } else {
+ arraySize = count * size;
+ }
+
+ byteArr = Blt_Malloc(arraySize);
+ assert(byteArr);
+
+ /* FIXME: restore old channel translation later? */
+ if (Tcl_SetChannelOption(interp, channel, "-translation",
+ "binary") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total = 0;
+ while (!Tcl_Eof(channel)) {
+ bytesRead = Tcl_Read(channel, byteArr, arraySize);
+ if (bytesRead < 0) {
+ Tcl_AppendResult(interp, "error reading channel: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((bytesRead % size) != 0) {
+ Tcl_AppendResult(interp, "error reading channel: short read",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ length = bytesRead / size;
+ if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total += length;
+ if (count > 0) {
+ break;
+ }
+ }
+ Blt_Free(byteArr);
+
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+
+ /* Set the result as the number of values read. */
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(total));
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ * Searchs for a value in the vector. Returns the indices of all
+ * vector elements matching a particular value.
+ *
+ * Results:
+ * Always returns TCL_OK. interp->result will contain a list of
+ * the indices of array elements matching value. If no elements
+ * match, interp->result will contain the empty string.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ double min, max;
+ register int i;
+ int wantValue;
+ char *string;
+ Tcl_Obj *listObjPtr;
+
+ wantValue = FALSE;
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-value") == 0)) {
+ wantValue = TRUE;
+ objv++, objc--;
+ }
+ if (GetDouble(interp, objv[2], &min) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ max = min;
+ if ((objc > 3) && (GetDouble(interp, objv[3], &max) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((min - max) >= DBL_EPSILON) {
+ return TCL_OK; /* Bogus range. Don't bother looking. */
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (wantValue) {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ }
+ } else {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(i + vPtr->offset));
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * OffsetOp --
+ *
+ * Queries or sets the offset of the array index from the base
+ * address of the data array of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffsetOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ if (objc == 3) {
+ int newOffset;
+
+ if (Tcl_GetIntFromObj(interp, objv[2], &newOffset) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->offset = newOffset;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(vPtr->offset));
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RandomOp --
+ *
+ * Generates random values for the length of the vector.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RandomOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv; /* Not used. */
+{
+#ifdef HAVE_DRAND48
+ register int i;
+
+ for (i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = drand48();
+ }
+#endif /* HAVE_DRAND48 */
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SequenceOp --
+ *
+ * Generates a sequence of values in the vector.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SequenceOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ register int i;
+ double start, finish, step;
+ int fillVector;
+ int nSteps;
+ char *string;
+
+ if (GetDouble(interp, objv[2], &start) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fillVector = FALSE;
+ string = Tcl_GetString(objv[3]);
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ fillVector = TRUE;
+ } else if (GetDouble(interp, objv[3], &finish) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ step = 1.0;
+ if ((objc > 4) && (GetDouble(interp, objv[4], &step) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (fillVector) {
+ nSteps = vPtr->length;
+ } else {
+ nSteps = (int)((finish - start) / step) + 1;
+ }
+ if (nSteps > 0) {
+ if (Blt_VectorChangeLength(vPtr, nSteps) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < nSteps; i++) {
+ vPtr->valueArr[i] = start + (step * (double)i);
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ * Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified.
+ * Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SetOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ int result;
+ VectorObject *v2Ptr;
+ int nElem;
+ Tcl_Obj **elemObjArr;
+
+ /* The source can be either a list of numbers or another vector. */
+
+ v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[2]), (char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ if (vPtr == v2Ptr) {
+ VectorObject *tmpPtr;
+ /*
+ * Source and destination vectors are the same. Copy the
+ * source first into a temporary vector to avoid memory
+ * overlaps.
+ */
+ tmpPtr = Blt_VectorNew(vPtr->dataPtr);
+ result = Blt_VectorDuplicate(tmpPtr, v2Ptr);
+ if (result == TCL_OK) {
+ result = Blt_VectorDuplicate(vPtr, tmpPtr);
+ }
+ Blt_VectorFree(tmpPtr);
+ } else {
+ result = Blt_VectorDuplicate(vPtr, v2Ptr);
+ }
+ } else if (Tcl_ListObjGetElements(interp, objv[2], &nElem, &elemObjArr)
+ == TCL_OK) {
+ result = CopyList(vPtr, nElem, elemObjArr);
+ } else {
+ return TCL_ERROR;
+ }
+
+ if (result == TCL_OK) {
+ /*
+ * The vector has changed; so flush the array indices (they're
+ * wrong now), find the new range of the data, and notify
+ * the vector's clients that it's been modified.
+ */
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ return result;
+}
+
+static VectorObject **sortVectorArr; /* Pointer to the array of values currently
+ * being sorted. */
+static int nSortVectors;
+static int reverse; /* Indicates the ordering of the sort. If
+ * non-zero, the vectors are sorted in
+ * decreasing order */
+
+static int
+CompareVectors(a, b)
+ void *a;
+ void *b;
+{
+ double delta;
+ int i;
+ int sign;
+ register VectorObject *vPtr;
+
+ sign = (reverse) ? -1 : 1;
+ for (i = 0; i < nSortVectors; i++) {
+ vPtr = sortVectorArr[i];
+ delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b];
+ if (delta < 0.0) {
+ return (-1 * sign);
+ } else if (delta > 0.0) {
+ return (1 * sign);
+ }
+ }
+ return 0;
+}
+
+int *
+Blt_VectorSortIndex(vPtrPtr, nVectors)
+ VectorObject **vPtrPtr;
+ int nVectors;
+{
+ int *indexArr;
+ register int i;
+ VectorObject *vPtr = *vPtrPtr;
+ int length;
+
+ length = vPtr->last - vPtr->first + 1;
+ indexArr = Blt_Malloc(sizeof(int) * length);
+ assert(indexArr);
+ for (i = vPtr->first; i <= vPtr->last; i++) {
+ indexArr[i] = i;
+ }
+ sortVectorArr = vPtrPtr;
+ nSortVectors = nVectors;
+ qsort((char *)indexArr, length, sizeof(int),
+ (QSortCompareProc *)CompareVectors);
+ return indexArr;
+}
+
+static int *
+SortVectors(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ VectorObject **vPtrArray, *v2Ptr;
+ int *iArr;
+ register int i;
+
+ vPtrArray = Blt_Malloc(sizeof(VectorObject *) * (objc + 1));
+ assert(vPtrArray);
+ vPtrArray[0] = vPtr;
+ iArr = NULL;
+ for (i = 0; i < objc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+ != TCL_OK) {
+ goto error;
+ }
+ if (v2Ptr->length != vPtr->length) {
+ Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+ "\" is not the same size as \"", vPtr->name, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ vPtrArray[i + 1] = v2Ptr;
+ }
+ iArr = Blt_VectorSortIndex(vPtrArray, objc + 1);
+ error:
+ Blt_Free(vPtrArray);
+ return iArr;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ * Sorts the vector object and any other vectors according to
+ * sorting order of the vector object.
+ *
+ * Results:
+ * A standard Tcl result. If any of the auxiliary vectors are
+ * a different size than the sorted vector object, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vectors are sorted.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+static int
+SortOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ VectorObject *v2Ptr;
+ char *string;
+ double *mergeArr;
+ int *iArr;
+ int refSize, nBytes;
+ int result;
+ register int i, n;
+
+ reverse = FALSE;
+ if (objc > 2) {
+ string = Tcl_GetString(objv[2]);
+ if (string[0] == '-') {
+ int length;
+
+ length = strlen(string);
+ if ((length > 1) && (strncmp(string, "-reverse", length) == 0)) {
+ reverse = TRUE;
+ } else {
+ Tcl_AppendResult(interp, "unknown flag \"", string,
+ "\": should be \"-reverse\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ objc--, objv++;
+ }
+ }
+ if (objc > 2) {
+ iArr = SortVectors(vPtr, interp, objc - 2, objv + 2);
+ } else {
+ iArr = Blt_VectorSortIndex(&vPtr, 1);
+ }
+ if (iArr == NULL) {
+ return TCL_ERROR;
+ }
+ refSize = vPtr->length;
+
+ /*
+ * Create an array to store a copy of the current values of the
+ * vector. We'll merge the values back into the vector based upon
+ * the indices found in the index array.
+ */
+ nBytes = sizeof(double) * refSize;
+ mergeArr = Blt_Malloc(nBytes);
+ assert(mergeArr);
+ memcpy((char *)mergeArr, (char *)vPtr->valueArr, nBytes);
+ for (n = 0; n < refSize; n++) {
+ vPtr->valueArr[n] = mergeArr[iArr[n]];
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+
+ /* Now sort any other vectors in the same fashion. The vectors
+ * must be the same size as the iArr though. */
+ result = TCL_ERROR;
+ for (i = 2; i < objc; i++) {
+ if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+ != TCL_OK) {
+ goto error;
+ }
+ if (v2Ptr->length != refSize) {
+ Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+ "\" is not the same size as \"", vPtr->name, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ memcpy((char *)mergeArr, (char *)v2Ptr->valueArr, nBytes);
+ for (n = 0; n < refSize; n++) {
+ v2Ptr->valueArr[n] = mergeArr[iArr[n]];
+ }
+ Blt_VectorUpdateClients(v2Ptr);
+ if (v2Ptr->flush) {
+ Blt_VectorFlushCache(v2Ptr);
+ }
+ }
+ result = TCL_OK;
+ error:
+ Blt_Free(mergeArr);
+ Blt_Free(iArr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InstExprOp --
+ *
+ * Computes the result of the expression which may be
+ * either a scalar (single value) or vector (list of values).
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InstExprOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+
+ if (Blt_ExprVector(interp, Tcl_GetString(objv[2]), (Blt_Vector *) vPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ArithOp --
+ *
+ * Results:
+ * A standard Tcl result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified.
+ * Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArithOp(vPtr, interp, objc, objv)
+ VectorObject *vPtr;
+ Tcl_Interp *interp;
+ int objc; /* Not used. */
+ Tcl_Obj *CONST *objv;
+{
+ register double value;
+ register int i;
+ VectorObject *v2Ptr;
+ double scalar;
+ Tcl_Obj *listObjPtr;
+ char *string;
+
+ v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[2]), (char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ register int j;
+ int length;
+
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (length != vPtr->length) {
+ Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[0]),
+ "\" and \"", Tcl_GetString(objv[2]),
+ "\" are not the same length", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[1]);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ switch (string[0]) {
+ case '*':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] * v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '/':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] / v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '-':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] - v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '+':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] + v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+
+ } else if (GetDouble(interp, objv[2], &scalar) == TCL_OK) {
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ string = Tcl_GetString(objv[1]);
+ switch (string[0]) {
+ case '*':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] * scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '/':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] / scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '-':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] - scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '+':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] + scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorInstCmd --
+ *
+ * Parses and invokes the appropriate vector instance command
+ * option.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec vectorInstOps[] =
+{
+ {"*", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"+", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"-", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"/", 1, (Blt_Op)ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"append", 1, (Blt_Op)AppendOp, 3, 0, "item ?item...?",},
+ {"binread", 1, (Blt_Op)BinreadOp, 3, 0, "channel ?numValues? ?flags?",},
+ {"clear", 1, (Blt_Op)ClearOp, 2, 2, "",},
+ {"delete", 2, (Blt_Op)DeleteOp, 2, 0, "index ?index...?",},
+ {"dup", 2, (Blt_Op)DupOp, 3, 0, "vecName",},
+ {"expr", 1, (Blt_Op)InstExprOp, 3, 3, "expression",},
+ {"index", 1, (Blt_Op)IndexOp, 3, 4, "index ?value?",},
+ {"length", 1, (Blt_Op)LengthOp, 2, 3, "?newSize?",},
+ {"merge", 1, (Blt_Op)MergeOp, 3, 0, "vecName ?vecName...?",},
+ {"normalize", 3, (Blt_Op)NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/
+ {"notify", 3, (Blt_Op)NotifyOp, 3, 3, "keyword",},
+ {"offset", 2, (Blt_Op)OffsetOp, 2, 3, "?offset?",},
+ {"populate", 1, (Blt_Op)PopulateOp, 4, 4, "vecName density",},
+ {"random", 4, (Blt_Op)RandomOp, 2, 2, "",}, /*Deprecated*/
+ {"range", 4, (Blt_Op)RangeOp, 4, 4, "first last",},
+ {"search", 3, (Blt_Op)SearchOp, 3, 4, "?-value? value ?value?",},
+ {"seq", 3, (Blt_Op)SequenceOp, 4, 5, "start end ?step?",},
+ {"set", 3, (Blt_Op)SetOp, 3, 3, "list",},
+ {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-reverse? ?vecName...?",},
+ {"variable", 1, (Blt_Op)MapOp, 2, 3, "?varName?",},
+};
+
+static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_VectorInstCmd(clientData, interp, objc, objv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ Blt_Op proc;
+ VectorObject *vPtr = clientData;
+
+ vPtr->first = 0;
+ vPtr->last = vPtr->length - 1;
+ proc = Blt_GetOpFromObj(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (vPtr, interp, objc, objv);
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorVarTrace --
+ *
+ * Results:
+ * Returns NULL on success. Only called from a variable trace.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+char *
+Blt_VectorVarTrace(clientData, interp, part1, part2, flags)
+ ClientData clientData; /* Vector object. */
+ Tcl_Interp *interp;
+ char *part1, *part2;
+ int flags;
+{
+ VectorObject *vPtr = clientData;
+#define MAX_ERR_MSG 1023
+ static char message[MAX_ERR_MSG + 1];
+ Blt_VectorIndexProc *indexProc;
+ int varFlags;
+ int first, last;
+ Tcl_Obj *objPtr;
+
+ if (part2 == NULL) {
+ if (flags & TCL_TRACE_UNSETS) {
+ Blt_Free(vPtr->arrayName);
+ vPtr->arrayName = NULL;
+ vPtr->varNsPtr = NULL;
+ if (vPtr->freeOnUnset) {
+ Blt_VectorFree(vPtr);
+ }
+ }
+ return NULL;
+ }
+ if (Blt_VectorGetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS,
+ &indexProc) != TCL_OK) {
+ goto error;
+ }
+ first = vPtr->first, last = vPtr->last;
+ varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags);
+ if (flags & TCL_TRACE_WRITES) {
+ double value;
+ Tcl_Obj *p1Ptr, *p2Ptr;
+
+ if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */
+ return "read-only index";
+ }
+ p1Ptr = Tcl_NewStringObj(part1, -1);
+ p2Ptr = Tcl_NewStringObj(part2, -1);
+ objPtr = Tcl_ObjGetVar2(interp, p1Ptr, p2Ptr, varFlags);
+ if (objPtr == NULL) {
+ goto error;
+ }
+ if (GetDouble(interp, objPtr, &value) != TCL_OK) {
+ if ((last == first) && (first >= 0)) {
+ /* Single numeric index. Reset the array element to
+ * its old value on errors */
+ Tcl_ObjSetVar2(interp, p1Ptr, p2Ptr, objPtr, varFlags);
+ }
+ goto error;
+ }
+ if (first == vPtr->length) {
+ if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+ return "error resizing vector";
+ }
+ }
+ /* Set possibly an entire range of values */
+ ReplicateValue(vPtr, first, last, value);
+ } else if (flags & TCL_TRACE_READS) {
+ double value;
+ Tcl_Obj *p1Ptr, *p2Ptr;
+
+ p1Ptr = Tcl_NewStringObj(part1, -1);
+ p2Ptr = Tcl_NewStringObj(part2, -1);
+ if (vPtr->length == 0) {
+ if (Tcl_ObjSetVar2(interp, p1Ptr, p2Ptr, Tcl_NewStringObj("", -1),
+ varFlags) == NULL) {
+ goto error;
+ }
+ return NULL;
+ }
+ if (first == vPtr->length) {
+ return "write-only index";
+ }
+ if (first == last) {
+ if (first >= 0) {
+ value = vPtr->valueArr[first];
+ } else {
+ vPtr->first = 0, vPtr->last = vPtr->length - 1;
+ value = (*indexProc) ((Blt_Vector *) vPtr);
+ }
+ objPtr = Tcl_NewDoubleObj(value);
+ if (Tcl_ObjSetVar2(interp, p1Ptr, p2Ptr, objPtr, varFlags)
+ == NULL) {
+ goto error;
+ }
+ } else {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ GetValues(vPtr, first, last, listObjPtr);
+ if (Tcl_ObjSetVar2(interp, p1Ptr, p2Ptr, listObjPtr, varFlags)
+ == NULL) {
+ goto error;
+ }
+ }
+ } else if (flags & TCL_TRACE_UNSETS) {
+ register int i, j;
+
+ if ((first == vPtr->length) || (first == SPECIAL_INDEX)) {
+ return "special vector index";
+ }
+ /*
+ * Collapse the vector from the point of the first unset element.
+ * Also flush any array variable entries so that the shift is
+ * reflected when the array variable is read.
+ */
+ for (i = first, j = last + 1; j < vPtr->length; i++, j++) {
+ vPtr->valueArr[i] = vPtr->valueArr[j];
+ }
+ vPtr->length -= ((last - first) + 1);
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ } else {
+ return "unknown variable trace flag";
+ }
+ if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) {
+ Blt_VectorUpdateClients(vPtr);
+ }
+ Tcl_ResetResult(interp);
+ return NULL;
+
+ error:
+ strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG);
+ message[MAX_ERR_MSG] = '\0';
+ return message;
+}
+
+#endif /* TCL_MAJOR_VERSION > 7 */
diff --git a/blt/src/bltVector.c b/blt/src/bltVector.c
new file mode 100644
index 00000000000..37bb1262476
--- /dev/null
+++ b/blt/src/bltVector.c
@@ -0,0 +1,2361 @@
+/*
+ * bltVector.c --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ * o Add H. Kirsch's vector binary read operation
+ * x binread file0
+ * x binread -file file0
+ *
+ * o Add ASCII/binary file reader
+ * x read fileName
+ *
+ * o Allow Tcl-based client notifications.
+ * vector x
+ * x notify call Display
+ * x notify delete Display
+ * x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define DEF_ARRAY_SIZE 64
+#define VECFLAGS(v) \
+ (((v)->varNsPtr != NULL) ? (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0;
+#define TRACE_ALL (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS)
+
+
+#define VECTOR_CHAR(c) ((isalnum(UCHAR(c))) || \
+ (c == '_') || (c == ':') || (c == '@') || (c == '.'))
+
+
+/*
+ * VectorClient --
+ *
+ * A vector can be shared by several clients. Each client
+ * allocates this structure that acts as its key for using the
+ * vector. Clients can also designate a callback routine that is
+ * executed whenever the vector is updated or destroyed.
+ *
+ */
+typedef struct {
+ unsigned int magic; /* Magic value designating whether this
+ * really is a vector token or not */
+
+ VectorObject *serverPtr; /* Pointer to the master record of the
+ * vector. If NULL, indicates that the
+ * vector has been destroyed but as of
+ * yet, this client hasn't recognized
+ * it. */
+
+ Blt_VectorChangedProc *proc;/* Routine to call when the contents
+ * of the vector change or the vector
+ * is deleted. */
+
+ ClientData clientData; /* Data passed whenever the vector
+ * change procedure is called. */
+
+ Blt_ChainLink *linkPtr; /* Used to quickly remove this entry from
+ * its server's client chain. */
+} VectorClient;
+
+
+#ifdef __STDC__
+static Tcl_CmdDeleteProc VectorInstDeleteProc;
+static Tcl_CmdProc VectorCmd;
+static Tcl_InterpDeleteProc VectorInterpDeleteProc;
+#endif /* __STDC__ */
+
+#if defined(HAVE_SRAND48) && defined(NO_DECL_SRAND48)
+extern void srand48 _ANSI_ARGS_((long int seed));
+#endif
+
+static VectorObject *
+FindVectorInNamespace(dataPtr, nsPtr, vecName)
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Namespace *nsPtr;
+ char *vecName;
+{
+ Tcl_DString dString;
+ char *name;
+ Blt_HashEntry *hPtr;
+
+ name = Blt_GetQualifiedName(nsPtr, vecName, &dString);
+ hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), name);
+ Tcl_DStringFree(&dString);
+ if (hPtr != NULL) {
+ return (VectorObject *)Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetVectorObject --
+ *
+ * Searches for the vector associated with the name given.
+ * Allow for a range specification.
+ *
+ * Results:
+ * Returns a pointer to the vector if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static VectorObject *
+GetVectorObject(dataPtr, name, flags)
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ char *name;
+ int flags;
+{
+ char *vecName;
+ Tcl_Namespace *nsPtr;
+ VectorObject *vPtr;
+
+ nsPtr = NULL;
+ vecName = name;
+ if (Blt_ParseQualifiedName(dataPtr->interp, name, &nsPtr, &vecName)
+ != TCL_OK) {
+ return NULL; /* Can't find namespace. */
+ }
+
+ vPtr = NULL;
+ if (nsPtr != NULL) {
+ vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+ } else {
+ if (flags & NS_SEARCH_CURRENT) {
+ nsPtr = Tcl_GetCurrentNamespace(dataPtr->interp);
+ vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+ }
+ if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+ nsPtr = Tcl_GetGlobalNamespace(dataPtr->interp);
+ vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+ }
+ }
+ return vPtr;
+}
+
+void
+Blt_VectorUpdateRange(vPtr)
+ VectorObject *vPtr;
+{
+ double min, max;
+ register int i;
+
+ min = DBL_MAX, max = -DBL_MAX;
+ for (i = 0; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ min = max = vPtr->valueArr[i];
+ break;
+ }
+ }
+ for (/* empty */; i < vPtr->length; i++) {
+ if (finite(vPtr->valueArr[i])) {
+ if (min > vPtr->valueArr[i]) {
+ min = vPtr->valueArr[i];
+ } else if (max < vPtr->valueArr[i]) {
+ max = vPtr->valueArr[i];
+ }
+ }
+ }
+ vPtr->min = min;
+ vPtr->max = max;
+ vPtr->notifyFlags &= ~UPDATE_RANGE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndex --
+ *
+ * Converts the string representing an index in the vector, to
+ * its numeric value. A valid index may be an numeric string of
+ * the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ * A standard Tcl result. If the string is a valid index, TCL_OK
+ * is returned. Otherwise TCL_ERROR is returned and interp->result
+ * will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndex(interp, vPtr, string, indexPtr, flags, procPtrPtr)
+ Tcl_Interp *interp;
+ VectorObject *vPtr;
+ char *string;
+ int *indexPtr;
+ int flags;
+ Blt_VectorIndexProc **procPtrPtr;
+{
+ char c;
+ int value;
+
+ c = string[0];
+
+ /* Treat the index "end" like a numeric index. */
+
+ if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ if (vPtr->length < 1) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad index \"end\": vector is empty",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *indexPtr = vPtr->length - 1;
+ return TCL_OK;
+ } else if ((c == '+') && (strcmp(string, "++end") == 0)) {
+ *indexPtr = vPtr->length;
+ return TCL_OK;
+ }
+ if (procPtrPtr != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(vPtr->dataPtr->indexProcTable), string);
+ if (hPtr != NULL) {
+ *indexPtr = SPECIAL_INDEX;
+ *procPtrPtr = (Blt_VectorIndexProc *) Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ }
+ if (Tcl_GetInt(interp, string, &value) != TCL_OK) {
+ long int lvalue;
+ /*
+ * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter,
+ * but the interp passed in may be NULL. So we have to use
+ * vPtr->interp and then reset the result.
+ */
+ if (Tcl_ExprLong(vPtr->interp, string, &lvalue) != TCL_OK) {
+ Tcl_ResetResult(vPtr->interp);
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad index \"", string, "\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ value = lvalue;
+ }
+ /*
+ * Correct the index by the current value of the offset. This makes
+ * all the numeric indices non-negative, which is how we distinguish
+ * the special non-numeric indices.
+ */
+ value -= vPtr->offset;
+
+ if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "index \"", string, "\" is out of range",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *indexPtr = (int)value;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndexRange --
+ *
+ * Converts the string representing an index in the vector, to
+ * its numeric value. A valid index may be an numeric string of
+ * the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ * A standard Tcl result. If the string is a valid index, TCL_OK
+ * is returned. Otherwise TCL_ERROR is returned and interp->result
+ * will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndexRange(interp, vPtr, string, flags, procPtrPtr)
+ Tcl_Interp *interp;
+ VectorObject *vPtr;
+ char *string;
+ int flags;
+ Blt_VectorIndexProc **procPtrPtr;
+{
+ int ielem;
+ char *colon;
+
+ colon = NULL;
+ if (flags & INDEX_COLON) {
+ colon = strchr(string, ':');
+ }
+ if (colon != NULL) {
+ if (string == colon) {
+ vPtr->first = 0; /* Default to the first index */
+ } else {
+ int result;
+
+ *colon = '\0';
+ result = Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags,
+ (Blt_VectorIndexProc **) NULL);
+ *colon = ':';
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->first = ielem;
+ }
+ if (*(colon + 1) == '\0') {
+ /* Default to the last index */
+ vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0;
+ } else {
+ if (Blt_VectorGetIndex(interp, vPtr, colon + 1, &ielem, flags,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->last = ielem;
+ }
+ if (vPtr->first > vPtr->last) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad range \"", string,
+ "\" (first > last)", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ } else {
+ if (Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags,
+ procPtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->last = vPtr->first = ielem;
+ }
+ return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorParseElement(interp, dataPtr, start, endPtr, flags)
+ Tcl_Interp *interp;
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ char *start;
+ char **endPtr;
+ int flags;
+{
+ register char *p;
+ char saved;
+ VectorObject *vPtr;
+
+ p = start;
+ /* Find the end of the vector name */
+ while (VECTOR_CHAR(*p)) {
+ p++;
+ }
+ saved = *p;
+ *p = '\0';
+
+ vPtr = GetVectorObject(dataPtr, start, flags);
+ if (vPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find vector \"", start, "\"",
+ (char *)NULL);
+ }
+ *p = saved;
+ return NULL;
+ }
+ *p = saved;
+ vPtr->first = 0;
+ vPtr->last = vPtr->length - 1;
+ if (*p == '(') {
+ int count, result;
+
+ start = p + 1;
+ p++;
+
+ /* Find the matching right parenthesis */
+ count = 1;
+ while (*p != '\0') {
+ if (*p == ')') {
+ count--;
+ if (count == 0) {
+ break;
+ }
+ } else if (*p == '(') {
+ count++;
+ }
+ p++;
+ }
+ if (count > 0) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "unbalanced parentheses \"", start,
+ "\"", (char *)NULL);
+ }
+ return NULL;
+ }
+ saved = *p;
+ *p = '\0';
+ result = Blt_VectorGetIndexRange(interp, vPtr, start,
+ (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL);
+ *p = ')';
+ if (result != TCL_OK) {
+ return NULL;
+ }
+ p++;
+ }
+ if (endPtr != NULL) {
+ *endPtr = p;
+ }
+ return vPtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorNotifyClients --
+ *
+ * Notifies each client of the vector that the vector has changed
+ * (updated or destroyed) by calling the provided function back.
+ * The function pointer may be NULL, in that case the client is
+ * not notified.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The results depend upon what actions the client callbacks
+ * take.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorNotifyClients(clientData)
+ ClientData clientData;
+{
+ VectorObject *vPtr = clientData;
+ Blt_ChainLink *linkPtr;
+ VectorClient *clientPtr;
+ Blt_VectorNotify notify;
+
+ notify = (vPtr->notifyFlags & NOTIFY_DESTROYED)
+ ? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE;
+ vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING);
+
+ for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ if (clientPtr->proc != NULL) {
+ (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify);
+ }
+ }
+ /*
+ * Some clients may not handle the "destroy" callback properly
+ * (they should call Blt_FreeVectorId to release the client
+ * identifier), so mark any remaining clients to indicate that
+ * vector's server has gone away.
+ */
+ if (notify == BLT_VECTOR_NOTIFY_DESTROY) {
+ for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ clientPtr->serverPtr = NULL;
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorUpdateClients --
+ *
+ * Notifies each client of the vector that the vector has changed
+ * (updated or destroyed) by calling the provided function back.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The individual client callbacks are eventually invoked.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorUpdateClients(vPtr)
+ VectorObject *vPtr;
+{
+ vPtr->dirty++;
+ if (vPtr->notifyFlags & NOTIFY_NEVER) {
+ return;
+ }
+ vPtr->notifyFlags |= NOTIFY_UPDATED;
+ if (vPtr->notifyFlags & NOTIFY_ALWAYS) {
+ Blt_VectorNotifyClients(vPtr);
+ return;
+ }
+ if (!(vPtr->notifyFlags & NOTIFY_PENDING)) {
+ vPtr->notifyFlags |= NOTIFY_PENDING;
+ Tcl_DoWhenIdle(Blt_VectorNotifyClients, vPtr);
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFlushCache --
+ *
+ * Unsets all the elements of the Tcl array variable associated
+ * with the vector, freeing memory associated with the variable.
+ * This includes both the hash table and the hash keys. The down
+ * side is that this effectively flushes the caching of vector
+ * elements in the array. This means that the subsequent reads
+ * of the array will require a decimal to string conversion.
+ *
+ * This is needed when the vector changes its values, making
+ * the array variable out-of-sync.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All elements of array variable (except one) are unset, freeing
+ * the memory associated with the variable.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFlushCache(vPtr)
+ VectorObject *vPtr;
+{
+ Tcl_CallFrame *framePtr;
+ Tcl_Interp *interp = vPtr->interp;
+
+ if (vPtr->arrayName == NULL) {
+ return; /* Doesn't use the variable API */
+ }
+ framePtr = NULL;
+ if (vPtr->varNsPtr != NULL) {
+ framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+ }
+ /* Turn off the trace temporarily so that we can unset all the
+ * elements in the array. */
+
+ Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+ /* Clear all the element entries from the entire array */
+ Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+ /* Restore the "end" index by default and the trace on the entire array */
+ Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags);
+ Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+ if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+ Blt_LeaveNamespace(interp, framePtr); /* Go back to current */
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorLookupName --
+ *
+ * Searches for the vector associated with the name given. Allow
+ * for a range specification.
+ *
+ * Results:
+ * Returns a pointer to the vector if found, otherwise NULL.
+ * If the name is not associated with a vector and the
+ * TCL_LEAVE_ERR_MSG flag is set, and interp->result will contain
+ * an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorLookupName(dataPtr, vecName, vPtrPtr)
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ char *vecName;
+ VectorObject **vPtrPtr;
+{
+ VectorObject *vPtr;
+ char *endPtr;
+
+ vPtr = Blt_VectorParseElement(dataPtr->interp, dataPtr, vecName, &endPtr,
+ NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (*endPtr != '\0') {
+ Tcl_AppendResult(dataPtr->interp,
+ "extra characters after vector name", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *vPtrPtr = vPtr;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteCommand --
+ *
+ * Deletes the Tcl command associated with the vector, without
+ * triggering a callback to "VectorInstDeleteProc".
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DeleteCommand(vPtr)
+ VectorObject *vPtr; /* Vector associated with the Tcl command. */
+{
+ Tcl_Interp *interp = vPtr->interp;
+ char *qualName; /* Name of Tcl command. */
+ Tcl_CmdInfo cmdInfo;
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ qualName = Blt_GetQualifiedName(
+ Blt_GetCommandNamespace(interp, vPtr->cmdToken),
+ Tcl_GetCommandName(interp, vPtr->cmdToken), &dString);
+ if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) {
+ cmdInfo.deleteProc = NULL; /* Disable the callback before
+ * deleting the Tcl command.*/
+ Tcl_SetCommandInfo(interp, qualName, &cmdInfo);
+ Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken);
+ }
+ Tcl_DStringFree(&dString);
+ vPtr->cmdToken = 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * UnmapVariable --
+ *
+ * Destroys the trace on the current Tcl variable designated
+ * to access the vector.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+UnmapVariable(vPtr)
+ VectorObject *vPtr;
+{
+ Tcl_Interp *interp = vPtr->interp;
+ Tcl_CallFrame *framePtr;
+
+ framePtr = NULL;
+ if (vPtr->varNsPtr != NULL) { /* Activate namespace */
+ framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+ }
+ /* Unset the entire array */
+ Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ (TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+ Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+ if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+ /* Go back to current namespace */
+ Blt_LeaveNamespace(interp, framePtr);
+ }
+ if (vPtr->arrayName != NULL) {
+ Blt_Free(vPtr->arrayName);
+ vPtr->arrayName = NULL;
+ }
+ vPtr->varNsPtr = NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorMapVariable --
+ *
+ * Sets up traces on a Tcl variable to access the vector.
+ *
+ * If another variable is already mapped, it's first untraced and
+ * removed. Don't do anything else for variables named "" (even
+ * though Tcl allows this pathology). Saves the name of the new
+ * array variable.
+ *
+ * Results:
+ * A standard Tcl result. If an error occurs setting the variable
+ * TCL_ERROR is returned and an error message is left in the
+ * interpreter.
+ *
+ * Side effects:
+ * Traces are set for the new variable. The new variable name is
+ * saved in a malloc'ed string in vPtr->arrayName. If this
+ * variable is non-NULL, it indicates that a Tcl variable has
+ * been mapped to this vector.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorMapVariable(interp, vPtr, name)
+ Tcl_Interp *interp;
+ VectorObject *vPtr;
+ char *name;
+{
+ Tcl_Namespace *nsPtr;
+ Tcl_CallFrame *framePtr;
+ char *varName, *result;
+
+ if (vPtr->arrayName != NULL) {
+ UnmapVariable(vPtr);
+ }
+ if ((name == NULL) || (name[0] == '\0')) {
+ return TCL_OK; /* If the variable name is the empty
+ * string, simply return after
+ * removing any existing variable. */
+ }
+ framePtr = NULL;
+
+ /* Get the variable name (without the namespace qualifier). */
+ if (Blt_ParseQualifiedName(interp, name, &nsPtr, &varName) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nsPtr != NULL) {
+ /* [incr Tcl] 2.x doesn't like qualifiers with variable names,
+ * so we need to enter the namespace if one was designated. */
+ framePtr = Blt_EnterNamespace(interp, nsPtr);
+ }
+ /*
+ * To play it safe, delete the variable first. This has
+ * side-effect of unmapping the variable from any vector that may
+ * be currently using it.
+ */
+ Tcl_UnsetVar2(interp, varName, (char *)NULL, 0);
+
+ /* Set the index "end" in the array. This will create the
+ * variable immediately so that we can check its namespace
+ * context. */
+ result = Tcl_SetVar2(interp, varName, "end", "", TCL_LEAVE_ERR_MSG);
+
+ /* Determine if the variable is global or not. If there wasn't a
+ * namespace qualifier, it still may be global. We need to look
+ * inside the Var structure to see what it's namespace field says.
+ * NULL indicates that it's local. */
+
+ vPtr->varNsPtr = Blt_GetVariableNamespace(interp, varName);
+ vPtr->varFlags = (vPtr->varNsPtr != NULL) ?
+ (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0;
+
+ if (result != NULL) {
+ /* Trace the array on reads, writes, and unsets */
+ Tcl_TraceVar2(interp, varName, (char *)NULL,
+ (TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+ }
+ if ((nsPtr != NULL) && (framePtr != NULL)) {
+ Blt_LeaveNamespace(interp, framePtr); /* Go back to current */
+ }
+ vPtr->arrayName = Blt_Strdup(varName);
+ return (result == NULL) ? TCL_ERROR : TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorChangeLength --
+ *
+ * Resizes the vector to the new size.
+ *
+ * The new size of the vector is computed by doubling the
+ * size of the vector until it fits the number of slots needed
+ * (designated by *length*).
+ *
+ * If the new size is the same as the old, simply adjust the
+ * length of the vector. Otherwise we're copying the data from
+ * one memory location to another. The trailing elements of the
+ * vector need to be reset to zero.
+ *
+ * If the storage changed memory locations, free up the old
+ * location if it was dynamically allocated.
+ *
+ * Results:
+ * A standard Tcl result. If the reallocation is successful,
+ * TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ * Memory for the array is reallocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+int
+Blt_VectorChangeLength(vPtr, length)
+ VectorObject *vPtr;
+ int length;
+{
+ int newSize; /* Size of array in elements */
+ double *newArr;
+ Tcl_FreeProc *freeProc;
+
+ newArr = NULL;
+ newSize = 0;
+ freeProc = TCL_STATIC;
+
+ if (length > 0) {
+ int wanted, used;
+
+ wanted = length;
+ used = vPtr->length;
+
+ /* Compute the new size by doubling old size until it's big enough */
+ newSize = DEF_ARRAY_SIZE;
+ if (wanted > DEF_ARRAY_SIZE) {
+ while (newSize < wanted) {
+ newSize += newSize;
+ }
+ }
+ freeProc = vPtr->freeProc;
+ if (newSize == vPtr->size) {
+ newArr = vPtr->valueArr; /* Same size, use current array. */
+ } else {
+ /* Dynamically allocate memory for the new array. */
+ newArr = Blt_Malloc(newSize * sizeof(double));
+ if (newArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "can't allocate ",
+ Blt_Itoa(newSize), " elements for vector \"", vPtr->name,
+ "\"", (char *)NULL); return TCL_ERROR;
+ }
+ if (used > wanted) {
+ used = wanted;
+ }
+ /* Copy any previous data */
+ if (used > 0) {
+ memcpy(newArr, vPtr->valueArr, used * sizeof(double));
+ }
+ freeProc = TCL_DYNAMIC;
+ }
+ /* Clear any new slots that we're now using in the array */
+ if (wanted > used) {
+ memset(newArr + used, 0, (wanted - used) * sizeof(double));
+ }
+ }
+ if ((newArr != vPtr->valueArr) && (vPtr->valueArr != NULL)) {
+ /*
+ * We're not using the old storage anymore, so free it if it's
+ * not static. It's static because the user previously reset
+ * the vector with a statically allocated array (setting freeProc
+ * to TCL_STATIC).
+ */
+ if (vPtr->freeProc != TCL_STATIC) {
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*vPtr->freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ }
+ vPtr->valueArr = newArr;
+ vPtr->size = newSize;
+ vPtr->length = length;
+ vPtr->first = 0;
+ vPtr->last = length - 1;
+ vPtr->freeProc = freeProc; /* Set the type of the new storage */
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ * Resets the vector data. This is called by a client to
+ * indicate that the vector data has changed. The vector does
+ * not need to point to different memory. Any clients of the
+ * vector will be notified of the change.
+ *
+ * Results:
+ * A standard Tcl result. If the new array size is invalid,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * new vector data is recorded.
+ *
+ * Side Effects:
+ * Any client designated callbacks will be posted. Memory may
+ * be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_VectorReset(vPtr, valueArr, length, size, freeProc)
+ VectorObject *vPtr;
+ double *valueArr; /* Array containing the elements of the
+ * vector. If NULL, indicates to reset the
+ * vector.*/
+ int length; /* The number of elements that the vector
+ * currently holds. */
+ int size; /* The maximum number of elements that the
+ * array can hold. */
+ Tcl_FreeProc *freeProc; /* Address of memory deallocation routine
+ * for the array of values. Can also be
+ * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+ if (vPtr->valueArr != valueArr) { /* New array of values resides
+ * in different memory than
+ * the current vector. */
+ if ((valueArr == NULL) || (size == 0)) {
+ /* Empty array. Set up default values */
+ freeProc = TCL_STATIC;
+ valueArr = NULL;
+ size = length = 0;
+ } else if (freeProc == TCL_VOLATILE) {
+ double *newArr;
+ /* Data is volatile. Make a copy of the value array. */
+ newArr = Blt_Malloc(size * sizeof(double));
+ if (newArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "can't allocate ",
+ Blt_Itoa(size), " elements for vector \"",
+ vPtr->name, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ memcpy((char *)newArr, (char *)valueArr,
+ sizeof(double) * length);
+ valueArr = newArr;
+ freeProc = TCL_DYNAMIC;
+ }
+
+ if (vPtr->freeProc != TCL_STATIC) {
+ /* Old data was dynamically allocated. Free it before
+ * attaching new data. */
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ vPtr->freeProc = freeProc;
+ vPtr->valueArr = valueArr;
+ vPtr->size = size;
+ }
+
+ vPtr->length = length;
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorNew(dataPtr)
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+{
+ VectorObject *vPtr;
+
+ vPtr = Blt_Calloc(1, sizeof(VectorObject));
+ assert(vPtr);
+ vPtr->notifyFlags = NOTIFY_WHENIDLE;
+ vPtr->freeProc = TCL_STATIC;
+ vPtr->dataPtr = dataPtr;
+ vPtr->valueArr = NULL;
+ vPtr->length = vPtr->size = 0;
+ vPtr->interp = dataPtr->interp;
+ vPtr->hashPtr = NULL;
+ vPtr->chainPtr = Blt_ChainCreate();
+ vPtr->flush = FALSE;
+ return vPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFree --
+ *
+ * Removes the memory and frees resources associated with the
+ * vector.
+ *
+ * o Removes the trace and the Tcl array variable and unsets
+ * the variable.
+ * o Notifies clients of the vector that the vector is being
+ * destroyed.
+ * o Removes any clients that are left after notification.
+ * o Frees the memory (if necessary) allocated for the array.
+ * o Removes the entry from the hash table of vectors.
+ * o Frees the memory allocated for the name.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFree(vPtr)
+ VectorObject *vPtr;
+{
+ Blt_ChainLink *linkPtr;
+ VectorClient *clientPtr;
+
+ if (vPtr->cmdToken != 0) {
+ DeleteCommand(vPtr);
+ }
+ if (vPtr->arrayName != NULL) {
+ UnmapVariable(vPtr);
+ }
+ vPtr->length = 0;
+
+ /* Immediately notify clients that vector is going away */
+ if (vPtr->notifyFlags & NOTIFY_PENDING) {
+ vPtr->notifyFlags &= ~NOTIFY_PENDING;
+ Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr);
+ }
+ vPtr->notifyFlags |= NOTIFY_DESTROYED;
+ Blt_VectorNotifyClients(vPtr);
+
+ for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(clientPtr);
+ }
+ Blt_ChainDestroy(vPtr->chainPtr);
+ if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) {
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*vPtr->freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ if (vPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&(vPtr->dataPtr->vectorTable), vPtr->hashPtr);
+ }
+#ifdef NAMESPACE_DELETE_NOTIFY
+ if (vPtr->nsPtr != NULL) {
+ Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr);
+ }
+#endif /* NAMESPACE_DELETE_NOTIFY */
+ Blt_Free(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VectorInstDeleteProc --
+ *
+ * Deletes the command associated with the vector. This is
+ * called only when the command associated with the vector is
+ * destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+VectorInstDeleteProc(clientData)
+ ClientData clientData;
+{
+ VectorObject *vPtr = clientData;
+
+ vPtr->cmdToken = 0;
+ Blt_VectorFree(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorCreate --
+ *
+ * Creates a vector structure and the following items:
+ *
+ * o Tcl command
+ * o Tcl array variable and establishes traces on the variable
+ * o Adds a new entry in the vector hash table
+ *
+ * Results:
+ * A pointer to the new vector structure. If an error occurred
+ * NULL is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * A new Tcl command and array variable is added to the
+ * interpreter.
+ *
+ * ---------------------------------------------------------------------- */
+VectorObject *
+Blt_VectorCreate(dataPtr, vecName, cmdName, varName, newPtr)
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ char *vecName; /* Namespace-qualified name of the vector */
+ char *cmdName; /* Name of the Tcl command mapped to
+ * the vector */
+ char *varName; /* Name of the Tcl array mapped to the
+ * vector */
+ int *newPtr;
+{
+ Tcl_DString dString;
+ VectorObject *vPtr;
+ int isNew;
+ char *name, *qualName;
+ Tcl_Namespace *nsPtr;
+ Blt_HashEntry *hPtr;
+ Tcl_Interp *interp = dataPtr->interp;
+
+ isNew = 0;
+ nsPtr = NULL;
+ vPtr = NULL;
+
+ if (Blt_ParseQualifiedName(interp, vecName, &nsPtr, &name) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", vecName, "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ Tcl_DStringInit(&dString);
+ if ((name[0] == '#') && (strcmp(name, "#auto") == 0)) {
+ char string[200];
+
+ do { /* Generate a unique vector name. */
+ sprintf(string, "vector%d", dataPtr->nextId++);
+ qualName = Blt_GetQualifiedName(nsPtr, string, &dString);
+ hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), qualName);
+ } while (hPtr != NULL);
+ } else {
+ register char *p;
+
+ for (p = name; *p != '\0'; p++) {
+ if (!VECTOR_CHAR(*p)) {
+ Tcl_AppendResult(interp, "bad vector name \"", name,
+ "\": must contain digits, letters, underscore, or period",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ qualName = Blt_GetQualifiedName(nsPtr, name, &dString);
+ vPtr = Blt_VectorParseElement((Tcl_Interp *)NULL, dataPtr, qualName,
+ (char **)NULL, NS_SEARCH_CURRENT);
+ }
+ if (vPtr == NULL) {
+ hPtr = Blt_CreateHashEntry(&(dataPtr->vectorTable), qualName, &isNew);
+ vPtr = Blt_VectorNew(dataPtr);
+ vPtr->hashPtr = hPtr;
+ vPtr->nsPtr = nsPtr;
+
+ vPtr->name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+#ifdef NAMESPACE_DELETE_NOTIFY
+ Blt_CreateNsDeleteNotify(interp, nsPtr, vPtr, VectorInstDeleteProc);
+#endif /* NAMESPACE_DELETE_NOTIFY */
+ Blt_SetHashValue(hPtr, vPtr);
+ }
+ if (cmdName != NULL) {
+ Tcl_CmdInfo cmdInfo;
+
+ if ((cmdName == vecName) ||
+ ((name[0] == '#') && (strcmp(name, "#auto") == 0))) {
+ cmdName = qualName;
+ }
+ if (Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
+#if TCL_MAJOR_VERSION > 7
+ if (vPtr != cmdInfo.objClientData) {
+#else
+ if (vPtr != cmdInfo.clientData) {
+#endif
+ Tcl_AppendResult(interp, "command \"", cmdName,
+ "\" already exists", (char *)NULL);
+ goto error;
+ }
+ /* We get here only if the old name is the same as the new. */
+ goto checkVariable;
+ }
+ }
+ if (vPtr->cmdToken != 0) {
+ DeleteCommand(vPtr); /* Command already exists, delete old first */
+ }
+ if (cmdName != NULL) {
+#if (TCL_MAJOR_VERSION == 7)
+ vPtr->cmdToken = Blt_CreateCommand(interp, cmdName, Blt_VectorInstCmd,
+ vPtr, VectorInstDeleteProc);
+#else
+ Tcl_DString dString2;
+
+ Tcl_DStringInit(&dString2);
+ if (cmdName != qualName) {
+ if (Blt_ParseQualifiedName(interp, cmdName, &nsPtr, &name)
+ != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", cmdName,
+ "\"", (char *)NULL);
+ goto error;
+ }
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ cmdName = Blt_GetQualifiedName(nsPtr, name, &dString2);
+ }
+ vPtr->cmdToken = Tcl_CreateObjCommand(interp, cmdName,
+ Blt_VectorInstCmd, vPtr, VectorInstDeleteProc);
+ Tcl_DStringFree(&dString2);
+#endif
+ }
+ checkVariable:
+ if (varName != NULL) {
+ if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) {
+ varName = qualName;
+ }
+ if (Blt_VectorMapVariable(interp, vPtr, varName) != TCL_OK) {
+ goto error;
+ }
+ }
+
+ Tcl_DStringFree(&dString);
+ *newPtr = isNew;
+ return vPtr;
+
+ error:
+ Tcl_DStringFree(&dString);
+ if (vPtr != NULL) {
+ Blt_VectorFree(vPtr);
+ }
+ return NULL;
+}
+
+
+int
+Blt_VectorDuplicate(destPtr, srcPtr)
+ VectorObject *destPtr, *srcPtr;
+{
+ int nBytes;
+ int length;
+
+ if (destPtr == srcPtr) {
+ /* Copying the same vector. */
+ }
+ length = srcPtr->last - srcPtr->first + 1;
+ if (Blt_VectorChangeLength(destPtr, length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nBytes = length * sizeof(double);
+ memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes);
+ destPtr->offset = srcPtr->offset;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorNamesOp --
+ *
+ * Reports the names of all the current vectors in the interpreter.
+ *
+ * Results:
+ * A standard Tcl result. interp->result will contain a list of
+ * all the names of the vector instances.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorNamesOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ VectorInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ char *name;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+ if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
+ Tcl_AppendElement(interp, name);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ * Creates a Tcl command, and array variable representing an
+ * instance of a vector.
+ *
+ * vector a
+ * vector b(20)
+ * vector c(-5:14)
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreate2(clientData, interp, argStart, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argStart;
+ int argc;
+ char **argv;
+{
+ VectorInterpData *dataPtr = clientData;
+ VectorObject *vPtr;
+ char *leftParen, *rightParen;
+ int isNew, size, first, last;
+ char *cmdName, *varName;
+ int length;
+ int inspectFlags, freeOnUnset, flush;
+ char **nameArr;
+ int count;
+ register int i;
+
+ /*
+ * Handle switches to the vector command and collect the vector
+ * name arguments into an array.
+ */
+ varName = cmdName = NULL;
+ freeOnUnset = 0;
+ nameArr = Blt_Malloc(sizeof(char *) * argc);
+ assert(nameArr);
+
+ inspectFlags = TRUE;
+ flush = FALSE;
+ count = 0;
+ vPtr = NULL;
+ for (i = argStart; i < argc; i++) {
+ if ((inspectFlags) && (argv[i][0] == '-')) {
+ length = strlen(argv[i]);
+ if ((length > 1) &&
+ (strncmp(argv[i], "-variable", length) == 0)) {
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp,
+ "no variable name supplied with \"",
+ argv[i], "\" switch", (char *)NULL);
+ goto error;
+ }
+ i++;
+ varName = argv[i];
+ } else if ((length > 1) &&
+ (strncmp(argv[i], "-command", length) == 0)) {
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp,
+ "no command name supplied with \"",
+ argv[i], "\" switch", (char *)NULL);
+ goto error;
+ }
+ i++;
+ cmdName = argv[i];
+ } else if ((length > 1) &&
+ (strncmp(argv[i], "-watchunset", length) == 0)) {
+ int bool;
+
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp, "no value name supplied with \"",
+ argv[i], "\" switch", (char *)NULL);
+ goto error;
+ }
+ i++;
+ if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+ goto error;
+ }
+ freeOnUnset = bool;
+ } else if ((length > 1) && (strncmp(argv[i], "-flush", length) == 0)) {
+ int bool;
+
+ if ((i + 1) == argc) {
+ Tcl_AppendResult(interp, "no value name supplied with \"",
+ argv[i], "\" switch", (char *)NULL);
+ goto error;
+ }
+ i++;
+ if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+ goto error;
+ }
+ flush = bool;
+ } else if ((length > 1) && (argv[i][1] == '-') &&
+ (argv[i][2] == '\0')) {
+ inspectFlags = FALSE; /* Allow vector names to start with - */
+ } else {
+ Tcl_AppendResult(interp, "bad vector switch \"", argv[i], "\"",
+ (char *)NULL);
+ goto error;
+ }
+ } else {
+ nameArr[count++] = argv[i];
+ }
+ }
+ if (count == 0) {
+ Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL);
+ goto error;
+ }
+ if (count > 1) {
+ if ((cmdName != NULL) && (cmdName[0] != '\0')) {
+ Tcl_AppendResult(interp,
+ "can't specify more than one vector with \"-command\" switch",
+ (char *)NULL);
+ goto error;
+ }
+ if ((varName != NULL) && (varName[0] != '\0')) {
+ Tcl_AppendResult(interp,
+ "can't specify more than one vector with \"-variable\" switch",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ size = first = last = 0;
+ leftParen = strchr(nameArr[i], '(');
+ rightParen = strchr(nameArr[i], ')');
+ if (((leftParen != NULL) && (rightParen == NULL)) ||
+ ((leftParen == NULL) && (rightParen != NULL)) ||
+ (leftParen > rightParen)) {
+ Tcl_AppendResult(interp, "bad vector specification \"", nameArr[i],
+ "\"", (char *)NULL);
+ goto error;
+ }
+ if (leftParen != NULL) {
+ int result;
+ char *colon;
+
+ *rightParen = '\0';
+ colon = strchr(leftParen + 1, ':');
+ if (colon != NULL) {
+
+ /* Specification is in the form vecName(first:last) */
+ *colon = '\0';
+ result = Tcl_GetInt(interp, leftParen + 1, &first);
+ if ((*(colon + 1) != '\0') && (result == TCL_OK)) {
+ result = Tcl_GetInt(interp, colon + 1, &last);
+ if (first > last) {
+ Tcl_AppendResult(interp, "bad vector range \"",
+ nameArr[i], "\"", (char *)NULL);
+ result = TCL_ERROR;
+ }
+ size = (last - first) + 1;
+ }
+ *colon = ':';
+ } else {
+ /* Specification is in the form vecName(size) */
+ result = Tcl_GetInt(interp, leftParen + 1, &size);
+ }
+ *rightParen = ')';
+ if (result != TCL_OK) {
+ goto error;
+ }
+ if (size < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"", nameArr[i], "\"",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ if (leftParen != NULL) {
+ *leftParen = '\0';
+ }
+ /*
+ * By default, we create a Tcl command by the name of the vector.
+ */
+ vPtr = Blt_VectorCreate(dataPtr, nameArr[i],
+ (cmdName == NULL) ? nameArr[i] : cmdName,
+ (varName == NULL) ? nameArr[i] : varName,
+ &isNew);
+ if (leftParen != NULL) {
+ *leftParen = '(';
+ }
+ if (vPtr == NULL) {
+ goto error;
+ }
+ vPtr->freeOnUnset = freeOnUnset;
+ vPtr->flush = flush;
+ vPtr->offset = first;
+ if (size > 0) {
+ if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+ goto error;
+ }
+ }
+ if (!isNew) {
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ }
+ }
+ Blt_Free(nameArr);
+ if (vPtr != NULL) {
+ /* Return the name of the last vector created */
+ Tcl_SetResult(interp, vPtr->name, TCL_VOLATILE);
+ }
+ return TCL_OK;
+ error:
+ Blt_Free(nameArr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ * Creates a Tcl command, and array variable representing an
+ * instance of a vector.
+ *
+ * vector a
+ * vector b(20)
+ * vector c(-5:14)
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreateOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return VectorCreate2(clientData, interp, 2, argc, argv);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorDestroyOp --
+ *
+ * Destroys the vector and its related Tcl command and array
+ * variable (if they exist).
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Deletes the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorDestroyOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ VectorInterpData *dataPtr = clientData;
+ VectorObject *vPtr;
+ register int i;
+
+ for (i = 2; i < argc; i++) {
+ if (Blt_VectorLookupName(dataPtr, argv[i], &vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_VectorFree(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorExprOp --
+ *
+ * Computes the result of the expression which may be
+ * either a scalar (single value) or vector (list of values).
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorExprOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not Used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return Blt_ExprVector(interp, argv[2], (Blt_Vector *) NULL);
+}
+
+static Blt_OpSpec vectorCmdOps[] =
+{
+ {"create", 1, (Blt_Op)VectorCreateOp, 3, 0,
+ "vecName ?vecName...? ?switches...?",},
+ {"destroy", 1, (Blt_Op)VectorDestroyOp, 3, 0,
+ "vecName ?vecName...?",},
+ {"expr", 1, (Blt_Op)VectorExprOp, 3, 3, "expression",},
+ {"names", 1, (Blt_Op)VectorNamesOp, 2, 3, "?pattern?...",},
+};
+
+static int nCmdOps = sizeof(vectorCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+VectorCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+
+ /*
+ * Try to replicate the old vector command's behavior:
+ */
+ if (argc > 1) {
+ char c;
+ register int i;
+ register Blt_OpSpec *specPtr;
+
+ c = argv[1][0];
+ for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) {
+ if ((c == specPtr->name[0]) &&
+ (strcmp(argv[1], specPtr->name) == 0)) {
+ goto doOp;
+ }
+ }
+ /*
+ * The first argument is not an operation, so assume that its
+ * actually the name of a vector to be created
+ */
+ return VectorCreate2(clientData, interp, 1, argc, argv);
+ }
+ doOp:
+ /* Do the usual vector operation lookup now. */
+ proc = Blt_GetOp(interp, nCmdOps, vectorCmdOps, BLT_OP_ARG1, argc, argv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (clientData, interp, argc, argv);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * VectorInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the "vector" command
+ * is deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys the math and index hash tables. In addition removes
+ * the hash table managing all vector names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+VectorInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ VectorInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ VectorObject *vPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ vPtr = (VectorObject *)Blt_GetHashValue(hPtr);
+ vPtr->hashPtr = NULL;
+ Blt_VectorFree(vPtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->vectorTable));
+
+ /* If any user-defined math functions were installed, remove them. */
+ Blt_VectorUninstallMathFunctions(&(dataPtr->mathProcTable));
+ Blt_DeleteHashTable(&(dataPtr->mathProcTable));
+
+ Blt_DeleteHashTable(&(dataPtr->indexProcTable));
+ Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+VectorInterpData *
+Blt_VectorGetInterpData(interp)
+ Tcl_Interp *interp;
+{
+ VectorInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (VectorInterpData *)
+ Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(VectorInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ dataPtr->nextId = 0;
+ Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->vectorTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(dataPtr->mathProcTable), BLT_STRING_KEYS);
+ Blt_InitHashTable(&(dataPtr->indexProcTable), BLT_STRING_KEYS);
+ Blt_VectorInstallMathFunctions(&(dataPtr->mathProcTable));
+ Blt_VectorInstallSpecialIndices(&(dataPtr->indexProcTable));
+#ifdef HAVE_SRAND48
+ srand48(time((time_t *) NULL));
+#endif
+ }
+ return dataPtr;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_VectorInit --
+ *
+ * This procedure is invoked to initialize the "vector" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds a new entry into a global Tcl
+ * associative array.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+int
+Blt_VectorInit(interp)
+ Tcl_Interp *interp;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ static Blt_CmdSpec cmdSpec = {"vector", VectorCmd, };
+
+ dataPtr = Blt_VectorGetInterpData(interp);
+ /*
+ * This routine may be run several times in the same interpreter.
+ * For example, if someone tries to initial the BLT commands from
+ * another namespace. Keep a reference count, so we know when it's
+ * safe to clean up.
+ */
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+
+/* C Application interface to vectors */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_CreateVector --
+ *
+ * Creates a new vector by the name and size.
+ *
+ * Results:
+ * A standard Tcl result. If the new array size is invalid or a
+ * vector already exists by that name, TCL_ERROR is returned.
+ * Otherwise TCL_OK is returned and the new vector is created.
+ *
+ * Side Effects:
+ * Memory will be allocated for the new vector. A new Tcl command
+ * and Tcl array variable will be created.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+int
+Blt_CreateVector2(interp, vecName, cmdName, varName, initialSize, vecPtrPtr)
+ Tcl_Interp *interp;
+ char *vecName;
+ char *cmdName, *varName;
+ int initialSize;
+ Blt_Vector **vecPtrPtr;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ VectorObject *vPtr;
+ int isNew;
+ char *nameCopy;
+
+ if (initialSize < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"", Blt_Itoa(initialSize),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ dataPtr = Blt_VectorGetInterpData(interp);
+
+ nameCopy = Blt_Strdup(vecName);
+ vPtr = Blt_VectorCreate(dataPtr, nameCopy, cmdName, varName, &isNew);
+ Blt_Free(nameCopy);
+
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (initialSize > 0) {
+ if (Blt_VectorChangeLength(vPtr, initialSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (vecPtrPtr != NULL) {
+ *vecPtrPtr = (Blt_Vector *) vPtr;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_CreateVector(interp, name, size, vecPtrPtr)
+ Tcl_Interp *interp;
+ char *name;
+ int size;
+ Blt_Vector **vecPtrPtr;
+{
+ return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVector --
+ *
+ * Deletes the vector of the given name. All clients with
+ * designated callback routines will be notified.
+ *
+ * Results:
+ * A standard Tcl result. If no vector exists by that name,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and
+ * vector is deleted.
+ *
+ * Side Effects:
+ * Memory will be released for the new vector. Both the Tcl
+ * command and array variable will be deleted. All clients which
+ * set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVector(vecPtr)
+ Blt_Vector *vecPtr;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+
+ Blt_VectorFree(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVectorByName --
+ *
+ * Deletes the vector of the given name. All clients with
+ * designated callback routines will be notified.
+ *
+ * Results:
+ * A standard Tcl result. If no vector exists by that name,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and
+ * vector is deleted.
+ *
+ * Side Effects:
+ * Memory will be released for the new vector. Both the Tcl
+ * command and array variable will be deleted. All clients which
+ * set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVectorByName(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ VectorObject *vPtr;
+ char *nameCopy;
+ int result;
+
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"),
+ * the Blt_VectorParseElement routine will segfault when it tries to write
+ * into the string. Therefore make a writable copy and free it
+ * when we're done.
+ */
+ nameCopy = Blt_Strdup(name);
+ dataPtr = Blt_VectorGetInterpData(interp);
+ result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_VectorFree(vPtr);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists2 --
+ *
+ * Returns whether the vector associated with the client token
+ * still exists.
+ *
+ * Results:
+ * Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists2(interp, vecName)
+ Tcl_Interp *interp;
+ char *vecName;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+
+ dataPtr = Blt_VectorGetInterpData(interp);
+ if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists --
+ *
+ * Returns whether the vector associated with the client token
+ * still exists.
+ *
+ * Results:
+ * Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists(interp, vecName)
+ Tcl_Interp *interp;
+ char *vecName;
+{
+ char *nameCopy;
+ int result;
+
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"),
+ * the Blt_VectorParseName routine will segfault when it tries to write
+ * into the string. Therefore make a writable copy and free it
+ * when we're done.
+ */
+ nameCopy = Blt_Strdup(vecName);
+ result = Blt_VectorExists2(interp, nameCopy);
+ Blt_Free(nameCopy);
+ return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVector --
+ *
+ * Returns a pointer to the vector associated with the given name.
+ *
+ * Results:
+ * A standard Tcl result. If there is no vector "name", TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned and vecPtrPtr will
+ * point to the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVector(interp, name, vecPtrPtr)
+ Tcl_Interp *interp;
+ char *name;
+ Blt_Vector **vecPtrPtr;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ VectorObject *vPtr;
+ char *nameCopy;
+ int result;
+
+ dataPtr = Blt_VectorGetInterpData(interp);
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"),
+ * the Blt_VectorParseName routine will segfault when it tries to write
+ * into the string. Therefore make a writable copy and free it
+ * when we're done.
+ */
+ nameCopy = Blt_Strdup(name);
+ result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_VectorUpdateRange(vPtr);
+ *vecPtrPtr = (Blt_Vector *) vPtr;
+ return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ * Resets the vector data. This is called by a client to
+ * indicate that the vector data has changed. The vector does
+ * not need to point to different memory. Any clients of the
+ * vector will be notified of the change.
+ *
+ * Results:
+ * A standard Tcl result. If the new array size is invalid,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * new vector data is recorded.
+ *
+ * Side Effects:
+ * Any client designated callbacks will be posted. Memory may
+ * be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResetVector(vecPtr, valueArr, length, size, freeProc)
+ Blt_Vector *vecPtr;
+ double *valueArr; /* Array containing the elements of the
+ * vector. If NULL, indicates to reset the
+ * vector.*/
+ int length; /* The number of elements that the vector
+ * currently holds. */
+ int size; /* The maximum number of elements that the
+ * array can hold. */
+ Tcl_FreeProc *freeProc; /* Address of memory deallocation routine
+ * for the array of values. Can also be
+ * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+
+ if (size < 0) {
+ Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return Blt_VectorReset(vPtr, valueArr, length, size, freeProc);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResizeVector --
+ *
+ * Changes the size of the vector. All clients with designated
+ * callback routines will be notified of the size change.
+ *
+ * Results:
+ * A standard Tcl result. If no vector exists by that name,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and
+ * vector is resized.
+ *
+ * Side Effects:
+ * Memory may be reallocated for the new vector size. All clients
+ * which set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResizeVector(vecPtr, length)
+ Blt_Vector *vecPtr;
+ int length;
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+
+ if (Blt_VectorChangeLength(vPtr, length) != TCL_OK) {
+ Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_VectorFlushCache(vPtr);
+ }
+ Blt_VectorUpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_AllocVectorId --
+ *
+ * Creates an identifier token for an existing vector. The
+ * identifier is used by the client routines to get call backs
+ * when (and if) the vector changes.
+ *
+ * Results:
+ * A standard Tcl result. If "vecName" is not associated with
+ * a vector, TCL_ERROR is returned and interp->result is filled
+ * with an error message.
+ *
+ *--------------------------------------------------------------
+ */
+Blt_VectorId
+Blt_AllocVectorId(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ VectorObject *vPtr;
+ VectorClient *clientPtr;
+ Blt_VectorId clientId;
+ int result;
+ char *nameCopy;
+
+ dataPtr = Blt_VectorGetInterpData(interp);
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"),
+ * the Blt_VectorParseName routine will segfault when it tries to write
+ * into the string. Therefore make a writable copy and free it
+ * when we're done.
+ */
+ nameCopy = Blt_Strdup(name);
+ result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+
+ if (result != TCL_OK) {
+ return (Blt_VectorId) 0;
+ }
+ /* Allocate a new client structure */
+ clientPtr = Blt_Calloc(1, sizeof(VectorClient));
+ assert(clientPtr);
+ clientPtr->magic = VECTOR_MAGIC;
+
+ /* Add the new client to the server's list of clients */
+ clientPtr->linkPtr = Blt_ChainAppend(vPtr->chainPtr, clientPtr);
+ clientPtr->serverPtr = vPtr;
+ clientId = (Blt_VectorId) clientPtr;
+ return clientId;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_SetVectorChangedProc --
+ *
+ * Sets the routine to be called back when the vector is changed
+ * or deleted. *clientData* will be provided as an argument. If
+ * *proc* is NULL, no callback will be made.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The designated routine will be called when the vector is changed
+ * or deleted.
+ *
+ * -----------------------------------------------------------------------
+ */
+void
+Blt_SetVectorChangedProc(clientId, proc, clientData)
+ Blt_VectorId clientId; /* Client token identifying the vector */
+ Blt_VectorChangedProc *proc;/* Address of routine to call when the contents
+ * of the vector change. If NULL, no routine
+ * will be called */
+ ClientData clientData; /* One word of information to pass along when
+ * the above routine is called */
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ return; /* Not a valid token */
+ }
+ clientPtr->clientData = clientData;
+ clientPtr->proc = proc;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_FreeVectorId --
+ *
+ * Releases the token for an existing vector. This indicates
+ * that the client is no longer interested the vector. Any
+ * previously specified callback routine will no longer be
+ * invoked when (and if) the vector changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Any previously specified callback routine will no longer be
+ * invoked when (and if) the vector changes.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_FreeVectorId(clientId)
+ Blt_VectorId clientId; /* Client token identifying the vector */
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ return; /* Not a valid token */
+ }
+ if (clientPtr->serverPtr != NULL) {
+ /* Remove the client from the server's list */
+ Blt_ChainDeleteLink(clientPtr->serverPtr->chainPtr, clientPtr->linkPtr);
+ }
+ Blt_Free(clientPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_NameOfVectorId --
+ *
+ * Returns the name of the vector (and array variable).
+ *
+ * Results:
+ * The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+char *
+Blt_NameOfVectorId(clientId)
+ Blt_VectorId clientId; /* Client token identifying the vector */
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) {
+ return NULL;
+ }
+ return clientPtr->serverPtr->name;
+}
+
+char *
+Blt_NameOfVector(vecPtr)
+ Blt_Vector *vecPtr; /* Vector to query. */
+{
+ VectorObject *vPtr = (VectorObject *)vecPtr;
+ return vPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_VectorNotifyPending --
+ *
+ * Returns the name of the vector (and array variable).
+ *
+ * Results:
+ * The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_VectorNotifyPending(clientId)
+ Blt_VectorId clientId; /* Client token identifying the vector */
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) {
+ return 0;
+ }
+ return (clientPtr->serverPtr->notifyFlags & NOTIFY_PENDING);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVectorById --
+ *
+ * Returns a pointer to the vector associated with the client
+ * token.
+ *
+ * Results:
+ * A standard Tcl result. If the client token is not associated
+ * with a vector any longer, TCL_ERROR is returned. Otherwise,
+ * TCL_OK is returned and vecPtrPtr will point to vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVectorById(interp, clientId, vecPtrPtr)
+ Tcl_Interp *interp;
+ Blt_VectorId clientId; /* Client token identifying the vector */
+ Blt_Vector **vecPtrPtr;
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ Tcl_AppendResult(interp, "bad vector token", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (clientPtr->serverPtr == NULL) {
+ Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_VectorUpdateRange(clientPtr->serverPtr);
+ *vecPtrPtr = (Blt_Vector *) clientPtr->serverPtr;
+ return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InstallIndexProc(interp, string, procPtr)
+ Tcl_Interp *interp;
+ char *string;
+ Blt_VectorIndexProc *procPtr; /* Pointer to function to be called
+ * when the vector finds the named index.
+ * If NULL, this indicates to remove
+ * the index from the table.
+ */
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ dataPtr = Blt_VectorGetInterpData(interp);
+ hPtr = Blt_CreateHashEntry(&(dataPtr->indexProcTable), string, &isNew);
+ if (procPtr == NULL) {
+ Blt_DeleteHashEntry(&(dataPtr->indexProcTable), hPtr);
+ } else {
+ Blt_SetHashValue(hPtr, procPtr);
+ }
+}
diff --git a/blt/src/bltVector.h b/blt/src/bltVector.h
new file mode 100644
index 00000000000..c36a6dbe1d0
--- /dev/null
+++ b/blt/src/bltVector.h
@@ -0,0 +1,125 @@
+
+/*
+ * bltVector.h --
+ *
+ * Copyright 1993-2000 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_VECTOR_H
+#define _BLT_VECTOR_H
+
+typedef enum {
+ BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */
+ BLT_VECTOR_NOTIFY_DESTROY /* The vector has been destroyed and the client
+ * should no longer use its data (calling
+ * Blt_FreeVectorId) */
+} Blt_VectorNotify;
+
+typedef struct Blt_VectorIdStruct *Blt_VectorId;
+
+typedef void (Blt_VectorChangedProc) _ANSI_ARGS_((Tcl_Interp *interp,
+ ClientData clientData, Blt_VectorNotify notify));
+
+typedef struct {
+ double *valueArr; /* Array of values (possibly malloc-ed) */
+ int numValues; /* Number of values in the array */
+ int arraySize; /* Size of the allocated space */
+ double min, max; /* Minimum and maximum values in the vector */
+ int dirty; /* Indicates if the vector has been updated */
+ int reserved; /* Reserved for future use */
+
+} Blt_Vector;
+
+typedef double (Blt_VectorIndexProc) _ANSI_ARGS_((Blt_Vector * vecPtr));
+
+typedef enum {
+ BLT_MATH_FUNC_SCALAR = 1, /* The function returns a single double
+ * precision value. */
+ BLT_MATH_FUNC_VECTOR /* The function processes the entire vector. */
+} Blt_MathFuncType;
+
+/*
+ * To be safe, use the macros below, rather than the fields of the
+ * structure directly.
+ *
+ * The Blt_Vector is basically an opaque type. But it's also the
+ * actual memory address of the vector itself. I wanted to make the
+ * API as unobtrusive as possible. So instead of giving you a copy of
+ * the vector, providing various functions to access and update the
+ * vector, you get your hands on the actual memory (array of doubles)
+ * shared by all the vector's clients.
+ *
+ * The trade-off for speed and convenience is safety. You can easily
+ * break things by writing into the vector when other clients are
+ * using it. Use Blt_ResetVector to get around this. At least the
+ * macros are a reminder it isn't really safe to reset the data
+ * fields, except by the API routines.
+ */
+#define Blt_VecData(v) ((v)->valueArr)
+#define Blt_VecLength(v) ((v)->numValues)
+#define Blt_VecSize(v) ((v)->arraySize)
+#define Blt_VecDirty(v) ((v)->dirty)
+
+EXTERN double Blt_VecMin _ANSI_ARGS_((Blt_Vector *vPtr));
+EXTERN double Blt_VecMax _ANSI_ARGS_((Blt_Vector *vPtr));
+
+EXTERN Blt_VectorId Blt_AllocVectorId _ANSI_ARGS_((Tcl_Interp *interp,
+ char *vecName));
+
+EXTERN void Blt_SetVectorChangedProc _ANSI_ARGS_((Blt_VectorId clientId,
+ Blt_VectorChangedProc * proc, ClientData clientData));
+
+EXTERN void Blt_FreeVectorId _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN int Blt_GetVectorById _ANSI_ARGS_((Tcl_Interp *interp,
+ Blt_VectorId clientId, Blt_Vector **vecPtrPtr));
+
+EXTERN char *Blt_NameOfVectorId _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN char *Blt_NameOfVector _ANSI_ARGS_((Blt_Vector *vecPtr));
+
+EXTERN int Blt_VectorNotifyPending _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN int Blt_CreateVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName,
+ int size, Blt_Vector ** vecPtrPtr));
+
+EXTERN int Blt_GetVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName,
+ Blt_Vector **vecPtrPtr));
+
+EXTERN int Blt_VectorExists _ANSI_ARGS_((Tcl_Interp *interp, char *vecName));
+
+EXTERN int Blt_ResetVector _ANSI_ARGS_((Blt_Vector *vecPtr, double *dataArr,
+ int nValues, int arraySize, Tcl_FreeProc *freeProc));
+
+EXTERN int Blt_ResizeVector _ANSI_ARGS_((Blt_Vector *vecPtr, int nValues));
+
+EXTERN int Blt_DeleteVectorByName _ANSI_ARGS_((Tcl_Interp *interp,
+ char *vecName));
+
+EXTERN int Blt_DeleteVector _ANSI_ARGS_((Blt_Vector *vecPtr));
+
+EXTERN int Blt_ExprVector _ANSI_ARGS_((Tcl_Interp *interp, char *expression,
+ Blt_Vector *vecPtr));
+
+EXTERN void Blt_InstallIndexProc _ANSI_ARGS_((Tcl_Interp *interp,
+ char *indexName, Blt_VectorIndexProc * procPtr));
+
+#endif /* _BLT_VECTOR_H */
diff --git a/blt/src/bltWait.h b/blt/src/bltWait.h
new file mode 100644
index 00000000000..2e4145264ce
--- /dev/null
+++ b/blt/src/bltWait.h
@@ -0,0 +1,242 @@
+/*
+ * bltWait.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_WAIT_H
+#define _BLT_WAIT_H
+
+#ifdef HAVE_WAITFLAGS_H
+# include <waitflags.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+/*
+ * Define EINPROGRESS in terms of WSAEINPROGRESS.
+ */
+
+#ifndef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+
+/*
+ * If ENOTSUP is not defined, define it to a value that will never occur.
+ */
+
+#ifndef ENOTSUP
+#define ENOTSUP -1030507
+#endif
+
+/*
+ * The following defines redefine the Windows Socket errors as
+ * BSD errors so Tcl_PosixError can do the right thing.
+ */
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+#ifndef EALREADY
+#define EALREADY 149 /* operation already in progress */
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK 95 /* Socket operation on non-socket */
+#endif
+#ifndef EDESTADDRREQ
+#define EDESTADDRREQ 96 /* Destination address required */
+#endif
+#ifndef EMSGSIZE
+#define EMSGSIZE 97 /* Message too long */
+#endif
+#ifndef EPROTOTYPE
+#define EPROTOTYPE 98 /* Protocol wrong type for socket */
+#endif
+#ifndef ENOPROTOOPT
+#define ENOPROTOOPT 99 /* Protocol not available */
+#endif
+#ifndef EPROTONOSUPPORT
+#define EPROTONOSUPPORT 120 /* Protocol not supported */
+#endif
+#ifndef ESOCKTNOSUPPORT
+#define ESOCKTNOSUPPORT 121 /* Socket type not supported */
+#endif
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP 122 /* Operation not supported on socket */
+#endif
+#ifndef EPFNOSUPPORT
+#define EPFNOSUPPORT 123 /* Protocol family not supported */
+#endif
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT 124 /* Address family not supported */
+#endif
+#ifndef EADDRINUSE
+#define EADDRINUSE 125 /* Address already in use */
+#endif
+#ifndef EADDRNOTAVAIL
+#define EADDRNOTAVAIL 126 /* Can't assign requested address */
+#endif
+#ifndef ENETDOWN
+#define ENETDOWN 127 /* Network is down */
+#endif
+#ifndef ENETUNREACH
+#define ENETUNREACH 128 /* Network is unreachable */
+#endif
+#ifndef ENETRESET
+#define ENETRESET 129 /* Network dropped connection on reset */
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED 130 /* Software caused connection abort */
+#endif
+#ifndef ECONNRESET
+#define ECONNRESET 131 /* Connection reset by peer */
+#endif
+#ifndef ENOBUFS
+#define ENOBUFS 132 /* No buffer space available */
+#endif
+#ifndef EISCONN
+#define EISCONN 133 /* Socket is already connected */
+#endif
+#ifndef ENOTCONN
+#define ENOTCONN 134 /* Socket is not connected */
+#endif
+#ifndef ESHUTDOWN
+#define ESHUTDOWN 143 /* Can't send after socket shutdown */
+#endif
+#ifndef ETOOMANYREFS
+#define ETOOMANYREFS 144 /* Too many references: can't splice */
+#endif
+#ifndef ETIMEDOUT
+#define ETIMEDOUT 145 /* Connection timed out */
+#endif
+#ifndef ECONNREFUSED
+#define ECONNREFUSED 146 /* Connection refused */
+#endif
+#ifndef ELOOP
+#define ELOOP 90 /* Symbolic link loop */
+#endif
+#ifndef EHOSTDOWN
+#define EHOSTDOWN 147 /* Host is down */
+#endif
+#ifndef EHOSTUNREACH
+#define EHOSTUNREACH 148 /* No route to host */
+#endif
+#ifndef ENOTEMPTY
+#define ENOTEMPTY 93 /* directory not empty */
+#endif
+#ifndef EUSERS
+#define EUSERS 94 /* Too many users (for UFS) */
+#endif
+#ifndef EDQUOT
+#define EDQUOT 49 /* Disc quota exceeded */
+#endif
+#ifndef ESTALE
+#define ESTALE 151 /* Stale NFS file handle */
+#endif
+#ifndef EREMOTE
+#define EREMOTE 66 /* The object is remote */
+#endif
+
+#ifndef WIFEXITED
+# define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0)
+#endif
+
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
+#endif
+
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff)))
+#endif
+
+#ifndef WTERMSIG
+# define WTERMSIG(stat) ((*((int *) &(stat))) & 0x7f)
+#endif
+
+#ifndef WIFSTOPPED
+# define WIFSTOPPED(stat) (((*((int *) &(stat))) & 0xff) == 0177)
+#endif
+
+#ifndef WSTOPSIG
+# define WSTOPSIG(stat) (((*((int *) &(stat))) >> 8) & 0xff)
+#endif
+
+/*
+ * Define constants for waitpid() system call if they aren't defined
+ * by a system header file.
+ */
+
+#ifndef WNOHANG
+# define WNOHANG 1
+#endif
+#ifndef WUNTRACED
+# define WUNTRACED 2
+#endif
+
+/*
+ * The type of the status returned by wait varies from UNIX system
+ * to UNIX system. The macro below defines it:
+ */
+
+#ifdef AIX
+# define WAIT_STATUS_TYPE pid_t
+#else
+#ifdef HAVE_UNION_WAIT
+# define WAIT_STATUS_TYPE union wait
+#else
+# define WAIT_STATUS_TYPE int
+#endif
+#endif
+
+/*
+ * Supply definitions for macros to query wait status, if not already
+ * defined in header files above.
+ */
+
+#ifndef WIFEXITED
+# define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0)
+#endif
+
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
+#endif
+
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff)))
+#endif
+
+#ifndef WTERMSIG
+# define WTERMSIG(stat) ((*((int *) &(stat))) & 0x7f)
+#endif
+
+#ifndef WIFSTOPPED
+# define WIFSTOPPED(stat) (((*((int *) &(stat))) & 0xff) == 0177)
+#endif
+
+#ifndef WSTOPSIG
+# define WSTOPSIG(stat) (((*((int *) &(stat))) >> 8) & 0xff)
+#endif
+
+#endif /* _BLT_WAIT_H */
diff --git a/blt/src/bltWatch.c b/blt/src/bltWatch.c
new file mode 100644
index 00000000000..a7e48d68b1a
--- /dev/null
+++ b/blt/src/bltWatch.c
@@ -0,0 +1,855 @@
+/*
+ * bltWatch.c --
+ *
+ * This module implements watch procedure callbacks for Tcl
+ * commands and procedures.
+ *
+ * Copyright 1994-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "watch" command was created by George Howlett.
+ */
+
+#include "bltInt.h"
+#include <bltHash.h>
+#include "bltSwitch.h"
+
+#define UNKNOWN_RETURN_CODE 5
+static char *codeNames[] =
+{
+ "OK", "ERROR", "RETURN", "BREAK", "CONTINUE"
+};
+
+#define WATCH_MAX_LEVEL 10000 /* Maximum depth of Tcl traces. */
+
+enum WatchStates {
+ WATCH_STATE_DONT_CARE = -1, /* Select watch regardless of state */
+ WATCH_STATE_IDLE = 0, /* */
+ WATCH_STATE_ACTIVE = 1
+};
+
+typedef struct {
+ Tcl_Interp *interp; /* Interpreter associated with the watch */
+ Tk_Uid nameId; /* Watch identifier */
+
+ /* User-configurable fields */
+ enum WatchStates state; /* Current state of watch: either
+ * WATCH_STATE_IDLE or WATCH_STATE_ACTIVE */
+ int maxLevel; /* Maximum depth of tracing allowed */
+ char **preCmd; /* Procedure to be invoked before the
+ * command is executed (but after
+ * substitutions have occurred). */
+ char **postCmd; /* Procedure to be invoked after the command
+ * is executed. */
+ Tcl_Trace trace; /* Trace handler which activates "pre"
+ * command procedures */
+ Tcl_AsyncHandler asyncHandle; /* Async handler which triggers the
+ * "post" command procedure (if one
+ * exists) */
+ int active; /* Indicates if a trace is currently
+ * active. This prevents recursive
+ * tracing of the "pre" and "post"
+ * procedures. */
+ int level; /* Current level of traced command. */
+ char *cmdPtr; /* Command string before substitutions.
+ * Points to a original command buffer. */
+ char *args; /* Tcl list of the command after
+ * substitutions. List is malloc-ed by
+ * Tcl_Merge. Must be freed in handler
+ * procs */
+} Watch;
+
+typedef struct {
+ Tk_Uid nameId; /* Name identifier of the watch */
+ Tcl_Interp *interp; /* Interpreter associated with the
+ * watch */
+} WatchKey;
+
+static Blt_HashTable watchTable;
+static int refCount = 0;
+
+static Blt_SwitchSpec switchSpecs[] =
+{
+ {BLT_SWITCH_LIST, "-precmd", Blt_Offset(Watch, preCmd), 0},
+ {BLT_SWITCH_LIST, "-postcmd", Blt_Offset(Watch, postCmd), 0},
+ {BLT_SWITCH_BOOLEAN, "-active", Blt_Offset(Watch, state), 0},
+ {BLT_SWITCH_INT_NONNEGATIVE, "-maxlevel", Blt_Offset(Watch, maxLevel), 0},
+ {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+#ifdef __STDC__
+static Tcl_CmdTraceProc PreCmdProc;
+static Tcl_AsyncProc PostCmdProc;
+static Tcl_CmdProc WatchCmd;
+static Tcl_CmdDeleteProc WatchDeleteCmd;
+#endif /* __STDC__ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PreCmdProc --
+ *
+ * Procedure callback for Tcl_Trace. Gets called before the
+ * command is executed, but after substitutions have occurred.
+ * If a watch procedure is active, it evals a Tcl command.
+ * Activates the "precmd" callback, if one exists.
+ *
+ * Stashes some information for the "pre" callback: command
+ * string, substituted argument list, and current level.
+ *
+ * Format of "pre" proc:
+ *
+ * proc beforeCmd { level cmdStr argList } {
+ *
+ * }
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A Tcl_AsyncHandler may be triggered, if a "post" procedure is
+ * defined.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+PreCmdProc(clientData, interp, level, command, cmdProc, cmdClientData,
+ argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Not used. */
+ int level; /* Current level */
+ char *command; /* Command before substitution */
+ Tcl_CmdProc *cmdProc; /* Not used. */
+ ClientData cmdClientData; /* Not used. */
+ int argc;
+ char **argv; /* Command after parsing, but before
+ * evaluation */
+{
+ Watch *watchPtr = clientData;
+
+ if (watchPtr->active) {
+ return; /* Don't re-enter from Tcl_Eval below */
+ }
+ watchPtr->cmdPtr = command;
+ watchPtr->level = level;
+ /*
+ * There's no guarantee that the calls to PreCmdProc will match
+ * up with PostCmdProc. So free up argument lists that are still
+ * hanging around before allocating a new one.
+ */
+ if (watchPtr->args != NULL) {
+ Blt_Free(watchPtr->args);
+ }
+ watchPtr->args = Tcl_Merge(argc, argv);
+
+ if (watchPtr->preCmd != NULL) {
+ Tcl_DString buffer;
+ char string[200];
+ int status;
+ register char **p;
+
+ /* Create the "pre" command procedure call */
+ Tcl_DStringInit(&buffer);
+ for (p = watchPtr->preCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&buffer, *p);
+ }
+ sprintf(string, "%d", watchPtr->level);
+ Tcl_DStringAppendElement(&buffer, string);
+ Tcl_DStringAppendElement(&buffer, watchPtr->cmdPtr);
+ Tcl_DStringAppendElement(&buffer, watchPtr->args);
+
+ watchPtr->active = 1;
+ status = Tcl_Eval(interp, Tcl_DStringValue(&buffer));
+ watchPtr->active = 0;
+
+ Tcl_DStringFree(&buffer);
+ if (status != TCL_OK) {
+ fprintf(stderr, "%s failed: %s\n", watchPtr->preCmd[0],
+ Tcl_GetStringResult(interp));
+ }
+ }
+ /* Set the trigger for the "post" command procedure */
+ if (watchPtr->postCmd != NULL) {
+ Tcl_AsyncMark(watchPtr->asyncHandle);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PostCmdProc --
+ *
+ * Procedure callback for Tcl_AsyncHandler. Gets called after
+ * the command has executed. It tests for a "post" command, but
+ * you really can't get here, if one doesn't exist.
+ *
+ * Save the current contents of interp->result before calling
+ * the "post" command, and restore it afterwards.
+ *
+ * Format of "post" proc:
+ *
+ * proc afterCmd { level cmdStr argList retCode results } {
+ *
+ * }
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Memory for argument list is released.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PostCmdProc(clientData, interp, code)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Not used. */
+ int code; /* Completion code of command */
+{
+ Watch *watchPtr = clientData;
+
+ if (watchPtr->active) {
+ return code;
+ }
+ if (watchPtr->postCmd != NULL) {
+ int status;
+ Tcl_DString buffer;
+ char string[200];
+ char *results;
+ register char **p;
+ char *retCode;
+ char *errorCode, *errorInfo;
+ errorInfo = errorCode = NULL;
+
+ results = "NO INTERPRETER AVAILABLE";
+
+ /*
+ * ----------------------------------------------------
+ *
+ * Save the state of the interpreter.
+ *
+ * ----------------------------------------------------
+ */
+ if (interp != NULL) {
+ errorInfo = Tcl_GetVar2(interp, "errorInfo", (char *)NULL,
+ TCL_GLOBAL_ONLY);
+ if (errorInfo != NULL) {
+ errorInfo = Blt_Strdup(errorInfo);
+ }
+ errorCode = Tcl_GetVar2(interp, "errorCode", (char *)NULL,
+ TCL_GLOBAL_ONLY);
+ if (errorCode != NULL) {
+ errorCode = Blt_Strdup(errorCode);
+ }
+ results = Blt_Strdup(Tcl_GetStringResult(interp));
+ }
+ /* Create the "post" command procedure call */
+ Tcl_DStringInit(&buffer);
+ for (p = watchPtr->postCmd; *p != NULL; p++) {
+ Tcl_DStringAppendElement(&buffer, *p);
+ }
+ sprintf(string, "%d", watchPtr->level);
+ Tcl_DStringAppendElement(&buffer, string);
+ Tcl_DStringAppendElement(&buffer, watchPtr->cmdPtr);
+ Tcl_DStringAppendElement(&buffer, watchPtr->args);
+ if (code < UNKNOWN_RETURN_CODE) {
+ retCode = codeNames[code];
+ } else {
+ sprintf(string, "%d", code);
+ retCode = string;
+ }
+ Tcl_DStringAppendElement(&buffer, retCode);
+ Tcl_DStringAppendElement(&buffer, results);
+
+ watchPtr->active = 1;
+ status = Tcl_Eval(watchPtr->interp, Tcl_DStringValue(&buffer));
+ watchPtr->active = 0;
+
+ Tcl_DStringFree(&buffer);
+ Blt_Free(watchPtr->args);
+ watchPtr->args = NULL;
+
+ if (status != TCL_OK) {
+ fprintf(stderr, "%s failed: %s\n", watchPtr->postCmd[0],
+ Tcl_GetStringResult(watchPtr->interp));
+ }
+ /*
+ * ----------------------------------------------------
+ *
+ * Restore the state of the interpreter.
+ *
+ * ----------------------------------------------------
+ */
+ if (interp != NULL) {
+ if (errorInfo != NULL) {
+ Tcl_SetVar2(interp, "errorInfo", (char *)NULL, errorInfo,
+ TCL_GLOBAL_ONLY);
+ Blt_Free(errorInfo);
+ }
+ if (errorCode != NULL) {
+ Tcl_SetVar2(interp, "errorCode", (char *)NULL, errorCode,
+ TCL_GLOBAL_ONLY);
+ Blt_Free(errorCode);
+ }
+ Tcl_SetResult(interp, results, TCL_DYNAMIC);
+ }
+ }
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewWatch --
+ *
+ * Creates a new watch. Uses the nameId and interpreter
+ * address to create a unique hash key. The new watch is
+ * registered into the "watchTable" hash table. Also creates a
+ * Tcl_AsyncHandler for triggering "post" events.
+ *
+ * Results:
+ * If memory for the watch could be allocated, a pointer to
+ * the new watch is returned. Otherwise NULL, and interp->result
+ * points to an error message.
+ *
+ * Side Effects:
+ * A new Tcl_AsyncHandler is created. A new hash table entry
+ * is created. Memory the watch structure is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static Watch *
+NewWatch(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ Watch *watchPtr;
+ WatchKey key;
+ Blt_HashEntry *hPtr;
+ int dummy;
+
+ watchPtr = Blt_Calloc(1, sizeof(Watch));
+ if (watchPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate watch structure", (char *)NULL);
+ return NULL;
+ }
+ watchPtr->state = WATCH_STATE_ACTIVE;
+ watchPtr->maxLevel = WATCH_MAX_LEVEL;
+ watchPtr->nameId = Blt_GetUid(name);
+ watchPtr->interp = interp;
+ watchPtr->asyncHandle = Tcl_AsyncCreate(PostCmdProc, watchPtr);
+ key.interp = interp;
+ key.nameId = watchPtr->nameId;
+ hPtr = Blt_CreateHashEntry(&watchTable, (char *)&key, &dummy);
+ Blt_SetHashValue(hPtr, watchPtr);
+ return watchPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyWatch --
+ *
+ * Removes the watch. The resources used by the watch
+ * are released.
+ * 1) If the watch is active, its trace is deleted.
+ * 2) Memory for command strings is free-ed.
+ * 3) Entry is removed from watch registry.
+ * 4) Async handler is deleted.
+ * 5) Memory for watch itself is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Everything associated with the watch is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyWatch(watchPtr)
+ Watch *watchPtr;
+{
+ WatchKey key;
+ Blt_HashEntry *hPtr;
+
+ Tcl_AsyncDelete(watchPtr->asyncHandle);
+ if (watchPtr->state == WATCH_STATE_ACTIVE) {
+ Tcl_DeleteTrace(watchPtr->interp, watchPtr->trace);
+ }
+ if (watchPtr->preCmd != NULL) {
+ Blt_Free(watchPtr->preCmd);
+ }
+ if (watchPtr->postCmd != NULL) {
+ Blt_Free(watchPtr->postCmd);
+ }
+ if (watchPtr->args != NULL) {
+ Blt_Free(watchPtr->args);
+ }
+ key.interp = watchPtr->interp;
+ key.nameId = watchPtr->nameId;
+ hPtr = Blt_FindHashEntry(&watchTable, (char *)&key);
+ Blt_DeleteHashEntry(&watchTable, hPtr);
+ Blt_FreeUid(key.nameId);
+ Blt_Free(watchPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameToWatch --
+ *
+ * Searches for the watch represented by the watch name and its
+ * associated interpreter in its directory.
+ *
+ * Results:
+ * If found, the pointer to the watch structure is returned,
+ * otherwise NULL. If requested, interp-result will be filled
+ * with an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static Watch *
+NameToWatch(interp, name, flags)
+ Tcl_Interp *interp;
+ char *name;
+ int flags;
+{
+ WatchKey key;
+ Blt_HashEntry *hPtr;
+
+ key.interp = interp;
+ key.nameId = Blt_FindUid(name);
+ if (key.nameId != NULL) {
+ hPtr = Blt_FindHashEntry(&watchTable, (char *)&key);
+ if (hPtr != NULL) {
+ return (Watch *) Blt_GetHashValue(hPtr);
+ }
+ }
+ if (flags & TCL_LEAVE_ERR_MSG) {
+ Tcl_AppendResult(interp, "can't find any watch named \"", name, "\"",
+ (char *)NULL);
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListWatches --
+ *
+ * Creates a list of all watches in the interpreter. The
+ * list search may be restricted to selected states by
+ * setting "state" to something other than WATCH_STATE_DONT_CARE.
+ *
+ * Results:
+ * A standard Tcl result. Interp->result will contain a list
+ * of all watches matches the state criteria.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ListWatches(interp, state)
+ Tcl_Interp *interp;
+ enum WatchStates state; /* Active flag */
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ register Watch *watchPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&watchTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ watchPtr = (Watch *)Blt_GetHashValue(hPtr);
+ if (watchPtr->interp == interp) {
+ if ((state == WATCH_STATE_DONT_CARE) ||
+ (state == watchPtr->state)) {
+ Tcl_AppendElement(interp, (char *)watchPtr->nameId);
+ }
+ }
+ }
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigWatch --
+ *
+ * Processes argument list of switches and values, setting
+ * Watch fields.
+ *
+ * Results:
+ * If found, the pointer to the watch structure is returned,
+ * otherwise NULL. If requested, interp-result will be filled
+ * with an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigWatch(watchPtr, interp, argc, argv)
+ Watch *watchPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ if (Blt_ProcessSwitches(interp, switchSpecs, argc, argv, (char *)watchPtr,
+ 0) < 0) {
+ return TCL_ERROR;
+ }
+ /*
+ * If the watch's max depth changed or its state, reset the traces.
+ */
+ if (watchPtr->trace != (Tcl_Trace) 0) {
+ Tcl_DeleteTrace(interp, watchPtr->trace);
+ watchPtr->trace = (Tcl_Trace) 0;
+ }
+ if (watchPtr->state == WATCH_STATE_ACTIVE) {
+ watchPtr->trace = Tcl_CreateTrace(interp, watchPtr->maxLevel,
+ PreCmdProc, watchPtr);
+ }
+ return TCL_OK;
+}
+
+/* Tcl interface routines */
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * Creates a new watch and processes any extra switches.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * A new watch is created.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CreateOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Watch *watchPtr;
+
+ watchPtr = NameToWatch(interp, argv[2], 0);
+ if (watchPtr != NULL) {
+ Tcl_AppendResult(interp, "a watch \"", argv[2], "\" already exists",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ watchPtr = NewWatch(interp, argv[2]);
+ if (watchPtr == NULL) {
+ return TCL_ERROR; /* Can't create new watch */
+ }
+ return ConfigWatch(watchPtr, interp, argc - 3, argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the watch.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Watch *watchPtr;
+
+ watchPtr = NameToWatch(interp, argv[2], TCL_LEAVE_ERR_MSG);
+ if (watchPtr == NULL) {
+ return TCL_ERROR;
+ }
+ DestroyWatch(watchPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Activate/deactivates the named watch.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ActivateOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Watch *watchPtr;
+ enum WatchStates state;
+
+ state = (argv[1][0] == 'a') ? WATCH_STATE_ACTIVE : WATCH_STATE_IDLE;
+ watchPtr = NameToWatch(interp, argv[2], TCL_LEAVE_ERR_MSG);
+ if (watchPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (state != watchPtr->state) {
+ if (watchPtr->trace == (Tcl_Trace) 0) {
+ watchPtr->trace = Tcl_CreateTrace(interp, watchPtr->maxLevel,
+ PreCmdProc, watchPtr);
+ } else {
+ Tcl_DeleteTrace(interp, watchPtr->trace);
+ watchPtr->trace = (Tcl_Trace) 0;
+ }
+ watchPtr->state = state;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns the names of all watches in the interpreter.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NamesOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ enum WatchStates state;
+
+ state = WATCH_STATE_DONT_CARE;
+ if (argc == 3) {
+ char c;
+ c = argv[2][0];
+ if ((c == 'a') && (strcmp(argv[2], "active") == 0)) {
+ state = WATCH_STATE_ACTIVE;
+ } else if ((c == 'i') && (strcmp(argv[2], "idle") == 0)) {
+ state = WATCH_STATE_IDLE;
+ } else if ((c == 'i') && (strcmp(argv[2], "ignore") == 0)) {
+ state = WATCH_STATE_DONT_CARE;
+ } else {
+ Tcl_AppendResult(interp, "bad state \"", argv[2], "\" should be \
+\"active\", \"idle\", or \"ignore\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ return ListWatches(interp, state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Convert the range of the pixel values allowed into a list.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Watch *watchPtr;
+
+ watchPtr = NameToWatch(interp, argv[2], TCL_LEAVE_ERR_MSG);
+ if (watchPtr == NULL) {
+ return TCL_ERROR;
+ }
+ return ConfigWatch(watchPtr, interp, argc - 3, argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoOp --
+ *
+ * Convert the limits of the pixel values allowed into a list.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ register Watch *watchPtr;
+ char string[200];
+ register char **p;
+
+ watchPtr = NameToWatch(interp, argv[2], TCL_LEAVE_ERR_MSG);
+ if (watchPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (watchPtr->preCmd != NULL) {
+ Tcl_AppendResult(interp, "-precmd", (char *)NULL);
+ for (p = watchPtr->preCmd; *p != NULL; p++) {
+ Tcl_AppendResult(interp, " ", *p, (char *)NULL);
+ }
+ }
+ if (watchPtr->postCmd != NULL) {
+ Tcl_AppendResult(interp, "-postcmd", (char *)NULL);
+ for (p = watchPtr->postCmd; *p != NULL; p++) {
+ Tcl_AppendResult(interp, " ", *p, (char *)NULL);
+ }
+ }
+ sprintf(string, "%d", watchPtr->maxLevel);
+ Tcl_AppendResult(interp, "-maxlevel ", string, " ", (char *)NULL);
+ Tcl_AppendResult(interp, "-active ",
+ (watchPtr->state == WATCH_STATE_ACTIVE)
+ ? "true" : "false", " ", (char *)NULL);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * WatchCmd --
+ *
+ * This procedure is invoked to process the Tcl "blt_watch"
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Blt_OpSpec watchOps[] =
+{
+ {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "watchName",},
+ {"configure", 2, (Blt_Op)ConfigureOp, 3, 0,
+ "watchName ?options...?"},
+ {"create", 2, (Blt_Op)CreateOp, 3, 0, "watchName ?switches?",},
+ {"deactivate", 3, (Blt_Op)ActivateOp, 3, 3, "watchName",},
+ {"delete", 3, (Blt_Op)DeleteOp, 3, 3, "watchName",},
+ {"info", 1, (Blt_Op)InfoOp, 3, 3, "watchName",},
+ {"names", 1, (Blt_Op)NamesOp, 2, 3, "?state?",},
+};
+static int nWatchOps = sizeof(watchOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+WatchCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nWatchOps, watchOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+/* ARGSUSED */
+static void
+WatchDeleteCmd(clientData)
+ ClientData clientData; /* Not Used. */
+{
+ refCount--;
+ if (refCount == 0) {
+ Blt_DeleteHashTable(&watchTable);
+ }
+}
+
+/* Public initialization routine */
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_WatchInit --
+ *
+ * This procedure is invoked to initialize the Tcl command
+ * "blt_watch".
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds a new entry into a
+ * global Tcl associative array.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_WatchInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {"watch", WatchCmd, WatchDeleteCmd};
+
+ if (refCount == 0) {
+ Blt_InitHashTable(&watchTable, sizeof(WatchKey) / sizeof(int));
+ }
+ refCount++;
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
diff --git a/blt/src/bltWinConfig.h b/blt/src/bltWinConfig.h
new file mode 100644
index 00000000000..7cda420f6a0
--- /dev/null
+++ b/blt/src/bltWinConfig.h
@@ -0,0 +1,159 @@
+/* src/bltConfig.h. Generated automatically by configure. */
+/* src/bltConfig.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#ifdef _MSC_VER
+#define pid_t int
+#endif
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+#undef size_t
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+
+/* Define if DBL_EPSILON is not defined in float.h */
+#undef BLT_DBL_EPSILON
+
+/* Define if drand48 isn't declared in math.h. */
+#define NEED_DECL_DRAND48 1
+
+/* Define if srand48 isn't declared in math.h. */
+#define NEED_DECL_SRAND48 1
+
+/* Define if strdup isn't declared in a standard header file. */
+#undef NEED_DECL_STRDUP
+
+/* Define if j1 isn't declared in a standard header file. */
+#define NEED_DECL_J1 1
+
+/* Define if union wait type is defined incorrectly. */
+#undef HAVE_UNION_WAIT
+
+/* Define if isfinite is found in libm. */
+#undef HAVE_ISFINITE
+
+/* The number of bytes in a long. */
+#define SIZEOF_LONG 4
+
+/* The number of bytes in a long long. */
+#define SIZEOF_LONG_LONG 8
+
+/* The number of bytes in a void *. */
+#define SIZEOF_VOID_P 4
+
+/* Define if you have the XExtendedMaxRequestSize function. */
+#undef HAVE_XEXTENDEDMAXREQUESTSIZE
+
+/* Define if you have the drand48 function. */
+#define HAVE_DRAND48 1
+
+/* Define if you have the finite function. */
+#undef HAVE_FINITE
+
+/* Define if you have the srand48 function. */
+#define HAVE_SRAND48 1
+
+/* Define if you have the strcasecmp function. */
+#define HAVE_STRCASECMP 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strncasecmp function. */
+#define HAVE_STRNCASECMP 1
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <float.h> header file. */
+#define HAVE_FLOAT_H 1
+
+/* Define if you have the <ieeefp.h> header file. */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <jpeglib.h> header file. */
+/* Defined in Makefile */
+/* #undef HAVE_JPEGLIB_H */
+#define HAVE_JPEGLIB_H 1
+
+/* Define if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define if you have the <math.h> header file. */
+#define HAVE_MATH_H 1
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <waitflags.h> header file. */
+#undef HAVE_WAITFLAGS_H
+
+/* Define if you have the m library (-lm). */
+#define HAVE_LIBM 1
+
+/* Define if you have the nsl library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Misc. definitions */
+
+#if !defined(O_NONBLOCK) && !defined(__CYGWIN__)
+#define O_NONBLOCK 1
+#endif
+
+#define NO_CUTBUFFER 1
+#define NO_TILESCROLLBAR 1
+#define NO_DND 1
+/*
+#define NO_TILEFRAME 1
+#define NO_TILEBUTTON 1
+*/
+
+#ifndef EXPORT
+#ifdef _MSC_VER
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif /* _MSC_VER */
+#endif /* EXPORT */
diff --git a/blt/src/bltWinDde.c b/blt/src/bltWinDde.c
new file mode 100644
index 00000000000..f0347b4f548
--- /dev/null
+++ b/blt/src/bltWinDde.c
@@ -0,0 +1,1312 @@
+
+/*
+ * bltWinDde.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.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_DDE
+
+#include <ddeml.h>
+
+/*
+ * The following structure is used to keep track of the interpreters
+ * registered by this process.
+ */
+
+typedef struct RegisteredInterp {
+ struct RegisteredInterp *nextPtr;
+ /* The next interp this application knows
+ * about. */
+ Tcl_Interp *interp; /* The interpreter attached to this name. */
+ char name[1]; /* Interpreter's name. Malloc-ed as
+ * part of the structure. */
+} RegisteredInterp;
+
+/*
+ * Used to keep track of conversations.
+ */
+
+typedef struct Conversation {
+ struct Conversation *nextPtr;
+ /* The next conversation in the list. */
+ RegisteredInterp *riPtr; /* The info we know about the conversation. */
+ HCONV hConv; /* The DDE handle for this conversation. */
+ Tcl_Obj *returnPackagePtr; /* The result package for this conversation. */
+
+} Conversation;
+
+static Conversation *conversations; /* A list of conversations currently
+ * being processed. */
+static RegisteredInterp *interps; /* List of all interpreters registered
+ * in the current process. */
+static HSZ globalService;
+static DWORD instance; /* The application instance handle given
+ * to us by DdeInitialize. */
+static int isServer;
+
+#define TCL_DDE_VERSION "1.2"
+#define TCL_DDE_PACKAGE_NAME "dde"
+#define TCL_DDE_SERVICE_NAME "TclEval"
+
+/*
+ * Forward declarations for procedures defined later in this file.
+ */
+
+static Tcl_Obj *ExecuteRemoteObject _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_Obj *objPtr));
+static int MakeConnection _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ HCONV *convPtr));
+static HDDEDATA CALLBACK ServerProc _ANSI_ARGS_((UINT uType, UINT uFmt,
+ HCONV hConv, HSZ topic, HSZ item, HDDEDATA hData, DWORD dwData1,
+ DWORD dwData2));
+
+static Tcl_ExitProc ExitProc;
+static Tcl_CmdDeleteProc DeleteProc;
+static void SetError _ANSI_ARGS_((Tcl_Interp *interp));
+
+static Tcl_ObjCmdProc DdeObjCmd;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Initialize --
+ *
+ * Initialize the global DDE instance.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Registers the DDE server proc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Initialize(void)
+{
+ int nameFound = 0;
+
+ /*
+ * See if the application is already registered; if so, remove its
+ * current name from the registry. The deletion of the command
+ * will take care of disposing of this entry.
+ */
+
+ if (interps != NULL) {
+ nameFound = 1;
+ }
+
+ /*
+ * Make sure that the DDE server is there. This is done only once,
+ * add an exit handler tear it down.
+ */
+
+ if (instance == 0) {
+ if (instance == 0) {
+ unsigned int flags;
+
+ flags = (CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS |
+ CBF_FAIL_POKES);
+ if (DdeInitialize(&instance, ServerProc, flags, 0)
+ != DMLERR_NO_ERROR) {
+ instance = 0;
+ }
+ }
+ }
+ if ((globalService == 0) && (nameFound != 0)) {
+ if ((globalService == 0) && (nameFound != 0)) {
+ isServer = TRUE;
+ Tcl_CreateExitHandler(ExitProc, NULL);
+ globalService = DdeCreateStringHandle(instance,
+ TCL_DDE_SERVICE_NAME, 0);
+ DdeNameService(instance, globalService, 0L, DNS_REGISTER);
+ } else {
+ isServer = FALSE;
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SetServerName --
+ *
+ * This procedure is called to associate an ASCII name with a Dde
+ * server. If the interpreter has already been named, the
+ * name replaces the old one.
+ *
+ * Results:
+ * The return value is the name actually given to the interp.
+ * This will normally be the same as name, but if name was already
+ * in use for a Dde Server 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.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+SetServerName(
+ Tcl_Interp *interp,
+ char *name /* The name that will be used to
+ * refer to the interpreter in later
+ * "send" commands. Must be globally
+ * unique. */
+ )
+{
+ int suffix, offset;
+ RegisteredInterp *riPtr, *prevPtr;
+ Tcl_DString dString;
+
+ /*
+ * See if the application is already registered; if so, remove its
+ * current name from the registry. The deletion of the command
+ * will take care of disposing of this entry.
+ */
+
+ for (riPtr = interps, prevPtr = NULL; riPtr != NULL;
+ prevPtr = riPtr, riPtr = riPtr->nextPtr) {
+ if (riPtr->interp == interp) {
+ if (name != NULL) {
+ if (prevPtr == NULL) {
+ interps = interps->nextPtr;
+ } else {
+ prevPtr->nextPtr = riPtr->nextPtr;
+ }
+ break;
+ } else {
+ /*
+ * the name was NULL, so the caller is asking for
+ * the name of the current interp.
+ */
+
+ return riPtr->name;
+ }
+ }
+ }
+
+ if (name == NULL) {
+ /*
+ * the name was NULL, so the caller is asking for
+ * the name of the current interp, but it doesn't
+ * have a name.
+ */
+
+ return "";
+ }
+
+ /*
+ * Pick a name to use for the application. Use "name" if it's not
+ * already in use. Otherwise add a suffix such as " #2", trying
+ * larger and larger numbers until we eventually find one that is
+ * unique.
+ */
+
+ suffix = 1;
+ offset = 0;
+ Tcl_DStringInit(&dString);
+
+ /*
+ * We have found a unique name. Now add it to the registry.
+ */
+
+ riPtr = Blt_Malloc(sizeof(RegisteredInterp) + strlen(name));
+ riPtr->interp = interp;
+ riPtr->nextPtr = interps;
+ interps = riPtr;
+ strcpy(riPtr->name, name);
+
+ Tcl_CreateObjCommand(interp, "dde", DdeObjCmd, riPtr, DeleteProc);
+ if (Tcl_IsSafe(interp)) {
+ Tcl_HideCommand(interp, "dde", "dde");
+ }
+ Tcl_DStringFree(&dString);
+
+ /*
+ * re-initialize with the new name
+ */
+ Initialize();
+
+ return riPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DeleteProc
+ *
+ * This procedure is called when the command "dde" is destroyed.
+ *
+ * Results:
+ * none
+ *
+ * Side effects:
+ * The interpreter given by riPtr is unregistered.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DeleteProc(clientData)
+ ClientData clientData; /* The interp we are deleting passed
+ * as ClientData. */
+{
+ RegisteredInterp *riPtr = clientData;
+ RegisteredInterp *searchPtr, *prevPtr;
+
+ for (searchPtr = interps, prevPtr = NULL;
+ (searchPtr != NULL) && (searchPtr != riPtr);
+ prevPtr = searchPtr, searchPtr = searchPtr->nextPtr) {
+ /*
+ * Empty loop body.
+ */
+ }
+
+ if (searchPtr != NULL) {
+ if (prevPtr == NULL) {
+ interps = interps->nextPtr;
+ } else {
+ prevPtr->nextPtr = searchPtr->nextPtr;
+ }
+ }
+ Tcl_EventuallyFree(clientData, TCL_DYNAMIC);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExecuteRemoteObject --
+ *
+ * Takes the package delivered by DDE and executes it in
+ * the server's interpreter.
+ *
+ * Results:
+ * A list Tcl_Obj * that describes what happened. The first
+ * element is the numerical return code (TCL_ERROR, etc.).
+ * The second element is the result of the script. If the
+ * return result was TCL_ERROR, then the third element
+ * will be the value of the global "errorCode", and the
+ * fourth will be the value of the global "errorInfo".
+ * The return result will have a refCount of 0.
+ *
+ * Side effects:
+ * A Tcl script is run, which can cause all kinds of other
+ * things to happen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+ExecuteRemoteObject(
+ Tcl_Interp *interp, /* Remote interpreter. */
+ Tcl_Obj *objPtr) /* The object to execute. */
+{
+ Tcl_Obj *listObjPtr;
+ int result;
+
+ result = Tcl_GlobalEval(interp, Tcl_GetString(objPtr));
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ Tcl_ListObjAppendElement(NULL, listObjPtr, Tcl_NewIntObj(result));
+ Tcl_ListObjAppendElement(NULL, listObjPtr, Tcl_GetObjResult(interp));
+ if (result == TCL_ERROR) {
+ char *value;
+ Tcl_Obj *objPtr;
+
+ value = Tcl_GetVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY);
+ objPtr = Tcl_NewStringObj(value, -1);
+ Tcl_ListObjAppendElement(NULL, listObjPtr, objPtr);
+ value = Tcl_GetVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
+ objPtr = Tcl_NewStringObj(value, -1);
+ Tcl_ListObjAppendElement(NULL, listObjPtr, objPtr);
+ }
+ return listObjPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ServerProc --
+ *
+ * Handles all transactions for this server. Can handle
+ * execute, request, and connect protocols. Dde will
+ * call this routine when a client attempts to run a dde
+ * command using this server.
+ *
+ * Results:
+ * A DDE Handle with the result of the dde command.
+ *
+ * Side effects:
+ * Depending on which command is executed, arbitrary
+ * Tcl scripts can be run.
+ *
+ *--------------------------------------------------------------
+ */
+
+static HDDEDATA CALLBACK
+ServerProc (
+ UINT uType, /* The type of DDE transaction we
+ * are performing. */
+ UINT uFmt, /* The format that data is sent or
+ * received. */
+ HCONV hConv, /* The conversation associated with the
+ * current transaction. */
+ HSZ topic, /* A string handle. Transaction-type
+ * dependent. */
+ HSZ item, /* A string handle. Transaction-type
+ * dependent. */
+ HDDEDATA hData, /* DDE data. Transaction-type dependent. */
+ DWORD dwData1, /* Transaction-dependent data. */
+ DWORD dwData2) /* Transaction-dependent data. */
+{
+ Tcl_DString dString;
+ int length;
+ char *utilString;
+ Tcl_Obj *objPtr;
+ HDDEDATA code = NULL;
+ RegisteredInterp *riPtr;
+ Conversation *convPtr, *prevConvPtr;
+
+ switch(uType) {
+ case XTYP_CONNECT:
+
+ /*
+ * Dde is trying to initialize a conversation with us. Check
+ * and make sure we have a valid topic.
+ */
+
+ length = DdeQueryString(instance, topic, NULL, 0, 0);
+ Tcl_DStringInit(&dString);
+ Tcl_DStringSetLength(&dString, length);
+ utilString = Tcl_DStringValue(&dString);
+ DdeQueryString(instance, topic, utilString, length + 1,
+ CP_WINANSI);
+
+ for (riPtr = interps; riPtr != NULL; riPtr = riPtr->nextPtr) {
+ if (strcasecmp(utilString, riPtr->name) == 0) {
+ Tcl_DStringFree(&dString);
+ return (HDDEDATA) TRUE;
+ }
+ }
+
+ Tcl_DStringFree(&dString);
+ return (HDDEDATA) FALSE;
+
+ case XTYP_CONNECT_CONFIRM:
+
+ /*
+ * Dde has decided that we can connect, so it gives us a
+ * conversation handle. We need to keep track of it
+ * so we know which execution result to return in an
+ * XTYP_REQUEST.
+ */
+
+ length = DdeQueryString(instance, topic, NULL, 0, 0);
+ Tcl_DStringInit(&dString);
+ Tcl_DStringSetLength(&dString, length);
+ utilString = Tcl_DStringValue(&dString);
+ DdeQueryString(instance, topic, utilString, length + 1,
+ CP_WINANSI);
+ for (riPtr = interps; riPtr != NULL; riPtr = riPtr->nextPtr) {
+ if (strcasecmp(riPtr->name, utilString) == 0) {
+ convPtr = Blt_Malloc(sizeof(Conversation));
+ convPtr->nextPtr = conversations;
+ convPtr->returnPackagePtr = NULL;
+ convPtr->hConv = hConv;
+ convPtr->riPtr = riPtr;
+ conversations = convPtr;
+ break;
+ }
+ }
+ Tcl_DStringFree(&dString);
+ return (HDDEDATA) TRUE;
+
+ case XTYP_DISCONNECT:
+
+ /*
+ * The client has disconnected from our server. Forget this
+ * conversation.
+ */
+
+ for (convPtr = conversations, prevConvPtr = NULL;
+ convPtr != NULL;
+ prevConvPtr = convPtr, convPtr = convPtr->nextPtr) {
+ if (hConv == convPtr->hConv) {
+ if (prevConvPtr == NULL) {
+ conversations = convPtr->nextPtr;
+ } else {
+ prevConvPtr->nextPtr = convPtr->nextPtr;
+ }
+ if (convPtr->returnPackagePtr != NULL) {
+ Tcl_DecrRefCount(convPtr->returnPackagePtr);
+ }
+ Blt_Free(convPtr);
+ break;
+ }
+ }
+ return (HDDEDATA) TRUE;
+
+ case XTYP_REQUEST:
+
+ /*
+ * This could be either a request for a value of a Tcl variable,
+ * or it could be the send command requesting the results of the
+ * last execute.
+ */
+
+ if (uFmt != CF_TEXT) {
+ return (HDDEDATA) FALSE;
+ }
+
+ code = (HDDEDATA) FALSE;
+ for (convPtr = conversations; (convPtr != NULL)
+ && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+ /*
+ * Empty loop body.
+ */
+ }
+
+ if (convPtr != NULL) {
+
+ length = DdeQueryString(instance, item, NULL, 0, CP_WINANSI);
+ Tcl_DStringInit(&dString);
+ Tcl_DStringSetLength(&dString, length);
+ utilString = Tcl_DStringValue(&dString);
+ DdeQueryString(instance, item, utilString, length + 1,
+ CP_WINANSI);
+ if (strcasecmp(utilString, "$TCLEVAL$EXECUTE$RESULT") == 0) {
+ char *value;
+
+ value = Tcl_GetStringFromObj(convPtr->returnPackagePtr,
+ &length);
+ code = DdeCreateDataHandle(instance, value, length+1, 0,
+ item, CF_TEXT, 0);
+ } else {
+ char *value;
+
+ value = Tcl_GetVar2(convPtr->riPtr->interp, utilString,
+ NULL, TCL_GLOBAL_ONLY);
+ if (value != NULL) {
+ length = strlen(value);
+ code = DdeCreateDataHandle(instance, value, length+1,
+ 0, item, CF_TEXT, 0);
+ } else {
+ code = NULL;
+ }
+ }
+ Tcl_DStringFree(&dString);
+ }
+ return code;
+
+ case XTYP_EXECUTE: {
+
+ /*
+ * Execute this script. The results will be saved into
+ * a list object which will be retreived later. See
+ * ExecuteRemoteObject.
+ */
+
+ Tcl_Obj *returnPackagePtr;
+
+ for (convPtr = conversations; (convPtr != NULL)
+ && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+ /*
+ * Empty loop body.
+ */
+
+ }
+
+ if (convPtr == NULL) {
+ return (HDDEDATA) DDE_FNOTPROCESSED;
+ }
+
+ utilString = (char *) DdeAccessData(hData, &length);
+ objPtr = Tcl_NewStringObj(utilString, -1);
+ Tcl_IncrRefCount(objPtr);
+ DdeUnaccessData(hData);
+ if (convPtr->returnPackagePtr != NULL) {
+ Tcl_DecrRefCount(convPtr->returnPackagePtr);
+ }
+ convPtr->returnPackagePtr = NULL;
+ returnPackagePtr = ExecuteRemoteObject(convPtr->riPtr->interp,
+ objPtr);
+ for (convPtr = conversations; (convPtr != NULL)
+ && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+ /*
+ * Empty loop body.
+ */
+
+ }
+ if (convPtr != NULL) {
+ Tcl_IncrRefCount(returnPackagePtr);
+ convPtr->returnPackagePtr = returnPackagePtr;
+ }
+ Tcl_DecrRefCount(objPtr);
+ if (returnPackagePtr == NULL) {
+ return (HDDEDATA) DDE_FNOTPROCESSED;
+ } else {
+ return (HDDEDATA) DDE_FACK;
+ }
+ }
+
+ case XTYP_WILDCONNECT: {
+
+ /*
+ * Dde wants a list of services and topics that we support.
+ */
+
+ HSZPAIR *returnPtr;
+ int i;
+ int numItems;
+
+ for (i = 0, riPtr = interps; riPtr != NULL;
+ i++, riPtr = riPtr->nextPtr) {
+ /*
+ * Empty loop body.
+ */
+
+ }
+
+ numItems = i;
+ code = DdeCreateDataHandle(instance, NULL,
+ (numItems + 1) * sizeof(HSZPAIR), 0, 0, 0, 0);
+ returnPtr = (HSZPAIR *) DdeAccessData(code, &length);
+ for (i = 0, riPtr = interps; i < numItems;
+ i++, riPtr = riPtr->nextPtr) {
+ returnPtr[i].hszSvc = DdeCreateStringHandle(
+ instance, "TclEval", CP_WINANSI);
+ returnPtr[i].hszTopic = DdeCreateStringHandle(
+ instance, riPtr->name, CP_WINANSI);
+ }
+ returnPtr[i].hszSvc = NULL;
+ returnPtr[i].hszTopic = NULL;
+ DdeUnaccessData(code);
+ return code;
+ }
+
+ }
+ return NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExitProc --
+ *
+ * Gets rid of our DDE server when we go away.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The DDE server is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ExitProc(
+ ClientData clientData) /* Not used in this handler. */
+{
+ DdeNameService(instance, NULL, 0, DNS_UNREGISTER);
+ DdeUninitialize(instance);
+ instance = 0;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MakeConnection --
+ *
+ * This procedure is a utility used to connect to a DDE
+ * server when given a server name and a topic name.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *
+ * Side effects:
+ * Passes back a conversation through ddeConvPtr
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MakeConnection(
+ Tcl_Interp *interp, /* Used to report errors. */
+ char *name, /* The connection to use. */
+ HCONV *convPtr)
+{
+ HSZ topic, service;
+ HCONV conv;
+
+ service = DdeCreateStringHandle(instance, "TclEval", 0);
+ topic = DdeCreateStringHandle(instance, name, 0);
+
+ conv = DdeConnect(instance, service, topic, NULL);
+ DdeFreeStringHandle(instance, service);
+ DdeFreeStringHandle(instance, topic);
+
+ if (conv == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "no registered server named \"", name,
+ "\"", (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+
+ *convPtr = conv;
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SetError --
+ *
+ * Sets the interp result to a cogent error message
+ * describing the last DDE error.
+ *
+ * Results:
+ * None.
+ *
+ *
+ * Side effects:
+ * The interp's result object is changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+SetError(
+ Tcl_Interp *interp) /* The interp to put the message in.*/
+{
+ int err;
+ char *mesg;
+
+ err = DdeGetLastError(instance);
+ switch (err) {
+ case DMLERR_DATAACKTIMEOUT:
+ case DMLERR_EXECACKTIMEOUT:
+ case DMLERR_POKEACKTIMEOUT:
+ mesg = "remote interpreter did not respond";
+ break;
+
+ case DMLERR_BUSY:
+ mesg = "remote server is busy";
+ break;
+
+ case DMLERR_NOTPROCESSED:
+ mesg = "remote server cannot handle this command";
+ break;
+
+ default:
+ mesg = "dde command failed";
+ break;
+ }
+ Tcl_SetResult(interp, mesg, TCL_VOLATILE);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DdeObjCmd --
+ *
+ * This procedure is invoked to process the "dde" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+DdeObjCmd(
+ ClientData clientData, /* Used only for deletion */
+ Tcl_Interp *interp, /* The interp we are sending from */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[]) /* The arguments */
+{
+ enum {
+ DDE_SERVERNAME,
+ DDE_EXECUTE,
+ DDE_POKE,
+ DDE_REQUEST,
+ DDE_SERVICES,
+ DDE_EVAL
+ };
+
+ static char *commands[] = {
+ "servername", "execute", "poke", "request", "services", "eval",
+ (char *) NULL
+ };
+ static char *options[] = {
+ "-async", (char *) NULL
+ };
+ int index, argIndex;
+ int async = 0, binary = 0;
+ int result = TCL_OK;
+ HSZ service = NULL;
+ HSZ topic = NULL;
+ HSZ item = NULL;
+ HDDEDATA data = NULL;
+ HDDEDATA itemData = NULL;
+ HCONV hConv = NULL;
+ HSZ cookie = 0;
+ char *serviceName, *topicName, *itemString, *dataString;
+ char *string;
+ int firstArg, length, dataLength;
+ HDDEDATA code;
+ RegisteredInterp *riPtr;
+ Tcl_Interp *sendInterp;
+ Tcl_Obj *objPtr;
+
+ /*
+ * Initialize DDE server/client
+ */
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "?-async? serviceName topicName value");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[1], commands, "command", 0,
+ &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ switch (index) {
+ case DDE_SERVERNAME:
+ if ((objc != 3) && (objc != 2)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "servername ?serverName?");
+ return TCL_ERROR;
+ }
+ firstArg = (objc - 1);
+ break;
+ case DDE_EXECUTE:
+ if ((objc < 5) || (objc > 6)) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "execute ?-async? serviceName topicName value");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObj(NULL, objv[2], options, "option", 0,
+ &argIndex) != TCL_OK) {
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "execute ?-async? serviceName topicName value");
+ return TCL_ERROR;
+ }
+ async = 0;
+ firstArg = 2;
+ } else {
+ if (objc != 6) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "execute ?-async? serviceName topicName value");
+ return TCL_ERROR;
+ }
+ async = 1;
+ firstArg = 3;
+ }
+ break;
+ case DDE_POKE:
+ if (objc != 6) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "poke serviceName topicName item value");
+ return TCL_ERROR;
+ }
+ firstArg = 2;
+ break;
+ case DDE_REQUEST:
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "request serviceName topicName value");
+ return TCL_ERROR;
+ }
+ binary = 0;
+ firstArg = 2;
+ break;
+ case DDE_SERVICES:
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "services serviceName topicName");
+ return TCL_ERROR;
+ }
+ firstArg = 2;
+ break;
+ case DDE_EVAL:
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "eval ?-async? serviceName args");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObj(NULL, objv[2], options, "option", 0,
+ &argIndex) != TCL_OK) {
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "eval ?-async? serviceName args");
+ return TCL_ERROR;
+ }
+ async = 0;
+ firstArg = 2;
+ } else {
+ if (objc < 5) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "eval ?-async? serviceName args");
+ return TCL_ERROR;
+ }
+ async = 1;
+ firstArg = 3;
+ }
+ break;
+ }
+
+ Initialize();
+
+ if (firstArg != 1) {
+ serviceName = Tcl_GetStringFromObj(objv[firstArg], &length);
+ } else {
+ length = 0;
+ }
+
+ if (length == 0) {
+ serviceName = NULL;
+ } else if ((index != DDE_SERVERNAME) && (index != DDE_EVAL)) {
+ service = DdeCreateStringHandle(instance, serviceName,
+ CP_WINANSI);
+ }
+
+ if ((index != DDE_SERVERNAME) &&(index != DDE_EVAL)) {
+ topicName = Tcl_GetStringFromObj(objv[firstArg + 1], &length);
+ if (length == 0) {
+ topicName = NULL;
+ } else {
+ topic = DdeCreateStringHandle(instance, topicName, CP_WINANSI);
+ }
+ }
+
+ switch (index) {
+ case DDE_SERVERNAME: {
+ serviceName = SetServerName(interp, serviceName);
+ if (serviceName != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ serviceName, -1);
+ } else {
+ Tcl_ResetResult(interp);
+ }
+ break;
+ }
+ case DDE_EXECUTE: {
+ dataString = Tcl_GetStringFromObj(objv[firstArg + 2], &dataLength);
+ if (dataLength == 0) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "cannot execute null data", -1);
+ result = TCL_ERROR;
+ break;
+ }
+ hConv = DdeConnect(instance, service, topic, NULL);
+ DdeFreeStringHandle(instance, service);
+ DdeFreeStringHandle(instance, topic);
+
+ if (hConv == NULL) {
+ SetError(interp);
+ result = TCL_ERROR;
+ break;
+ }
+
+ data = DdeCreateDataHandle(instance, dataString, dataLength + 1,
+ 0, 0, CF_TEXT, 0);
+ if (data != NULL) {
+ if (async) {
+ DWORD status;
+
+ DdeClientTransaction((LPBYTE) data, 0xFFFFFFFF, hConv, 0,
+ CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &status);
+ DdeAbandonTransaction(instance, hConv, status);
+ } else {
+ code = DdeClientTransaction((LPBYTE) data, 0xFFFFFFFF,
+ hConv, 0, CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+ if (code == 0) {
+ SetError(interp);
+ result = TCL_ERROR;
+ }
+ }
+ DdeFreeDataHandle(data);
+ } else {
+ SetError(interp);
+ result = TCL_ERROR;
+ }
+ break;
+ }
+ case DDE_REQUEST: {
+ itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
+ if (length == 0) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "cannot request value of null data", -1);
+ return TCL_ERROR;
+ }
+ hConv = DdeConnect(instance, service, topic, NULL);
+ DdeFreeStringHandle(instance, service);
+ DdeFreeStringHandle(instance, topic);
+
+ if (hConv == NULL) {
+ SetError(interp);
+ result = TCL_ERROR;
+ } else {
+ item = DdeCreateStringHandle(instance, itemString, CP_WINANSI);
+ if (item != NULL) {
+ data = DdeClientTransaction(NULL, 0, hConv, item, CF_TEXT,
+ XTYP_REQUEST, 5000, NULL);
+ if (data == NULL) {
+ SetError(interp);
+ result = TCL_ERROR;
+ } else {
+ Tcl_Obj *objPtr;
+
+ dataString = DdeAccessData(data, &dataLength);
+ objPtr = Tcl_NewStringObj(dataString, -1);
+ DdeUnaccessData(data);
+ DdeFreeDataHandle(data);
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ } else {
+ SetError(interp);
+ result = TCL_ERROR;
+ }
+ }
+
+ break;
+ }
+ case DDE_POKE: {
+ itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
+ if (length == 0) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "cannot have a null item", -1);
+ return TCL_ERROR;
+ }
+ dataString = Tcl_GetStringFromObj(objv[firstArg + 3], &length);
+
+ hConv = DdeConnect(instance, service, topic, NULL);
+ DdeFreeStringHandle(instance, service);
+ DdeFreeStringHandle(instance, topic);
+
+ if (hConv == NULL) {
+ SetError(interp);
+ result = TCL_ERROR;
+ } else {
+ item = DdeCreateStringHandle(instance, itemString,
+ CP_WINANSI);
+ if (item != NULL) {
+ data = DdeClientTransaction(dataString,length+1,
+ hConv, item, CF_TEXT, XTYP_POKE, 5000, NULL);
+ if (data == NULL) {
+ SetError(interp);
+ result = TCL_ERROR;
+ }
+ } else {
+ SetError(interp);
+ result = TCL_ERROR;
+ }
+ }
+ break;
+ }
+
+ case DDE_SERVICES: {
+ HCONVLIST hConvList;
+ CONVINFO convInfo;
+ Tcl_Obj *convListObjPtr, *elementObjPtr;
+ Tcl_DString dString;
+ char *name;
+
+ convInfo.cb = sizeof(CONVINFO);
+ hConvList = DdeConnectList(instance, service,
+ topic, 0, NULL);
+ DdeFreeStringHandle(instance, service);
+ DdeFreeStringHandle(instance, topic);
+ hConv = 0;
+ convListObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ Tcl_DStringInit(&dString);
+
+ while (hConv = DdeQueryNextServer(hConvList, hConv), hConv != 0) {
+ elementObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ DdeQueryConvInfo(hConv, QID_SYNC, &convInfo);
+ length = DdeQueryString(instance,
+ convInfo.hszSvcPartner, NULL, 0, CP_WINANSI);
+ Tcl_DStringSetLength(&dString, length);
+ name = Tcl_DStringValue(&dString);
+ DdeQueryString(instance, convInfo.hszSvcPartner,
+ name, length + 1, CP_WINANSI);
+ Tcl_ListObjAppendElement(interp, elementObjPtr,
+ Tcl_NewStringObj(name, length));
+ length = DdeQueryString(instance, convInfo.hszTopic,
+ NULL, 0, CP_WINANSI);
+ Tcl_DStringSetLength(&dString, length);
+ name = Tcl_DStringValue(&dString);
+ DdeQueryString(instance, convInfo.hszTopic, name,
+ length + 1, CP_WINANSI);
+ Tcl_ListObjAppendElement(interp, elementObjPtr,
+ Tcl_NewStringObj(name, length));
+ Tcl_ListObjAppendElement(interp, convListObjPtr,
+ elementObjPtr);
+ }
+ DdeDisconnectList(hConvList);
+ Tcl_SetObjResult(interp, convListObjPtr);
+ Tcl_DStringFree(&dString);
+ break;
+ }
+ case DDE_EVAL: {
+ objc -= (async + 3);
+ ((Tcl_Obj **) objv) += (async + 3);
+
+ /*
+ * See if the target interpreter is local. If so, execute
+ * the command directly without going through the DDE
+ * server. Don't exchange objects between interps. The
+ * target interp could compile an object, producing a
+ * bytecode structure that refers to other objects owned
+ * by the target interp. If the target interp is then
+ * deleted, the bytecode structure would be referring to
+ * deallocated objects.
+ */
+
+ for (riPtr = interps; riPtr != NULL;
+ riPtr = riPtr->nextPtr) {
+ if (strcasecmp(serviceName, riPtr->name) == 0) {
+ break;
+ }
+ }
+
+ if (riPtr != NULL) {
+ /*
+ * This command is to a local interp. No need to go through
+ * the server.
+ */
+
+ Tcl_Preserve(riPtr);
+ sendInterp = riPtr->interp;
+ Tcl_Preserve(sendInterp);
+
+ /*
+ * Don't exchange objects between interps. The target interp
+ * would compile an object, producing a bytecode structure that
+ * refers to other objects owned by the target interp. If the
+ * target interp is then deleted, the bytecode structure would
+ * be referring to deallocated objects.
+ */
+
+ if (objc == 1) {
+ result = Tcl_GlobalEval(sendInterp,Tcl_GetString(objv[0]));
+ } else {
+ objPtr = Tcl_ConcatObj(objc, objv);
+ Tcl_IncrRefCount(objPtr);
+ result = Tcl_GlobalEval(sendInterp, Tcl_GetString(objPtr));
+ Tcl_DecrRefCount(objPtr);
+ }
+ if (interp != sendInterp) {
+ if (result == TCL_ERROR) {
+ char *value;
+ /*
+ * An error occurred, so transfer error information
+ * from the destination interpreter back to our
+ * interpreter.
+ */
+
+ Tcl_ResetResult(interp);
+ value = Tcl_GetVar2(sendInterp, "errorInfo", NULL,
+ TCL_GLOBAL_ONLY);
+ Tcl_AddObjErrorInfo(interp, value, length);
+
+ value = Tcl_GetVar2(sendInterp, "errorCode", NULL,
+ TCL_GLOBAL_ONLY);
+ Tcl_SetErrorCode(interp, value, (char *)NULL);
+ }
+ Tcl_SetObjResult(interp, Tcl_GetObjResult(sendInterp));
+ }
+ Tcl_Release(riPtr);
+ Tcl_Release(sendInterp);
+ } else {
+ /*
+ * This is a non-local request. Send the script to the server
+ * and poll it for a result.
+ */
+
+ if (MakeConnection(interp, serviceName, &hConv) != TCL_OK) {
+ goto error;
+ }
+
+ objPtr = Tcl_ConcatObj(objc, objv);
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ itemData = DdeCreateDataHandle(instance, string,
+ length+1, 0, 0, CF_TEXT, 0);
+
+ if (async) {
+ DWORD status;
+
+ data = DdeClientTransaction((LPBYTE) itemData, 0xFFFFFFFF,
+ hConv, 0, CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC,
+ &status);
+ DdeAbandonTransaction(instance, hConv, status);
+ } else {
+ data = DdeClientTransaction((LPBYTE) itemData,
+ 0xFFFFFFFF, hConv, 0,
+ CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+ if (data != 0) {
+
+ cookie = DdeCreateStringHandle(instance,
+ "$TCLEVAL$EXECUTE$RESULT", CP_WINANSI);
+ data = DdeClientTransaction(NULL, 0, hConv,
+ cookie, CF_TEXT, XTYP_REQUEST, 30000, NULL);
+ }
+ }
+
+ Tcl_DecrRefCount(objPtr);
+
+ if (data == 0) {
+ SetError(interp);
+ goto errorNoResult;
+ }
+
+ if (async == 0) {
+ Tcl_Obj *resultPtr;
+
+ /*
+ * The return handle has a two or four element list in
+ * it. The first element is the return code (TCL_OK,
+ * TCL_ERROR, etc.). The second is the result of the
+ * script. If the return code is TCL_ERROR, then the third
+ * element is the value of the variable "errorCode", and
+ * the fourth is the value of the variable "errorInfo".
+ */
+
+ resultPtr = Tcl_NewObj();
+ length = DdeGetData(data, NULL, 0, 0);
+ Tcl_SetObjLength(resultPtr, length);
+ string = Tcl_GetString(resultPtr);
+ DdeGetData(data, string, length, 0);
+ Tcl_SetObjLength(resultPtr, strlen(string));
+
+ if (Tcl_ListObjIndex(NULL, resultPtr, 0, &objPtr)
+ != TCL_OK) {
+ Tcl_DecrRefCount(resultPtr);
+ goto error;
+ }
+ if (Tcl_GetIntFromObj(NULL, objPtr, &result) != TCL_OK) {
+ Tcl_DecrRefCount(resultPtr);
+ goto error;
+ }
+ if (result == TCL_ERROR) {
+ Tcl_ResetResult(interp);
+
+ if (Tcl_ListObjIndex(NULL, resultPtr, 3, &objPtr)
+ != TCL_OK) {
+ Tcl_DecrRefCount(resultPtr);
+ goto error;
+ }
+ length = -1;
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ Tcl_AddObjErrorInfo(interp, string, length);
+
+ Tcl_ListObjIndex(NULL, resultPtr, 2, &objPtr);
+ Tcl_SetObjErrorCode(interp, objPtr);
+ }
+ if (Tcl_ListObjIndex(NULL, resultPtr, 1, &objPtr)
+ != TCL_OK) {
+ Tcl_DecrRefCount(resultPtr);
+ goto error;
+ }
+ Tcl_SetObjResult(interp, objPtr);
+ Tcl_DecrRefCount(resultPtr);
+ }
+ }
+ }
+ }
+ if (cookie != NULL) {
+ DdeFreeStringHandle(instance, cookie);
+ }
+ if (item != NULL) {
+ DdeFreeStringHandle(instance, item);
+ }
+ if (itemData != NULL) {
+ DdeFreeDataHandle(itemData);
+ }
+ if (data != NULL) {
+ DdeFreeDataHandle(data);
+ }
+ if (hConv != NULL) {
+ DdeDisconnect(hConv);
+ }
+ return result;
+
+ error:
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "invalid data returned from server", -1);
+
+ errorNoResult:
+ if (cookie != NULL) {
+ DdeFreeStringHandle(instance, cookie);
+ }
+ if (item != NULL) {
+ DdeFreeStringHandle(instance, item);
+ }
+ if (itemData != NULL) {
+ DdeFreeDataHandle(itemData);
+ }
+ if (data != NULL) {
+ DdeFreeDataHandle(data);
+ }
+ if (hConv != NULL) {
+ DdeDisconnect(hConv);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DdeInit --
+ *
+ * This procedure initializes the dde command.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Blt_DdeInit(interp)
+ Tcl_Interp *interp;
+{
+ Tcl_CreateObjCommand(interp, "dde", DdeObjCmd, NULL, NULL);
+ conversations = NULL;
+ interps = NULL;
+ Tcl_CreateExitHandler(ExitProc, NULL);
+ return Tcl_PkgProvide(interp, TCL_DDE_PACKAGE_NAME, TCL_DDE_VERSION);
+}
+
+#endif /* NO_DDE */
diff --git a/blt/src/bltWinDraw.c b/blt/src/bltWinDraw.c
new file mode 100644
index 00000000000..9628a8c56a4
--- /dev/null
+++ b/blt/src/bltWinDraw.c
@@ -0,0 +1,2716 @@
+/*
+ * bltWinDraw.c --
+ *
+ * This module contains WIN32 routines not included in the Tcl/Tk
+ * libraries.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include <bltInt.h>
+#include <X11/Xutil.h>
+#include <X11/Xlib.h>
+
+#define WINDEBUG 0
+
+/*
+ * Data structure for setting graphics context.
+ */
+typedef struct {
+ int function; /* logical operation */
+ unsigned long plane_mask; /* plane mask */
+ unsigned long foreground; /* foreground pixel */
+ unsigned long background; /* background pixel */
+ int line_width; /* line width */
+ int line_style; /* LineSolid, LineOnOffDash, LineDoubleDash */
+ int cap_style; /* CapNotLast, CapButt,
+ CapRound, CapProjecting */
+ int join_style; /* JoinMiter, JoinRound, JoinBevel */
+ int fill_style; /* FillSolid, FillTiled,
+ FillStippled, FillOpaeueStippled */
+ int fill_rule; /* EvenOddRule, WindingRule */
+ int arc_mode; /* ArcChord, ArcPieSlice */
+ Pixmap tile; /* tile pixmap for tiling operations */
+ Pixmap stipple; /* stipple 1 plane pixmap for stipping */
+ int ts_x_origin; /* offset for tile or stipple operations */
+ int ts_y_origin;
+ Font font; /* default text font for text operations */
+ int subwindow_mode; /* ClipByChildren, IncludeInferiors */
+ Bool graphics_exposures; /* boolean, should exposures be generated */
+ int clip_x_origin; /* origin for clipping */
+ int clip_y_origin;
+ Pixmap clip_mask; /* bitmap clipping; other calls for rects */
+ int dash_offset; /* patterned/dashed line information */
+ char dashes; /* If -1, indicates that the extended
+ * information below is available. */
+ int nDashValues;
+ char dashValues[12];
+} XGCValuesEx;
+
+static 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 */
+};
+#define MASKPAT 0x00E20746 /* dest = (src & pat) | (!src & dst) */
+#define COPYFG 0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG 0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+/*
+ * 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 src) AND dest */
+#define NOTSRCINVERT (DWORD)0x00990066 /* dest = (NOT src) XOR dest */
+#define SRCORREVERSE (DWORD)0x00DD0228 /* dest = src OR (NOT dest) */
+#define SRCNAND (DWORD)0x007700E6 /* dest = NOT (src 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 */
+};
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+typedef void *Tcl_Encoding; /* Make up dummy type for encoding. */
+#endif
+static Tcl_Encoding systemEncoding = NULL;
+
+HPALETTE
+Blt_GetSystemPalette(void)
+{
+ HDC hDC;
+ HPALETTE hPalette;
+ DWORD flags;
+
+ hPalette = NULL;
+ hDC = GetDC(NULL); /* Get the desktop device context */
+ flags = GetDeviceCaps(hDC, RASTERCAPS);
+ if (flags & RC_PALETTE) {
+ LOGPALETTE *palettePtr;
+
+ palettePtr = (LOGPALETTE *)
+ GlobalAlloc(GPTR, sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY));
+ palettePtr->palVersion = 0x300;
+ palettePtr->palNumEntries = 256;
+ GetSystemPaletteEntries(hDC, 0, 256, palettePtr->palPalEntry);
+ hPalette = CreatePalette(palettePtr);
+ GlobalFree(palettePtr);
+ }
+ ReleaseDC(NULL, hDC);
+ return hPalette;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateRotatedFont --
+ *
+ * Creates a rotated copy of the given font. This only works
+ * for TrueType fonts.
+ *
+ * Results:
+ * Returns the newly create font or NULL if the font could not
+ * be created.
+ *
+ *----------------------------------------------------------------------
+ */
+HFONT
+CreateRotatedFont(
+ unsigned long fontId, /* Font identifier (actually a Tk_Font) */
+ double theta)
+{ /* Number of degrees to rotate font */
+ TkFontAttributes *faPtr; /* Set of attributes to match. */
+ TkFont *fontPtr;
+ HFONT hFont;
+ LOGFONTW lf;
+
+ fontPtr = (TkFont *) fontId;
+ faPtr = &fontPtr->fa;
+ ZeroMemory(&lf, sizeof(LOGFONT));
+ lf.lfHeight = -faPtr->pointsize;
+ if (lf.lfHeight < 0) {
+ HDC dc;
+
+ dc = GetDC(NULL);
+ lf.lfHeight = -MulDiv(faPtr->pointsize,
+ GetDeviceCaps(dc, LOGPIXELSY), 72);
+ ReleaseDC(NULL, dc);
+ }
+ lf.lfWidth = 0;
+ lf.lfEscapement = lf.lfOrientation = ROUND(theta * 10.0);
+#define TK_FW_NORMAL 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_TT_ONLY_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = DEFAULT_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ hFont = NULL;
+ if (faPtr->family == NULL) {
+ lf.lfFaceName[0] = '\0';
+ } else {
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ Tcl_DString dString;
+
+ Tcl_UtfToExternalDString(systemEncoding, faPtr->family, -1, &dString);
+
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ Tcl_UniChar *src, *dst;
+
+ /*
+ * We can only store up to LF_FACESIZE wide characters
+ */
+ if (Tcl_DStringLength(&dString) >= (LF_FACESIZE * sizeof(WCHAR))) {
+ Tcl_DStringSetLength(&dString, LF_FACESIZE);
+ }
+ src = (Tcl_UniChar *)Tcl_DStringValue(&dString);
+ dst = (Tcl_UniChar *)lf.lfFaceName;
+ while (*src != '\0') {
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ hFont = CreateFontIndirectW((LOGFONTW *)&lf);
+ } else {
+ /*
+ * We can only store up to LF_FACESIZE characters
+ */
+ if (Tcl_DStringLength(&dString) >= LF_FACESIZE) {
+ Tcl_DStringSetLength(&dString, LF_FACESIZE);
+ }
+ strcpy((char *)lf.lfFaceName, Tcl_DStringValue(&dString));
+ hFont = CreateFontIndirectA((LOGFONTA *)&lf);
+ }
+ Tcl_DStringFree(&dString);
+#else
+ strncpy((char *)lf.lfFaceName, faPtr->family, LF_FACESIZE - 1);
+ lf.lfFaceName[LF_FACESIZE] = '\0';
+#endif /* TCL_VERSION_NUMBER >= 8.1.0 */
+ }
+
+ if (hFont == NULL) {
+#if WINDEBUG
+ PurifyPrintf("can't create font: %s\n", Blt_LastError());
+#endif
+ } else {
+ HFONT oldFont;
+ TEXTMETRIC tm;
+ HDC hRefDC;
+ int result;
+
+ /* Check if the rotated font is really a TrueType font. */
+
+ hRefDC = GetDC(NULL); /* Get the desktop device context */
+ oldFont = SelectFont(hRefDC, hFont);
+ result = ((GetTextMetrics(hRefDC, &tm)) &&
+ (tm.tmPitchAndFamily & TMPF_TRUETYPE));
+ SelectFont(hRefDC, oldFont);
+ ReleaseDC(NULL, hRefDC);
+ if (!result) {
+#if WINDEBUG
+ PurifyPrintf("not a true type font");
+#endif
+ DeleteFont(hFont);
+ return NULL;
+ }
+ }
+ return hFont;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetBitmapData --
+ *
+ * Returns the DIB bits from a bitmap.
+ *
+ * Results:
+ * Returns a byte array of bitmap data or NULL if an error
+ * occurred. The parameter pitchPtr returns the number
+ * of bytes per row.
+ *
+ *----------------------------------------------------------------------
+ */
+unsigned char *
+Blt_GetBitmapData(
+ Display *display, /* Display of bitmap */
+ Pixmap bitmap, /* Bitmap to query */
+ int width, /* Width of bitmap */
+ int height, /* Height of bitmap */
+ int *pitchPtr) /* (out) Number of bytes per row */
+{
+ TkWinDCState state;
+ HDC dc;
+ int result;
+ unsigned char *bits;
+ unsigned int size;
+ HBITMAP hBitmap;
+ BITMAPINFOHEADER *bmiPtr;
+ HANDLE hMem, hMem2;
+ int bytesPerRow, imageSize;
+
+ size = sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);
+ hMem = GlobalAlloc(GHND, size);
+ bmiPtr = (BITMAPINFOHEADER *)GlobalLock(hMem);
+ bmiPtr->biSize = sizeof(BITMAPINFOHEADER);
+ bmiPtr->biPlanes = 1;
+ bmiPtr->biBitCount = 1;
+ bmiPtr->biCompression = BI_RGB;
+ bmiPtr->biWidth = width;
+ bmiPtr->biHeight = height;
+
+ hBitmap = ((TkWinDrawable *)bitmap)->bitmap.handle;
+ dc = TkWinGetDrawableDC(display, bitmap, &state);
+ result = GetDIBits(dc, hBitmap, 0, height, (LPVOID)NULL,
+ (BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
+ TkWinReleaseDrawableDC(bitmap, dc, &state);
+ if (!result) {
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ return NULL;
+ }
+ imageSize = bmiPtr->biSizeImage;
+ GlobalUnlock(hMem);
+ bytesPerRow = ((width + 31) & ~31) / 8;
+ if (imageSize == 0) {
+ imageSize = bytesPerRow * height;
+ }
+ hMem2 = GlobalReAlloc(hMem, size + imageSize, 0);
+ if (hMem2 == NULL) {
+ GlobalFree(hMem);
+ return NULL;
+ }
+ hMem = hMem2;
+ bmiPtr = (LPBITMAPINFOHEADER)GlobalLock(hMem);
+ dc = TkWinGetDrawableDC(display, bitmap, &state);
+ result = GetDIBits(dc, hBitmap, 0, height, (unsigned char *)bmiPtr + size,
+ (BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
+ TkWinReleaseDrawableDC(bitmap, dc, &state);
+ bits = NULL;
+ if (!result) {
+ OutputDebugString("GetDIBits failed\n");
+ } else {
+ bits = Blt_Malloc(imageSize);
+ if (bits != NULL) {
+ memcpy (bits, (unsigned char *)bmiPtr + size, imageSize);
+ }
+ }
+ *pitchPtr = bytesPerRow;
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ return bits;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFree --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXFree(void *ptr)
+{
+ Blt_Free(ptr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XMaxRequestSize --
+ *
+ *----------------------------------------------------------------------
+ */
+long
+Blt_EmulateXMaxRequestSize(Display *display)
+{
+ return (SHRT_MAX / 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XLowerWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXLowerWindow(
+ Display *display,
+ Window window)
+{
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(window);
+ display->request++;
+ SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XRaiseWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXRaiseWindow(
+ Display *display,
+ Window window)
+{
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(window);
+ display->request++;
+ SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XUnmapWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXUnmapWindow(
+ Display *display,
+ Window window)
+{
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(window);
+ display->request++;
+ ShowWindow(hWnd, SW_HIDE);
+ /* SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XWarpPointer --
+ *
+ * If destWindow is None, moves the pointer by the offsets (destX,
+ * destY) relative to the current position of the pointer.
+ * If destWindow is a window, moves the pointer to the offsets
+ * (destX, destY) relative to the origin of destWindow. However,
+ * if srcWindow is a window, the move only takes place if the window
+ * srcWindow contains the pointer and if the specified rectangle of
+ * srcWindow contains the pointer.
+ *
+ * The srcX and srcY coordinates are relative to the origin of
+ * srcWindow. If srcHeight is zero, it is replaced with the current
+ * height of srcWindow minus srcY. If srcWidth is zero, it is
+ * replaced with the current width of srcWindow minus srcX.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXWarpPointer(
+ Display *display,
+ Window srcWindow,
+ Window destWindow,
+ int srcX,
+ int srcY,
+ unsigned int srcWidth,
+ unsigned int srcHeight,
+ int destX,
+ int destY)
+{
+ HWND hWnd;
+ POINT point;
+
+ hWnd = Tk_GetHWND(destWindow);
+ point.x = destX, point.y = destY;
+ if (ClientToScreen(hWnd, &point)) {
+ SetCursorPos(point.x, point.y);
+ }
+}
+
+static Blt_HashTable gcTable;
+static int gcInitialized = FALSE;
+
+typedef struct {
+ HDC dc;
+ int count;
+ COLORREF color;
+ int offset, nBits;
+} DashInfo;
+
+void
+Blt_SetDashes(Display *display, GC gc, Blt_Dashes *dashesPtr)
+{
+ XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+ /* This must be used only with a privately created GC */
+ assert((int)gcPtr->dashes == -1);
+ gcPtr->nDashValues = strlen(dashesPtr->values);
+ gcPtr->dash_offset = dashesPtr->offset;
+ strcpy(gcPtr->dashValues, dashesPtr->values);
+}
+
+static int
+GetDashInfo(
+ HDC dc,
+ GC gc,
+ DashInfo *infoPtr)
+{
+ int dashOffset, dashValue;
+
+ dashValue = 0;
+ dashOffset = gc->dash_offset;
+ if ((int)gc->dashes == -1) {
+ XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+ if (gcPtr->nDashValues == 1) {
+ dashValue = gcPtr->dashValues[0];
+ }
+ } else if (gc->dashes > 0) {
+ dashValue = (int)gc->dashes;
+ }
+ if (dashValue == 0) {
+ return FALSE;
+ }
+ infoPtr->dc = dc;
+ infoPtr->nBits = dashValue;
+ infoPtr->offset = dashOffset;
+ infoPtr->count = 0;
+ infoPtr->color = gc->foreground;
+ return TRUE;
+}
+
+void
+Blt_SetROP2(HDC dc, int function)
+{
+ SetROP2(dc, tkpWinRopModes[function]);
+}
+
+static XGCValuesEx *
+CreateGC()
+{
+ XGCValuesEx *gcPtr;
+
+ gcPtr = Blt_Malloc(sizeof(XGCValuesEx));
+ if (gcPtr == NULL) {
+ return NULL;
+ }
+ gcPtr->arc_mode = ArcPieSlice;
+ gcPtr->background = 0xffffff;
+ gcPtr->cap_style = CapNotLast;
+ gcPtr->clip_mask = None;
+ gcPtr->clip_x_origin = gcPtr->clip_y_origin = 0;
+ gcPtr->dash_offset = 0;
+ gcPtr->fill_rule = WindingRule;
+ gcPtr->fill_style = FillSolid;
+ gcPtr->font = None;
+ gcPtr->foreground = 0;
+ gcPtr->function = GXcopy;
+ gcPtr->graphics_exposures = True;
+ gcPtr->join_style = JoinMiter;
+ gcPtr->line_style = LineSolid;
+ gcPtr->line_width = 0;
+ gcPtr->plane_mask = ~0;
+ gcPtr->stipple = None;
+ gcPtr->subwindow_mode = ClipByChildren;
+ gcPtr->tile = None;
+ gcPtr->ts_x_origin = gcPtr->ts_y_origin = 0;
+
+ gcPtr->dashes = -1; /* Mark that this an extended GC */
+ gcPtr->nDashValues = 0;
+
+ return gcPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCreateGC --
+ *
+ * Allocate a new extended GC, and initialize the specified fields.
+ *
+ * Results:
+ * Returns a newly allocated GC.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_EmulateXCreateGC(
+ Display *display,
+ Drawable drawable,
+ unsigned long mask,
+ XGCValues *srcPtr)
+{
+ XGCValuesEx *destPtr;
+
+ destPtr = CreateGC();
+ if (destPtr == NULL) {
+ return None;
+ }
+ if (mask & GCFunction) {
+ destPtr->function = srcPtr->function;
+ }
+ if (mask & GCPlaneMask) {
+ destPtr->plane_mask = srcPtr->plane_mask;
+ }
+ if (mask & GCForeground) {
+ destPtr->foreground = srcPtr->foreground;
+ }
+ if (mask & GCBackground) {
+ destPtr->background = srcPtr->background;
+ }
+ if (mask & GCLineWidth) {
+ destPtr->line_width = srcPtr->line_width;
+ }
+ if (mask & GCLineStyle) {
+ destPtr->line_style = srcPtr->line_style;
+ }
+ if (mask & GCCapStyle) {
+ destPtr->cap_style = srcPtr->cap_style;
+ }
+ if (mask & GCJoinStyle) {
+ destPtr->join_style = srcPtr->join_style;
+ }
+ if (mask & GCFillStyle) {
+ destPtr->fill_style = srcPtr->fill_style;
+ }
+ if (mask & GCFillRule) {
+ destPtr->fill_rule = srcPtr->fill_rule;
+ }
+ if (mask & GCArcMode) {
+ destPtr->arc_mode = srcPtr->arc_mode;
+ }
+ if (mask & GCTile) {
+ destPtr->tile = srcPtr->tile;
+ }
+ if (mask & GCStipple) {
+ destPtr->stipple = srcPtr->stipple;
+ }
+ if (mask & GCTileStipXOrigin) {
+ destPtr->ts_x_origin = srcPtr->ts_x_origin;
+ }
+ if (mask & GCTileStipXOrigin) {
+ destPtr->ts_y_origin = srcPtr->ts_y_origin;
+ }
+ if (mask & GCFont) {
+ destPtr->font = srcPtr->font;
+ }
+ if (mask & GCSubwindowMode) {
+ destPtr->subwindow_mode = srcPtr->subwindow_mode;
+ }
+ if (mask & GCGraphicsExposures) {
+ destPtr->graphics_exposures = srcPtr->graphics_exposures;
+ }
+ if (mask & GCClipXOrigin) {
+ destPtr->clip_x_origin = srcPtr->clip_x_origin;
+ }
+ if (mask & GCClipYOrigin) {
+ destPtr->clip_y_origin = srcPtr->clip_y_origin;
+ }
+ if (mask & GCDashOffset) {
+ destPtr->dash_offset = srcPtr->dash_offset;
+ }
+ if (mask & GCDashList) {
+ destPtr->dashes = srcPtr->dashes;
+ }
+ if (mask & GCClipMask) {
+ struct ClipMask {
+ int type; /* TKP_CLIP_PIXMAP or TKP_CLIP_REGION */
+ Pixmap pixmap;
+ } *clipPtr;
+
+ clipPtr = Blt_Malloc(sizeof(struct ClipMask));
+#define TKP_CLIP_PIXMAP 0
+ clipPtr->type = TKP_CLIP_PIXMAP;
+ clipPtr->pixmap = srcPtr->clip_mask;
+ destPtr->clip_mask = (Pixmap) clipPtr;
+ }
+ return (GC)destPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GCToPen --
+ *
+ * Set up the graphics port from the given GC.
+ *
+ * Geometric and cosmetic pens available under both 95 and NT.
+ * Geometric pens differ from cosmetic pens in that they can
+ * 1. Draw in world units (can have thick lines: line width > 1).
+ * 2. Under NT, allow arbitrary line style.
+ * 3. Can have caps and join (needed for thick lines).
+ * 4. Draw very, very slowly.
+ *
+ * Cosmetic pens are single line width only.
+ *
+ * 95 98 NT
+ * PS_SOLID c,g c,g c,g
+ * PS_DASH c,g c,g c,g
+ * PS_DOT c c c,g
+ * PS_DASHDOT c - c,g
+ * PS_DASHDOTDOT c - c,g
+ * PS_USERSTYLE - - c,g
+ * PS_ALTERNATE - - c
+ *
+ * Geometric only for 95/98
+ *
+ * PS_ENDCAP_ROUND
+ * PS_ENDCAP_SQUARE
+ * PS_ENDCAP_FLAT
+ * PS_JOIN_BEVEL
+ * PS_JOIN_ROUND
+ * PS_JOIN_MITER
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The current port is adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+HPEN
+Blt_GCToPen(HDC dc, GC gc)
+{
+ DWORD lineAttrs, lineStyle;
+ DWORD dashArr[12];
+ DWORD *dashPtr;
+ int nValues, lineWidth;
+ LOGBRUSH lBrush;
+ HPEN pen;
+
+ nValues = 0;
+ lineWidth = (gc->line_width < 1) ? 1 : gc->line_width;
+ if ((gc->line_style == LineOnOffDash) ||
+ (gc->line_style == LineDoubleDash)) {
+ XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+ if ((int)gc->dashes == -1) {
+ register int i;
+
+ nValues = strlen(gcPtr->dashValues);
+ for (i = 0; i < nValues; i++) {
+ dashArr[i] = (DWORD)gcPtr->dashValues[i];
+ }
+ if (nValues == 1) {
+ dashArr[1] = dashArr[0];
+ nValues = 2;
+ }
+ } else {
+ dashArr[1] = dashArr[0] = (DWORD) gc->dashes;
+ nValues = 2;
+ gc->dashes = -1;
+ }
+ }
+
+ switch (nValues) {
+ case 0:
+ lineStyle = PS_SOLID;
+ break;
+ case 3:
+ lineStyle = PS_DASHDOT;
+ break;
+ case 4:
+ lineStyle = PS_DASHDOTDOT;
+ break;
+ case 2:
+ default:
+ /* PS_DASH style dash length is too long. */
+ lineStyle = PS_DOT;
+ break;
+ }
+
+ lBrush.lbStyle = BS_SOLID;
+ lBrush.lbColor = gc->foreground;
+ lBrush.lbHatch = 0; /* Value is ignored when style is BS_SOLID. */
+
+ lineAttrs = 0;
+ switch (gc->cap_style) {
+ case CapNotLast:
+ case CapButt:
+ lineAttrs |= PS_ENDCAP_FLAT;
+ break;
+ case CapRound:
+ lineAttrs |= PS_ENDCAP_ROUND;
+ break;
+ default:
+ lineAttrs |= PS_ENDCAP_SQUARE;
+ break;
+ }
+ switch (gc->join_style) {
+ case JoinMiter:
+ lineAttrs |= PS_JOIN_MITER;
+ break;
+ case JoinBevel:
+ lineAttrs |= PS_JOIN_BEVEL;
+ break;
+ case JoinRound:
+ default:
+ lineAttrs |= PS_JOIN_ROUND;
+ break;
+ }
+ pen = NULL;
+ SetBkMode(dc, TRANSPARENT);
+
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ /* Windows NT. */
+ if (nValues > 0) {
+ lineStyle = PS_USERSTYLE;
+ dashPtr = dashArr;
+ } else {
+ dashPtr = NULL;
+ }
+ if (lineWidth > 1) {
+ /* Limit the use of geometric pens to thick lines. */
+ pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
+ &lBrush, nValues, dashPtr);
+ } else {
+ /* Cosmetic pens are much faster. */
+ pen = ExtCreatePen(PS_COSMETIC | lineAttrs | lineStyle, 1, &lBrush,
+ nValues, dashPtr);
+ }
+ } else {
+ /* Windows 95. */
+ if ((lineStyle == PS_SOLID) && (lineWidth > 1)) {
+ /* Use geometric pens with solid, thick lines only. */
+ pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
+ &lBrush, 0, NULL);
+ } else {
+ /* Otherwise sacrifice thick lines for dashes. */
+ pen = ExtCreatePen(PS_COSMETIC | lineStyle, 1, &lBrush, 0, NULL);
+ }
+ }
+ assert(pen != NULL);
+ return pen;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawRectangles --
+ *
+ * Draws the outlines of the specified rectangles as if a
+ * five-point PolyLine protocol request were specified for each
+ * rectangle:
+ *
+ * [x,y] [x+width,y] [x+width,y+height] [x,y+height]
+ * [x,y]
+ *
+ * For the specified rectangles, these functions do not draw a
+ * pixel more than once. XDrawRectangles draws the rectangles in
+ * the order listed in the array. If rectangles intersect, the
+ * intersecting pixels are drawn multiple times. Draws a
+ * rectangle.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws rectangles on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawRectangles(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XRectangle *rectArr,
+ int nRects)
+{
+ HPEN pen, oldPen;
+ TkWinDCState state;
+ HBRUSH brush, oldBrush;
+ HDC dc;
+ register XRectangle *rectPtr;
+ register int i;
+
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ pen = Blt_GCToPen(dc, gc);
+ brush = GetStockObject(NULL_BRUSH);
+ oldPen = SelectPen(dc, pen);
+ oldBrush = SelectBrush(dc, brush);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ rectPtr = rectArr;
+ for (i = 0; i < nRects; i++, rectPtr++) {
+ Rectangle(dc, (int)rectPtr->x, (int)rectPtr->y,
+ (int)(rectPtr->x + rectPtr->width + 1),
+ (int)(rectPtr->y + rectPtr->height + 1));
+ }
+ DeletePen(SelectPen(dc, oldPen));
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#ifdef notdef
+/*
+ * Implements the "pixeling" of small arcs, because GDI-performance
+ * for this is awful
+ * was made especially for BLT, graph4 demo now runs 4x faster
+ *
+ */
+/* O-outer , I-inner, B-both */
+#define NEITHER_ 0
+#define OUTLINE 1
+#define FILL 2
+#define BOTH (OUTLINE|FILL)
+#define MINIARCS 5
+static int arcus0[1] =
+{
+ BOTH
+};
+static int arcus1[4] =
+{
+ BOTH, BOTH,
+ BOTH, BOTH
+};
+
+static int arcus2[9] =
+{
+ NEITHER, OUTLINE, NEITHER,
+ OUTLINE, FILL, OUTLINE,
+ NEITHER, OUTLINE, NEITHER
+};
+
+static int arcus3[16] =
+{
+ NEITHER, OUTLINE, OUTLINE, NEITHER,
+ OUTLINE, FILL, FILL, OUTLINE,
+ OUTLINE, FILL, FILL, OUTLINE,
+ NEITHER, OUTLINE, OUTLINE, NEITHER
+};
+
+static int arcus4[25] =
+{
+ NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER,
+ OUTLINE, FILL, FILL, FILL, OUTLINE,
+ OUTLINE, FILL, FILL, FILL, OUTLINE,
+ OUTLINE, FILL, FILL, FILL, OUTLINE,
+ NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER
+};
+
+static int *arcis[MINIARCS] =
+{
+ arcus0, arcus1, arcus2, arcus3, arcus4
+};
+
+static void
+DrawMiniArc(
+ HDC dc,
+ int width,
+ int x,
+ int y,
+ int mask,
+ COLORREF inner,
+ COLORREF outer)
+{
+ int *arc;
+ int i, j;
+
+ if (width > MINIARCS) {
+ return;
+ }
+ arc = arcis[width];
+ for (i = 0; i <= width; i++) {
+ for (j = 0; j <= width; j++) {
+ bit = (mask & *arc);
+ if (bit & OUTLINE) {
+ SetPixelV(dc, x + i, y + j, outer);
+ } else if (bit & FILL) {
+ SetPixelV(dc, x + i, y + j, inner);
+ }
+ arc++;
+ }
+ }
+}
+
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawArc --
+ *
+ * This procedure handles the rendering of drawn or filled
+ * arcs and chords.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Renders the requested arcs.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawArc(
+ HDC dc,
+ int arcMode, /* Mode: either ArcChord or ArcPieSlice */
+ XArc *arcPtr,
+ HPEN pen,
+ HBRUSH brush)
+{
+ int start, extent, clockwise;
+ int xstart, ystart, xend, yend;
+ double radian_start, radian_end, xr, yr;
+ double dx, dy;
+
+ if ((arcPtr->angle1 == 0) && (arcPtr->angle2 == 23040)) {
+ /* Handle special case of circle or ellipse */
+ Ellipse(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+ arcPtr->y + arcPtr->height + 1);
+ return;
+ }
+ start = arcPtr->angle1, extent = arcPtr->angle2;
+ clockwise = (extent < 0); /* Non-zero if clockwise */
+
+ /*
+ * 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;
+ }
+#define XAngleToRadians(a) ((double)(a) / 64 * M_PI / 180);
+ 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.
+ */
+ dx = arcPtr->width * 0.5;
+ dy = arcPtr->height * 0.5;
+
+ xr = arcPtr->x + dx;
+ yr = arcPtr->y + dy;
+ xstart = (int)((xr + cos(radian_start) * dx) + 0.5);
+ ystart = (int)((yr + sin(-radian_start) * dy) + 0.5);
+ xend = (int)((xr + cos(radian_end) * dx) + 0.5);
+ yend = (int)((yr + sin(-radian_end) * dy) + 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.
+ */
+
+ if (brush == 0) {
+ /*
+ * 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, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+ arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+ /* FIXME: */
+ } else {
+ if (arcMode == ArcChord) {
+ Chord(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+ arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+ } else if (arcMode == ArcPieSlice) {
+ Pie(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+ arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawArcs --
+ *
+ * Draws multiple circular or elliptical arcs. Each arc is
+ * specified by a rectangle and two angles. The center of the
+ * circle or ellipse is the center of the rect- angle, and the
+ * major and minor axes are specified by the width and height.
+ * Positive angles indicate counterclock- wise motion, and
+ * negative angles indicate clockwise motion. If the magnitude
+ * of angle2 is greater than 360 degrees, XDrawArcs truncates it
+ * to 360 degrees.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws an arc for each array element on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawArcs(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XArc *arcArr,
+ int nArcs)
+{
+ HPEN pen, oldPen;
+ HBRUSH brush, oldBrush;
+ HDC dc;
+ TkWinDCState state;
+ register XArc *arcPtr, *endPtr;
+
+ display->request++;
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ pen = Blt_GCToPen(dc, gc);
+ oldPen = SelectPen(dc, pen);
+ brush = GetStockBrush(NULL_BRUSH);
+ oldBrush = SelectBrush(dc, brush);
+ endPtr = arcArr + nArcs;
+ for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
+ DrawArc(dc, gc->arc_mode, arcPtr, pen, 0);
+ }
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ DeletePen(SelectPen(dc, oldPen));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillArcs --
+ *
+ * Draw a filled arc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws a filled arc for each array element on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXFillArcs(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XArc *arcArr,
+ int nArcs)
+{
+ HBRUSH brush, oldBrush;
+ HPEN pen, oldPen;
+ HDC dc;
+ register XArc *arcPtr, *endPtr;
+ TkWinDCState state;
+
+ display->request++;
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ pen = Blt_GCToPen(dc, gc);
+ oldPen = SelectPen(dc, pen);
+ brush = CreateSolidBrush(gc->foreground);
+ oldBrush = SelectBrush(dc, brush);
+ endPtr = arcArr + nArcs;
+ for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
+ DrawArc(dc, gc->arc_mode, arcPtr, pen, brush);
+ }
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ DeletePen(SelectPen(dc, oldPen));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawLines --
+ *
+ * Draw connected lines.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Renders a series of connected lines.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void CALLBACK
+DrawDot(
+ int x, int y, /* Coordinates of point */
+ LPARAM clientData)
+{ /* Line information */
+ DashInfo *infoPtr = (DashInfo *) clientData;
+ int count;
+
+ infoPtr->count++;
+ count = (infoPtr->count + infoPtr->offset) / infoPtr->nBits;
+ if (count & 0x1) {
+ SetPixelV(infoPtr->dc, x, y, infoPtr->color);
+ }
+}
+
+
+void
+Blt_EmulateXDrawLine(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ int x1, int y1,
+ int x2, int y2)
+{
+ TkWinDCState state;
+ HDC dc;
+
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ if (gc->line_style != LineSolid) {
+ /* Handle dotted lines specially */
+ DashInfo info;
+
+ if (!GetDashInfo(dc, gc, &info)) {
+ goto solidLine;
+ }
+ LineDDA(x1, y1, x2, y2, DrawDot, (LPARAM) & info);
+ } else {
+ HPEN pen, oldPen;
+ HBRUSH brush, oldBrush;
+
+ solidLine:
+ pen = Blt_GCToPen(dc, gc);
+ oldPen = SelectPen(dc, pen);
+ brush = CreateSolidBrush(gc->foreground);
+ oldBrush = SelectBrush(dc, brush);
+ MoveToEx(dc, x1, y1, (LPPOINT) NULL);
+ LineTo(dc, x2, y2);
+ DeletePen(SelectPen(dc, oldPen));
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+static void
+DrawLine(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ POINT *points,
+ int nPoints)
+{
+ TkWinDCState state;
+ HDC dc;
+ register int i, n;
+ int start, extra, size;
+ HPEN pen, oldPen;
+ HBRUSH brush, oldBrush;
+
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ pen = Blt_GCToPen(dc, gc);
+ oldPen = SelectPen(dc, pen);
+ brush = CreateSolidBrush(gc->foreground);
+ oldBrush = SelectBrush(dc, brush);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ start = extra = 0;
+ /*
+ * Depending if the line is wide (> 1 pixel), arbitrarily break
+ * the line in sections of 100 points. This bit of weirdness has
+ * to do with wide geometric pens. The longer the polyline, the
+ * slower it draws. The trade off is that we lose dash and
+ * cap uniformity for unbearably slow polyline draws.
+ */
+ if (gc->line_width > 1) {
+ size = 100;
+ } else {
+ size = nPoints;
+ }
+ for (i = nPoints; i > 0; i -= size) {
+ n = MIN(i, size);
+ Polyline(dc, points + start, n + extra);
+ start += (n - 1);
+ extra = 1;
+ }
+ DeletePen(SelectPen(dc, oldPen));
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+void
+Blt_DrawPoint2DLine(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ Point2D *screenPts,
+ int nScreenPts)
+{
+ POINT *points;
+ Point2D *srcPtr, *endPtr;
+ register POINT *destPtr; /* Array of points. */
+
+ if (drawable == None) {
+ return;
+ }
+ points = Blt_Malloc(sizeof(POINT) * nScreenPts);
+ if (points == NULL) {
+ return;
+ }
+ destPtr = points;
+ endPtr = screenPts + nScreenPts;
+ for (srcPtr = screenPts; srcPtr < endPtr; srcPtr++) {
+ destPtr->x = (int)srcPtr->x;
+ destPtr->y = (int)srcPtr->y;
+ destPtr++;
+ }
+ DrawLine(display, drawable, gc, points, nScreenPts);
+ Blt_Free(points);
+}
+
+void
+Blt_EmulateXDrawLines(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XPoint *pointArr,
+ int nPoints,
+ int mode)
+{
+ if (drawable == None) {
+ return;
+ }
+ if (gc->line_style != LineSolid) { /* Handle dotted lines specially */
+ DashInfo info;
+ TkWinDCState state;
+ HDC dc;
+ int result;
+
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ result = GetDashInfo(dc, gc, &info);
+ if (result) {
+ register XPoint *p1, *p2;
+ register int i;
+
+ p1 = pointArr;
+ p2 = p1 + 1;
+ for (i = 1; i < nPoints; i++, p1++, p2++) {
+ LineDDA(p1->x, p1->y, p2->x, p2->y, DrawDot, (LPARAM) & info);
+ }
+ result = TCL_OK;
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+ if (result) {
+ return;
+ }
+ } else {
+ POINT *points, *destPtr;
+ XPoint *srcPtr, *endPtr;
+
+ points = Blt_Malloc(sizeof(POINT) * nPoints);
+ if (points == NULL) {
+ return;
+ }
+ endPtr = pointArr + nPoints;
+ if (mode == CoordModeOrigin) {
+ destPtr = points;
+ for (srcPtr = pointArr; srcPtr < endPtr; srcPtr++) {
+ destPtr->x = (int)srcPtr->x;
+ destPtr->y = (int)srcPtr->y;
+ destPtr++;
+ }
+ } else {
+ POINT *lastPtr;
+
+ srcPtr = pointArr;
+ destPtr = points;
+ destPtr->x = (int)srcPtr->x;
+ destPtr->y = (int)srcPtr->y;
+ lastPtr = destPtr;
+ srcPtr++, destPtr++;
+ for (/*empty*/; srcPtr < endPtr; srcPtr++) {
+ destPtr->x = lastPtr->x + (int)srcPtr->x;
+ destPtr->y = lastPtr->y + (int)srcPtr->y;
+ lastPtr = destPtr;
+ destPtr++;
+ }
+ }
+ DrawLine(display, drawable, gc, points, nPoints);
+ Blt_Free(points);
+ }
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawSegments --
+ *
+ * Draws multiple, unconnected lines. For each segment, draws a
+ * line between (x1, y1) and (x2, y2). It draws the lines in the
+ * order listed in the array of XSegment structures and does not
+ * perform joining at coincident endpoints. For any given line,
+ * does not draw a pixel more than once. If lines intersect, the
+ * intersecting pixels are drawn multiple times.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws unconnected line segments on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawSegments(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XSegment *segArr,
+ int nSegments)
+{
+ HDC dc;
+ register XSegment *segPtr, *endPtr;
+ TkWinDCState state;
+
+ display->request++;
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ if (gc->line_style != LineSolid) {
+ /* Handle dotted lines specially */
+ DashInfo info;
+
+ if (!GetDashInfo(dc, gc, &info)) {
+ goto solidLine;
+ }
+ endPtr = segArr + nSegments;
+ for (segPtr = segArr; segPtr < endPtr; segPtr++) {
+ info.count = 0; /* Reset dash counter after every segment. */
+ LineDDA(segPtr->x1, segPtr->y1, segPtr->x2, segPtr->y2, DrawDot,
+ (LPARAM)&info);
+ }
+ } else {
+ HPEN pen, oldPen;
+
+ solidLine:
+ pen = Blt_GCToPen(dc, gc);
+ oldPen = SelectPen(dc, pen);
+ endPtr = segArr + nSegments;
+ for (segPtr = segArr; segPtr < endPtr; segPtr++) {
+ MoveToEx(dc, segPtr->x1, segPtr->y1, (LPPOINT) NULL);
+ LineTo(dc, segPtr->x2, segPtr->y2);
+ }
+ DeletePen(SelectPen(dc, oldPen));
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawRectangle --
+ *
+ * Draws the outlines of the specified rectangle as if a
+ * five-point PolyLine protocol request were specified for each
+ * rectangle:
+ *
+ * [x,y] [x+width,y] [x+width,y+height] [x,y+height]
+ * [x,y]
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws a rectangle on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawRectangle(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ int x, int y,
+ unsigned int width,
+ unsigned int height)
+{
+ TkWinDCState state;
+ HPEN pen, oldPen;
+ HBRUSH brush, oldBrush;
+ HDC dc;
+
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ pen = Blt_GCToPen(dc, gc);
+ brush = GetStockObject(NULL_BRUSH);
+ oldPen = SelectPen(dc, pen);
+ oldBrush = SelectBrush(dc, brush);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ if (gc->line_style != LineSolid) {
+ /* Handle dotted lines specially */
+ register int x2, y2;
+ DashInfo info;
+
+ if (!GetDashInfo(dc, gc, &info)) {
+ goto solidLine;
+ }
+ x2 = x + width;
+ y2 = y + height;
+ LineDDA(x, y, x2, y, DrawDot, (LPARAM)&info);
+ LineDDA(x2, y, x2, y2, DrawDot, (LPARAM)&info);
+ LineDDA(x2, y2, x, y2, DrawDot, (LPARAM)&info);
+ LineDDA(x, y2, x, y, DrawDot, (LPARAM)&info);
+ } else {
+ solidLine:
+ Rectangle(dc, x, y, x + width + 1, y + height + 1);
+ }
+ DeletePen(SelectPen(dc, oldPen));
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXDrawPoints --
+ *
+ * Uses the foreground pixel and function components of the GC to
+ * draw a multiple points into the specified drawable.
+ * CoordModeOrigin treats all coordinates as relative to the
+ * origin, and CoordModePrevious treats all coordinates after
+ * the first as relative to the previous point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws points on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawPoints(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XPoint *pointArr,
+ int nPoints,
+ int mode)
+{ /* Ignored. CoordModeOrigin is assumed. */
+ HDC dc;
+ register XPoint *pointPtr, *endPtr;
+ TkWinDCState state;
+
+ display->request++;
+ if (drawable == None) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ endPtr = pointArr + nPoints;
+ for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+ SetPixelV(dc, pointPtr->x, pointPtr->y, gc->foreground);
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXReparentWindow --
+ *
+ * If the specified window is mapped, automatically performs an
+ * UnmapWindow request on it, removes it from its current
+ * position in the hierarchy, and inserts it as the child of the
+ * specified parent. The window is placed in the stacking order
+ * on top with respect to sibling windows.
+ *
+ * Note: In WIN32 you can't reparent to/from another application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Reparents the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXReparentWindow(
+ Display *display,
+ Window window,
+ Window parent,
+ int x,
+ int y)
+{
+ HWND child, newParent;
+
+ child = Tk_GetHWND(window);
+ newParent = Tk_GetHWND(parent);
+ SetParent(child, newParent);
+ SetWindowLong(child, GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN |
+ WS_CLIPSIBLINGS);
+
+ XMoveWindow(display, window, x, y);
+ XRaiseWindow(display, window);
+ XMapWindow(display, window);
+}
+
+void
+Blt_EmulateXSetDashes(
+ Display *display,
+ GC gc,
+ int dashOffset,
+ _Xconst char *dashList,
+ int n)
+{
+ gc->dashes = (unsigned char)strlen(dashList);
+ gc->dash_offset = (int)dashList;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawString --
+ *
+ * Draw a single string in the current font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Renders the specified string in the drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawString(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ int x,
+ int y,
+ _Xconst char *string,
+ int length)
+{
+ if (drawable == None) {
+ return;
+ }
+ Tk_DrawChars(display, drawable, gc, (Tk_Font)gc->font, string, length,
+ x, y);
+}
+
+static void
+TileArea(destDC, srcDC, tileOriginX, tileOriginY, tileWidth, tileHeight,
+ x, y, width, height)
+ HDC destDC, srcDC;
+ int tileOriginX, tileOriginY, tileWidth, tileHeight;
+ int x, y, width, height;
+{
+ int destX, destY;
+ int destWidth, destHeight;
+ int srcX, srcY;
+ int xOrigin, yOrigin;
+ int delta;
+ int left, top, right, bottom;
+
+ xOrigin = x, yOrigin = y;
+ if (x < tileOriginX) {
+ delta = (tileOriginX - x) % tileWidth;
+ if (delta > 0) {
+ xOrigin -= (tileWidth - delta);
+ }
+ } else if (x > tileOriginX) {
+ delta = (x - tileOriginX) % tileWidth;
+ if (delta > 0) {
+ xOrigin -= delta;
+ }
+ }
+ if (y < tileOriginY) {
+ delta = (tileOriginY - y) % tileHeight;
+ if (delta > 0) {
+ yOrigin -= (tileHeight - delta);
+ }
+ } else if (y >= tileOriginY) {
+ delta = (y - tileOriginY) % tileHeight;
+ if (delta > 0) {
+ yOrigin -= delta;
+ }
+ }
+#ifdef notdef
+ PurifyPrintf("tile is (%d,%d,%d,%d)\n", tileOriginX, tileOriginY,
+ tileWidth, tileHeight);
+ PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+ PurifyPrintf("starting at %d,%d\n", xOrigin, yOrigin);
+#endif
+ left = x;
+ right = x + width;
+ top = y;
+ bottom = y + height;
+ for (y = yOrigin; y < bottom; y += tileHeight) {
+ srcY = 0;
+ destY = y;
+ destHeight = tileHeight;
+ if (y < top) {
+ srcY = (top - y);
+ destHeight = tileHeight - srcY;
+ destY = top;
+ }
+ if ((destY + destHeight) > bottom) {
+ destHeight = (bottom - destY);
+ }
+ for (x = xOrigin; x < right; x += tileWidth) {
+ srcX = 0;
+ destX = x;
+ destWidth = tileWidth;
+ if (x < left) {
+ srcX = (left - x);
+ destWidth = tileWidth - srcX;
+ destX = left;
+ }
+ if ((destX + destWidth) > right) {
+ destWidth = (right - destX);
+ }
+#ifdef notdef
+ PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+ srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+ BitBlt(destDC, destX, destY, destWidth, destHeight,
+ srcDC, srcX, srcY, SRCCOPY);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXFillRectangles --
+ *
+ * Fill multiple rectangular areas in the given drawable.
+ * Handles tiling.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Blt_EmulateXFillRectangles(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XRectangle *rectArr,
+ int nRectangles)
+{
+ BITMAP bm;
+ HBITMAP oldBitmap, hBitmap;
+ HBRUSH oldBrush, hFgBrush, hBgBrush, hBrush;
+ HDC hDC;
+ HDC memDC;
+ RECT rect;
+ TkWinDCState state;
+ TkWinDrawable *twdPtr;
+ register XRectangle *rectPtr, *endPtr;
+
+ if (drawable == None) {
+ return;
+ }
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[gc->function]);
+
+ switch(gc->fill_style) {
+ case FillTiled:
+ if (gc->tile == None) {
+ goto fillSolid;
+ }
+#ifdef notdef
+ if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
+ goto fillSolid;
+ }
+#endif
+ twdPtr = (TkWinDrawable *)gc->tile;
+ GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+ endPtr = rectArr + nRectangles;
+ for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+ TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth,
+ bm.bmHeight, (int)rectPtr->x, (int)rectPtr->y,
+ (int)rectPtr->width, (int)rectPtr->height);
+ }
+ SelectBitmap(memDC, oldBitmap);
+ DeleteDC(memDC);
+ break;
+
+ case FillOpaqueStippled:
+ case FillStippled:
+ if (gc->stipple == None) {
+ goto fillSolid;
+ }
+ twdPtr = (TkWinDrawable *)gc->stipple;
+ if (twdPtr->type != TWD_BITMAP) {
+ panic("unexpected drawable type in stipple");
+ }
+ hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectBrush(hDC, hBrush);
+ memDC = CreateCompatibleDC(hDC);
+
+ /*
+ * 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.
+ */
+ hFgBrush = CreateSolidBrush(gc->foreground);
+ hBgBrush = CreateSolidBrush(gc->background);
+ endPtr = rectArr + nRectangles;
+ for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+ hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width,
+ rectPtr->height);
+ oldBitmap = SelectObject(memDC, hBitmap);
+ rect.left = rect.top = 0;
+ rect.right = rectPtr->width;
+ rect.bottom = rectPtr->height;
+ FillRect(memDC, &rect, hFgBrush);
+ BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+ memDC, 0, 0, COPYBG);
+ if (gc->fill_style == FillOpaqueStippled) {
+ FillRect(memDC, &rect, hBgBrush);
+ BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width,
+ rectPtr->height, memDC, 0, 0, COPYFG);
+ }
+ SelectObject(memDC, oldBitmap);
+ DeleteObject(hBitmap);
+ }
+ DeleteBrush(hFgBrush);
+ DeleteBrush(hBgBrush);
+ DeleteDC(memDC);
+ SelectBrush(hDC, oldBrush);
+ DeleteBrush(hBrush);
+ break;
+
+ case FillSolid:
+ fillSolid:
+ memDC = CreateCompatibleDC(hDC);
+ hFgBrush = CreateSolidBrush(gc->foreground);
+ endPtr = rectArr + nRectangles;
+ for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+ hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width,
+ rectPtr->height);
+ oldBitmap = SelectObject(memDC, hBitmap);
+ rect.left = rect.top = 0;
+ rect.right = rectPtr->width;
+ rect.bottom = rectPtr->height;
+ FillRect(memDC, &rect, hFgBrush);
+ BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+ memDC, 0, 0, SRCCOPY);
+ SelectObject(memDC, oldBitmap);
+ DeleteObject(hBitmap);
+ }
+ DeleteBrush(hFgBrush);
+ DeleteDC(memDC);
+ break;
+ }
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_EmulateXFillRectangle(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height)
+{
+ HDC hDC;
+ RECT rect;
+ TkWinDCState state;
+
+ if (drawable == None) {
+ return;
+ }
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[gc->function]);
+ rect.left = rect.top = 0;
+ rect.right = width;
+ rect.bottom = height;
+
+ switch(gc->fill_style) {
+ case FillTiled:
+ {
+ TkWinDrawable *twdPtr;
+ HBITMAP oldBitmap;
+ HDC memDC;
+ BITMAP bm;
+
+ if (gc->tile == None) {
+ goto fillSolid;
+ }
+#ifdef notdef
+ if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
+ goto fillSolid;
+ }
+#endif
+ twdPtr = (TkWinDrawable *)gc->tile;
+ /* The tiling routine needs to know the size of the bitmap */
+ GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+ TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth,
+ bm.bmHeight, x, y, width, height);
+ SelectBitmap(memDC, oldBitmap);
+ DeleteDC(memDC);
+ }
+ break;
+
+ case FillOpaqueStippled:
+ case FillStippled:
+ {
+ TkWinDrawable *twdPtr;
+ HBRUSH oldBrush, hBrush;
+ HBRUSH hBrushFg, hBrushBg;
+ HBITMAP oldBitmap, hBitmap;
+ HDC memDC;
+
+ if (gc->stipple == None) {
+ goto fillSolid;
+ }
+ twdPtr = (TkWinDrawable *)gc->stipple;
+ if (twdPtr->type != TWD_BITMAP) {
+ panic("unexpected drawable type in stipple");
+ }
+ hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectBrush(hDC, hBrush);
+ memDC = CreateCompatibleDC(hDC);
+
+ hBrushFg = CreateSolidBrush(gc->foreground);
+ hBrushBg = CreateSolidBrush(gc->background);
+ hBitmap = CreateCompatibleBitmap(hDC, width, height);
+ oldBitmap = SelectObject(memDC, hBitmap);
+ FillRect(memDC, &rect, hBrushFg);
+ SetBkMode(hDC, TRANSPARENT);
+ BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYFG);
+ if (gc->fill_style == FillOpaqueStippled) {
+ FillRect(memDC, &rect, hBrushBg);
+ BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYBG);
+ }
+ SelectBrush(hDC, oldBrush);
+ SelectBitmap(memDC, oldBitmap);
+ DeleteBrush(hBrushFg);
+ DeleteBrush(hBrushBg);
+ DeleteBrush(hBrush);
+ DeleteBitmap(hBitmap);
+ DeleteDC(memDC);
+ }
+ break;
+
+ case FillSolid:
+ {
+ HBRUSH hBrush;
+ HBITMAP oldBitmap, hBitmap;
+ HDC memDC;
+
+ fillSolid:
+ /* TkWinFillRect(hDC, x, y, width, height, gc->foreground); */
+ memDC = CreateCompatibleDC(hDC);
+ hBrush = CreateSolidBrush(gc->foreground);
+ hBitmap = CreateCompatibleBitmap(hDC, width, height);
+ oldBitmap = SelectBitmap(memDC, hBitmap);
+ rect.left = rect.top = 0;
+ rect.right = width;
+ rect.bottom = height;
+ FillRect(memDC, &rect, hBrush);
+ BitBlt(hDC, x, y, width, height, memDC, 0, 0, SRCCOPY);
+ SelectObject(memDC, oldBitmap);
+ DeleteBitmap(hBitmap);
+ DeleteBrush(hBrush);
+ DeleteDC(memDC);
+ }
+ break;
+ }
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+static BOOL
+DrawChars(HDC dc, int x, int y, char *string, int length)
+{
+ BOOL result;
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ if (systemEncoding == NULL) {
+ result = TextOutA(dc, x, y, string, length);
+ } else {
+ const unsigned short *wstring;
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_UtfToExternalDString(systemEncoding, string, length, &dString);
+ length = Tcl_NumUtfChars(string, -1);
+ wstring = (const unsigned short *)Tcl_DStringValue(&dString);
+ result = TextOutW(dc, x, y, wstring, length);
+ Tcl_DStringFree(&dString);
+ }
+#else
+ result = TextOutA(dc, x, y, string, length);
+#endif /* TCL_VERSION_NUMBER >= 8.1.0 */
+ return result;
+}
+
+int
+Blt_DrawRotatedText(
+ Display *display,
+ Drawable drawable,
+ int x, int y,
+ double theta,
+ TextStyle *tsPtr,
+ TextLayout *textPtr)
+{
+ HFONT hFont, oldFont;
+ TkWinDCState state;
+ HDC hDC;
+ int isActive;
+ int bbWidth, bbHeight;
+ double sinTheta, cosTheta;
+ Point2D p, q, center;
+ register TextFragment *fragPtr, *endPtr;
+ static int initialized = 0;
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+ if (!initialized) {
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ /*
+ * If running NT, then we will be calling some Unicode functions
+ * explictly. So, even if the Tcl system encoding isn't Unicode,
+ * make sure we convert to/from the Unicode char set.
+ */
+ systemEncoding = Tcl_GetEncoding(NULL, "unicode");
+ }
+ initialized = 1;
+ }
+#endif
+ hFont = CreateRotatedFont(tsPtr->gc->font, theta);
+ if (hFont == NULL) {
+ return FALSE;
+ }
+ isActive = (tsPtr->state & STATE_ACTIVE);
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(hDC, tsPtr->gc->function);
+ oldFont = SelectFont(hDC, hFont);
+
+ Blt_GetBoundingBox(textPtr->width, textPtr->height, theta, &bbWidth,
+ &bbHeight, (Point2D *)NULL);
+ Blt_TranslateAnchor(x, y, bbWidth, bbHeight, tsPtr->anchor, &x, &y);
+ center.x = (double)textPtr->width * -0.5;
+ center.y = (double)textPtr->height * -0.5;
+ theta = (-theta / 180.0) * M_PI;
+ sinTheta = sin(theta), cosTheta = cos(theta);
+
+ endPtr = textPtr->fragArr + textPtr->nFrags;
+
+ for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+ p.x = center.x + (double)fragPtr->x;
+ p.y = center.y + (double)fragPtr->y;
+ q.x = x + (p.x * cosTheta) - (p.y * sinTheta) + (bbWidth * 0.5);
+ q.y = y + (p.x * sinTheta) + (p.y * cosTheta) + (bbHeight * 0.5);
+ fragPtr->sx = ROUND(q.x);
+ fragPtr->sy = ROUND(q.y);
+ }
+ SetBkMode(hDC, TRANSPARENT);
+ SetTextAlign(hDC, TA_LEFT | TA_BASELINE);
+
+ if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (tsPtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ SetTextColor(hDC, color1->pixel);
+ for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+ DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text,
+ fragPtr->count);
+ }
+ }
+ if (color2 != NULL) {
+ SetTextColor(hDC, color2->pixel);
+ for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+ DrawChars(hDC, fragPtr->sx + 1, fragPtr->sy + 1, fragPtr->text,
+ fragPtr->count);
+ }
+ }
+ goto done; /* Done */
+ }
+ SetBkMode(hDC, TRANSPARENT);
+ if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+ SetTextColor(hDC, tsPtr->shadow.color->pixel);
+ for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+ DrawChars(hDC, fragPtr->sx + tsPtr->shadow.offset,
+ fragPtr->sy + tsPtr->shadow.offset, fragPtr->text,
+ fragPtr->count);
+ }
+ }
+ if (isActive) {
+ SetTextColor(hDC, tsPtr->activeColor->pixel);
+ } else {
+ SetTextColor(hDC, tsPtr->color->pixel);
+ }
+
+ for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+ DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text,
+ fragPtr->count);
+ }
+
+ if (isActive) {
+ SetTextColor(hDC, tsPtr->color->pixel);
+ }
+ done:
+ SelectFont(hDC, oldFont);
+ DeleteFont(hFont);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+ return TRUE;
+}
+
+static void
+DrawPixel(
+ HDC hDC,
+ int x,
+ int y,
+ COLORREF color)
+{
+ HDC memDC;
+ HBRUSH hBrushFg;
+ HBITMAP oldBitmap, hBitmap;
+ RECT rect;
+ int size;
+
+ size = 1;
+ memDC = CreateCompatibleDC(hDC);
+ hBrushFg = CreateSolidBrush(color);
+ hBitmap = CreateCompatibleBitmap(hDC, size, size);
+ oldBitmap = SelectObject(memDC, hBitmap);
+ rect.left = rect.top = 0;
+ rect.right = rect.bottom = size;
+ FillRect(memDC, &rect, hBrushFg);
+ BitBlt(hDC, x, y, size, size, memDC, 0, 0, SRCCOPY);
+ SelectObject(memDC, oldBitmap);
+ DeleteObject(hBitmap);
+ DeleteBrush(hBrushFg);
+ DeleteDC(memDC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PixelateBitmap --
+ *
+ * Draws a masked bitmap in given device (should be printer)
+ * context. Bit operations on print devices usually fail because
+ * there's no way to read back from the device surface to get the
+ * previous pixel values, rendering BitBlt useless. The bandaid
+ * solution here is to draw 1x1 pixel rectangles at each
+ * coordinate as directed by the the mask and source bitmaps.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+PixelateBitmap(
+ Display *display,
+ Drawable drawable,
+ Pixmap srcBitmap,
+ Pixmap maskBitmap,
+ int width,
+ int height,
+ GC gc,
+ int destX,
+ int destY)
+{
+ register int x, y;
+ register int dx, dy;
+ int pixel;
+ unsigned char *srcBits;
+ register unsigned char *srcPtr;
+ int bitPos, bytesPerRow;
+ HDC hDC;
+ TkWinDCState state;
+
+ srcBits = Blt_GetBitmapData(display, srcBitmap, width, height,
+ &bytesPerRow);
+ if (srcBits == NULL) {
+ return;
+ }
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+ if (maskBitmap != None) {
+ register unsigned char *maskPtr;
+ unsigned char *maskBits;
+ maskBits = Blt_GetBitmapData(display, maskBitmap, width, height,
+ &bytesPerRow);
+ bytesPerRow = ((width + 31) & ~31) / 8;
+ for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
+ maskPtr = maskBits + (bytesPerRow * y);
+ srcPtr = srcBits + (bytesPerRow * y);
+ for (dx = destX, x = 0; x < width; x++, dx++) {
+ bitPos = x % 8;
+ pixel = (*maskPtr & (0x80 >> bitPos));
+ if (pixel) {
+ pixel = (*srcPtr & (0x80 >> bitPos));
+ DrawPixel(hDC, dx, dy,
+ (pixel) ? gc->foreground : gc->background);
+ }
+ if (bitPos == 7) {
+ srcPtr++, maskPtr++;
+ }
+ } /* x */
+ }
+ Blt_Free(maskBits);
+ } else {
+ bytesPerRow = ((width + 31) & ~31) / 8;
+ for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
+ srcPtr = srcBits + (bytesPerRow * y);
+ for (dx = destX, x = 0; x < width; x++, dx++) {
+ bitPos = x % 8;
+ pixel = (*srcPtr & (0x80 >> bitPos));
+ DrawPixel(hDC, dx, dy,
+ (pixel) ? gc->foreground : gc->background);
+ if (bitPos == 7) {
+ srcPtr++;
+ }
+ }
+ }
+ }
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+ Blt_Free(srcBits);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCopyPlane --
+ *
+ * Simplified version of XCopyPlane. Right now it ignores
+ * function,
+ * clip_x_origin,
+ * clip_y_origin
+ *
+ * The plane argument must always be 1.
+ *
+ * This routine differs from the Tk version in how it handles
+ * transparency. It uses a different method of drawing transparent
+ * bitmaps that doesn't copy the background or use brushes. The
+ * second change is to call a special routine when the destDC is
+ * a printer. Stippling is done by a very slow brute-force
+ * method of drawing 1x1 rectangles for each pixel (bleech).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the destination drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXCopyPlane(
+ Display *display,
+ Drawable src,
+ Drawable dest,
+ GC gc,
+ int srcX,
+ int srcY,
+ unsigned int width,
+ unsigned int height,
+ int destX,
+ int destY,
+ unsigned long plane)
+{
+ HDC srcDC, destDC;
+ TkWinDCState srcState, destState;
+ 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 != NULL) && (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, destX, destY, width, height, srcDC, srcX, srcY,
+ SRCCOPY);
+
+ SelectClipRgn(destDC, NULL);
+
+ } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
+ Drawable mask;
+ /*
+ * Case 2: transparent bitmaps are handled by setting the
+ * destination to the foreground color whenever the source
+ * pixel is set.
+ */
+ /*
+ * 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.
+ */
+ mask = clipPtr->value.pixmap;
+
+#if WINDEBUG
+ PurifyPrintf("mask %s src", (mask == src) ? "==" : "!=");
+ PurifyPrintf("GetDeviceCaps=%x",
+ GetDeviceCaps(destDC, TECHNOLOGY) & DT_RASDISPLAY);
+#endif
+ {
+ HDC maskDC;
+ TkWinDCState maskState;
+
+ if (mask != src) {
+ maskDC = TkWinGetDrawableDC(display, mask, &maskState);
+ } else {
+ maskDC = srcDC;
+ }
+ SetBkMode(destDC, OPAQUE);
+ SetTextColor(destDC, gc->background);
+ SetBkColor(destDC, gc->foreground);
+ BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
+ SRCINVERT);
+ /*
+ * Make sure we treat the mask as a monochrome bitmap.
+ * We can get alpha-blending with non-black/white fg/bg
+ * color selections.
+ */
+ SetTextColor(destDC, RGB(255,255,255));
+ SetBkColor(destDC, RGB(0,0,0));
+
+ /* FIXME: Handle gc->clip_?_origin's */
+ BitBlt(destDC, destX, destY, width, height, maskDC, 0, 0, SRCAND);
+
+ SetTextColor(destDC, gc->background);
+ SetBkColor(destDC, gc->foreground);
+ BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
+ SRCINVERT);
+ if (mask != src) {
+ TkWinReleaseDrawableDC(mask, maskDC, &maskState);
+ }
+ }
+ }
+ if (src != dest) {
+ TkWinReleaseDrawableDC(dest, destDC, &destState);
+ }
+ TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCopyArea --
+ *
+ * Copies data from one drawable to another using block transfer
+ * routines. The small enhancement over the version in Tk is
+ * that it doesn't assume that the source and destination devices
+ * have the same resolution. This isn't true when the destination
+ * device is a printer.
+ *
+ * FIXME: not true anymore. delete this routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Data is moved from a window or bitmap to a second window,
+ * bitmap, or printer.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXCopyArea(
+ Display *display,
+ Drawable src,
+ Drawable dest,
+ GC gc,
+ int srcX, /* Source X-coordinate */
+ int srcY, /* Source Y-coordinate. */
+ unsigned int width, /* Width of area. */
+ unsigned int height, /* Height of area. */
+ int destX, /* Destination X-coordinate (in screen
+ * coordinates). */
+ int destY) /* Destination Y-coordinate (in screen
+ * coordinates). */
+{
+ HDC srcDC, destDC;
+ TkWinDCState srcState, destState;
+ TkpClipMask *clipPtr;
+
+ srcDC = TkWinGetDrawableDC(display, src, &srcState);
+ if (src != dest) {
+ destDC = TkWinGetDrawableDC(display, dest, &destState);
+ } else {
+ destDC = srcDC;
+ }
+ clipPtr = (TkpClipMask *)gc->clip_mask;
+ if ((clipPtr != NULL) && (clipPtr->type == TKP_CLIP_REGION)) {
+ SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+ OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+ }
+
+ BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
+ bltModes[gc->function]);
+ SelectClipRgn(destDC, NULL);
+
+ if (src != dest) {
+ TkWinReleaseDrawableDC(dest, destDC, &destState);
+ }
+ TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+static void
+StippleRegion(
+ Display *display,
+ HDC hDC, /* Device context: For polygons, clip
+ * region will be installed. */
+ GC gc,
+ int x, int y,
+ int width, int height)
+{
+ BITMAP bm;
+ HBITMAP oldBitmap;
+ HDC maskDC, memDC;
+ Pixmap mask;
+ TkWinDCState maskState;
+ TkWinDrawable *twdPtr;
+ int destX, destY, destWidth, destHeight;
+ int dx, dy;
+ int left, top, right, bottom;
+ int srcX, srcY;
+ int startX, startY; /* Starting upper left corner of region. */
+
+ twdPtr = (TkWinDrawable *)gc->stipple;
+ GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+
+ startX = x;
+ if (x < gc->ts_x_origin) {
+ dx = (gc->ts_x_origin - x) % bm.bmWidth;
+ if (dx > 0) {
+ startX -= (bm.bmWidth - dx);
+ }
+ } else if (x > gc->ts_x_origin) {
+ dx = (x - gc->ts_x_origin) % bm.bmWidth;
+ if (dx > 0) {
+ startX -= dx;
+ }
+ }
+ startY = y;
+ if (y < gc->ts_y_origin) {
+ dy = (gc->ts_y_origin - y) % bm.bmHeight;
+ if (dy > 0) {
+ startY -= (bm.bmHeight - dy);
+ }
+ } else if (y >= gc->ts_y_origin) {
+ dy = (y - gc->ts_y_origin) % bm.bmHeight;
+ if (dy > 0) {
+ startY -= dy;
+ }
+ }
+#ifdef notdef
+ PurifyPrintf("tile is (%d,%d,%d,%d)\n", gc->ts_x_origin, gc->ts_y_origin,
+ bm.bmWidth, bm.bmHeight);
+ PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+ PurifyPrintf("starting at %d,%d\n", startX, startY);
+#endif
+ left = x;
+ right = x + width;
+ top = y;
+ bottom = y + height;
+
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+ if (gc->fill_style == FillStippled) { /* With transparency. */
+ mask = gc->stipple;
+ if (gc->clip_mask != None) {
+ TkpClipMask *clipPtr;
+
+ clipPtr = (TkpClipMask *)gc->clip_mask;
+ if (clipPtr->type == TKP_CLIP_PIXMAP) {
+ mask = clipPtr->value.pixmap;
+ }
+ }
+ if (mask != gc->stipple) {
+ maskDC = TkWinGetDrawableDC(display, mask, &maskState);
+ } else {
+ maskDC = memDC;
+ }
+ }
+
+ for (y = startY; y < bottom; y += bm.bmHeight) {
+ srcY = 0;
+ destY = y;
+ destHeight = bm.bmHeight;
+ if (y < top) {
+ srcY = (top - y);
+ destHeight = bm.bmHeight - srcY;
+ destY = top;
+ }
+ if ((destY + destHeight) > bottom) {
+ destHeight = (bottom - destY);
+ }
+ for (x = startX; x < right; x += bm.bmWidth) {
+ srcX = 0;
+ destX = x;
+ destWidth = bm.bmWidth;
+ if (x < left) {
+ srcX = (left - x);
+ destWidth = bm.bmWidth - srcX;
+ destX = left;
+ }
+ if ((destX + destWidth) > right) {
+ destWidth = (right - destX);
+ }
+#ifdef notdef
+ PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+ srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+ if (gc->fill_style == FillStippled) { /* With transparency. */
+ SetBkMode(hDC, OPAQUE);
+ SetTextColor(hDC, gc->background);
+ SetBkColor(hDC, gc->foreground);
+ BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
+ srcX, srcY, SRCINVERT);
+ SetTextColor(hDC, RGB(255,255,255));
+ SetBkColor(hDC, RGB(0,0,0));
+ BitBlt(hDC, destX, destY, destWidth, destHeight, maskDC,
+ srcX, srcY, SRCAND);
+ SetTextColor(hDC, gc->background);
+ SetBkColor(hDC, gc->foreground);
+ BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
+ srcX, srcY, SRCINVERT);
+ } else if (gc->fill_style == FillOpaqueStippled) { /* Opaque. */
+ SetBkColor(hDC, gc->foreground);
+ SetTextColor(hDC, gc->background);
+ BitBlt(hDC, destX, destY, destWidth, destHeight, memDC,
+ srcX, srcY, SRCCOPY);
+ }
+ }
+ }
+ SelectBitmap(memDC, oldBitmap);
+ if ((gc->fill_style == FillStippled) && (mask != gc->stipple)) {
+ TkWinReleaseDrawableDC(mask, maskDC, &maskState);
+ }
+ DeleteDC(memDC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXFillPolygon --
+ *
+ * This differs from Tk's XFillPolygon in that it works around
+ * deficencies in Windows 95/98:
+ * 1. Stippling bitmap is limited to 8x8.
+ * 2. No tiling (with or without mask).
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXFillPolygon(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ XPoint *pointPtr,
+ int nPoints,
+ int shape,
+ int mode)
+{
+ HDC hDC;
+ HRGN hRgn;
+ POINT *p, *winPts, *endPtr;
+ Region2D bbox;
+ TkWinDCState state;
+ int fillMode;
+
+ if (drawable == None) {
+ return;
+ }
+
+ /* Determine the bounding box of the polygon. */
+ bbox.left = bbox.right = pointPtr->x;
+ bbox.top = bbox.bottom = pointPtr->y;
+
+ /* Allocate array of POINTS to create the polygon's path. */
+ winPts = Blt_Malloc(sizeof(POINT) * nPoints);
+ endPtr = winPts + nPoints;
+ for (p = winPts; p < endPtr; p++) {
+ if (pointPtr->x < bbox.left) {
+ bbox.left = pointPtr->x;
+ }
+ if (pointPtr->x > bbox.right) {
+ bbox.right = pointPtr->x;
+ }
+ if (pointPtr->y < bbox.top) {
+ bbox.top = pointPtr->y;
+ }
+ if (pointPtr->y > bbox.bottom) {
+ bbox.bottom = pointPtr->y;
+ }
+ p->x = pointPtr->x;
+ p->y = pointPtr->y;
+ pointPtr++;
+ }
+
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+ SetROP2(hDC, tkpWinRopModes[gc->function]);
+ fillMode = (gc->fill_rule == EvenOddRule) ? ALTERNATE : WINDING;
+
+ if ((gc->fill_style == FillStippled) ||
+ (gc->fill_style == FillOpaqueStippled)) {
+ int width, height;
+
+ /* Points are offsets within the bounding box. */
+ for (p = winPts; p < endPtr; p++) {
+ p->x -= bbox.left;
+ p->y -= bbox.top;
+ }
+ /* Use the polygon as a clip path. */
+ hRgn = CreatePolygonRgn(winPts, nPoints, fillMode);
+ SelectClipRgn(hDC, hRgn);
+ OffsetClipRgn(hDC, bbox.left, bbox.top);
+
+ /* Tile the bounding box. */
+ width = bbox.right - bbox.left + 1;
+ height = bbox.bottom - bbox.top + 1;
+ StippleRegion(display, hDC, gc, bbox.left, bbox.top, width, height);
+
+ SelectClipRgn(hDC, NULL);
+ DeleteRgn(hRgn);
+ } else {
+ HPEN oldPen;
+ HBRUSH oldBrush;
+
+ /*
+ * FIXME: Right now, we're assuming that it's solid or
+ * stippled and ignoring tiling. I'll merge the bits from
+ * Blt_TilePolygon later.
+ */
+ oldPen = SelectPen(hDC, GetStockObject(NULL_PEN));
+ oldBrush = SelectBrush(hDC, CreateSolidBrush(gc->foreground));
+ SetPolyFillMode(hDC, fillMode);
+ Polygon(hDC, winPts, nPoints);
+ SelectPen(hDC, oldPen);
+ DeleteBrush(SelectBrush(hDC, oldBrush));
+ }
+ Blt_Free(winPts);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
diff --git a/blt/src/bltWinImage.c b/blt/src/bltWinImage.c
new file mode 100644
index 00000000000..dd6812f4991
--- /dev/null
+++ b/blt/src/bltWinImage.c
@@ -0,0 +1,801 @@
+
+/*
+ * bltWinImage.c --
+ *
+ * This module implements image processing procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include <X11/Xutil.h>
+
+#define CLAMP(c) ((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+enum RightAngles { ROTATE_0, ROTATE_90, ROTATE_180, ROTATE_270 };
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPixmap --
+ *
+ * Converts a color image into a pixmap.
+ *
+ * Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ * The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorimageToPixmap(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ Blt_Colorimage image,
+ ColorTable *colorTablePtr) /* Points to array of colormap indices */
+{
+ HDC pixmapDC;
+ TkWinDCState state;
+ Display *display;
+ int width, height, depth;
+ Pixmap pixmap;
+ register int x, y;
+ register Pix32 *srcPtr;
+ COLORREF rgb;
+
+ *colorTablePtr = NULL;
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+ display = Tk_Display(tkwin);
+ depth = Tk_Depth(tkwin);
+
+ pixmap = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height, depth);
+ pixmapDC = TkWinGetDrawableDC(display, pixmap, &state);
+
+ srcPtr = Blt_ColorimageBits(image);
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ rgb = PALETTERGB(srcPtr->Red, srcPtr->Green, srcPtr->Blue);
+ SetPixelV(pixmapDC, x, y, rgb);
+ srcPtr++;
+ }
+ }
+ TkWinReleaseDrawableDC(pixmap, pixmapDC, &state);
+ return pixmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorimageToPixmap2 --
+ *
+ * Converts a color image into a pixmap.
+ *
+ * Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ * The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorimageToPixmap2(
+ Display *display,
+ int depth,
+ Blt_Colorimage image,
+ ColorTable *colorTablePtr) /* Points to array of colormap indices */
+{
+ BITMAP bm;
+ HBITMAP hBitmap;
+ TkWinBitmap *twdPtr;
+ int width, height;
+ register Pix32 *srcPtr;
+ register int x, y;
+ register unsigned char *destPtr;
+ unsigned char *bits;
+
+ *colorTablePtr = NULL;
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+
+ /*
+ * Copy the color image RGB data into the DIB. The DIB scanlines
+ * are stored bottom-to-top and the order of the RGB color
+ * components is BGR. Who says Win32 GDI programming isn't
+ * backwards?
+ */
+ bits = Blt_Malloc(width * height * sizeof(unsigned char));
+ assert(bits);
+ srcPtr = Blt_ColorimageBits(image);
+ for (y = height - 1; y >= 0; y--) {
+ destPtr = bits + (y * width);
+ for (x = 0; x < width; x++) {
+ *destPtr++ = srcPtr->Blue;
+ *destPtr++ = srcPtr->Green;
+ *destPtr++ = srcPtr->Red;
+ *destPtr++ = (unsigned char)-1;
+ srcPtr++;
+ }
+ }
+ bm.bmType = 0;
+ bm.bmWidth = width;
+ bm.bmHeight = height;
+ bm.bmWidthBytes = width;
+ bm.bmPlanes = 1;
+ bm.bmBitsPixel = 32;
+ bm.bmBits = bits;
+ hBitmap = CreateBitmapIndirect(&bm);
+
+ /* Create a windows version of a drawable. */
+ twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+ assert(twdPtr);
+ twdPtr->type = TWD_BITMAP;
+ twdPtr->handle = hBitmap;
+ twdPtr->depth = depth;
+ twdPtr->colormap = DefaultColormap(display, DefaultScreen(display));
+ return (Pixmap)twdPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DrawableToColorimage --
+ *
+ * Takes a snapshot of an X drawable (pixmap or window) and
+ * converts it to a color image.
+ *
+ * Results:
+ * Returns a color image of the drawable. If an error occurred,
+ * NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_DrawableToColorimage(
+ Tk_Window tkwin,
+ Drawable drawable,
+ int x, int y,
+ int width, int height, /* Dimension of the drawable. */
+ double inputGamma)
+{
+ void *data;
+ BITMAPINFO info;
+ DIBSECTION ds;
+ HBITMAP hBitmap, oldBitmap;
+ HPALETTE hPalette;
+ HDC memDC;
+ unsigned char *srcArr;
+ register unsigned char *srcPtr;
+ HDC hDC;
+ TkWinDCState state;
+ register Pix32 *destPtr;
+ Blt_Colorimage image;
+ unsigned char lut[256];
+
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+
+ /* Create the intermediate drawing surface at window resolution. */
+ ZeroMemory(&info, sizeof(info));
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = width;
+ info.bmiHeader.biHeight = height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, hBitmap);
+
+ hPalette = Blt_GetSystemPalette();
+ if (hPalette != NULL) {
+ SelectPalette(hDC, hPalette, FALSE);
+ RealizePalette(hDC);
+ SelectPalette(memDC, hPalette, FALSE);
+ RealizePalette(memDC);
+ }
+ image = NULL;
+ /* Copy the window contents to the memory surface. */
+ if (!BitBlt(memDC, 0, 0, width, height, hDC, x, y, SRCCOPY)) {
+#ifdef notdef
+ PurifyPrintf("can't blit: %s\n", Blt_LastError());
+#endif
+ goto done;
+ }
+ if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+#ifdef notdef
+ PurifyPrintf("can't get object: %s\n", Blt_LastError());
+#endif
+ goto done;
+ }
+ srcArr = (unsigned char *)ds.dsBm.bmBits;
+ image = Blt_CreateColorimage(width, height);
+ destPtr = Blt_ColorimageBits(image);
+
+ {
+ register int i;
+ double value;
+
+ for (i = 0; i < 256; i++) {
+ value = pow(i / 255.0, inputGamma) * 255.0 + 0.5;
+ lut[i] = (unsigned char)CLAMP(value);
+ }
+ }
+
+ /*
+ * Copy the DIB RGB data into the color image. The DIB scanlines
+ * are stored bottom-to-top and the order of the RGB color
+ * components is BGR. Who says Win32 GDI programming isn't
+ * backwards?
+ */
+
+ for (y = height - 1; y >= 0; y--) {
+ srcPtr = srcArr + (y * ds.dsBm.bmWidthBytes);
+ for (x = 0; x < width; x++) {
+ destPtr->Blue = lut[*srcPtr++];
+ destPtr->Green = lut[*srcPtr++];
+ destPtr->Red = lut[*srcPtr++];
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ srcPtr++;
+ }
+ }
+ done:
+ DeleteBitmap(SelectBitmap(memDC, oldBitmap));
+ DeleteDC(memDC);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+ if (hPalette != NULL) {
+ DeletePalette(hPalette);
+ }
+ return image;
+}
+
+
+Pixmap
+Blt_PhotoImageMask(
+ Tk_Window tkwin,
+ Tk_PhotoImageBlock src)
+{
+ TkWinBitmap *twdPtr;
+ int bytesPerRow;
+ int offset, count;
+ register int x, y;
+ unsigned char *bits;
+ unsigned char *srcPtr;
+
+ bytesPerRow = ((src.width + 31) & ~31) / 8;
+ bits = Blt_Calloc(src.height, bytesPerRow);
+
+#define SetBit(x, y) \
+ bits[(bytesPerRow * y) + (x / 8)] |= (0x80 >> (x % 8))
+
+ offset = count = 0;
+ for (y = 0; y < src.height; y++) {
+ srcPtr = src.pixelPtr + offset;
+ for (x = 0; x < src.width; x++) {
+ if (srcPtr[src.offset[3]] == 0x00) {
+ SetBit(x, y);
+ count++;
+ }
+ srcPtr += src.pixelSize;
+ }
+ offset += src.pitch;
+ }
+ if (count > 0) {
+ HBITMAP hBitmap;
+ BITMAP bm;
+
+ bm.bmType = 0;
+ bm.bmWidth = src.width;
+ bm.bmHeight = src.height;
+ bm.bmWidthBytes = bytesPerRow;
+ bm.bmPlanes = 1;
+ bm.bmBitsPixel = 1;
+ bm.bmBits = bits;
+ hBitmap = CreateBitmapIndirect(&bm);
+
+ twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+ assert(twdPtr);
+ twdPtr->type = TWD_BITMAP;
+ twdPtr->handle = hBitmap;
+ twdPtr->depth = 1;
+ if (Tk_WindowId(tkwin) == None) {
+ twdPtr->colormap = DefaultColormap(Tk_Display(tkwin),
+ DefaultScreen(Tk_Display(tkwin)));
+ } else {
+ twdPtr->colormap = Tk_Colormap(tkwin);
+ }
+ } else {
+ twdPtr = NULL;
+ }
+ if (bits != NULL) {
+ Blt_Free(bits);
+ }
+ return (Pixmap)twdPtr;
+}
+
+Pixmap
+Blt_ColorimageMask(
+ Tk_Window tkwin,
+ Blt_Colorimage image)
+{
+ TkWinBitmap *twdPtr;
+ int bytesPerRow;
+ int count;
+ register int x, y;
+ unsigned char *bits;
+ Pix32 *srcPtr;
+ int width, height;
+
+ width = Blt_ColorimageWidth(image);
+ height = Blt_ColorimageHeight(image);
+ bytesPerRow = ((width + 31) & ~31) / 8;
+ bits = Blt_Calloc(height, bytesPerRow);
+
+ count = 0;
+ srcPtr = Blt_ColorimageBits(image);
+ for (y = height - 1; y >= 0; y--) {
+ for (x = 0; x < width; x++) {
+ if (srcPtr->Alpha == 0x00) {
+ SetBit(x, y);
+ count++;
+ }
+ srcPtr++;
+ }
+ }
+ if (count > 0) {
+ HBITMAP hBitmap;
+ BITMAP bm;
+
+ bm.bmType = 0;
+ bm.bmWidth = Blt_ColorimageWidth(image);
+ bm.bmHeight = Blt_ColorimageHeight(image);
+ bm.bmWidthBytes = bytesPerRow;
+ bm.bmPlanes = 1;
+ bm.bmBitsPixel = 1;
+ bm.bmBits = bits;
+ hBitmap = CreateBitmapIndirect(&bm);
+
+ twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+ assert(twdPtr);
+ twdPtr->type = TWD_BITMAP;
+ twdPtr->handle = hBitmap;
+ twdPtr->depth = 1;
+ if (Tk_WindowId(tkwin) == None) {
+ twdPtr->colormap = DefaultColormap(Tk_Display(tkwin),
+ DefaultScreen(Tk_Display(tkwin)));
+ } else {
+ twdPtr->colormap = Tk_Colormap(tkwin);
+ }
+ } else {
+ twdPtr = NULL;
+ }
+ if (bits != NULL) {
+ Blt_Free(bits);
+ }
+ return (Pixmap)twdPtr;
+}
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_BlendColorimage --
+ *
+ * Takes a snapshot of an X drawable (pixmap or window) and
+ * converts it to a color image.
+ *
+ * Results:
+ * Returns a color image of the drawable. If an error occurred,
+ * NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_BlendColorimage(
+ Tk_Window tkwin,
+ Drawable drawable,
+ int width, int height, /* Dimension of the drawable. */
+ Region2D *regionPtr) /* Region to be snapped. */
+{
+ void *data;
+ BITMAPINFO info;
+ DIBSECTION ds;
+ HBITMAP hBitmap, oldBitmap;
+ HPALETTE hPalette;
+ HDC memDC;
+ unsigned char *srcArr;
+ register unsigned char *srcPtr;
+ HDC hDC;
+ TkWinDCState state;
+ register Pix32 *destPtr;
+ Blt_Colorimage image;
+ register int x, y;
+
+ if (regionPtr == NULL) {
+ regionPtr = Blt_SetRegion(0, 0, ColorimageWidth(image),
+ ColorimageHeight(image), &region);
+ }
+ if (regionPtr->left < 0) {
+ regionPtr->left = 0;
+ }
+ if (regionPtr->right >= destWidth) {
+ regionPtr->right = destWidth - 1;
+ }
+ if (regionPtr->top < 0) {
+ regionPtr->top = 0;
+ }
+ if (regionPtr->bottom >= destHeight) {
+ regionPtr->bottom = destHeight - 1;
+ }
+ width = RegionWidth(regionPtr);
+ height = RegionHeight(regionPtr);
+
+ hDC = TkWinGetDrawableDC(display, drawable, &state);
+
+ /* Create the intermediate drawing surface at window resolution. */
+ ZeroMemory(&info, sizeof(info));
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = width;
+ info.bmiHeader.biHeight = height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, hBitmap);
+
+ hPalette = Blt_GetSystemPalette();
+ if (hPalette != NULL) {
+ SelectPalette(hDC, hPalette, FALSE);
+ RealizePalette(hDC);
+ SelectPalette(memDC, hPalette, FALSE);
+ RealizePalette(memDC);
+ }
+ image = NULL;
+ /* Copy the window contents to the memory surface. */
+ if (!BitBlt(memDC, 0, 0, width, height, hDC, regionPtr->left,
+ regionPtr->top, SRCCOPY)) {
+#ifdef notdef
+ PurifyPrintf("can't blit: %s\n", Blt_LastError());
+#endif
+ goto done;
+ }
+ if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+#ifdef notdef
+ PurifyPrintf("can't get object: %s\n", Blt_LastError());
+#endif
+ goto done;
+ }
+ srcArr = (unsigned char *)ds.dsBm.bmBits;
+ image = Blt_CreateColorimage(width, height);
+ destPtr = Blt_ColorimageBits(image);
+
+ /*
+ * Copy the DIB RGB data into the color image. The DIB scanlines
+ * are stored bottom-to-top and the order of the RGBA color
+ * components is BGRA. Who says Win32 GDI programming isn't
+ * backwards?
+ */
+ for (y = height - 1; y >= 0; y--) {
+ srcPtr = srcArr + (y * ds.dsBm.bmWidthBytes);
+ for (x = 0; x < width; x++) {
+ if (destPtr->Alpha > 0) {
+ /* Blend colorimage with background. */
+ destPtr->Blue = *srcPtr++;
+ destPtr->Green = *srcPtr++;
+ destPtr->Red = *srcPtr++;
+ destPtr->Alpha = (unsigned char)-1;
+ srcPtr++;
+ }
+ destPtr++;
+ }
+ }
+ done:
+ DeleteBitmap(SelectBitmap(memDC, oldBitmap));
+ DeleteDC(memDC);
+ TkWinReleaseDrawableDC(drawable, hDC, &state);
+ if (hPalette != NULL) {
+ DeletePalette(hPalette);
+ }
+ return image;
+}
+#endif
+
+#ifdef HAVE_IJL_H
+
+#include <ijl.h>
+
+Blt_Colorimage
+Blt_JPEGToColorimage(interp, fileName)
+ Tcl_Interp *interp;
+ char *fileName;
+{
+ JPEG_CORE_PROPERTIES jpgProps;
+ Blt_Colorimage image;
+
+ ZeroMemory(&jpgProps, sizeof(JPEG_CORE_PROPERTIES));
+ if(ijlInit(&jpgProps) != IJL_OK) {
+ Tcl_AppendResult(interp, "can't initialize Intel JPEG library",
+ (char *)NULL);
+ return NULL;
+ }
+ jpgProps.JPGFile = fileName;
+ if (ijlRead(&jpgProps, IJL_JFILE_READPARAMS) != IJL_OK) {
+ Tcl_AppendResult(interp, "can't read JPEG file header from \"",
+ fileName, "\" file.", (char *)NULL);
+ goto error;
+ }
+
+ // !dudnik: to fix bug case 584680, [OT:287A305B]
+ // Set the JPG color space ... this will always be
+ // somewhat of an educated guess at best because JPEG
+ // is "color blind" (i.e., nothing in the bit stream
+ // tells you what color space the data was encoded from).
+ // However, in this example we assume that we are
+ // reading JFIF files which means that 3 channel images
+ // are in the YCbCr color space and 1 channel images are
+ // in the Y color space.
+ switch(jpgProps.JPGChannels) {
+ case 1:
+ jpgProps.JPGColor = IJL_G;
+ jpgProps.DIBChannels = 4;
+ jpgProps.DIBColor = IJL_RGBA_FPX;
+ break;
+
+ case 3:
+ jpgProps.JPGColor = IJL_YCBCR;
+ jpgProps.DIBChannels = 4;
+ jpgProps.DIBColor = IJL_RGBA_FPX;
+ break;
+
+ case 4:
+ jpgProps.JPGColor = IJL_YCBCRA_FPX;
+ jpgProps.DIBChannels = 4;
+ jpgProps.DIBColor = IJL_RGBA_FPX;
+ break;
+
+ default:
+ /* This catches everything else, but no color twist will be
+ performed by the IJL. */
+ jpgProps.DIBColor = (IJL_COLOR)IJL_OTHER;
+ jpgProps.JPGColor = (IJL_COLOR)IJL_OTHER;
+ jpgProps.DIBChannels = jpgProps.JPGChannels;
+ break;
+ }
+
+ jpgProps.DIBWidth = jpgProps.JPGWidth;
+ jpgProps.DIBHeight = jpgProps.JPGHeight;
+ jpgProps.DIBPadBytes = IJL_DIB_PAD_BYTES(jpgProps.DIBWidth,
+ jpgProps.DIBChannels);
+
+ image = Blt_CreateColorimage(jpgProps.JPGWidth, jpgProps.JPGHeight);
+
+ jpgProps.DIBBytes = (BYTE *)Blt_ColorimageBits(image);
+ if (ijlRead(&jpgProps, IJL_JFILE_READWHOLEIMAGE) != IJL_OK) {
+ Tcl_AppendResult(interp, "can't read image data from \"", fileName,
+ "\"", (char *)NULL);
+ goto error;
+ }
+ if (ijlFree(&jpgProps) != IJL_OK) {
+ fprintf(stderr, "can't free Intel(R) JPEG library\n");
+ }
+ return image;
+
+ error:
+ ijlFree(&jpgProps);
+ if (image != NULL) {
+ Blt_FreeColorimage(image);
+ }
+ ijlFree(&jpgProps);
+ return NULL;
+}
+
+#else
+
+#ifdef HAVE_JPEGLIB_H
+
+#undef HAVE_STDLIB_H
+#undef EXTERN
+#ifdef WIN32
+#define XMD_H 1
+#endif
+#include "jpeglib.h"
+#include <setjmp.h>
+
+typedef struct {
+ struct jpeg_error_mgr pub; /* "public" fields */
+ jmp_buf jmpBuf;
+ Tcl_DString dString;
+} ReaderHandler;
+
+static void ErrorProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+static void MessageProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+static void
+ErrorProc(jpgPtr)
+ j_common_ptr jpgPtr;
+{
+ ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+
+ (*handlerPtr->pub.output_message) (jpgPtr);
+ longjmp(handlerPtr->jmpBuf, 1);
+}
+
+static void
+MessageProc(jpgPtr)
+ j_common_ptr jpgPtr;
+{
+ ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message and append it into the dynamic string. */
+ (*handlerPtr->pub.format_message) (jpgPtr, buffer);
+ Tcl_DStringAppend(&(handlerPtr->dString), " ", -1);
+ Tcl_DStringAppend(&(handlerPtr->dString), buffer, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToColorimage --
+ *
+ * Reads a JPEG file and converts it into a color image.
+ *
+ * Results:
+ * The color image is returned. If an error occured, such
+ * as the designated file could not be opened, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Colorimage
+Blt_JPEGToColorimage(interp, fileName)
+ Tcl_Interp *interp;
+ char *fileName;
+{
+ struct jpeg_decompress_struct jpg;
+ Blt_Colorimage image;
+ unsigned int imageWidth, imageHeight;
+ register Pix32 *destPtr;
+ ReaderHandler handler;
+ FILE *f;
+ JSAMPLE **readBuffer;
+ int row_stride;
+ register int i;
+ register JSAMPLE *bufPtr;
+
+ f = fopen(fileName, "rb");
+ if (f == NULL) {
+ Tcl_AppendResult(interp, "can't open \"", fileName, "\":",
+ Tcl_PosixError(interp), (char *)NULL);
+ return NULL;
+ }
+ image = NULL;
+
+ /* Step 1: allocate and initialize JPEG decompression object */
+
+ /* We set up the normal JPEG error routines, then override error_exit. */
+ jpg.dct_method = JDCT_IFAST;
+ jpg.err = jpeg_std_error(&handler.pub);
+ handler.pub.error_exit = ErrorProc;
+ handler.pub.output_message = MessageProc;
+
+ Tcl_DStringInit(&handler.dString);
+ Tcl_DStringAppend(&handler.dString, "error reading \"", -1);
+ Tcl_DStringAppend(&handler.dString, fileName, -1);
+ Tcl_DStringAppend(&handler.dString, "\": ", -1);
+
+ if (setjmp(handler.jmpBuf)) {
+ jpeg_destroy_decompress(&jpg);
+ fclose(f);
+ Tcl_DStringResult(interp, &(handler.dString));
+ return NULL;
+ }
+ jpeg_create_decompress(&jpg);
+ jpeg_stdio_src(&jpg, f);
+
+ jpeg_read_header(&jpg, TRUE); /* Step 3: read file parameters */
+
+ jpeg_start_decompress(&jpg); /* Step 5: Start decompressor */
+ imageWidth = jpg.output_width;
+ imageHeight = jpg.output_height;
+ if ((imageWidth < 1) || (imageHeight < 1)) {
+ Tcl_AppendResult(interp, "bad JPEG image size", (char *)NULL);
+ fclose(f);
+ return NULL;
+ }
+ /* JSAMPLEs per row in output buffer */
+ row_stride = imageWidth * jpg.output_components;
+
+ /* Make a one-row-high sample array that will go away when done
+ * with image */
+ readBuffer = (*jpg.mem->alloc_sarray) ((j_common_ptr)&jpg, JPOOL_IMAGE,
+ row_stride, 1);
+ image = Blt_CreateColorimage(imageWidth, imageHeight);
+ destPtr = Blt_ColorimageBits(image);
+
+ if (jpg.output_components == 1) {
+ while (jpg.output_scanline < imageHeight) {
+ jpeg_read_scanlines(&jpg, readBuffer, 1);
+ bufPtr = readBuffer[0];
+ for (i = 0; i < (int)imageWidth; i++) {
+ destPtr->Red = destPtr->Green = destPtr->Blue = *bufPtr++;
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else {
+ while (jpg.output_scanline < imageHeight) {
+ jpeg_read_scanlines(&jpg, readBuffer, 1);
+ bufPtr = readBuffer[0];
+ for (i = 0; i < (int)imageWidth; i++) {
+ destPtr->Red = *bufPtr++;
+ destPtr->Green = *bufPtr++;
+ destPtr->Blue = *bufPtr++;
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ }
+ jpeg_finish_decompress(&jpg); /* We can ignore the return value
+ * since suspension is not
+ * possible with the stdio data
+ * source. */
+ jpeg_destroy_decompress(&jpg);
+
+
+ /*
+ * After finish_decompress, we can close the input file. Here we
+ * postpone it until after no more JPEG errors are possible, so as
+ * to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume
+ * anything...)
+ */
+ fclose(f);
+
+ /*
+ * At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+ */
+ if (handler.pub.num_warnings > 0) {
+ Tcl_SetErrorCode(interp, "IMAGE", "JPEG",
+ Tcl_DStringValue(&(handler.dString)), (char *)NULL);
+ } else {
+ Tcl_SetErrorCode(interp, "NONE", (char *)NULL);
+ }
+ /*
+ * We're ready to call the Tk_Photo routines. They'll take the RGB
+ * array we've processed to build the Tk image of the JPEG.
+ */
+ Tcl_DStringFree(&(handler.dString));
+ return image;
+}
+
+#endif /* HAVE_JPEGLIB_H */
+#endif /* HAVE_IJL_H */
+
diff --git a/blt/src/bltWinMain.c b/blt/src/bltWinMain.c
new file mode 100644
index 00000000000..8c5cf65d8df
--- /dev/null
+++ b/blt/src/bltWinMain.c
@@ -0,0 +1,402 @@
+
+/*
+ * bltWinMain.c --
+ *
+ * Main entry point for wish and other Tk-based applications.
+ *
+ * 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: @(#) winMain.c 1.37 98/01/20 22:47:06
+ */
+
+#include "bltInt.h"
+#include <locale.h>
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
+
+static BOOL consoleRequired = TRUE;
+
+extern EXPORT int Blt_Init(Tcl_Interp *interp);
+extern EXPORT int Blt_SafeInit(Tcl_Interp *interp);
+static Tcl_AppInitProc AppInit;
+
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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(
+ 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(); /* INTL: BUG */
+
+ /*
+ * 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 ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
+ size++;
+ while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ }
+ }
+ argSpace = (char *)Tcl_Alloc(
+ (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 ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
+ 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 && ((*p == ' ') ||
+ (*p == '\t')))) { /* INTL: ISO space. */
+ break;
+ }
+ if (copy != 0) {
+ *arg = *p;
+ arg++;
+ }
+ p++;
+ }
+ *arg = '\0';
+ argSpace = arg + 1;
+ }
+ argv[argc] = NULL;
+
+ *argcPtr = argc;
+ *argvPtr = argv;
+}
+
+
+#ifdef TCL_ONLY
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tcl_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+ char buffer[MAX_PATH +1];
+ char *p;
+ /*
+ * Set up the default locale to be standard "C" locale so parsing
+ * is performed correctly.
+ */
+
+ setlocale(LC_ALL, "C");
+ 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 = '/';
+ }
+ }
+ Tcl_Main(argc, argv, AppInit);
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+#else /* TCL_ONLY */
+
+#if (TK_VERSION_NUMBER < _VERSION(8,2,0))
+/*
+ * 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);
+
+#endif /* TK_VERSION_NUMBER < 8.2.0 */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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
+ DebugBreak();
+#ifdef notdef /* Panics shouldn't cause exceptions.
+ * Simply let the program exit. */
+ _asm {
+ int 3
+ }
+#endif
+#endif
+ ExitProcess(1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpszCmdLine,
+ int nCmdShow)
+{
+ char **argv;
+ int argc;
+
+ Tcl_SetPanicProc(WishPanic);
+
+ /*
+ * Set up the default locale to be standard "C" locale so parsing
+ * is performed correctly.
+ */
+
+ setlocale(LC_ALL, "C");
+ setargv(&argc, &argv);
+
+ /*
+ * 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.
+ */
+#if (TCL_VERSION_NUMBER >= _VERSION(8,2,0))
+ consoleRequired = TRUE;
+#else
+ TkConsoleCreate();
+#endif
+ Tk_Main(argc, argv, AppInit);
+ return 1;
+}
+
+#endif /* TCL_ONLY */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 the interp's result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef TCL_ONLY
+static int
+AppInit(Tcl_Interp *interp)
+{ /* Interpreter for application. */
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (Blt_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Blt", Blt_Init, Blt_SafeInit);
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/tclshrc.tcl", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
+
+#else
+
+static int
+AppInit(Tcl_Interp *interp)
+{ /* Interpreter for application. */
+#ifdef TCLLIBPATH
+ /*
+ * It seems that some distributions of Tcl don't compile-in a
+ * default location of the library. This causes Tcl_Init to fail
+ * if bltwish and bltsh are moved to another directory. The
+ * workaround is to set the magic variable "tclDefaultLibrary".
+ */
+ Tcl_SetVar(interp, "tclDefaultLibrary", TCLLIBPATH, TCL_GLOBAL_ONLY);
+#endif
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ if (Tk_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit);
+ if (Blt_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ Tcl_StaticPackage(interp, "Blt", Blt_Init, Blt_SafeInit);
+
+ /*
+ * Initialize the console only if we are running as an interactive
+ * application.
+ */
+#if (TCL_VERSION_NUMBER >= _VERSION(8,2,0))
+ if (consoleRequired) {
+ if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) {
+ goto error;
+ }
+ }
+#else
+ /*
+ * Initialize the console only if we are running as an interactive
+ * application.
+ */
+ if (TkConsoleInit(interp) == TCL_ERROR) {
+ goto error;
+ }
+#endif /* TCL_VERSION_NUMBER >= 8.2.0 */
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/wishrc.tcl", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+
+ error:
+ /* WishPanic(Tcl_GetStringResult(interp)); */
+ return TCL_ERROR;
+}
+
+#endif /* TCL_ONLY */
diff --git a/blt/src/bltWinPipe.c b/blt/src/bltWinPipe.c
new file mode 100644
index 00000000000..aa4973db365
--- /dev/null
+++ b/blt/src/bltWinPipe.c
@@ -0,0 +1,2439 @@
+
+/*
+ * bltWinPipe.c --
+ *
+ * Lifted from tclPipe.c and tclWinPipe.c in the Tcl
+ * distribution, this is the first step toward freedom from the
+ * tyranny of the former Tcl_CreatePipeline API.
+ *
+ * This file contains the generic portion of the command channel
+ * driver as well as various utility routines used in managing
+ * subprocesses.
+ *
+ * [It's not clear why we needed a whole new API for I/O. Channels
+ * are one of the few losing propositions in Tcl. While it's easy
+ * to see that one needs to handle the different platform I/O
+ * semantics in a coherent fashion, it's usually better to pick
+ * an API from one of platforms (hopefully a mature, well-known model)
+ * and crowbar the other platforms to follow that. At least then
+ * you're working from a known set of sematics. With Tcl Channels,
+ * no one's an expert and the interface is incomplete.]
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Todo:
+ * Test on win95
+ * Does terminating bltwish kill child processes?
+ * Handle EOL translation more cleanly.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+#include <fcntl.h>
+
+#define PEEK_DEBUG 0
+#define QUEUE_DEBUG 0
+#define READER_DEBUG 0
+#define ASYNC_DEBUG 0
+#define KILL_DEBUG 0
+
+typedef struct {
+ DWORD pid;
+ HANDLE hProcess;
+} Process;
+
+/*
+ * The following type identifies the various types of applications that
+ * run under windows. There is special case code for the various types.
+ */
+typedef enum ApplicationTypes {
+ APPL_NONE, APPL_DOS, APPL_WIN3X, APPL_WIN32, APPL_INTERP
+} ApplicationType;
+
+#ifndef IMAGE_OS2_SIGNATURE
+# define IMAGE_OS2_SIGNATURE (0x454E)
+#endif
+#ifndef IMAGE_VXD_SIGNATURE
+# define IMAGE_VXD_SIGNATURE (0x454C)
+#endif
+
+#define PIPE_BUFSIZ (BUFSIZ*2) /* Size of pipe read buffer. */
+
+#define PIPE_PENDING (1<<13) /* Message is pending in the queue. */
+#define PIPE_EOF (1<<14) /* Pipe has reached EOF. */
+#define PIPE_DELETED (1<<15) /* Indicates if the pipe has been deleted
+ * but its memory hasn't been freed yet. */
+
+typedef struct {
+ int flags; /* State flags, see above for a list. */
+ HANDLE hPipe; /* Pipe handle */
+ HANDLE thread; /* Thread watching I/O on this pipe. */
+ HANDLE parent; /* Handle of main thread. */
+ DWORD parentId; /* Main thread ID. */
+ HWND hWindow; /* Notifier window in main thread. Used to
+ * goose the Tcl notifier system indicating
+ * that an event has occurred that it
+ * needs to process. */
+ HANDLE idleEvent; /* Signals that the pipe is idle (no one
+ * is reading/writing from it). */
+ HANDLE readyEvent; /* Signals that the pipe is ready for
+ * the next I/O operation. */
+
+ DWORD lastError; /* Error. */
+
+ char *buffer; /* Current background output buffer. */
+ size_t start, end; /* Pointers into the output buffer */
+ size_t size; /* Size of buffer. */
+
+ Tcl_FileProc *proc;
+ ClientData clientData;
+
+} PipeHandler;
+
+
+typedef struct {
+ Tcl_Event header; /* Information that is standard for
+ * all events. */
+
+ PipeHandler *pipePtr; /* Pointer to pipe handler structure.
+ * Note that we still have to verify
+ * that the pipe exists before
+ * dereferencing this pointer. */
+} PipeEvent;
+
+static int initialized = 0;
+static Blt_Chain pipeChain;
+static CRITICAL_SECTION pipeCriticalSection;
+
+static DWORD WINAPI PipeWriterThread(void *clientData);
+static DWORD WINAPI PipeReaderThread(void *clientData);
+
+static Tcl_FreeProc DestroyPipe;
+
+extern void Blt_MapPid(HANDLE hProcess, DWORD pid);
+extern HINSTANCE TclWinGetTclInstance(void);
+extern void TclWinConvertError(DWORD lastError);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifierWindowProc --
+ *
+ * This procedure is called to "goose" the Tcl notifier layer to
+ * service pending events. The notifier layer is built upon the
+ * Windows message system. It may need to be awakened if it's
+ * blocked waiting on messages, since synthetic events (i.e.
+ * data available on a pipe) won't do that. There may be events
+ * pending in the Tcl queue, but the Windows message system knows
+ * nothing about Tcl events and won't unblock until the next
+ * message arrives (whenever that may be).
+ *
+ * This callback is triggered by messages posted to the notifier
+ * window (we set up earlier) from the reader/writer pipe
+ * threads. It's purpose is to 1) unblock Windows (posting the
+ * message does that) and 2) call Tcl_ServiceAll from the main
+ * thread. It has to be called from the main thread, not
+ * directly from the pipe threads.
+ *
+ * Results:
+ * A standard Windows result.
+ *
+ * Side effects:
+ * Services any pending Tcl events.
+ *
+ *----------------------------------------------------------------------
+ */
+static LRESULT CALLBACK
+NotifierWindowProc(
+ HWND hWindow,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (message) {
+ case WM_USER:
+ case WM_TIMER:
+ break;
+
+ default:
+ return DefWindowProc(hWindow, message, wParam, lParam);
+ }
+
+ Tcl_ServiceAll(); /* Process all run-able events. */
+ return 0;
+}
+
+static void
+WakeupNotifier(HWND hWindow)
+{
+ PostMessage(hWindow, WM_USER, 0, 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNotifierWindow --
+ *
+ * Initializes the platform specific notifier state.
+ *
+ * Results:
+ * Returns a handle to the notifier state for this thread..
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static HWND
+GetNotifierWindow(void)
+{
+ static HWND hWindow = NULL;
+ /*
+ * Register Notifier window class if this is the first thread to
+ * use this module.
+ */
+ if (hWindow == NULL) {
+ WNDCLASS class;
+ HINSTANCE hInstance;
+
+ memset(&class, 0, sizeof(WNDCLASS));
+ hInstance = TclWinGetTclInstance();
+ class.hInstance = hInstance;
+ class.lpszClassName = "PipeNotifier";
+ class.lpfnWndProc = NotifierWindowProc;
+
+ if (!RegisterClassA(&class)) {
+ panic("Unable to register PipeNotifier window class");
+ }
+ /*
+ * Create a window for communication with the notifier.
+ */
+ hWindow = CreateWindowA("PipeNotifier", "PipeNotifier", WS_TILED,
+ 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
+ }
+ return hWindow;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PeekOnPipe --
+ *
+ * See if some data is available, the pipe is at EOF, or the
+ * reader thread is currently blocked waiting for data.
+ *
+ * Results:
+ * Return TRUE if data is available, FALSE if otherwise. Note
+ * that errors and EOF always return TRUE. We always make the
+ * condition available until the caller handles it by deleting
+ * the pipe event handler.
+ *
+ * On TRUE, the number of bytes returned indicates the following:
+ * 0 EOF.
+ * -1 An error has occured or the thread is currently
+ * blocked reading. In that last case, errno is set
+ * to EAGAIN.
+ * >0 Number of bytes of data in the buffer.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+PeekOnPipe(
+ PipeHandler *pipePtr, /* Pipe state. */
+ int *nAvailPtr)
+{
+ int state;
+
+ *nAvailPtr = -1;
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): waiting for reader\n", pipePtr->hPipe);
+#endif
+ state = WaitForSingleObject(pipePtr->readyEvent, 0);
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): state is %d\n", pipePtr->hPipe, state);
+#endif
+ if (state == WAIT_TIMEOUT) {
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): try again, %d\n", pipePtr->hPipe, state);
+#endif
+ errno = EAGAIN;
+ return FALSE; /* Reader thread is currently blocked. */
+ }
+ /*
+ * At this point the two threads are synchronized. So it's safe
+ * to access shared information.
+ */
+ if (state == WAIT_OBJECT_0) {
+ int nAvail;
+
+ nAvail = pipePtr->end - pipePtr->start;
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): Found %d bytes available\n",
+ pipePtr->hPipe, nAvail);
+#endif
+ if ((nAvail <= 0) && !(pipePtr->flags & PIPE_EOF)) {
+ TclWinConvertError(pipePtr->lastError);
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): Error = %d\n",
+ pipePtr->hPipe, pipePtr->lastError);
+#endif
+ nAvail = -1;
+ }
+ *nAvailPtr = nAvail;
+ }
+#if PEEK_DEBUG
+ PurifyPrintf("PEEK(%d): Reseting events\n", pipePtr->hPipe);
+#endif
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PipeEventProc --
+ *
+ * This function is invoked by Tcl_ServiceEvent when a file event
+ * reaches the front of the event queue. This procedure calls back
+ * the handler procedure designated for this pipe.
+ *
+ * Results:
+ * Returns 1 if the event was handled, meaning it should be removed
+ * from the queue. Returns 0 if the event was not handled, meaning
+ * it should stay on the queue. The only time the event isn't
+ * handled is if the TCL_FILE_EVENTS flag bit isn't set.
+ *
+ * Side effects:
+ * Whatever the pipe handler callback does.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+PipeEventProc(Tcl_Event * eventPtr, int flags)
+{
+ PipeHandler *pipePtr;
+
+ if (!(flags & TCL_FILE_EVENTS)) {
+ return 0;
+ }
+ pipePtr = ((PipeEvent *) eventPtr)->pipePtr;
+ if ((pipePtr != NULL) && !(pipePtr->flags & PIPE_DELETED)) {
+ Tcl_Preserve(pipePtr);
+ if (pipePtr->proc != NULL) {
+ (*pipePtr->proc) (pipePtr->clientData, flags);
+ }
+ /* Allow more events again. */
+ pipePtr->flags &= ~PIPE_PENDING;
+ Tcl_Release(pipePtr);
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetupHandlers --
+ *
+ * This procedure is invoked before Tcl_DoOneEvent blocks waiting
+ * for an event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Adjusts the block time if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+SetupHandlers(ClientData clientData, int flags)
+{
+ Blt_Chain *chainPtr = (Blt_Chain *) clientData;
+ register PipeHandler *pipePtr;
+ Blt_ChainLink *linkPtr;
+ int dontBlock, nBytes;
+ Tcl_Time blockTime;
+
+ if (!(flags & TCL_FILE_EVENTS)) {
+ return;
+ }
+ /*
+ * Loop through the list of pipe handlers. Check if any I/O
+ * events are currently pending.
+ */
+ dontBlock = FALSE;
+ blockTime.sec = blockTime.usec = 0L;
+#if QUEUE_DEBUG
+ PurifyPrintf("SetupHandlers: before loop\n");
+#endif
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ pipePtr = Blt_ChainGetValue(linkPtr);
+ if (pipePtr->flags & PIPE_DELETED) {
+ continue; /* Ignore pipes pending to be freed. */
+ }
+ if (pipePtr->flags & TCL_READABLE) {
+ if (PeekOnPipe(pipePtr, &nBytes)) {
+ dontBlock = TRUE;
+ }
+ }
+ if (pipePtr->flags & TCL_WRITABLE) {
+ if (WaitForSingleObject(pipePtr->readyEvent, 0) != WAIT_TIMEOUT) {
+ dontBlock = TRUE;
+ }
+ }
+ }
+#if QUEUE_DEBUG
+ PurifyPrintf("SetupHandlers: after loop\n");
+#endif
+ if (dontBlock) {
+ Tcl_SetMaxBlockTime(&blockTime);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckHandlers --
+ *
+ * This procedure is called by Tcl_DoOneEvent to check the pipe
+ * event source for events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May queue an event.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CheckHandlers(ClientData clientData, int flags)
+{
+ Blt_Chain *chainPtr = (Blt_Chain *) clientData;
+ PipeHandler *pipePtr;
+ Blt_ChainLink *linkPtr;
+ int queueEvent, nBytes;
+
+ if ((flags & TCL_FILE_EVENTS) == 0) {
+ return;
+ }
+ /* Queue events for any ready pipes that aren't already queued. */
+
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ pipePtr = Blt_ChainGetValue(linkPtr);
+ if (pipePtr->flags & (PIPE_PENDING | PIPE_DELETED)) {
+ continue; /* If this pipe already is scheduled to
+ * service an event, wait for it to handle
+ * it. */
+ }
+ /* Queue an event if the pipe is signaled for reading or writing. */
+ queueEvent = FALSE;
+ if (pipePtr->flags & TCL_READABLE) {
+ if (PeekOnPipe(pipePtr, &nBytes)) {
+ queueEvent = TRUE;
+ }
+ }
+ if (pipePtr->flags & TCL_WRITABLE) {
+ if (WaitForSingleObject(pipePtr->readyEvent, 0) != WAIT_TIMEOUT) {
+ queueEvent = TRUE;
+ }
+ }
+#if QUEUE_DEBUG
+ PurifyPrintf("Queue event is %d \n", queueEvent);
+#endif
+ if (queueEvent) {
+ PipeEvent *eventPtr;
+
+ pipePtr->flags |= PIPE_PENDING;
+ eventPtr = Blt_Malloc(sizeof(PipeEvent));
+ assert(eventPtr);
+ eventPtr->header.proc = PipeEventProc;
+ eventPtr->pipePtr = pipePtr;
+ Tcl_QueueEvent((Tcl_Event *) eventPtr, TCL_QUEUE_TAIL);
+ }
+ }
+}
+
+static PipeHandler *
+CreatePipeHandler(HANDLE hFile, int flags)
+{
+ DWORD id;
+ PipeHandler *pipePtr;
+ LPTHREAD_START_ROUTINE threadProc;
+
+ pipePtr = Blt_Calloc(1, sizeof(PipeHandler));
+ assert(pipePtr);
+
+ pipePtr->hPipe = hFile;
+ pipePtr->flags = flags;
+ pipePtr->parentId = GetCurrentThreadId();
+ pipePtr->parent = GetCurrentThread();
+ pipePtr->hWindow = GetNotifierWindow();
+ pipePtr->readyEvent = CreateEvent(
+ NULL, /* Security attributes. */
+ TRUE, /* Manual reset event */
+ FALSE, /* Initially not signaled. */
+ NULL); /* Event object's name. */
+ pipePtr->idleEvent = CreateEvent(
+ NULL, /* Security attributes. */
+ FALSE, /* Auto reset event. */
+ TRUE, /* Initially signaled. */
+ NULL); /* Event object's name. */
+
+ if (flags & TCL_READABLE) {
+ threadProc = (LPTHREAD_START_ROUTINE) PipeReaderThread;
+ pipePtr->buffer = Blt_Calloc(1, PIPE_BUFSIZ);
+ pipePtr->size = PIPE_BUFSIZ;
+ } else {
+ threadProc = (LPTHREAD_START_ROUTINE) PipeWriterThread;
+ }
+
+ pipePtr->thread = CreateThread(
+ NULL, /* Security attributes */
+ 8000, /* Initial stack size. */
+ threadProc, /* Starting address of thread routine */
+ (DWORD *) pipePtr, /* One-word of data passed to routine. */
+ 0, /* Creation flags */
+ &id); /* (out) Will contain Id of new thread. */
+ return pipePtr;
+}
+
+static void
+DestroyPipe(DestroyData data)
+{
+ PipeHandler *pipePtr = (PipeHandler *)data;
+
+ if (pipePtr->buffer != NULL) {
+ Blt_Free(pipePtr->buffer);
+ }
+ Blt_Free(pipePtr);
+}
+
+static void
+DeletePipeHandler(PipeHandler * pipePtr)
+{
+ DWORD status;
+
+#if KILL_DEBUG
+ PurifyPrintf("DestroyPipeHandler(%d)", pipePtr->hPipe);
+#endif
+ if ((pipePtr->flags & TCL_WRITABLE) &&
+ (pipePtr->hPipe != INVALID_HANDLE_VALUE)) {
+ /* Wait for the writer thread to finish with the current buffer */
+ WaitForSingleObject(pipePtr->idleEvent, INFINITE);
+ }
+ if (pipePtr->hPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(pipePtr->hPipe);
+ }
+ CloseHandle(pipePtr->readyEvent);
+ CloseHandle(pipePtr->idleEvent);
+ status = (DWORD)-1;
+ CloseHandle(pipePtr->thread);
+
+ pipePtr->idleEvent = pipePtr->readyEvent = INVALID_HANDLE_VALUE;
+ pipePtr->thread = pipePtr->hPipe = INVALID_HANDLE_VALUE;
+ pipePtr->flags |= PIPE_DELETED; /* Mark the pipe has deleted. */
+
+ Tcl_EventuallyFree(pipePtr, DestroyPipe);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PipeInit --
+ *
+ * This function initializes the static variables for this file.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates a new event source.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+PipeInit(void)
+{
+ initialized = TRUE;
+ InitializeCriticalSection(&pipeCriticalSection);
+ Blt_ChainInit(&pipeChain);
+ Tcl_CreateEventSource(SetupHandlers, CheckHandlers, &pipeChain);
+}
+
+static PipeHandler *
+GetPipeHandler(HANDLE hPipe)
+{
+ PipeHandler *pipePtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(&pipeChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ pipePtr = Blt_ChainGetValue(linkPtr);
+ if ((pipePtr->hPipe == hPipe) && !(pipePtr->flags & PIPE_DELETED)){
+ return pipePtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PipeTeardown --
+ *
+ * This function releases any storage allocated for this file.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates a new event source.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_PipeTeardown(void)
+{
+ Blt_ChainLink *linkPtr;
+ PipeHandler *pipePtr;
+
+ if (!initialized) {
+ return; /* Was never initialized. */
+ }
+ initialized = FALSE;
+ EnterCriticalSection(&pipeCriticalSection);
+ for (linkPtr = Blt_ChainFirstLink(&pipeChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ pipePtr = Blt_ChainGetValue(linkPtr);
+ if ((pipePtr != NULL) && !(pipePtr->flags & PIPE_DELETED)) {
+ DeletePipeHandler(pipePtr);
+ }
+ }
+ DestroyWindow(GetNotifierWindow());
+ UnregisterClassA("PipeNotifier", TclWinGetTclInstance());
+
+ Blt_ChainReset(&pipeChain);
+ LeaveCriticalSection(&pipeCriticalSection);
+ Tcl_DeleteEventSource(SetupHandlers, CheckHandlers, &pipeChain);
+ DeleteCriticalSection(&pipeCriticalSection);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PipeReaderThread --
+ *
+ * This function runs in a separate thread and waits for input
+ * to become available on a pipe.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Signals the main thread when input become available. May
+ * cause the main thread to wake up by posting a message.
+ *
+ *----------------------------------------------------------------------
+ */
+static DWORD WINAPI
+PipeReaderThread(void *clientData)
+{
+ PipeHandler *pipePtr = (PipeHandler *) clientData;
+ DWORD count;
+ BOOL result;
+
+ for (;;) {
+ if (pipePtr->flags & PIPE_DELETED) {
+ break;
+ }
+ /* Synchronize with the main thread so that we don't try to
+ * read from the pipe while it's copying to the buffer. */
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): waiting", pipePtr->hPipe);
+#endif
+ WaitForSingleObject(pipePtr->idleEvent, INFINITE);
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): ok", pipePtr->hPipe);
+#endif
+ /* Read from the pipe. The thread will block here until some
+ * data is read into its buffer. */
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): before read", pipePtr->hPipe);
+#endif
+ assert(pipePtr->start == pipePtr->end);
+ result = ReadFile(
+ pipePtr->hPipe, /* Handle to anonymous pipe. */
+ pipePtr->buffer, /* Data buffer. */
+ pipePtr->size, /* Requested number of bytes (the size
+ * of the buffer) */
+ &count, /* (out) Number of bytes actually read. */
+ NULL); /* Overlapping I/O */
+
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): after read. status=%d, count=%d",
+ pipePtr->hPipe, result, count);
+#endif
+ /*
+ * Reset counters to indicate that the buffer has been refreshed.
+ */
+ pipePtr->start = 0;
+ pipePtr->end = count;
+ if (count == 0) {
+ /* We've hit EOF or an error. */
+ pipePtr->lastError = GetLastError();
+ if ((pipePtr->lastError == ERROR_BROKEN_PIPE) ||
+ (pipePtr->lastError == ERROR_HANDLE_EOF)) {
+ pipePtr->flags |= PIPE_EOF;
+ }
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): error is %s",
+ pipePtr->hPipe, Blt_LastError());
+#endif
+ }
+ WakeupNotifier(pipePtr->hWindow);
+ SetEvent(pipePtr->readyEvent);
+ if (count == 0) {
+#if READER_DEBUG
+ PurifyPrintf("READER(%d): exiting\n", pipePtr->hPipe);
+#endif
+ ExitThread(0);
+ }
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PipeWriterThread --
+ *
+ * This function runs in a separate thread and writes data
+ * to the process' standard input pipe.
+ *
+ * Results:
+ * Always returns 0.
+ *
+ * Side effects:
+ * Signals the main thread when an output operation is completed.
+ * May cause the main thread to wake up by posting a message.
+ *
+ *----------------------------------------------------------------------
+ */
+static DWORD WINAPI
+PipeWriterThread(void *clientData)
+{
+ PipeHandler *pipePtr = (PipeHandler *) clientData;
+ DWORD count, bytesLeft;
+ register char *ptr;
+
+ for (;;) {
+ if (pipePtr->flags & PIPE_DELETED) {
+ break;
+ }
+
+ /*
+ * Synchronize with the main thread so that we don't test the
+ * pipe until its done writing.
+ */
+
+ WaitForSingleObject(pipePtr->idleEvent, INFINITE);
+
+ ptr = pipePtr->buffer;
+ bytesLeft = pipePtr->end;
+
+ /* Loop until all of the bytes are written or an error occurs. */
+
+ while (bytesLeft > 0) {
+ if (!WriteFile(pipePtr->hPipe, ptr, bytesLeft, &count, NULL)) {
+ pipePtr->lastError = GetLastError();
+ break;
+ }
+ bytesLeft -= count;
+ ptr += count;
+ }
+
+ /* Tell the main thread that data can be written to the pipe.
+ * Remember to wake up the notifier thread. */
+
+ SetEvent(pipePtr->readyEvent);
+ WakeupNotifier(pipePtr->hWindow);
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TempFileName --
+ *
+ * Gets a temporary file name and deals with the fact that the
+ * temporary file path provided by Windows may not actually exist
+ * if the TMP or TEMP environment variables refer to a
+ * non-existent directory.
+ *
+ * Results:
+ * 0 if error, non-zero otherwise. If non-zero is returned, the
+ * name buffer will be filled with a name that can be used to
+ * construct a temporary file.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TempFileName(char *name) /* (out) Buffer to hold name of
+ * temporary file. */
+{
+ if ((GetTempPath(MAX_PATH, name) > 0) &&
+ (GetTempFileName(name, "TCL", 0, name))) {
+ return 1;
+ }
+ /* Bail out and use the current working directory. */
+ return GetTempFileName(".", "TCL", 0, name);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OpenRedirectFile --
+ *
+ * Open a file for use in a pipeline.
+ *
+ * Results:
+ * Returns a new handle or NULL on failure.
+ *
+ * Side effects:
+ * May cause a file to be created on the file system.
+ *
+ *----------------------------------------------------------------------
+ */
+static HANDLE
+OpenRedirectFile(
+ const char *path,
+ DWORD accessFlags,
+ DWORD createFlags)
+{
+ HANDLE hFile;
+ DWORD attribFlags;
+ int useExisting;
+
+ attribFlags = 0;
+ useExisting = (createFlags & (TRUNCATE_EXISTING | OPEN_EXISTING));
+ if (useExisting) {
+ attribFlags = GetFileAttributes(path);
+ if (attribFlags == 0xFFFFFFFF) {
+ attribFlags = 0;
+ }
+ }
+ hFile = CreateFile(path,
+ accessFlags, /* Access mode flags */
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, /* No security */
+ createFlags, /* Creation attributes */
+ attribFlags, /* File attribute flags */
+ NULL); /* Template file */
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ DWORD lastError;
+
+ lastError = GetLastError();
+ if ((lastError & 0xffffL) == ERROR_OPEN_FAILED) {
+ lastError = (useExisting)
+ ? ERROR_FILE_NOT_FOUND : ERROR_FILE_EXISTS;
+ }
+ TclWinConvertError(lastError);
+ return INVALID_HANDLE_VALUE;
+ }
+ /*
+ * Seek to the end of file if we are writing.
+ */
+ if (createFlags & GENERIC_WRITE) {
+ SetFilePointer(hFile, 0, NULL, FILE_END);
+ }
+ return hFile;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateTempFile --
+ *
+ * This function creates a temporary file initialized with an
+ * optional string, and returns a file handle with the file pointer
+ * at the beginning of the file.
+ *
+ * Results:
+ * A handle to a file.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static HANDLE
+CreateTempFile(const char *data) /* String to write into temp file, or
+ * NULL. */
+{
+ char fileName[MAX_PATH + 1];
+ HANDLE hFile;
+ DWORD lastError;
+
+ if (!TempFileName(fileName)) {
+ return INVALID_HANDLE_VALUE;
+ }
+ hFile = CreateFile(
+ fileName, /* File path */
+ GENERIC_READ | GENERIC_WRITE, /* Access mode */
+ 0, /* No sharing. */
+ NULL, /* Security attributes */
+ CREATE_ALWAYS, /* Overwrite any existing file */
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL); /* No template file */
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+ if (data != NULL) {
+ DWORD result, length;
+ const char *p;
+ const char *string;
+
+ string = data;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '\n') {
+ length = p - string;
+ if (length > 0) {
+ if (!WriteFile(hFile, string, length, &result, NULL)) {
+ goto error;
+ }
+ }
+ if (!WriteFile(hFile, "\r\n", 2, &result, NULL)) {
+ goto error;
+ }
+ string = p + 1;
+ }
+ }
+ length = p - string;
+ if (length > 0) {
+ if (!WriteFile(hFile, string, length, &result, NULL)) {
+ goto error;
+ }
+ }
+ if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == (DWORD) - 1) {
+ goto error;
+ }
+ }
+ return hFile;
+
+ error:
+ lastError = GetLastError();
+ CloseHandle(hFile);
+ DeleteFile(fileName); /* Do I need this? Delete on close? */
+ TclWinConvertError(lastError);
+ return INVALID_HANDLE_VALUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HasConsole --
+ *
+ * Determines whether the current application is attached to a
+ * console.
+ *
+ * Results:
+ * Returns TRUE if this application has a console, else FALSE.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static BOOL
+HasConsole(void)
+{
+ HANDLE hFile;
+
+ hFile = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return FALSE;
+ }
+ CloseHandle(hFile);
+ return TRUE;
+}
+
+static ApplicationType
+GetApplicationType(const char *file, char *cmdPrefix)
+{
+ char *dot;
+ HANDLE hFile;
+ IMAGE_DOS_HEADER imageDosHeader;
+ ULONG signature;
+ BOOL result;
+ DWORD offset;
+ DWORD nBytes;
+ ApplicationType type;
+
+ dot = strrchr(file, '.');
+ if ((dot != NULL) && (strcasecmp(dot, ".bat") == 0)) {
+ return APPL_DOS;
+ }
+ /* Work a little harder. Open the binary and read the header */
+ hFile = CreateFileA(file, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return APPL_NONE;
+ }
+ type = APPL_NONE;
+ result = ReadFile(hFile, &imageDosHeader, sizeof(IMAGE_DOS_HEADER),
+ &nBytes, NULL);
+ if ((!result) || (nBytes != sizeof(IMAGE_DOS_HEADER))) {
+ goto done;
+ }
+#if KILL_DEBUG
+ PurifyPrintf("magic number is %x\n", imageDosHeader.e_magic);
+#endif
+ if (imageDosHeader.e_magic == 0x2123) { /* #! */
+ register char *p;
+ register unsigned int i;
+
+ offset = SetFilePointer(hFile, 2, NULL, FILE_BEGIN);
+ if (offset == (DWORD) - 1) {
+ goto done;
+ }
+ result = ReadFile(hFile, cmdPrefix, MAX_PATH + 1, &nBytes, NULL);
+ if ((!result) || (nBytes < 1)) {
+ goto done;
+ }
+ for (p = cmdPrefix, i = 0; i < nBytes; i++, p++) {
+ if ((*p == '\n') || (*p == '\r')) {
+ break;
+ }
+ }
+ *p = '\0';
+ type = APPL_INTERP;
+ goto done;
+ }
+ /*
+ * Doesn't have the magic number for relocatable executables. If
+ * filename ends with .com, assume it's a DOS application anyhow.
+ * Note that we didn't make this assumption at first, because some
+ * supposed .com files are really 32-bit executables with all the
+ * magic numbers and everything.
+ */
+ if ((dot != NULL) && (strcmp(dot, ".com") == 0)) {
+#if KILL_DEBUG
+ PurifyPrintf(".com\n");
+#endif
+ type = APPL_DOS;
+ goto done;
+ }
+ if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE) {
+#if KILL_DEBUG
+ PurifyPrintf("Application doesn't have correct sig?\n");
+#endif
+ }
+ if (imageDosHeader.e_lfarlc != sizeof(IMAGE_DOS_HEADER)) {
+ /* This assumes that all 3.x and Win32 programs have their
+ * file relocation table immediately following this header. */
+ /*
+ * All Windows 3.X and Win32 and some DOS programs have this value
+ * set here. If it doesn't, assume that since it already had the
+ * other magic number it was a DOS application.
+ */
+#if KILL_DEBUG
+ PurifyPrintf("wrong reloc table address\n");
+#endif
+ type = APPL_DOS;
+ goto done;
+ }
+ offset = SetFilePointer(hFile, imageDosHeader.e_lfanew, NULL, FILE_BEGIN);
+ if (offset == (DWORD) - 1) {
+ goto done;
+ }
+ result = ReadFile(hFile, &signature, sizeof(ULONG), &nBytes, NULL);
+ if ((!result) || (nBytes != sizeof(ULONG))) {
+ goto done;
+ }
+#if KILL_DEBUG
+ PurifyPrintf("signature is %x\n", signature);
+#endif
+ switch (signature) {
+ case IMAGE_NT_SIGNATURE:
+ type = APPL_WIN32;
+ break;
+ case IMAGE_OS2_SIGNATURE:
+ type = APPL_WIN3X;
+ break;
+ case IMAGE_VXD_SIGNATURE:
+ type = APPL_WIN32;
+ break;
+ default:
+ type = APPL_DOS;
+ break;
+ }
+ done:
+ CloseHandle(hFile);
+ return type;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetFullPath --
+ *
+ * Look for the program as an external program. First try the
+ * name as it is, then try adding .com, .exe, and .bat, in that
+ * order, to the name, looking for an executable.
+ *
+ * Using the raw SearchPath() procedure doesn't do quite what is
+ * necessary. If the name of the executable already contains a
+ * '.' character, it will not try appending the specified
+ * extension when searching (in other words, SearchPath will
+ * not find the program "a.b.exe" if the arguments specified
+ * "a.b" and ".exe"). So, first look for the file as it is
+ * named. Then manually append extensions, looking for a
+ * match.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetFullPath(
+ Tcl_Interp *interp, /* Interpreter to report errors to */
+ const char *program, /* Name of program. */
+ char *fullPath, /* (out) Returned full path. */
+ char *cmdPrefix, /* (out) If program is a script, this contains
+ * the name of the interpreter. */
+ ApplicationType * typePtr)
+{ /* (out) Type of program */
+ TCHAR *rest;
+ DWORD attr;
+ int length;
+ char cmd[MAX_PATH + 5];
+ register char **p;
+
+ static char *dosExts[] =
+ {
+ "", ".com", ".exe", ".bat", NULL
+ };
+
+ *typePtr = APPL_NONE;
+
+ length = strlen(program);
+ strcpy(cmd, program);
+ cmdPrefix[0] = '\0';
+ for (p = dosExts; *p != NULL; p++) {
+ cmd[length] = '\0'; /* Reset to original program name. */
+ strcat(cmd, *p); /* Append the DOS extension to the
+ * program name. */
+
+ if (!SearchPath(
+ NULL, /* Use standard Windows search paths */
+ cmd, /* Program name */
+ NULL, /* Extension provided by program name. */
+ MAX_PATH, /* Buffer size */
+ fullPath, /* Buffer for absolute path of program */
+ &rest)) {
+ continue; /* Can't find program with that extension */
+ }
+ /*
+ * Ignore matches on directories or data files.
+ * Return when we identify a known program type.
+ */
+ attr = GetFileAttributesA(fullPath);
+ if ((attr == (DWORD)-1) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ continue;
+ }
+ *typePtr = GetApplicationType(fullPath, cmdPrefix);
+ if (*typePtr != APPL_NONE) {
+ break;
+ }
+ }
+ if (*typePtr == APPL_NONE) {
+ /*
+ * Can't find the program. Check if it's an internal shell command
+ * like "copy" or "dir" and let cmd.exe deal with it.
+ */
+ static char *shellCmds[] =
+ {
+ "copy", "del", "dir", "echo", "edit", "erase", "label",
+ "md", "rd", "ren", "start", "time", "type", "ver", "vol", NULL
+ };
+
+ for (p = shellCmds; *p != NULL; p++) {
+ if (((*p)[0] == program[0]) && (strcmp(*p, program) == 0)) {
+ break;
+ }
+ }
+ if (*p == NULL) {
+ Tcl_AppendResult(interp, "can't execute \"", program,
+ "\": no such file or directory", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *typePtr = APPL_DOS;
+ strcpy(fullPath, program);
+ }
+ if ((*typePtr == APPL_DOS) || (*typePtr == APPL_WIN3X)) {
+
+ /* For 16-bit applications, convert the long executable path
+ * name to a short one. Otherwise the application may not be
+ * able to correctly parse its own command line. */
+
+ GetShortPathName(fullPath, fullPath, MAX_PATH);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConcatCmdArgs --
+ *
+ * Concatonates command line arguments parsed from Tcl into a
+ * single string. If an argument contain spaces, it is grouped
+ * with surrounding double quotes. Must also escape any quotes we
+ * find.
+ *
+ * Results:
+ * Returns a malloc-ed string containing the concatonated command
+ * line.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+ConcatCmdArgs(
+ int argc,
+ char **argv,
+ Tcl_DString *resultPtr)
+{
+ BOOL needQuote;
+ register const char *s;
+ register char *cp;
+ char *string; /* Will contain the new command line */
+ register int count;
+ register int i;
+
+ /*
+ * Pass 1. Compute how much space we need for an array to hold the entire
+ * command line. Then allocate the string.
+ */
+ count = 0;
+ for (i = 0; i < argc; i++) {
+ needQuote = FALSE;
+ if (*argv[i] == '\0') {
+ needQuote = TRUE; /* Zero length args also need quotes. */
+ }
+ for (s = argv[i]; *s != '\0'; s++) {
+ if (*s == '"') {
+ register const char *bp;
+
+ count++; /* +1 Backslash needed to escape quote */
+ for (bp = s - 1; (*bp == '\\') && (bp >= argv[i]); bp--) {
+ count++; /* +? one for each preceding backslash */
+ }
+ } else if (isspace(*s)) {
+ needQuote = TRUE;
+ }
+ count++; /* +1 Normal character */
+ }
+ if (needQuote) {
+ count += 2; /* +2 Pair of quotes */
+ }
+ count++; /* +1 Space separating arguments */
+ }
+
+ string = Blt_Malloc(count + 1);
+ assert(string);
+
+ /*
+ * Pass 2. Copy the arguments, quoting arguments with embedded spaces and
+ * escaping all other quotes in the string.
+ */
+ cp = string;
+ for (i = 0; i < argc; i++) {
+ needQuote = FALSE;
+
+ if (*argv[i] == '\0') {
+ needQuote = TRUE;
+ }
+ for (s = argv[i]; *s != '\0'; s++) {
+ if (isspace(*s)) {
+ needQuote = TRUE;
+ }
+ }
+ if (needQuote) {
+ *cp++ = '"';
+ }
+ for (s = argv[i]; *s; s++) {
+ if (*s == '"') {
+ register const char *bp;
+
+ for (bp = s - 1; (*bp == '\\') && (bp >= argv[i]); bp--) {
+ *cp++ = '\\';
+ }
+ *cp++ = '\\';
+ }
+ *cp++ = *s;
+ }
+ if (needQuote) {
+ *cp++ = '"';
+ }
+ *cp++ = ' ';
+ }
+ *cp = '\0';
+ assert((cp - string) == count);
+ Tcl_DStringAppend(resultPtr, string, count);
+ Blt_Free(string);
+ return Tcl_DStringValue(resultPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StartProcess --
+ *
+ * Create a child process that has the specified files as its
+ * standard input, output, and error.
+ *
+ * The complete Windows search path is searched to find the specified
+ * executable. If an executable by the given name is not found,
+ * automatically tries appending ".com", ".exe", and ".bat" to the
+ * executable name.
+ *
+ * Results:
+ * The return value is TCL_ERROR and an error message is left in
+ * the interp's result if there was a problem creating the child
+ * process. Otherwise, the return value is TCL_OK and *pidPtr is
+ * filled with the process id of the child process.
+ *
+ * Side effects:
+ * A process is created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StartProcess(
+ Tcl_Interp *interp, /* Interpreter to report errors that
+ * occurred when creating the child process.
+ * Error messages from the child process
+ * itself are sent to errorFile. */
+ int argc, /* Number of arguments. */
+ char **argv, /* Command line arguments. */
+ HANDLE hStdin, /* File handle to use as input (stdin) for the
+ * child process. If handle is -1, no
+ * standard input. */
+ HANDLE hStdout, /* File handle to receive output (stdout)
+ * from the child process. If -1, output
+ * is discarded. */
+ HANDLE hStderr, /* File handle to receive errors (stderr)
+ * from the child process. If -1, stderr
+ * will be discarded. Can be the same handle
+ * as hStdOut */
+ HANDLE *hProcessPtr, /* (out) Handle of child process. */
+ DWORD *pidPtr) /* (out) Id of child process. */
+{
+ int result, createFlags;
+ ApplicationType applType;
+ Tcl_DString dString; /* Complete command line */
+ char *command;
+ BOOL hasConsole;
+#ifdef notdef
+ DWORD idleResult;
+#endif
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES securityAttrs;
+ HANDLE hProcess, hPipe;
+ char progPath[MAX_PATH];
+ char cmdPrefix[MAX_PATH];
+
+ *hProcessPtr = INVALID_HANDLE_VALUE;
+ GetFullPath(interp, argv[0], progPath, cmdPrefix, &applType);
+#if KILL_DEBUG
+ PurifyPrintf("Application type is %d\n", (int)applType);
+#endif
+ if (applType == APPL_NONE) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+
+ hProcess = GetCurrentProcess();
+
+ ZeroMemory(&si, sizeof(STARTUPINFOA));
+ si.cb = sizeof(STARTUPINFOA);
+
+ /*
+ * The flag STARTF_USESTDHANDLES must be set to pass handles to
+ * the child process. Using SetStdHandle and/or dup2 works only
+ * when a console mode parent process is spawning an attached
+ * console mode child process.
+ */
+
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = si.hStdOutput = si.hStdError = INVALID_HANDLE_VALUE;
+
+ securityAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ securityAttrs.lpSecurityDescriptor = NULL;
+ securityAttrs.bInheritHandle = TRUE;
+
+ /*
+ * Duplicate all the handles to be passed off as stdin, stdout and
+ * stderr of the child process. The duplicate handles are set to
+ * be inheritable, so the child process can use them.
+ */
+ if (hStdin == INVALID_HANDLE_VALUE) {
+ /*
+ * If handle was not set, stdin should return immediate EOF.
+ * Under Windows95, some applications (both 16 and 32 bit!)
+ * can't read from the NUL device; they read from console
+ * instead. When running tk, this is fatal because the child
+ * process would hang forever waiting for EOF from the unmapped
+ * console window used by the helper application.
+ *
+ * Fortunately, the helper application detects a closed pipe
+ * as an immediate EOF and can pass that information to the
+ * child process.
+ */
+ if (CreatePipe(&si.hStdInput, &hPipe, &securityAttrs, 0)) {
+ CloseHandle(hPipe);
+ }
+ } else {
+ DuplicateHandle(hProcess, hStdin, hProcess, &si.hStdInput, 0, TRUE,
+ DUPLICATE_SAME_ACCESS);
+ }
+
+ if (si.hStdInput == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't duplicate input handle: ",
+ Blt_LastError(), (char *)NULL);
+ goto closeHandles;
+ }
+ if (hStdout == INVALID_HANDLE_VALUE) {
+ /*
+ * If handle was not set, output should be sent to an infinitely
+ * deep sink. Under Windows 95, some 16 bit applications cannot
+ * have stdout redirected to NUL; they send their output to
+ * the console instead. Some applications, like "more" or "dir /p",
+ * when outputting multiple pages to the console, also then try and
+ * read from the console to go the next page. When running tk, this
+ * is fatal because the child process would hang forever waiting
+ * for input from the unmapped console window used by the helper
+ * application.
+ *
+ * Fortunately, the helper application will detect a closed pipe
+ * as a sink.
+ */
+ if ((Blt_GetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
+ && (applType == APPL_DOS)) {
+ if (CreatePipe(&hPipe, &si.hStdOutput, &securityAttrs, 0)) {
+ CloseHandle(hPipe);
+ }
+ } else {
+ si.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0,
+ &securityAttrs, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+ } else {
+ DuplicateHandle(hProcess, hStdout, hProcess, &si.hStdOutput, 0, TRUE,
+ DUPLICATE_SAME_ACCESS);
+ }
+ if (si.hStdOutput == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't duplicate output handle: ",
+ Blt_LastError(), (char *)NULL);
+ goto closeHandles;
+ }
+ if (hStderr == INVALID_HANDLE_VALUE) {
+ /*
+ * If handle was not set, errors should be sent to an infinitely
+ * deep sink.
+ */
+ si.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0,
+ &securityAttrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ } else {
+ DuplicateHandle(hProcess, hStderr, hProcess, &si.hStdError, 0, TRUE,
+ DUPLICATE_SAME_ACCESS);
+ }
+ if (si.hStdError == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't duplicate error handle: ",
+ Blt_LastError(), (char *)NULL);
+ goto closeHandles;
+ }
+ Tcl_DStringInit(&dString);
+ createFlags = 0;
+ hasConsole = HasConsole();
+ if (!hasConsole) {
+ createFlags |= DETACHED_PROCESS;
+ }
+ /*
+ * If we do not have a console window, then we must run DOS and
+ * WIN32 console mode applications as detached processes. This tells
+ * the loader that the child application should not inherit the
+ * console, and that it should not create a new console window for
+ * the child application. The child application should get its stdio
+ * from the redirection handles provided by this application, and run
+ * in the background.
+ *
+ * If we are starting a GUI process, they don't automatically get a
+ * console, so it doesn't matter if they are started as foreground or
+ * detached processes. The GUI window will still pop up to the
+ * foreground.
+ */
+ if (applType == APPL_DOS) {
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ /*
+ * Under NT, 16-bit DOS applications will not run unless they
+ * can be attached to a console. If we are running without a
+ * console, run the 16-bit program as an normal process inside
+ * of a hidden console application, and then run that hidden
+ * console as a detached process.
+ */
+ si.wShowWindow = SW_HIDE;
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ Tcl_DStringAppend(&dString, "cmd.exe /c ", -1);
+ } else {
+ /*
+ * Under Windows 95, 16-bit DOS applications do not work well
+ * with pipes:
+ *
+ * 1. EOF on a pipe between a detached 16-bit DOS application
+ * and another application is not seen at the other
+ * end of the pipe, so the listening process blocks forever on
+ * reads. This inablity to detect EOF happens when either a
+ * 16-bit app or the 32-bit app is the listener.
+ *
+ * 2. If a 16-bit DOS application (detached or not) blocks when
+ * writing to a pipe, it will never wake up again, and it
+ * eventually brings the whole system down around it.
+ *
+ * The 16-bit application is run as a normal process
+ * inside of a hidden helper console app, and this helper
+ * may be run as a detached process. If a stdio handle is
+ * a pipe, the helper application accumulates information
+ * into temp files and forwards it to or from the DOS
+ * application as appropriate. This means that DOS apps
+ * must receive EOF from a stdin pipe before they will
+ * actually begin, and must finish generating stdout or
+ * stderr before the data will be sent to the next stage
+ * of the pipe.
+ *
+ * The helper app should be located in the same directory
+ * as the tcl dll.
+ */
+ if (!hasConsole) {
+ si.wShowWindow = SW_HIDE;
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ }
+ Tcl_DStringAppend(&dString, "tclpip" STRINGIFY(TCL_MAJOR_VERSION)
+ STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1);
+ }
+ } else if (applType == APPL_INTERP) {
+ Tcl_DStringAppend(&dString, cmdPrefix, -1);
+ Tcl_DStringAppend(&dString, " ", -1);
+ }
+ argv[0] = progPath;
+
+ command = ConcatCmdArgs(argc, argv, &dString);
+#if KILL_DEBUG
+ PurifyPrintf("command is %s\n", command);
+#endif
+ result = CreateProcess(
+ NULL, /* Module name. */
+ (TCHAR *)command, /* Command line */
+ NULL, /* Process security */
+ NULL, /* Thread security */
+ TRUE, /* Inherit handles */
+ createFlags, /* Creation flags */
+ NULL, /* Environment */
+ NULL, /* Current working directory */
+ &si, /* Initialization for process: includes
+ * standard handles, appearance and location
+ * of window */
+ &pi); /* (out) Information about newly
+ created process */
+ Tcl_DStringFree(&dString);
+
+ if (!result) {
+ Tcl_AppendResult(interp, "can't execute \"", argv[0], "\": ",
+ Blt_LastError(), (char *)NULL);
+ goto closeHandles;
+ }
+#if KILL_DEBUG
+ PurifyPrintf("Starting process with handle of %d\n", pi.hProcess);
+ PurifyPrintf("Starting process with id of %d\n", pi.dwProcessId);
+#endif
+ if (applType == APPL_DOS) {
+ /* Force the OS to give some time to the DOS process. */
+ WaitForSingleObject(hProcess, 50);
+ }
+#ifdef notdef /* FIXME: I don't think this actually
+ * ever worked. WaitForInputIdle
+ * usually fails with "Access is
+ * denied" (maybe the process handle
+ * isn't valid yet?). When you add a
+ * delay, WaitForInputIdle will time
+ * out instead. */
+ /*
+ * PSS ID Number: Q124121
+ *
+ * "When an application spawns a process repeatedly, a new
+ * thread instance will be created for each process but the
+ * previous instances may not be cleaned up. This results in
+ * a significant virtual memory loss each time the process is
+ * spawned. If there is a WaitForInputIdle() call between
+ * CreateProcess() and CloseHandle(), the problem does not
+ * occur." */
+ idleResult = WaitForInputIdle(pi.hProcess, 1000);
+ if (idleResult == (DWORD) - 1) {
+#if KILL_DEBUG
+ PurifyPrintf("wait failed on %d: %s\n", pi.hProcess, Blt_LastError());
+#endif
+ }
+#endif
+ CloseHandle(pi.hThread);
+
+ *hProcessPtr = pi.hProcess;
+
+ /*
+ * Add the entry to mapping table. Its purpose is to translate
+ * process handles to process ids. Most things we do with the
+ * Win32 API take handles, but we still want to present process
+ * ids to the user. */
+ *pidPtr = pi.dwProcessId;
+ result = TCL_OK;
+
+ closeHandles:
+ if (si.hStdInput != INVALID_HANDLE_VALUE) {
+ CloseHandle(si.hStdInput);
+ }
+ if (si.hStdOutput != INVALID_HANDLE_VALUE) {
+ CloseHandle(si.hStdOutput);
+ }
+ if (si.hStdError != INVALID_HANDLE_VALUE) {
+ CloseHandle(si.hStdError);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileForRedirect --
+ *
+ * This procedure does much of the work of parsing redirection
+ * operators. It handles "@" if specified and allowed, and a file
+ * name, and opens the file if necessary.
+ *
+ * Results:
+ * The return value is the descriptor number for the file. If an
+ * error occurs then NULL is returned and an error message is left
+ * in interp->result. Several arguments are side-effected; see
+ * the argument list below for details.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static HANDLE
+FileForRedirect(
+ Tcl_Interp *interp, /* Intepreter to use for error reporting. */
+ char *spec, /* Points to character just after
+ * redirection character. */
+ BOOL atOK, /* Non-zero means that '@' notation can be
+ * used to specify a channel, zero means that
+ * it isn't. */
+ char *arg, /* Pointer to entire argument containing
+ * spec: used for error reporting. */
+ char *nextArg, /* Next argument in argc/argv array, if needed
+ * for file name or channel name. May be
+ * NULL. */
+ DWORD accessFlags, /* Flags to use for opening file or to
+ * specify mode for channel. */
+ DWORD createFlags, /* Flags to use for opening file or to
+ * specify mode for channel. */
+ int *skipPtr, /* Filled with 1 if redirection target was
+ * in spec, 2 if it was in nextArg. */
+ int *closePtr) /* Filled with one if the caller should
+ * close the file when done with it, zero
+ * otherwise. */
+{
+ int writing = (accessFlags & GENERIC_WRITE);
+ Tcl_Channel chan;
+ HANDLE hFile;
+
+ *skipPtr = 1;
+ *closePtr = FALSE;
+ if ((atOK) && (*spec == '@')) {
+ spec++;
+ if (*spec == '\0') {
+ spec = nextArg;
+ if (spec == NULL) {
+ goto badLastArg;
+ }
+ *skipPtr = 2;
+ }
+ chan = Tcl_GetChannel(interp, spec, NULL);
+ if (chan == NULL) {
+ return INVALID_HANDLE_VALUE;
+ }
+ if (Tcl_GetChannelHandle(chan, (writing) ? TCL_WRITABLE : TCL_READABLE,
+ (ClientData *)&hFile) != TCL_OK) {
+ hFile = INVALID_HANDLE_VALUE;
+ }
+ if (hFile == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
+ "\" wasn't opened for ",
+ ((writing) ? "writing" : "reading"), (char *)NULL);
+ return INVALID_HANDLE_VALUE;
+ }
+ if (writing) {
+ /*
+ * Be sure to flush output to the file, so that anything
+ * written by the child appears after stuff we've already
+ * written.
+ */
+ Tcl_Flush(chan);
+ }
+ } else {
+ char *name;
+ Tcl_DString dString;
+
+ if (*spec == '\0') {
+ spec = nextArg;
+ if (spec == NULL) {
+ goto badLastArg;
+ }
+ *skipPtr = 2;
+ }
+ name = Tcl_TranslateFileName(interp, spec, &dString);
+ if (name != NULL) {
+ hFile = OpenRedirectFile(name, accessFlags, createFlags);
+ } else {
+ hFile = INVALID_HANDLE_VALUE;
+ }
+ Tcl_DStringFree(&dString);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't ", (writing) ? "write" : "read",
+ " file \"", spec, "\": ", Tcl_PosixError(interp),
+ (char *)NULL);
+ return INVALID_HANDLE_VALUE;
+ }
+ *closePtr = TRUE;
+ }
+ return hFile;
+
+ badLastArg:
+ Tcl_AppendResult(interp, "can't specify \"", arg,
+ "\" as last word in command", (char *)NULL);
+ return INVALID_HANDLE_VALUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreatePipeline --
+ *
+ * Given an argc/argv array, instantiate a pipeline of processes
+ * as described by the argv.
+ *
+ * Results:
+ * The return value is a count of the number of new processes
+ * created, or -1 if an error occurred while creating the pipeline.
+ * *pidArrayPtr is filled in with the address of a dynamically
+ * allocated array giving the ids of all of the processes. It
+ * is up to the caller to free this array when it isn't needed
+ * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
+ * with the file id for the input pipe for the pipeline (if any):
+ * the caller must eventually close this file. If outPipePtr
+ * isn't NULL, then *outPipePtr is filled in with the file id
+ * for the output pipe from the pipeline: the caller must close
+ * this file. If errPipePtr isn't NULL, then *errPipePtr is filled
+ * with a file id that may be used to read error output after the
+ * pipeline completes.
+ *
+ * Side effects:
+ * Processes and pipes are created.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreatePipeline(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting. */
+ int argc, /* Number of entries in argv. */
+ char **argv, /* Array of strings describing commands in
+ * pipeline plus I/O redirection with <,
+ * <<, >, etc. Argv[argc] must be NULL. */
+ Process **procArrPtr, /* *procArrPtr gets filled in with
+ * address of array of pids for processes
+ * in pipeline (first pid is first process
+ * in pipeline). */
+ int *inPipePtr, /* If non-NULL, input to the pipeline comes
+ * from a pipe (unless overridden by
+ * redirection in the command). The file
+ * id with which to write to this pipe is
+ * stored at *inPipePtr. NULL means command
+ * specified its own input source. */
+ int *outPipePtr, /* If non-NULL, output to the pipeline goes
+ * to a pipe, unless overriden by redirection
+ * in the command. The file id with which to
+ * read frome this pipe is stored at
+ * *outPipePtr. NULL means command specified
+ * its own output sink. */
+ int *errPipePtr) /* If non-NULL, all stderr output from the
+ * pipeline will go to a temporary file
+ * created here, and a descriptor to read
+ * the file will be left at *errPipePtr.
+ * The file will be removed already, so
+ * closing this descriptor will be the end
+ * of the file. If this is NULL, then
+ * all stderr output goes to our stderr.
+ * If the pipeline specifies redirection
+ * then the file will still be created
+ * but it will never get any data. */
+{
+ Process *procArr = NULL; /* Points to malloc-ed array holding all
+ * the handles of child processes. */
+ int nPids; /* Actual number of processes that exist
+ * at *procArr right now. */
+ int cmdCount; /* Count of number of distinct commands
+ * found in argc/argv. */
+ char *inputLiteral = NULL; /* If non-null, then this points to a
+ * string containing input data (specified
+ * via <<) to be piped to the first process
+ * in the pipeline. */
+ HANDLE hStdin; /* If != -1, gives file to use as input for
+ * first process in pipeline (specified via <
+ * or <@). */
+ BOOL closeStdin; /* If non-zero, then hStdin should be
+ * closed when cleaning up. */
+ HANDLE hStdout; /* Writable file for output from last command
+ * in pipeline (could be file or pipe). NULL
+ * means use stdout. */
+ BOOL closeStdout; /* If non-zero, then hStdout should be
+ * closed when cleaning up. */
+ HANDLE hStderr; /* Writable file for error output from all
+ * commands in pipeline. NULL means use
+ * stderr. */
+ BOOL closeStderr; /* If non-zero, then hStderr should be
+ * closed when cleaning up. */
+
+ HANDLE hInPipe, hOutPipe, hErrPipe;
+ char *p;
+ int skip, lastBar, lastArg, i, j, flags;
+ int pid;
+ BOOL atOK, errorToOutput;
+ Tcl_DString dString;
+ HANDLE hPipe;
+ HANDLE thisInput, thisOutput, thisError;
+
+ if (inPipePtr != NULL) {
+ *inPipePtr = -1;
+ }
+ if (outPipePtr != NULL) {
+ *outPipePtr = -1;
+ }
+ if (errPipePtr != NULL) {
+ *errPipePtr = -1;
+ }
+ Tcl_DStringInit(&dString);
+
+ hStdin = hStdout = hStderr = INVALID_HANDLE_VALUE;
+ hPipe = thisInput = thisOutput = INVALID_HANDLE_VALUE;
+ hInPipe = hOutPipe = hErrPipe = INVALID_HANDLE_VALUE;
+ closeStdin = closeStdout = closeStderr = FALSE;
+ nPids = 0;
+
+ /*
+ * First, scan through all the arguments to figure out the structure
+ * of the pipeline. Process all of the input and output redirection
+ * arguments and remove them from the argument list in the pipeline.
+ * Count the number of distinct processes (it's the number of "|"
+ * arguments plus one) but don't remove the "|" arguments because
+ * they'll be used in the second pass to seperate the individual
+ * child processes. Cannot start the child processes in this pass
+ * because the redirection symbols may appear anywhere in the
+ * command line -- e.g., the '<' that specifies the input to the
+ * entire pipe may appear at the very end of the argument list.
+ */
+
+ lastBar = -1;
+ cmdCount = 1;
+ for (i = 0; i < argc; i++) {
+ skip = 0;
+ p = argv[i];
+ switch (*p++) {
+ case '|':
+ if (*p == '&') {
+ p++;
+ }
+ if (*p == '\0') {
+ if ((i == (lastBar + 1)) || (i == (argc - 1))) {
+ Tcl_AppendResult(interp,
+ "illegal use of | or |& in command",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ lastBar = i;
+ cmdCount++;
+ break;
+
+ case '<':
+ if (closeStdin) {
+ closeStdin = FALSE;
+ CloseHandle(hStdin);
+ }
+ if (*p == '<') {
+ hStdin = INVALID_HANDLE_VALUE;
+ inputLiteral = p + 1;
+ skip = 1;
+ if (*inputLiteral == '\0') {
+ inputLiteral = argv[i + 1];
+ if (inputLiteral == NULL) {
+ Tcl_AppendResult(interp, "can't specify \"", argv[i],
+ "\" as last word in command", (char *)NULL);
+ goto error;
+ }
+ skip = 2;
+ }
+ } else {
+ inputLiteral = NULL;
+ hStdin = FileForRedirect(interp, p, TRUE, argv[i], argv[i + 1],
+ GENERIC_READ, OPEN_EXISTING, &skip, &closeStdin);
+ if (hStdin == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+ }
+ break;
+
+ case '>':
+ atOK = 1;
+ flags = CREATE_ALWAYS;
+ errorToOutput = FALSE;
+ if (*p == '>') {
+ p++;
+ atOK = 0;
+ flags = OPEN_ALWAYS;
+ }
+ if (*p == '&') {
+ if (closeStderr) {
+ closeStderr = FALSE;
+ CloseHandle(hStderr);
+ }
+ errorToOutput = TRUE;
+ p++;
+ }
+ if (closeStdout) {
+ closeStdout = FALSE;
+ CloseHandle(hStdout);
+ }
+ hStdout = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1],
+ GENERIC_WRITE, flags, &skip, &closeStdout);
+ if (hStdout == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+ if (errorToOutput) {
+ closeStderr = FALSE;
+ hStderr = hStdout;
+ }
+ break;
+
+ case '2':
+ if (*p != '>') {
+ break;
+ }
+ p++;
+ atOK = TRUE;
+ flags = CREATE_ALWAYS;
+ if (*p == '>') {
+ p++;
+ atOK = FALSE;
+ flags = OPEN_ALWAYS;
+ }
+ if (closeStderr) {
+ closeStderr = FALSE;
+ CloseHandle(hStderr);
+ }
+ hStderr = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1],
+ GENERIC_WRITE, flags, &skip, &closeStderr);
+ if (hStderr == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+ break;
+ }
+
+ if (skip != 0) {
+ for (j = i + skip; j < argc; j++) {
+ argv[j - skip] = argv[j];
+ }
+ argc -= skip;
+ i -= 1;
+ }
+ }
+
+ if (hStdin == INVALID_HANDLE_VALUE) {
+ if (inputLiteral != NULL) {
+ /*
+ * The input for the first process is immediate data coming from
+ * Tcl. Create a temporary file for it and put the data into the
+ * file.
+ */
+ hStdin = CreateTempFile(inputLiteral);
+ if (hStdin == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp,
+ "can't create input file for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ closeStdin = TRUE;
+ } else if (inPipePtr != NULL) {
+ /*
+ * The input for the first process in the pipeline is to
+ * come from a pipe that can be written from by the caller.
+ */
+
+ if (!CreatePipe(&hStdin, &hInPipe, NULL, 0)) {
+ Tcl_AppendResult(interp,
+ "can't create input pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ closeStdin = TRUE;
+ } else {
+ /*
+ * The input for the first process comes from stdin.
+ */
+ }
+ }
+ if (hStdout == INVALID_HANDLE_VALUE) {
+ if (outPipePtr != NULL) {
+ /*
+ * Output from the last process in the pipeline is to go to a
+ * pipe that can be read by the caller.
+ */
+
+ if (!CreatePipe(&hOutPipe, &hStdout, NULL, 0)) {
+ Tcl_AppendResult(interp,
+ "can't create output pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ closeStdout = TRUE;
+ } else {
+ /*
+ * The output for the last process goes to stdout.
+ */
+ }
+ }
+ if (hStderr == INVALID_HANDLE_VALUE) {
+ if (errPipePtr != NULL) {
+ /*
+ * Stderr from the last process in the pipeline is to go to a
+ * pipe that can be read by the caller.
+ */
+ if (CreatePipe(&hErrPipe, &hStderr, NULL, 0) == 0) {
+ Tcl_AppendResult(interp,
+ "can't create error pipe for command: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ closeStderr = TRUE;
+ } else {
+ /*
+ * Errors from the pipeline go to stderr.
+ */
+ }
+ }
+
+ /*
+ * Scan through the argc array, creating a process for each
+ * group of arguments between the "|" characters.
+ */
+
+ Tcl_ReapDetachedProcs();
+ procArr = Blt_Malloc((unsigned)((cmdCount + 1) * sizeof(Process)));
+ assert(procArr);
+ thisInput = hStdin;
+ if (argc == 0) {
+ Tcl_AppendResult(interp, "invalid null command", (char *)NULL);
+ goto error;
+ }
+ lastArg = 0; /* Suppress compiler warning */
+ for (i = 0; i < argc; i = lastArg + 1) {
+ BOOL joinThisError;
+ HANDLE hProcess;
+
+ /* Convert the program name into native form. */
+ argv[i] = Tcl_TranslateFileName(interp, argv[i], &dString);
+ if (argv[i] == NULL) {
+ goto error;
+ }
+ /* Find the end of the current segment of the pipeline. */
+ joinThisError = FALSE;
+ for (lastArg = i; lastArg < argc; lastArg++) {
+ if (argv[lastArg][0] == '|') {
+ if (argv[lastArg][1] == '\0') {
+ break;
+ }
+ if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
+ joinThisError = TRUE;
+ break;
+ }
+ }
+ }
+ argv[lastArg] = NULL;
+ if ((lastArg - i) == 0) {
+ Tcl_AppendResult(interp, "invalid null command", (char *)NULL);
+ goto error;
+ }
+
+ /*
+ * If this is the last segment, use the specified output handle.
+ * Otherwise create an intermediate pipe. hPipe will become the
+ * input for the next segment of the pipe.
+ */
+ if (lastArg == argc) {
+ thisOutput = hStdout;
+ } else {
+ if (CreatePipe(&hPipe, &thisOutput, NULL, 0) == 0) {
+ Tcl_AppendResult(interp, "can't create pipe: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ goto error;
+ }
+ }
+
+ if (joinThisError) {
+ thisError = thisOutput;
+ } else {
+ thisError = hStderr;
+ }
+
+ if (StartProcess(interp, lastArg - i, argv + i, thisInput, thisOutput,
+ thisError, &hProcess, &pid) != TCL_OK) {
+ goto error;
+ }
+ Tcl_DStringFree(&dString);
+
+ procArr[nPids].hProcess = hProcess;
+ procArr[nPids].pid = pid;
+ nPids++;
+
+ /*
+ * Close off our copies of file descriptors that were set up for
+ * this child, then set up the input for the next child.
+ */
+
+ if ((thisInput != INVALID_HANDLE_VALUE) && (thisInput != hStdin)) {
+ CloseHandle(thisInput);
+ }
+ thisInput = hPipe;
+ hPipe = INVALID_HANDLE_VALUE;
+
+ if ((thisOutput != INVALID_HANDLE_VALUE) && (thisOutput != hStdout)) {
+ CloseHandle(thisOutput);
+ }
+ thisOutput = INVALID_HANDLE_VALUE;
+ }
+
+ *procArrPtr = (Process *)procArr;
+
+ if (inPipePtr != NULL) {
+ *inPipePtr = (int)hInPipe;
+ }
+ if (outPipePtr != NULL) {
+ *outPipePtr = (int)hOutPipe;
+ }
+ if (errPipePtr != NULL) {
+ *errPipePtr = (int)hErrPipe;
+ }
+ /*
+ * All done. Cleanup open files lying around and then return.
+ */
+ cleanup:
+ Tcl_DStringFree(&dString);
+
+ if (closeStdin) {
+ CloseHandle(hStdin);
+ }
+ if (closeStdout) {
+ CloseHandle(hStdout);
+ }
+ if (closeStderr) {
+ CloseHandle(hStderr);
+ }
+ return nPids;
+
+ /*
+ * An error occurred. There could have been extra files open, such
+ * as pipes between children. Clean them all up. Detach any child
+ * processes that have been created.
+ */
+ error:
+ if (hPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(hPipe);
+ }
+ if ((thisOutput != INVALID_HANDLE_VALUE) && (thisOutput != hStdout)) {
+ CloseHandle(thisOutput);
+ }
+ if ((thisInput != INVALID_HANDLE_VALUE) && (thisInput != hStdin)) {
+ CloseHandle(thisInput);
+ }
+ if (hInPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(hInPipe);
+ }
+ if (hOutPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(hOutPipe);
+ }
+ if (hErrPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(hErrPipe);
+ }
+ if (procArr != NULL) {
+ for (i = 0; i < nPids; i++) {
+ if (procArr[i].hProcess != INVALID_HANDLE_VALUE) {
+ /* It's Ok to use Tcl_DetachPids, since for WIN32 it's really
+ * using process handles, not process ids. */
+ Tcl_DetachPids(1, (Tcl_Pid *)&(procArr[i].pid));
+ }
+ }
+ Blt_Free(procArr);
+ }
+ nPids = -1;
+ goto cleanup;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateFileHandler --
+ *
+ * Limited emulation Tcl_CreateFileHandler for Win32. Works
+ * with pipes. Don't know if anything else will (such as sockets).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Registers procedure and data to call back when data
+ * is available on the pipe.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_CreateFileHandler(
+ int fd, /* Descriptor or handle of file */
+ int flags, /* TCL_READABLE or TCL_WRITABLE */
+ Tcl_FileProc *proc,
+ ClientData clientData)
+{
+ PipeHandler *pipePtr;
+
+ if (!initialized) {
+ PipeInit();
+ }
+ if ((flags != TCL_READABLE) && (flags != TCL_WRITABLE)) {
+ return; /* Only one of the flags can be set. */
+ }
+ pipePtr = CreatePipeHandler((HANDLE) fd, flags);
+ pipePtr->proc = proc;
+ pipePtr->clientData = clientData;
+
+ /* Add the handler to the list of managed pipes. */
+ EnterCriticalSection(&pipeCriticalSection);
+ Blt_ChainAppend(&pipeChain, pipePtr);
+ LeaveCriticalSection(&pipeCriticalSection);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DeleteFileHandler --
+ *
+ * Win32 emulation Tcl_DeleteFileHandler. Cleans up resources
+ * used.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DeleteFileHandler(int fd) /* Descriptor or handle of file */
+{
+ PipeHandler *pipePtr;
+ Blt_ChainLink *linkPtr;
+ HANDLE hPipe;
+
+ if (!initialized) {
+ PipeInit();
+ }
+#if KILL_DEBUG
+ PurifyPrintf("Blt_DeleteFileHandler(%d)", fd);
+#endif
+ hPipe = (HANDLE) fd;
+ EnterCriticalSection(&pipeCriticalSection);
+
+ for (linkPtr = Blt_ChainFirstLink(&pipeChain); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ pipePtr = Blt_ChainGetValue(linkPtr);
+ if ((pipePtr->hPipe == hPipe) && !(pipePtr->flags & PIPE_DELETED)) {
+ Blt_ChainDeleteLink(&pipeChain, linkPtr);
+ DeletePipeHandler(pipePtr);
+ break;
+ }
+ }
+ LeaveCriticalSection(&pipeCriticalSection);
+#if KILL_DEBUG
+ PurifyPrintf("Blt_DeleteFileHandler: done\n");
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_AsyncRead --
+ *
+ * Reads input from the pipe into the given buffer.
+ *
+ * Results:
+ * Returns the number of bytes read.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_AsyncRead(
+ int f,
+ char *buffer,
+ unsigned int size)
+{
+ register PipeHandler *pipePtr;
+ unsigned int count;
+ int nBytes;
+
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead(f=%d)\n", f);
+#endif
+ pipePtr = GetPipeHandler((HANDLE) f);
+ if ((pipePtr == NULL) || (pipePtr->flags & PIPE_DELETED)) {
+ errno = EBADF;
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: bad file\n");
+#endif
+ return -1;
+ }
+ if (!PeekOnPipe(pipePtr, &nBytes)) {
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: pipe is drained (nBytes=%d).\n", nBytes);
+#endif
+ return -1; /* No data available. */
+ }
+ /*
+ * nBytes is 0 EOF found.
+ * -1 Error occured.
+ * 1+ Number of bytes available.
+ */
+ if (nBytes == -1) {
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: Error\n");
+#endif
+ return -1;
+ }
+ if (nBytes == 0) {
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: EOF\n");
+#endif
+ return 0;
+ }
+ count = pipePtr->end - pipePtr->start;
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: nBytes is %d, %d\n", nBytes, count);
+#endif
+ assert(count == (unsigned int)nBytes);
+ if (size > count) {
+ size = count; /* Reset request to what's available. */
+ }
+ memcpy(buffer, pipePtr->buffer + pipePtr->start, size);
+ pipePtr->start += size;
+ if (pipePtr->start == pipePtr->end) {
+#if ASYNC_DEBUG
+ PurifyPrintf("Blt_AsyncRead: signaling idle\n");
+#endif
+ ResetEvent(pipePtr->readyEvent);
+ SetEvent(pipePtr->idleEvent);
+ }
+ return size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_AsyncWrite --
+ *
+ * Writes output to the pipe from the given buffer.
+ *
+ * Results:
+ * Returns the number of bytes written.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_AsyncWrite(
+ int f,
+ char *buffer,
+ unsigned int size)
+{
+ register PipeHandler *pipePtr;
+
+ pipePtr = GetPipeHandler((HANDLE) f);
+ if ((pipePtr == NULL) || (pipePtr->flags & PIPE_DELETED)) {
+ errno = EBADF;
+ return -1;
+ }
+ if (WaitForSingleObject(pipePtr->readyEvent, 0) == WAIT_TIMEOUT) {
+ /*
+ * Writer thread is currently blocked waiting for a write to
+ * complete.
+ */
+ errno = EAGAIN;
+ return -1;
+ }
+ /* Check for a background error on the last write. */
+ if (pipePtr->lastError) {
+ TclWinConvertError(pipePtr->lastError);
+ pipePtr->lastError = 0;
+ return -1;
+ }
+ /* Reallocate the buffer to be large enough to hold the data. */
+ if (size > pipePtr->size) {
+ char *ptr;
+
+ ptr = Blt_Malloc(size);
+ assert(ptr);
+ Blt_Free(pipePtr->buffer);
+ pipePtr->buffer = ptr;
+ }
+ memcpy(pipePtr->buffer, buffer, size);
+ pipePtr->end = pipePtr->size = size;
+ ResetEvent(pipePtr->readyEvent);
+ SetEvent(pipePtr->idleEvent);
+ return size;
+}
diff --git a/blt/src/bltWinPrnt.c b/blt/src/bltWinPrnt.c
new file mode 100644
index 00000000000..09ea5a777a5
--- /dev/null
+++ b/blt/src/bltWinPrnt.c
@@ -0,0 +1,1548 @@
+/*
+ * bltWinPrnt.c --
+ *
+ * This module implements Win32 printer access.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ */
+
+#include <bltInt.h>
+#include <bltHash.h>
+#ifndef NO_PRINTER
+#include <X11/Xutil.h>
+#undef Status
+#ifdef _MSC_VER
+#include <winspool.h>
+#endif
+
+/*
+ set pid [printer open name]
+ printer close $pid
+ printer write $pid $data
+ printer snap $pid .window
+ printer names
+ printer enum things
+ printer getattr $pid
+ printer setattr $pid
+
+ set pid [open ]
+ blt::printer open {\\alprint\2a211} p1
+ p1 getattr varName
+ p1 setattr varName
+ p1 write $data
+ .graph print p1
+ p1 snap .window
+ p1 close
+ blt::printer names
+ blt::printer emum things
+*/
+
+#define PRINTER_THREAD_KEY "BLT Printer Data"
+
+typedef struct {
+ Blt_HashTable idTable; /* Hash table of printer structures keyed by
+ * the name of the printer. */
+ int nextId;
+} PrinterInterpData;
+
+typedef struct {
+ int type;
+ HDC hDC;
+} PrintDrawable;
+
+typedef struct {
+ Tcl_Interp *interp;
+ Tcl_Command cmdToken; /* Token for vector's Tcl command. */
+ char *name;
+ char *fileName;
+ PrintDrawable drawable;
+ HANDLE hPrinter;
+ Blt_HashEntry *hashPtr;
+ Blt_HashTable *tablePtr;
+ char *driverName;
+ char *deviceName;
+ char *printerName;
+ char *docName;
+ char *portName;
+ DEVMODE *dmPtr;
+ int dmSize;
+} PrinterHandler;
+
+typedef struct {
+ DWORD token;
+ char *string;
+} TokenString;
+
+static TokenString sizeTable[] =
+{
+ /* Letter 8 1/2 x 11 in */
+ DMPAPER_LETTER, "Letter",
+ /* Letter Small 8 1/2 x 11 in */
+ DMPAPER_LETTERSMALL, "Letter Small",
+ /* Tabloid 11 x 17 in */
+ DMPAPER_TABLOID, "Tabloid",
+ /* Ledger 17 x 11 in */
+ DMPAPER_LEDGER, "Ledger",
+ /* Legal 8 1/2 x 14 in */
+ DMPAPER_LEGAL, "Legal",
+ /* Statement 5 1/2 x 8 1/2 in */
+ DMPAPER_STATEMENT, "Statement",
+ /* Executive 7 1/4 x 10 1/2 in */
+ DMPAPER_EXECUTIVE, "Executive",
+ /* A3 297 x 420 mm */
+ DMPAPER_A3, "A3",
+ /* A4 210 x 297 mm */
+ DMPAPER_A4, "A4",
+ /* A4 Small 210 x 297 mm */
+ DMPAPER_A4SMALL, "A4 Small",
+ /* A5 148 x 210 mm */
+ DMPAPER_A5, "A5",
+ /* B4 (JIS) 250 x 354 */
+ DMPAPER_B4, "B4 (JIS)",
+ /* B5 (JIS) 182 x 257 mm */
+ DMPAPER_B5, "B5 (JIS)",
+ /* Folio 8 1/2 x 13 in */
+ DMPAPER_FOLIO, "Folio",
+ /* Quarto 215 x 275 mm */
+ DMPAPER_QUARTO, "Quarto",
+ /* 10x14 in */
+ DMPAPER_10X14, "10x14",
+ /* 11x17 in */
+ DMPAPER_11X17, "11x17",
+ /* Note 8 1/2 x 11 in */
+ DMPAPER_NOTE, "Note",
+ /* Envelope #9 3 7/8 x 8 7/8 */
+ DMPAPER_ENV_9, "Envelope #9",
+ /* Envelope #10 4 1/8 x 9 1/2 */
+ DMPAPER_ENV_10, "Envelope #10",
+ /* Envelope #11 4 1/2 x 10 3/8 */
+ DMPAPER_ENV_11, "Envelope #11",
+ /* Envelope #12 4 \276 x 11 */
+ DMPAPER_ENV_12, "Envelope #12",
+ /* Envelope #14 5 x 11 1/2 */
+ DMPAPER_ENV_14, "Envelope #14",
+ /* C size sheet */
+ DMPAPER_CSHEET, "C size sheet",
+ /* D size sheet */
+ DMPAPER_DSHEET, "D size sheet",
+ /* E size sheet */
+ DMPAPER_ESHEET, "E size sheet",
+ /* Envelope DL 110 x 220mm */
+ DMPAPER_ENV_DL, "Envelope DL",
+ /* Envelope C5 162 x 229 mm */
+ DMPAPER_ENV_C5, "Envelope C5",
+ /* Envelope C3 324 x 458 mm */
+ DMPAPER_ENV_C3, "Envelope C3",
+ /* Envelope C4 229 x 324 mm */
+ DMPAPER_ENV_C4, "Envelope C4",
+ /* Envelope C6 114 x 162 mm */
+ DMPAPER_ENV_C6, "Envelope C6",
+ /* Envelope C65 114 x 229 mm */
+ DMPAPER_ENV_C65, "Envelope C65",
+ /* Envelope B4 250 x 353 mm */
+ DMPAPER_ENV_B4, "Envelope B4",
+ /* Envelope B5 176 x 250 mm */
+ DMPAPER_ENV_B5, "Envelope B5",
+ /* Envelope B6 176 x 125 mm */
+ DMPAPER_ENV_B6, "Envelope B6",
+ /* Envelope 110 x 230 mm */
+ DMPAPER_ENV_ITALY, "Envelope Italy",
+ /* Env Monarch 3 7/8 x 7 1/2 in */
+ DMPAPER_ENV_MONARCH, "Envelope Monarch",
+ /* 6 3/4 Envelope 3 5/8 x 6 1/2 in */
+ DMPAPER_ENV_PERSONAL, "6 3/4 Envelope",
+ /* US Std Fanfold 14 7/8 x 11 in */
+ DMPAPER_FANFOLD_US, "US Std Fanfold",
+ /* German Std Fanfold 8 1/2 x 12 in */
+ DMPAPER_FANFOLD_STD_GERMAN, "German Std Fanfold",
+ /* German Legal Fanfold 8 1/2 x 13 in */
+ DMPAPER_FANFOLD_LGL_GERMAN, "German Legal Fanfold",
+ /* B4 (ISO) 250 x 353 mm */
+ DMPAPER_ISO_B4, "ISOB4",
+ /* Japanese Postcard 100 x 148 mm */
+ DMPAPER_JAPANESE_POSTCARD, "Postcard (JIS)",
+ /* 9 x 11 in */
+ DMPAPER_9X11, "9x11",
+ /* 10 x 11 in */
+ DMPAPER_10X11, "10x11",
+ /* 15 x 11 in */
+ DMPAPER_15X11, "15x11",
+ /* Envelope Invite 220 x 220 mm */
+ DMPAPER_ENV_INVITE, "Envelope Invite",
+ /* Letter Extra 9 \275 x 12 in */
+ DMPAPER_LETTER_EXTRA, "Letter Extra",
+ /* Legal Extra 9 \275 x 15 in */
+ DMPAPER_LEGAL_EXTRA, "Legal Extra",
+ /* Tabloid Extra 11.69 x 18 in */
+ DMPAPER_TABLOID_EXTRA, "Tabloid Extra",
+ /* A4 Extra 9.27 x 12.69 in */
+ DMPAPER_A4_EXTRA, "A4 Extra",
+ /* Letter Transverse 8 \275 x 11 in */
+ DMPAPER_LETTER_TRANSVERSE, "Letter Transverse",
+ /* A4 Transverse 210 x 297 mm */
+ DMPAPER_A4_TRANSVERSE, "A4 Transverse",
+ /* Letter Extra Transverse 9\275 x 12 in */
+ DMPAPER_LETTER_EXTRA_TRANSVERSE, "Letter Extra Transverse",
+ /* SuperA/SuperA/A4 227 x 356 mm */
+ DMPAPER_A_PLUS, "Super A Plus",
+ /* SuperB/SuperB/A3 305 x 487 mm */
+ DMPAPER_B_PLUS, "Super B Plus",
+ /* Letter Plus 8.5 x 12.69 in */
+ DMPAPER_LETTER_PLUS, "Letter Plus",
+ /* A4 Plus 210 x 330 mm */
+ DMPAPER_A4_PLUS, "A4 Plus",
+ /* A5 Transverse 148 x 210 mm */
+ DMPAPER_A5_TRANSVERSE, "A5 Transverse",
+ /* B5 (JIS) Transverse 182 x 257 mm */
+ DMPAPER_B5_TRANSVERSE, "B5 Transverse",
+ /* A3 Extra 322 x 445 mm */
+ DMPAPER_A3_EXTRA, "A3 Extra",
+ /* A5 Extra 174 x 235 mm */
+ DMPAPER_A5_EXTRA, "A5 Extra",
+ /* B5 (ISO) Extra 201 x 276 mm */
+ DMPAPER_B5_EXTRA, "B5 Extra",
+ /* A2 420 x 594 mm */
+ DMPAPER_A2, "A2",
+ /* A3 Transverse 297 x 420 mm */
+ DMPAPER_A3_TRANSVERSE, "A3 Transverse",
+ /* A3 Extra Transverse 322 x 445 mm */
+ DMPAPER_A3_EXTRA_TRANSVERSE, "A3 Extra Transverse",
+ 0, NULL
+};
+
+static TokenString statusTable[] =
+{
+ PRINTER_STATUS_BUSY, "Busy",
+ PRINTER_STATUS_DOOR_OPEN, "Door Open",
+ PRINTER_STATUS_ERROR, "Error",
+ PRINTER_STATUS_INITIALIZING, "Initializing",
+ PRINTER_STATUS_IO_ACTIVE, "IO Active",
+ PRINTER_STATUS_MANUAL_FEED, "Manual Feed",
+ PRINTER_STATUS_NOT_AVAILABLE, "Not Available",
+ PRINTER_STATUS_NO_TONER, "No Toner",
+ PRINTER_STATUS_OFFLINE, "Offline",
+ PRINTER_STATUS_OUTPUT_BIN_FULL, "Bin Full",
+ PRINTER_STATUS_OUT_OF_MEMORY, "Out Of Memory",
+ PRINTER_STATUS_PAGE_PUNT, "Page Punt",
+ PRINTER_STATUS_PAPER_JAM, "Paper Jam",
+ PRINTER_STATUS_PAPER_OUT, "Paper Out",
+ PRINTER_STATUS_PAPER_PROBLEM, "Paper Problem",
+ PRINTER_STATUS_PAUSED, "Paused",
+ PRINTER_STATUS_PENDING_DELETION, "Pending Deletion",
+ PRINTER_STATUS_POWER_SAVE, "Power Save",
+ PRINTER_STATUS_PRINTING, "Printing",
+ PRINTER_STATUS_PROCESSING, "Processing",
+ PRINTER_STATUS_SERVER_UNKNOWN, "Server Unknown",
+ PRINTER_STATUS_TONER_LOW, "Toner Low",
+ PRINTER_STATUS_USER_INTERVENTION, "User Intervention",
+ PRINTER_STATUS_WAITING, "Waiting",
+ PRINTER_STATUS_WARMING_UP, "Warming Up",
+ 0, NULL
+};
+
+static TokenString attributeTable[] =
+{
+ PRINTER_ATTRIBUTE_DEFAULT, "Default",
+ PRINTER_ATTRIBUTE_DIRECT, "Direct",
+ PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST, "Do Complete First",
+ PRINTER_ATTRIBUTE_ENABLE_BIDI, "Enable BIDI",
+ PRINTER_ATTRIBUTE_ENABLE_DEVQ, "Enable Devq",
+ PRINTER_ATTRIBUTE_HIDDEN, "Hidden",
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS, "Keep Printed Jobs",
+ PRINTER_ATTRIBUTE_LOCAL, "Local",
+ PRINTER_ATTRIBUTE_NETWORK, "Network",
+ PRINTER_ATTRIBUTE_QUEUED, "Queued",
+ PRINTER_ATTRIBUTE_RAW_ONLY, "Raw Only",
+ PRINTER_ATTRIBUTE_SHARED, "Shared",
+ PRINTER_ATTRIBUTE_WORK_OFFLINE, "Offline",
+ 0, NULL
+};
+
+static TokenString binTable[] =
+{
+ DMBIN_UPPER, "Upper",
+ DMBIN_LOWER, "Lower",
+ DMBIN_MIDDLE, "Middle",
+ DMBIN_MANUAL, "Manual",
+ DMBIN_ENVELOPE, "Envelope",
+ DMBIN_ENVMANUAL, "Envelope Manual",
+ DMBIN_AUTO, "Automatic",
+ DMBIN_TRACTOR, "Tractor",
+ DMBIN_SMALLFMT, "Small Format",
+ DMBIN_LARGEFMT, "Large Format",
+ DMBIN_LARGECAPACITY, "Large Capacity",
+ DMBIN_CASSETTE, "Cassette",
+ DMBIN_FORMSOURCE, "Form Source",
+ 0, NULL
+};
+
+static TokenString orientationTable[] =
+{
+ DMORIENT_PORTRAIT, "Portrait",
+ DMORIENT_LANDSCAPE, "Landscape",
+ 0, NULL
+};
+
+static TokenString qualityTable[] =
+{
+ DMRES_HIGH, "High",
+ DMRES_MEDIUM, "Medium",
+ DMRES_LOW, "Low",
+ DMRES_DRAFT, "Draft",
+ 0, NULL
+};
+
+static TokenString colorTable[] =
+{
+ DMCOLOR_COLOR, "Color",
+ DMCOLOR_MONOCHROME, "Monochrome",
+ 0, NULL
+};
+
+static TokenString duplexTable[] =
+{
+ DMDUP_SIMPLEX, "Simplex",
+ DMDUP_HORIZONTAL, "Horizontal",
+ DMDUP_VERTICAL, "Vertical",
+ 0, NULL
+};
+
+static TokenString ttOptionTable[] =
+{
+ DMTT_BITMAP, "Bitmap",
+ DMTT_DOWNLOAD, "Download",
+ DMTT_SUBDEV, "Substitute Device",
+ DMTT_DOWNLOAD_OUTLINE, "Download Outline",
+ 0, NULL
+};
+
+#ifdef __STDC__
+static Tcl_CmdProc PrinterCmd;
+static Tcl_InterpDeleteProc PrinterInterpDeleteProc;
+#endif /* __STDC__ */
+
+void
+Blt_GetPrinterScale(HDC printerDC, double *xRatioPtr, double *yRatioPtr)
+{
+ double xScreen, yScreen;
+ double xPrinter, yPrinter;
+ HDC screenDC;
+
+ xPrinter = (double)GetDeviceCaps(printerDC, LOGPIXELSX);
+ yPrinter = (double)GetDeviceCaps(printerDC, LOGPIXELSY);
+ screenDC = GetDC(NULL);
+ xScreen = (double)GetDeviceCaps(screenDC, LOGPIXELSX);
+ yScreen = (double)GetDeviceCaps(screenDC, LOGPIXELSY);
+ ReleaseDC(NULL, screenDC);
+ *xRatioPtr = (xPrinter / xScreen);
+ *yRatioPtr = (yPrinter / yScreen);
+}
+
+static int
+GetPrinterHandler(
+ PrinterInterpData *dataPtr,
+ Tcl_Interp *interp,
+ const char *id,
+ PrinterHandler **handlerPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(dataPtr->idTable), id);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find printer id \"", id, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *handlerPtrPtr = (PrinterHandler *) Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+static HANDLE
+OpenPrinter2(
+ Tcl_Interp *interp,
+ char *printerName)
+{
+ PRINTER_DEFAULTS pd;
+ HANDLE hPrinter;
+
+ ZeroMemory(&pd, sizeof(pd));
+ pd.DesiredAccess = PRINTER_ALL_ACCESS;
+ if (!OpenPrinter(printerName, &hPrinter, &pd)) {
+ Tcl_AppendResult(interp, "can't open printer \"", printerName,
+ "\": ", Blt_LastError(), (char *)NULL);
+ return NULL;
+ }
+ return hPrinter;
+}
+
+static HGLOBAL
+GetPrinterProperties(
+ PrinterHandler *handlerPtr,
+ HANDLE hPrinter,
+ DEVMODE **dmPtrPtr)
+{
+ HWND hWnd;
+ unsigned int dmSize;
+ HGLOBAL hMem;
+ DEVMODE *dmPtr;
+
+ hWnd = GetDesktopWindow();
+ dmSize = DocumentProperties(hWnd, hPrinter, handlerPtr->printerName,
+ NULL, NULL, 0);
+ if (dmSize == 0) {
+ Tcl_AppendResult(handlerPtr->interp,
+ "can't get document properties for \"",
+ handlerPtr->printerName,
+ "\": ", Blt_LastError(), (char *)NULL);
+ return NULL;
+ }
+ hMem = GlobalAlloc(GHND, dmSize);
+ dmPtr = (DEVMODE *)GlobalLock(hMem);
+ if (!DocumentProperties(hWnd, hPrinter, handlerPtr->printerName, dmPtr,
+ NULL, DM_OUT_BUFFER)) {
+ Tcl_AppendResult(handlerPtr->interp,
+ "can't allocate document properties for \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(),
+ (char *)NULL);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ return NULL;
+ }
+ *dmPtrPtr = dmPtr;
+ handlerPtr->dmSize = dmSize;
+ return hMem;
+}
+
+static int
+SetPrinterProperties(
+ Tcl_Interp *interp,
+ PrinterHandler *handlerPtr,
+ HANDLE hPrinter,
+ DEVMODE *dmPtr)
+{
+ HWND hWnd;
+ int result;
+
+ hWnd = GetDesktopWindow();
+ result = DocumentProperties(hWnd, hPrinter, handlerPtr->printerName, dmPtr,
+ dmPtr, DM_IN_BUFFER | DM_OUT_BUFFER);
+ if (result == 0) {
+ Tcl_AppendResult(interp, "can't set document properties for \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (handlerPtr->dmPtr != NULL) {
+ Blt_Free(handlerPtr->dmPtr);
+ }
+ handlerPtr->dmPtr = Blt_Malloc(handlerPtr->dmSize);
+ *handlerPtr->dmPtr = *dmPtr;
+ return TCL_OK;
+}
+
+static void
+DestroyPrinter(PrinterHandler *handlerPtr)
+{
+ if (handlerPtr->drawable.hDC != NULL) {
+ DeleteDC(handlerPtr->drawable.hDC);
+ }
+ if (handlerPtr->name != NULL) {
+ Blt_Free(handlerPtr->name);
+ }
+ if (handlerPtr->printerName != NULL) {
+ Blt_Free(handlerPtr->printerName);
+ }
+ if (handlerPtr->deviceName != NULL) {
+ Blt_Free(handlerPtr->deviceName);
+ }
+ if (handlerPtr->portName != NULL) {
+ Blt_Free(handlerPtr->portName);
+ }
+ if (handlerPtr->driverName != NULL) {
+ Blt_Free(handlerPtr->driverName);
+ }
+ if (handlerPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(handlerPtr->tablePtr, handlerPtr->hashPtr);
+ }
+ if (handlerPtr->dmPtr != NULL) {
+ Blt_Free(handlerPtr->dmPtr);
+ }
+ Blt_Free(handlerPtr);
+}
+
+static char *
+AttributesToString(DWORD attributes, Tcl_DString * resultPtr)
+{
+ register TokenString *p;
+
+ Tcl_DStringInit(resultPtr);
+ for (p = attributeTable; p->string != NULL; p++) {
+ if (attributes & p->token) {
+ Tcl_DStringAppendElement(resultPtr, p->string);
+ }
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+static char *
+StatusToString(DWORD status, Tcl_DString * resultPtr)
+{
+ register TokenString *p;
+
+ Tcl_DStringInit(resultPtr);
+ for (p = statusTable; p->string != NULL; p++) {
+ if (status & p->token) {
+ Tcl_DStringAppendElement(resultPtr, p->string);
+ }
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+static char *
+TokenToString(TokenString *table, DWORD token)
+{
+ register TokenString *p;
+
+ for (p = table; p->string != NULL; p++) {
+ if (token == p->token) {
+ return p->string;
+ }
+ }
+ return "???";
+}
+
+static DWORD
+StringToToken(TokenString * table, char *string)
+{
+ register TokenString *p;
+ char c;
+
+ c = toupper(string[0]);
+ for (p = table; p->string != NULL; p++) {
+ if ((c == toupper(p->string[0])) &&
+ (strcasecmp(string, p->string) == 0)) {
+ return p->token;
+ }
+ }
+ return 0;
+}
+
+static void
+GetFormInfo(
+ Tcl_Interp *interp,
+ FORM_INFO_1 * infoArr,
+ int nForms,
+ char *varName)
+{
+ Tcl_DString dString;
+ register int i;
+
+ Tcl_DStringInit(&dString);
+ for (i = 0; i < nForms; i++) {
+ Tcl_DStringAppendElement(&dString, infoArr[i].pName);
+ }
+ Tcl_SetVar2(interp, varName, "EnumForms", Tcl_DStringValue(&dString),
+ TCL_LEAVE_ERR_MSG);
+ Tcl_DStringFree(&dString);
+}
+
+
+static int
+GetPrinterAttributes(
+ Tcl_Interp *interp, /* Interpreter context. */
+ PrinterHandler *handlerPtr,
+ char *varName) /* Name of array variable to contain
+ * printer device information. */
+{
+ char *string;
+ Tcl_DString dString;
+ DEVMODE *dmPtr;
+ unsigned int bytesNeeded;
+ HGLOBAL hMem1, hMem2;
+ PRINTER_INFO_2* pi2Ptr;
+ LPVOID buffer;
+ HANDLE hPrinter;
+ int result = TCL_ERROR;
+
+ hPrinter = OpenPrinter2(interp, handlerPtr->name);
+ if (hPrinter == NULL) {
+ return TCL_ERROR;
+ }
+
+ Tcl_DStringInit(&dString);
+ hMem1 = hMem2 = NULL;
+
+ GetPrinter(hPrinter, 2, NULL, 0, &bytesNeeded);
+
+ /* Windows 95/98 seems to only want locked memory. Allocating
+ * unlocked memory will sometimes crash the printer driver and
+ * therefore Windows itself. */
+
+ hMem1 = GlobalAlloc(GHND, bytesNeeded);
+ if (hMem1 == NULL) {
+ Tcl_AppendResult(interp, "can't allocate memory for printer \"",
+ handlerPtr->name, "\": ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ buffer = (LPVOID)GlobalLock(hMem1);
+ if (!GetPrinter(hPrinter, 2, buffer, bytesNeeded, &bytesNeeded)) {
+ Tcl_AppendResult(interp, "can't get printer \"", handlerPtr->name, "\": ",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ hMem2 = GetPrinterProperties(handlerPtr, hPrinter, &dmPtr);
+ if (hMem2 == NULL) {
+ Tcl_AppendResult(interp, "can't allocate memory for printer \"",
+ handlerPtr->name, "\" properties: ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ pi2Ptr = (PRINTER_INFO_2 *)buffer;
+ Tcl_SetVar2(interp, varName, "ServerName", pi2Ptr->pServerName, 0);
+ Tcl_SetVar2(interp, varName, "PrinterName", pi2Ptr->pPrinterName, 0);
+ Tcl_SetVar2(interp, varName, "PortName", pi2Ptr->pPortName, 0);
+ Tcl_SetVar2(interp, varName, "DriverName", pi2Ptr->pDriverName, 0);
+ Tcl_SetVar2(interp, varName, "Comment", pi2Ptr->pComment, 0);
+ Tcl_SetVar2(interp, varName, "Location", pi2Ptr->pLocation, 0);
+ Tcl_SetVar2(interp, varName, "SepFile", pi2Ptr->pSepFile, 0);
+ Tcl_SetVar2(interp, varName, "PrintProcessor", pi2Ptr->pPrintProcessor, 0);
+ Tcl_SetVar2(interp, varName, "Datatype", pi2Ptr->pDatatype, 0);
+ Tcl_SetVar2(interp, varName, "Parameters", pi2Ptr->pParameters, 0);
+ Tcl_SetVar2(interp, varName, "Attributes",
+ AttributesToString(pi2Ptr->Attributes, &dString), 0);
+ Tcl_SetVar2(interp, varName, "Priority", Blt_Itoa(pi2Ptr->Priority), 0);
+ Tcl_SetVar2(interp, varName, "DefaultPriority",
+ Blt_Itoa(pi2Ptr->DefaultPriority), 0);
+ Tcl_SetVar2(interp, varName, "StartTime", Blt_Itoa(pi2Ptr->StartTime), 0);
+ Tcl_SetVar2(interp, varName, "UntilTime", Blt_Itoa(pi2Ptr->UntilTime), 0);
+ Tcl_SetVar2(interp, varName, "Status",
+ StatusToString(pi2Ptr->Status, &dString), 0);
+ Tcl_SetVar2(interp, varName, "Jobs", Blt_Itoa(pi2Ptr->cJobs), 0);
+ Tcl_SetVar2(interp, varName, "AveragePPM", Blt_Itoa(pi2Ptr->AveragePPM), 0);
+
+ if (dmPtr->dmFields & DM_ORIENTATION) {
+ Tcl_SetVar2(interp, varName, "Orientation",
+ TokenToString(orientationTable, dmPtr->dmOrientation), 0);
+ }
+ if (dmPtr->dmFields & DM_PAPERSIZE) {
+ Tcl_SetVar2(interp, varName, "PaperSize",
+ TokenToString(sizeTable, dmPtr->dmPaperSize), 0);
+ }
+ if (dmPtr->dmFields & DM_PAPERWIDTH) {
+ Tcl_SetVar2(interp, varName, "PaperWidth",
+ Blt_Itoa(dmPtr->dmPaperWidth), 0);
+ }
+ if (dmPtr->dmFields & DM_PAPERLENGTH) {
+ Tcl_SetVar2(interp, varName, "PaperLength",
+ Blt_Itoa(dmPtr->dmPaperLength), 0);
+ }
+ if (dmPtr->dmFields & DM_SCALE) {
+ Tcl_SetVar2(interp, varName, "Scale", Blt_Itoa(dmPtr->dmScale), 0);
+ }
+ if (dmPtr->dmFields & DM_COPIES) {
+ Tcl_SetVar2(interp, varName, "Copies", Blt_Itoa(dmPtr->dmCopies), 0);
+ }
+ if (dmPtr->dmFields & DM_DEFAULTSOURCE) {
+ Tcl_SetVar2(interp, varName, "DefaultSource",
+ TokenToString(binTable, dmPtr->dmDefaultSource), 0);
+ }
+ if (dmPtr->dmFields & DM_PRINTQUALITY) {
+ if (dmPtr->dmPrintQuality < 0) {
+ string = TokenToString(qualityTable, dmPtr->dmPrintQuality);
+ } else {
+ string = Blt_Itoa(dmPtr->dmPrintQuality);
+ }
+ Tcl_SetVar2(interp, varName, "PrintQuality", string, 0);
+ }
+ if (dmPtr->dmFields & DM_COLOR) {
+ Tcl_SetVar2(interp, varName, "Color",
+ TokenToString(colorTable, dmPtr->dmColor), 0);
+ }
+ if (dmPtr->dmFields & DM_DUPLEX) {
+ Tcl_SetVar2(interp, varName, "Duplex",
+ TokenToString(duplexTable, dmPtr->dmDuplex), 0);
+ }
+ if (dmPtr->dmFields & DM_YRESOLUTION) {
+ Tcl_SetVar2(interp, varName, "YResolution",
+ Blt_Itoa(dmPtr->dmYResolution), 0);
+ }
+ if (dmPtr->dmFields & DM_TTOPTION) {
+ Tcl_SetVar2(interp, varName, "TTOption",
+ TokenToString(ttOptionTable, dmPtr->dmTTOption), 0);
+ }
+ if (dmPtr->dmFields & DM_COLLATE) {
+ if (dmPtr->dmCollate == DMCOLLATE_TRUE) {
+ string = "true";
+ } else if (dmPtr->dmCollate == DMCOLLATE_FALSE) {
+ string = "false";
+ } else {
+ string = "???";
+ }
+ Tcl_SetVar2(interp, varName, "Collate", string, 0);
+ }
+ if (dmPtr->dmFields & DM_FORMNAME) {
+ Tcl_SetVar2(interp, varName, "FormName", dmPtr->dmFormName, 0);
+ }
+ Tcl_SetVar2(interp, varName, "OutputFile", dmPtr->dmDeviceName, 0);
+ result = TCL_OK;
+
+ error:
+ Tcl_DStringFree(&dString);
+ ClosePrinter(hPrinter);
+ if (hMem1 != NULL) {
+ GlobalUnlock(hMem1);
+ GlobalFree(hMem1);
+ }
+ if (hMem2 != NULL) {
+ GlobalUnlock(hMem2);
+ GlobalFree(hMem2);
+ }
+ return result;
+}
+
+
+static int
+SetPrinterAttributes(
+ Tcl_Interp *interp,
+ PrinterHandler *handlerPtr,
+ char *varName)
+{
+ char *string;
+ DEVMODE *dmPtr;
+ int value;
+ HGLOBAL hMem;
+ HANDLE hPrinter;
+ int result;
+
+ hPrinter = OpenPrinter2(interp, handlerPtr->name);
+ if (hPrinter == NULL) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+ hMem = GetPrinterProperties(handlerPtr, hPrinter, &dmPtr);
+ ClosePrinter(hPrinter);
+ if (hMem == NULL) {
+ return TCL_ERROR;
+ }
+ dmPtr->dmFields = 0;
+ string = Tcl_GetVar2(interp, varName, "Orientation", 0);
+ if (string != NULL) {
+ value = StringToToken(orientationTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_ORIENTATION;
+ dmPtr->dmOrientation = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "PaperSize", 0);
+ if (string != NULL) {
+ value = StringToToken(sizeTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_PAPERSIZE;
+ dmPtr->dmPaperSize = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "PaperWidth", 0);
+ if (string != NULL) {
+ if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_PAPERWIDTH;
+ dmPtr->dmPaperWidth = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "PaperLength", 0);
+ if (string != NULL) {
+ if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_PAPERLENGTH;
+ dmPtr->dmPaperLength = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "Scale", 0);
+ if (string != NULL) {
+ if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_SCALE;
+ dmPtr->dmScale = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "Copies", 0);
+ if (string != NULL) {
+ if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_COPIES;
+ dmPtr->dmCopies = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "DefaultSource", 0);
+ if (string != NULL) {
+ value = StringToToken(binTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_DEFAULTSOURCE;
+ dmPtr->dmDefaultSource = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "PrintQuality", 0);
+ if (string != NULL) {
+ value = StringToToken(qualityTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_PRINTQUALITY;
+ dmPtr->dmPrintQuality = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "Color", 0);
+ if (string != NULL) {
+ value = StringToToken(colorTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_COLOR;
+ dmPtr->dmColor = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "Duplex", 0);
+ if (string != NULL) {
+ value = StringToToken(duplexTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_DUPLEX;
+ dmPtr->dmDuplex = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "YResolution", 0);
+ if (string != NULL) {
+ if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_YRESOLUTION;
+ dmPtr->dmYResolution = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "TTOption", 0);
+ if (string != NULL) {
+ value = StringToToken(ttOptionTable, string);
+ if (value > 0) {
+ dmPtr->dmFields |= DM_TTOPTION;
+ dmPtr->dmTTOption = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "Collate", 0);
+ if (string != NULL) {
+ if (Tcl_GetBoolean(interp, string, &value) == TCL_OK) {
+ dmPtr->dmFields |= DM_COLLATE;
+ dmPtr->dmCollate = value;
+ }
+ }
+ string = Tcl_GetVar2(interp, varName, "OutputFile", 0);
+ if (string != NULL) {
+ if (handlerPtr->fileName != NULL) {
+ Blt_Free(handlerPtr->fileName);
+ }
+ handlerPtr->fileName = Blt_Strdup(string);
+ }
+ if (handlerPtr->dmPtr != NULL) {
+ Blt_Free(handlerPtr->dmPtr);
+ }
+ string = Tcl_GetVar2(interp, varName, "DocumentName", 0);
+ if (string != NULL) {
+ if (handlerPtr->docName != NULL) {
+ Blt_Free(handlerPtr->docName);
+ }
+ handlerPtr->docName = Blt_Strdup(string);
+ }
+ result = SetPrinterProperties(interp, handlerPtr, hPrinter, dmPtr);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ ClosePrinter(hPrinter);
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+EnumOp(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ TokenString *p;
+ char c;
+ unsigned int length;
+
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'p') && (strncmp(argv[2], "paper", length) == 0)) {
+ p = sizeTable;
+ } else if ((c == 'q') && (strncmp(argv[2], "quality", length) == 0)) {
+ p = qualityTable;
+ } else if ((c == 'b') && (strncmp(argv[2], "bin", length) == 0)) {
+ p = binTable;
+ } else if ((c == 'o') && (strncmp(argv[2], "orientation", length) == 0)) {
+ p = orientationTable;
+ } else if ((c == 'c') && (strncmp(argv[2], "color", length) == 0)) {
+ p = colorTable;
+ } else if ((c == 'd') && (strncmp(argv[2], "duplex", length) == 0)) {
+ p = duplexTable;
+ } else if ((c == 't') && (strncmp(argv[2], "ttoption", length) == 0)) {
+ p = ttOptionTable;
+ } else {
+ Tcl_AppendResult(interp, "bad enumeration field \"", argv[2],
+"\": should be \"paper\", \"quality\", \"bin\", \"orientation\", \"color\", \"duplex\", or \"ttoption\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ for ( /*empty*/ ; p->string != NULL; p++) {
+ Tcl_AppendElement(interp, p->string);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+OpenOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ PrinterHandler *handlerPtr;
+ HANDLE hPrinter;
+ char string[200];
+ LPVOID buffer;
+ PRINTER_INFO_2* pi2Ptr;
+ int bytesNeeded;
+ int isNew;
+ Blt_HashEntry *hPtr;
+ HANDLE hMem;
+
+ hPrinter = OpenPrinter2(interp, argv[2]);
+ if (hPrinter == NULL) {
+ return TCL_ERROR;
+ }
+ /* Call the first time to determine the amount of memory needed. */
+ GetPrinter(hPrinter, 2, NULL, 0, &bytesNeeded);
+ if ((bytesNeeded == 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+ Tcl_AppendResult(interp, "can't get size of attribute buffer for \"",
+ argv[2], "\": ", Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Allocate a buffer to contain all printer information. */
+ hMem = GlobalAlloc(GHND, bytesNeeded);
+ if (hMem == NULL) {
+ return TCL_ERROR;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ handlerPtr = Blt_Calloc(1, sizeof(PrinterHandler));
+
+ /* And call the again to actually get the printer. */
+ if (!GetPrinter(hPrinter, 2, buffer, bytesNeeded, &bytesNeeded)) {
+ Tcl_AppendResult(interp, "can't get printer attributes for \"",
+ argv[2], "\": ", Blt_LastError(), (char *)NULL);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ return TCL_ERROR;
+ }
+ pi2Ptr = (PRINTER_INFO_2 *)buffer;
+ if (pi2Ptr->pDevMode != NULL) {
+ handlerPtr->deviceName = Blt_Strdup(pi2Ptr->pDevMode->dmDeviceName);
+ }
+ handlerPtr->driverName = Blt_Strdup(pi2Ptr->pDriverName);
+ handlerPtr->printerName = Blt_Strdup(pi2Ptr->pPrinterName);
+ handlerPtr->portName = Blt_Strdup(pi2Ptr->pPortName);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ handlerPtr->interp = interp;
+ handlerPtr->name = Blt_Strdup(argv[2]);
+ do {
+ sprintf(string, "printer%d", dataPtr->nextId++);
+ hPtr = Blt_CreateHashEntry(&(dataPtr->idTable), string, &isNew);
+ } while (!isNew);
+ Blt_SetHashValue(hPtr, handlerPtr);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ handlerPtr->hashPtr = hPtr;
+ handlerPtr->tablePtr = &(dataPtr->idTable);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NamesOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ register int i;
+ int nPrinters, bytesNeeded;
+ int elemSize, level;
+ unsigned char *buffer;
+ int result, flags;
+ char *p;
+ HANDLE hMem;
+
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ level = 4;
+ elemSize = sizeof(PRINTER_INFO_4);
+ flags = PRINTER_ENUM_NAME;
+ } else {
+ level = 5;
+ elemSize = sizeof(PRINTER_INFO_5);
+ flags = PRINTER_ENUM_LOCAL;
+ }
+ result = EnumPrinters(
+ flags, /* Flags */
+ NULL, /* Printer name */
+ level, /* Information level: 1, 2, 4, or 5 */
+ NULL, /* Array of returned information */
+ 0, /* Size of array */
+ &bytesNeeded, /* Size needed for array */
+ &nPrinters); /* Number of structures returned */
+
+ if ((!result) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+ Tcl_AppendResult(interp, "can't enumerate printers (memory alloc): ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ hMem = GlobalAlloc(GHND, bytesNeeded);
+ buffer = (unsigned char *)GlobalLock(hMem);
+
+ result = EnumPrinters(
+ flags, /* Flags */
+ NULL, /* Printer name */
+ level, /* Information level: 1, 2, 4, or 5 */
+ buffer, /* Array of returned information */
+ bytesNeeded, /* Size of array */
+ &bytesNeeded, /* Size needed for array */
+ &nPrinters); /* Number of structures returned */
+
+ if (!result) {
+ Tcl_AppendResult(interp, "can't enumerate printers: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ p = buffer;
+ for (i = 0; i < nPrinters; i++) {
+ if ((argc == 2) || (Tcl_StringMatch(p, argv[2]))) {
+ Tcl_AppendElement(interp, *(char **)p);
+ }
+ p += elemSize;
+ }
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CloseOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc, /* Not used. */
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ PrinterHandler *handlerPtr;
+
+ if (GetPrinterHandler(dataPtr, interp, argv[2], &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ DestroyPrinter(handlerPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+GetAttrOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc, /* Not used. */
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ PrinterHandler *handlerPtr;
+
+ if (GetPrinterHandler(dataPtr, interp, argv[2], &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return GetPrinterAttributes(interp, handlerPtr, argv[3]);
+}
+
+/*ARGSUSED*/
+static int
+SetAttrOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ PrinterHandler *handlerPtr;
+
+ if (GetPrinterHandler(dataPtr, interp, argv[2], &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return SetPrinterAttributes(interp, handlerPtr, argv[3]);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ * Prints a snapshot of a Tk_Window to the designated printer.
+ *
+ * Results:
+ * Returns a standard Tcl result. If an error occurred
+ * TCL_ERROR is returned and interp->result will contain an
+ * error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+static int
+SnapOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ BITMAPINFO bi;
+ DIBSECTION ds;
+ HBITMAP hBitmap, oldBitmap;
+ HPALETTE hPalette;
+ HDC hDC, printDC, memDC;
+ void *data;
+ Tk_Window tkwin;
+ TkWinDCState state;
+ int result;
+ PrinterHandler *handlerPtr;
+ DOCINFO di;
+ double pageWidth, pageHeight;
+ int jobId;
+ char *driverName;
+ HANDLE hPrinter;
+ DEVMODE *dmPtr;
+ HGLOBAL hMem;
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ if (GetPrinterHandler(dataPtr, interp, argv[2], &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tkwin = Tk_NameToWindow(interp, argv[3], Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_WindowId(tkwin) == None) {
+ Tk_MakeWindowExist(tkwin);
+ }
+
+ result = TCL_ERROR;
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), Tk_WindowId(tkwin), &state);
+
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = Tk_Width(tkwin);
+ bi.bmiHeader.biHeight = Tk_Height(tkwin);
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = 32;
+ bi.bmiHeader.biCompression = BI_RGB;
+ hBitmap = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &data, NULL, 0);
+ memDC = CreateCompatibleDC(hDC);
+ oldBitmap = SelectBitmap(memDC, hBitmap);
+ hPalette = Blt_GetSystemPalette();
+ if (hPalette != NULL) {
+ SelectPalette(hDC, hPalette, FALSE);
+ RealizePalette(hDC);
+ SelectPalette(memDC, hPalette, FALSE);
+ RealizePalette(memDC);
+ }
+ /* Copy the window contents to the memory surface. */
+ if (!BitBlt(memDC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), hDC, 0, 0,
+ SRCCOPY)) {
+ Tcl_AppendResult(interp, "can't blit \"", Tk_PathName(tkwin), "\": ",
+ Blt_LastError(), (char *)NULL);
+ goto done;
+ }
+ /* Now that the DIB contains the image of the window, get the
+ * databits and write them to the printer device, stretching the
+ * image to the fit the printer's resolution. */
+ if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+ Tcl_AppendResult(interp, "can't get DIB object: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ driverName = NULL;
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ driverName = handlerPtr->driverName;
+ }
+ hPrinter = OpenPrinter2(interp, handlerPtr->name);
+ if (hPrinter == NULL) {
+ goto done;
+ }
+ hMem = GetPrinterProperties(handlerPtr, hPrinter, &dmPtr);
+ if (hMem == NULL) {
+ goto done;
+ }
+ printDC = CreateDC(driverName, handlerPtr->deviceName, NULL, dmPtr);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ if (printDC == NULL) {
+ Tcl_AppendResult(interp, "can't allocate printer DC for \"",
+ handlerPtr->name, "\": ", Blt_LastError(), (char *)NULL);
+ goto done;
+ }
+ {
+ double scale, sx, sy;
+
+ /* Get the resolution of the printer device. */
+ sx = (double)GetDeviceCaps(printDC, HORZRES)/(double)Tk_Width(tkwin);
+ sy = (double)GetDeviceCaps(printDC, VERTRES)/(double)Tk_Height(tkwin);
+ scale = MIN(sx, sy);
+ pageWidth = scale * Tk_Width(tkwin);
+ pageHeight = scale * Tk_Height(tkwin);
+ }
+ ZeroMemory(&di, sizeof(di));
+ di.cbSize = sizeof(di);
+ Tcl_DStringAppend(&dString, "Snapshot of \"", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(tkwin), -1);
+ Tcl_DStringAppend(&dString, "\"", -1);
+ di.lpszDocName = Tcl_DStringValue(&dString);
+ jobId = StartDoc(printDC, &di);
+ if (jobId <= 0) {
+ Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ if (StartPage(printDC) <= 0) {
+ Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ StretchDIBits(printDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), ds.dsBm.bmBits,
+ (LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
+ EndPage(printDC);
+ EndDoc(printDC);
+ DeleteDC(printDC);
+ Tcl_SetResult(interp, Blt_Itoa(jobId), TCL_VOLATILE);
+ result = TCL_OK;
+
+ done:
+ Tcl_DStringFree(&dString);
+ if (hPrinter != NULL) {
+ ClosePrinter(hPrinter);
+ }
+ DeleteBitmap(hBitmap);
+ DeleteDC(memDC);
+ TkWinReleaseDrawableDC(Tk_WindowId(tkwin), hDC, &state);
+ if (hPalette != NULL) {
+ DeletePalette(hPalette);
+ }
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+WriteOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ PrinterInterpData *dataPtr = clientData;
+ DWORD bytesLeft, nBytes;
+ DOC_INFO_1 di1;
+ DWORD jobId;
+ char *title, *printer;
+ register char *data;
+ static int nextJob = 0;
+ char string[200];
+ PrinterHandler *handlerPtr;
+ HANDLE hPrinter;
+ int result;
+
+ if (GetPrinterHandler(dataPtr, interp, argv[2], &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hPrinter = OpenPrinter2(interp, handlerPtr->name);
+ if (hPrinter == NULL) {
+ return TCL_ERROR;
+ }
+ if (argc == 5) {
+ title = argv[3], data = argv[4];
+ } else {
+ sprintf(string, "Print Job #%d", nextJob++);
+ title = string, data = argv[3];
+ }
+ bytesLeft = strlen(data);
+ ZeroMemory(&di1, sizeof(DOC_INFO_1));
+ di1.pDocName = title;
+ if (handlerPtr->fileName != NULL) {
+ di1.pOutputFile = handlerPtr->fileName;
+ } else {
+ di1.pOutputFile = NULL;
+ }
+ di1.pDatatype = "RAW";
+
+ result = TCL_ERROR;
+ printer = handlerPtr->printerName;
+ /* Start new document */
+ jobId = StartDocPrinter(hPrinter, 1, (unsigned char *)&di1);
+ if (jobId == 0) {
+ Tcl_AppendResult(interp, "error starting document on \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* Start new page */
+ if (!StartPagePrinter(hPrinter)) {
+ Tcl_AppendResult(interp, "error starting page on \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ do {
+ if (!WritePrinter(hPrinter, data, bytesLeft, &nBytes)) {
+ Tcl_AppendResult(interp, "can't write data to \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ EndDocPrinter(hPrinter);
+ goto error;
+ }
+ data += nBytes;
+ bytesLeft -= nBytes;
+ } while (bytesLeft > 0);
+ /* End last page */
+ if (!EndPagePrinter(hPrinter)) {
+ Tcl_AppendResult(interp, "error ending page on \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ /* End document */
+ if (!EndDocPrinter(hPrinter)) {
+ Tcl_AppendResult(interp, "error ending document on \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ result = TCL_OK;
+ error:
+ ClosePrinter(hPrinter);
+ return result;
+}
+
+static Blt_OpSpec printerOps[] =
+{
+ {"close", 1, (Blt_Op)CloseOp, 3, 3, "pid",},
+ {"enum", 1, (Blt_Op)EnumOp, 3, 3, "attribute",},
+ {"getattrs", 1, (Blt_Op)GetAttrOp, 4, 4, "pid varName",},
+ {"names", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+ {"open", 1, (Blt_Op)OpenOp, 3, 3, "printerName",},
+ {"setattrs", 1, (Blt_Op)SetAttrOp, 4, 4, "pid varName",},
+ {"snap", 1, (Blt_Op)SnapOp, 4, 4, "pid window",},
+ {"write", 1, (Blt_Op)WriteOp, 4, 5, "pid ?title? string",},
+};
+static int nPrinterOps = sizeof(printerOps) / sizeof(Blt_OpSpec);
+
+/* ARGSUSED */
+static int
+PrinterCmd(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ int argc,
+ char **argv)
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nPrinterOps, printerOps, BLT_OP_ARG1, argc, argv,
+ 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PrinterInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting one or more printer
+ * commands is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Closes and removes all open printers.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+PrinterInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ PrinterInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ PrinterHandler *handlerPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->idTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ handlerPtr = (PrinterHandler *)Blt_GetHashValue(hPtr);
+ handlerPtr->hashPtr = NULL;
+ DestroyPrinter(handlerPtr);
+ }
+ Blt_DeleteHashTable(&(dataPtr->idTable));
+ Tcl_DeleteAssocData(interp, PRINTER_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+static PrinterInterpData *
+GetPrinterInterpData(interp)
+ Tcl_Interp *interp;
+{
+ PrinterInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (PrinterInterpData *)
+ Tcl_GetAssocData(interp, PRINTER_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(PrinterInterpData));
+ dataPtr->nextId = 0;
+ assert(dataPtr);
+ Tcl_SetAssocData(interp, PRINTER_THREAD_KEY, PrinterInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->idTable), BLT_STRING_KEYS);
+ }
+ return dataPtr;
+}
+
+int
+Blt_PrinterInit(Tcl_Interp *interp)
+{
+ static Blt_CmdSpec cmdSpec = {
+ "printer", PrinterCmd,
+ };
+ PrinterInterpData *dataPtr;
+
+ dataPtr = GetPrinterInterpData(interp);
+ cmdSpec.clientData = dataPtr;
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/* Public routines */
+int
+Blt_GetOpenPrinter(
+ Tcl_Interp *interp,
+ const char *id,
+ Drawable *drawablePtr)
+{
+ PrinterHandler *handlerPtr;
+ PrinterInterpData *dataPtr;
+
+ dataPtr = GetPrinterInterpData(interp);
+
+ if (GetPrinterHandler(dataPtr, interp, id, &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (handlerPtr->drawable.hDC == NULL) {
+ char *driverName;
+ HANDLE hPrinter;
+ HGLOBAL hMem;
+ DEVMODE *dmPtr;
+ HDC hDC;
+
+ driverName = NULL;
+ if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ driverName = handlerPtr->driverName;
+ }
+ hPrinter = OpenPrinter2(interp, handlerPtr->name);
+ if (hPrinter == NULL) {
+ return TCL_ERROR;
+ }
+ hMem = GetPrinterProperties(handlerPtr, hPrinter, &dmPtr);
+ if (hMem == NULL) {
+ ClosePrinter(hPrinter);
+ return TCL_ERROR;
+ }
+ if (handlerPtr->dmPtr != NULL) {
+ *dmPtr = *handlerPtr->dmPtr;
+ }
+ hDC = CreateDC(driverName, handlerPtr->deviceName, NULL, dmPtr);
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ ClosePrinter(hPrinter);
+ if (hDC == NULL) {
+ Tcl_AppendResult(interp, "can't allocate printer DC for \"",
+ handlerPtr->name, "\": ", Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ handlerPtr->drawable.hDC = hDC;
+ handlerPtr->drawable.type = TWD_WINDC;
+ handlerPtr->hPrinter = hPrinter;
+ }
+ *drawablePtr = (Drawable)(&handlerPtr->drawable);
+ return TCL_OK;
+}
+
+int
+Blt_StartPrintJob(
+ Tcl_Interp *interp,
+ const char *id)
+{
+ PrinterHandler *handlerPtr;
+ DOCINFO di;
+ int jobId;
+ PrinterInterpData *dataPtr;
+
+ dataPtr = GetPrinterInterpData(interp);
+ if (GetPrinterHandler(dataPtr, interp, id, &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ZeroMemory((char *)&di, sizeof(DOCINFO));
+ di.cbSize = sizeof(DOCINFO);
+ di.lpszDocName = handlerPtr->docName;
+ jobId = StartDoc(handlerPtr->drawable.hDC, &di);
+ if (jobId == 0) {
+ Tcl_AppendResult(interp, "error starting document on \"",
+ handlerPtr->printerName, "\": ", Blt_LastError(),
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_EndPrintJob(
+ Tcl_Interp *interp,
+ const char *id)
+{
+ PrinterHandler *handlerPtr;
+ PrinterInterpData *dataPtr;
+
+ dataPtr = GetPrinterInterpData(interp);
+ if (GetPrinterHandler(dataPtr, interp, id, &handlerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ EndPage(handlerPtr->drawable.hDC);
+ EndDoc(handlerPtr->drawable.hDC);
+ /* ClosePrinter(handlerPtr->hPrinter); */
+ return TCL_OK;
+}
+
+#endif /* NO_PRINTER */
diff --git a/blt/src/bltWinUtil.c b/blt/src/bltWinUtil.c
new file mode 100644
index 00000000000..7454b9fb4be
--- /dev/null
+++ b/blt/src/bltWinUtil.c
@@ -0,0 +1,77 @@
+/*
+ * bltWinUtil.c --
+ *
+ * This module contains WIN32 routines not included in the Tcl/Tk
+ * libraries.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that the
+ * copyright notice and warranty disclaimer appear in supporting documentation,
+ * and that the names of Lucent Technologies any of their entities not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this software,
+ * including all implied warranties of merchantability and fitness. In no event
+ * shall Lucent Technologies be liable for any special, indirect or
+ * consequential damages or any damages whatsoever resulting from loss of use,
+ * data or profits, whether in an action of contract, negligence or other
+ * tortuous action, arising out of or in connection with the use or performance
+ * of this software.
+ *
+ */
+
+#include <bltInt.h>
+
+double
+drand48(void)
+{
+ return (double) rand() / (double)RAND_MAX;
+}
+
+void
+srand48(long int seed)
+{
+ srand(seed);
+}
+
+int
+Blt_GetPlatformId(void)
+{
+ static int platformId = 0;
+
+ if (platformId == 0) {
+ OSVERSIONINFO opsysInfo;
+
+ opsysInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&opsysInfo)) {
+ platformId = opsysInfo.dwPlatformId;
+ }
+ }
+ return platformId;
+}
+
+char *
+Blt_LastError(void)
+{
+ static char buffer[1024];
+ int length;
+
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ buffer,
+ 1024,
+ NULL);
+ length = strlen(buffer);
+ if (buffer[length - 2] == '\r') {
+ buffer[length - 2] = '\0';
+ }
+ return buffer;
+}
+
diff --git a/blt/src/bltWindow.c b/blt/src/bltWindow.c
new file mode 100644
index 00000000000..fceecd5c0e3
--- /dev/null
+++ b/blt/src/bltWindow.c
@@ -0,0 +1,1666 @@
+/*
+ * bltWindow.c --
+ *
+ * This module implements additional window functionality for
+ * the BLT toolkit, such as transparent Tk windows,
+ * and reparenting Tk windows.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#include <X11/Xlib.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+typedef struct TkIdStackStruct TkIdStack;
+typedef struct TkErrorHandlerStruct TkErrorHandler;
+typedef struct TkSelectionInfoStruct TkSelectionInfo;
+typedef struct TkClipboardTargetStruct TkClipboardTarget;
+
+#ifndef WIN32
+typedef struct TkWindowStruct TkWindow;
+#endif
+typedef struct TkWindowEventStruct TkWindowEvent;
+typedef struct TkMainInfoStruct TkMainInfo;
+typedef struct TkEventHandlerStruct TkEventHandler;
+typedef struct TkSelHandlerStruct TkSelHandler;
+typedef struct TkWinInfoStruct TkWinInfo;
+typedef struct TkClassProcsStruct TkClassProcs;
+typedef struct TkWindowPrivateStruct TkWindowPrivate;
+typedef struct TkGrabEventStruct TkGrabEvent;
+typedef struct TkColormapStruct TkColormap;
+typedef struct TkStressedCmapStruct TkStressedCmap;
+typedef struct TkWmInfoStruct TkWmInfo;
+
+#ifdef XNQueryInputStyle
+#define TK_USE_INPUT_METHODS
+#endif
+
+#ifndef TK_REPARENTED
+#define TK_REPARENTED 0
+#endif
+
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+
+/*
+ * One of the following structures is maintained for each display
+ * containing a window managed by Tk. In part, the structure is
+ * used to store thread-specific data, since each thread will have
+ * its own TkDisplay structure.
+ */
+
+typedef struct TkDisplayStruct {
+ Display *display; /* Xlib's info about display. */
+ struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
+ char *name; /* Name of display (with any screen
+ * identifier removed). Malloc-ed. */
+ Time lastEventTime; /* Time of last event received for this
+ * display. */
+
+ /*
+ * Information used primarily by tk3d.c:
+ */
+
+ int borderInit; /* 0 means borderTable needs initializing. */
+ Tcl_HashTable borderTable; /* Maps from color name to TkBorder
+ * structure. */
+
+ /*
+ * Information used by tkAtom.c only:
+ */
+
+ int atomInit; /* 0 means stuff below hasn't been
+ * initialized yet. */
+ Tcl_HashTable nameTable; /* Maps from names to Atom's. */
+ Tcl_HashTable atomTable; /* Maps from Atom's back to names. */
+
+ /*
+ * Information used primarily by tkBind.c:
+ */
+
+ int bindInfoStale; /* Non-zero means the variables in this
+ * part of the structure are potentially
+ * incorrect and should be recomputed. */
+ unsigned int modeModMask; /* Has one bit set to indicate the modifier
+ * corresponding to "mode shift". If no
+ * such modifier, than this is zero. */
+ unsigned int metaModMask; /* Has one bit set to indicate the modifier
+ * corresponding to the "Meta" key. If no
+ * such modifier, then this is zero. */
+ unsigned int altModMask; /* Has one bit set to indicate the modifier
+ * corresponding to the "Meta" key. If no
+ * such modifier, then this is zero. */
+ enum {
+ LU_IGNORE, LU_CAPS, LU_SHIFT
+ } lockUsage; /* Indicates how to interpret lock modifier. */
+ int numModKeyCodes; /* Number of entries in modKeyCodes array
+ * below. */
+ KeyCode *modKeyCodes; /* Pointer to an array giving keycodes for
+ * all of the keys that have modifiers
+ * associated with them. Malloc'ed, but
+ * may be NULL. */
+
+ /*
+ * Information used by tkBitmap.c only:
+ */
+
+ int bitmapInit; /* 0 means tables above need initializing. */
+ int bitmapAutoNumber; /* Used to number bitmaps. */
+ Tcl_HashTable bitmapNameTable;
+ /* Maps from name of bitmap to the first
+ * TkBitmap record for that name. */
+ Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap
+ * structure for the bitmap. */
+ Tcl_HashTable bitmapDataTable;
+ /* Used by Tk_GetBitmapFromData to map from
+ * a collection of in-core data about a
+ * bitmap to a reference giving an auto-
+ * matically-generated name for the bitmap. */
+
+ /*
+ * Information used by tkCanvas.c only:
+ */
+
+ int numIdSearches;
+ int numSlowSearches;
+
+ /*
+ * Used by tkColor.c only:
+ */
+
+ int colorInit; /* 0 means color module needs initializing. */
+ TkStressedCmap *stressPtr; /* First in list of colormaps that have
+ * filled up, so we have to pick an
+ * approximate color. */
+ Tcl_HashTable colorNameTable;
+ /* Maps from color name to TkColor structure
+ * for that color. */
+ Tcl_HashTable colorValueTable;
+ /* Maps from integer RGB values to TkColor
+ * structures. */
+
+ /*
+ * Used by tkCursor.c only:
+ */
+
+ int cursorInit; /* 0 means cursor module need initializing. */
+ Tcl_HashTable cursorNameTable;
+ /* Maps from a string name to a cursor to the
+ * TkCursor record for the cursor. */
+ Tcl_HashTable cursorDataTable;
+ /* Maps from a collection of in-core data
+ * about a cursor to a TkCursor structure. */
+ Tcl_HashTable cursorIdTable;
+ /* Maps from a cursor id to the TkCursor
+ * structure for the cursor. */
+ char cursorString[20]; /* Used to store a cursor id string. */
+ Font cursorFont; /* Font to use for standard cursors.
+ * None means font not loaded yet. */
+
+ /*
+ * Information used by tkError.c only:
+ */
+
+ TkErrorHandler *errorPtr;
+ /* First in list of error handlers
+ * for this display. NULL means
+ * no handlers exist at present. */
+ int deleteCount; /* Counts # of handlers deleted since
+ * last time inactive handlers were
+ * garbage-collected. When this number
+ * gets big, handlers get cleaned up. */
+
+ /*
+ * Used by tkEvent.c only:
+ */
+
+ TkWindowEvent *delayedMotionPtr;
+ /* Points to a malloc-ed motion event
+ * whose processing has been delayed in
+ * the hopes that another motion event
+ * will come along right away and we can
+ * merge the two of them together. NULL
+ * means that there is no delayed motion
+ * event. */
+
+ /*
+ * Information used by tkFocus.c only:
+ */
+
+ int focusDebug; /* 1 means collect focus debugging
+ * statistics. */
+ TkWindow *implicitWinPtr;
+ /* If the focus arrived at a toplevel window
+ * implicitly via an Enter event (rather
+ * than via a FocusIn event), this points
+ * to the toplevel window. Otherwise it is
+ * NULL. */
+ TkWindow *focusPtr; /* Points to the window on this display that
+ * should be receiving keyboard events. When
+ * multiple applications on the display have
+ * the focus, this will refer to the
+ * innermost window in the innermost
+ * application. This information isn't used
+ * under Unix or Windows, but it's needed on
+ * the Macintosh. */
+
+ /*
+ * Information used by tkGC.c only:
+ */
+
+ Tcl_HashTable gcValueTable; /* Maps from a GC's values to a TkGC structure
+ * describing a GC with those values. */
+ Tcl_HashTable gcIdTable; /* Maps from a GC to a TkGC. */
+ int gcInit; /* 0 means the tables below need
+ * initializing. */
+
+ /*
+ * Information used by tkGeometry.c only:
+ */
+
+ Tcl_HashTable maintainHashTable;
+ /* Hash table that maps from a master's
+ * Tk_Window token to a list of slaves
+ * managed by that master. */
+ int geomInit;
+
+ /*
+ * Information used by tkGet.c only:
+ */
+
+ Tcl_HashTable uidTable; /* Stores all Tk_Uids used in a thread. */
+ int uidInit; /* 0 means uidTable needs initializing. */
+
+ /*
+ * Information used by tkGrab.c only:
+ */
+
+ TkWindow *grabWinPtr;
+ /* Window in which the pointer is currently
+ * grabbed, or NULL if none. */
+ TkWindow *eventualGrabWinPtr;
+ /* Value that grabWinPtr will have once the
+ * grab event queue (below) has been
+ * completely emptied. */
+ TkWindow *buttonWinPtr;
+ /* Window in which first mouse button was
+ * pressed while grab was in effect, or NULL
+ * if no such press in effect. */
+ TkWindow *serverWinPtr;
+ /* If no application contains the pointer then
+ * this is NULL. Otherwise it contains the
+ * last window for which we've gotten an
+ * Enter or Leave event from the server (i.e.
+ * the last window known to have contained
+ * the pointer). Doesn't reflect events
+ * that were synthesized in tkGrab.c. */
+ TkGrabEvent *firstGrabEventPtr;
+ /* First in list of enter/leave events
+ * synthesized by grab code. These events
+ * must be processed in order before any other
+ * events are processed. NULL means no such
+ * events. */
+ TkGrabEvent *lastGrabEventPtr;
+ /* Last in list of synthesized events, or NULL
+ * if list is empty. */
+ int grabFlags; /* Miscellaneous flag values. See definitions
+ * in tkGrab.c. */
+
+ /*
+ * Information used by tkGrid.c only:
+ */
+
+ int gridInit; /* 0 means table below needs initializing. */
+ Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to
+ * corresponding Grid structures. */
+
+ /*
+ * Information used by tkImage.c only:
+ */
+
+ int imageId; /* Value used to number image ids. */
+
+ /*
+ * Information used by tkMacWinMenu.c only:
+ */
+
+ int postCommandGeneration;
+
+ /*
+ * Information used by tkOption.c only.
+ */
+
+
+
+ /*
+ * Information used by tkPack.c only.
+ */
+
+ int packInit; /* 0 means table below needs initializing. */
+ Tcl_HashTable packerHashTable;
+ /* Maps from Tk_Window tokens to
+ * corresponding Packer structures. */
+
+
+ /*
+ * Information used by tkPlace.c only.
+ */
+
+ int placeInit; /* 0 means tables below need initializing. */
+ Tcl_HashTable masterTable; /* Maps from Tk_Window toke to the Master
+ * structure for the window, if it exists. */
+ Tcl_HashTable slaveTable; /* Maps from Tk_Window toke to the Slave
+ * structure for the window, if it exists. */
+
+ /*
+ * Information used by tkSelect.c and tkClipboard.c only:
+ */
+
+ TkSelectionInfo *selectionInfoPtr;
+ /* First in list of selection information
+ * records. Each entry contains information
+ * about the current owner of a particular
+ * selection on this display. */
+ Atom multipleAtom; /* Atom for MULTIPLE. None means
+ * selection stuff isn't initialized. */
+ Atom incrAtom; /* Atom for INCR. */
+ Atom targetsAtom; /* Atom for TARGETS. */
+ Atom timestampAtom; /* Atom for TIMESTAMP. */
+ Atom textAtom; /* Atom for TEXT. */
+ Atom compoundTextAtom; /* Atom for COMPOUND_TEXT. */
+ Atom applicationAtom; /* Atom for TK_APPLICATION. */
+ Atom windowAtom; /* Atom for TK_WINDOW. */
+ Atom clipboardAtom; /* Atom for CLIPBOARD. */
+
+ Tk_Window clipWindow; /* Window used for clipboard ownership and to
+ * retrieve selections between processes. NULL
+ * means clipboard info hasn't been
+ * initialized. */
+ int clipboardActive; /* 1 means we currently own the clipboard
+ * selection, 0 means we don't. */
+ TkMainInfo *clipboardAppPtr;
+ /* Last application that owned clipboard. */
+ TkClipboardTarget *clipTargetPtr;
+ /* First in list of clipboard type information
+ * records. Each entry contains information
+ * about the buffers for a given selection
+ * target. */
+
+ /*
+ * Information used by tkSend.c only:
+ */
+
+ Tk_Window commTkwin; /* Window used for communication
+ * between interpreters during "send"
+ * commands. NULL means send info hasn't
+ * been initialized yet. */
+ Atom commProperty; /* X's name for comm property. */
+ Atom registryProperty; /* X's name for property containing
+ * registry of interpreter names. */
+ Atom appNameProperty; /* X's name for property used to hold the
+ * application name on each comm window. */
+
+ /*
+ * Information used by tkXId.c only:
+ */
+
+ TkIdStack *idStackPtr;
+ /* First in list of chunks of free resource
+ * identifiers, or NULL if there are no free
+ * resources. */
+ XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
+ /* Default resource allocator for display. */
+ TkIdStack *windowStackPtr;
+ /* First in list of chunks of window
+ * identifers that can't be reused right
+ * now. */
+ int idCleanupScheduled; /* 1 means a call to WindowIdCleanup has
+ * already been scheduled, 0 means it
+ * hasn't. */
+
+ /*
+ * Information used by tkUnixWm.c and tkWinWm.c only:
+ */
+
+ int wmTracing; /* 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. */
+ TkWmInfo *firstWmPtr; /* Points to first top-level window. */
+ TkWmInfo *foregroundWmPtr;
+ /* Points to the foreground window. */
+
+ /*
+ * Information maintained by tkWindow.c for use later on by tkXId.c:
+ */
+
+
+ int destroyCount; /* Number of Tk_DestroyWindow operations
+ * in progress. */
+ unsigned long lastDestroyRequest;
+ /* Id of most recent XDestroyWindow request;
+ * can re-use ids in windowStackPtr when
+ * server has seen this request and event
+ * queue is empty. */
+
+ /*
+ * Information used by tkVisual.c only:
+ */
+
+ TkColormap *cmapPtr; /* First in list of all non-default colormaps
+ * allocated for this display. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+#ifdef TK_USE_INPUT_METHODS
+ XIM inputMethod; /* Input method for this display */
+#endif /* TK_USE_INPUT_METHODS */
+ Tcl_HashTable winTable; /* Maps from X window ids to TkWindow ptrs. */
+ int refCount; /* Reference count of how many Tk applications
+ * are using this display. Used to clean up
+ * the display when we no longer have any
+ * Tk applications using it.
+ */
+#if (TK_VERSION_NUMBER >= _VERSION(8,3,0))
+ /*
+ * The following field were all added for Tk8.3
+ */
+ int mouseButtonState; /* current mouse button state for this
+ * display */
+ int warpInProgress;
+ Window warpWindow;
+ int warpX;
+ int warpY;
+ int useInputMethods; /* Whether to use input methods */
+#endif
+} TkDisplay;
+
+#else
+
+/*
+ * One of the following structures is maintained for each display
+ * containing a window managed by Tk:
+ */
+typedef struct TkDisplayStruct {
+ Display *display; /* Xlib's info about display. */
+ struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
+ char *name; /* Name of display (with any screen
+ * identifier removed). Malloc-ed. */
+ Time lastEventTime; /* Time of last event received for this
+ * display. */
+
+ /*
+ * Information used primarily by tkBind.c:
+ */
+
+ int bindInfoStale; /* Non-zero means the variables in this
+ * part of the structure are potentially
+ * incorrect and should be recomputed. */
+ unsigned int modeModMask; /* Has one bit set to indicate the modifier
+ * corresponding to "mode shift". If no
+ * such modifier, than this is zero. */
+ unsigned int metaModMask; /* Has one bit set to indicate the modifier
+ * corresponding to the "Meta" key. If no
+ * such modifier, then this is zero. */
+ unsigned int altModMask; /* Has one bit set to indicate the modifier
+ * corresponding to the "Meta" key. If no
+ * such modifier, then this is zero. */
+ enum {
+ LU_IGNORE, LU_CAPS, LU_SHIFT
+ } lockUsage;
+ /* Indicates how to interpret lock modifier. */
+ int numModKeyCodes; /* Number of entries in modKeyCodes array
+ * below. */
+ KeyCode *modKeyCodes; /* Pointer to an array giving keycodes for
+ * all of the keys that have modifiers
+ * associated with them. Malloc'ed, but
+ * may be NULL. */
+
+ /*
+ * Information used by tkError.c only:
+ */
+
+ TkErrorHandler *errorPtr;
+ /* First in list of error handlers
+ * for this display. NULL means
+ * no handlers exist at present. */
+ int deleteCount; /* Counts # of handlers deleted since
+ * last time inactive handlers were
+ * garbage-collected. When this number
+ * gets big, handlers get cleaned up. */
+
+ /*
+ * Information used by tkSend.c only:
+ */
+
+ Tk_Window commTkwin; /* Window used for communication
+ * between interpreters during "send"
+ * commands. NULL means send info hasn't
+ * been initialized yet. */
+ Atom commProperty; /* X's name for comm property. */
+ Atom registryProperty; /* X's name for property containing
+ * registry of interpreter names. */
+ Atom appNameProperty; /* X's name for property used to hold the
+ * application name on each comm window. */
+
+ /*
+ * Information used by tkSelect.c and tkClipboard.c only:
+ */
+
+ TkSelectionInfo *selectionInfoPtr;
+ /* First in list of selection information
+ * records. Each entry contains information
+ * about the current owner of a particular
+ * selection on this display. */
+ Atom multipleAtom; /* Atom for MULTIPLE. None means
+ * selection stuff isn't initialized. */
+ Atom incrAtom; /* Atom for INCR. */
+ Atom targetsAtom; /* Atom for TARGETS. */
+ Atom timestampAtom; /* Atom for TIMESTAMP. */
+ Atom textAtom; /* Atom for TEXT. */
+ Atom compoundTextAtom; /* Atom for COMPOUND_TEXT. */
+ Atom applicationAtom; /* Atom for TK_APPLICATION. */
+ Atom windowAtom; /* Atom for TK_WINDOW. */
+ Atom clipboardAtom; /* Atom for CLIPBOARD. */
+
+ Tk_Window clipWindow; /* Window used for clipboard ownership and to
+ * retrieve selections between processes. NULL
+ * means clipboard info hasn't been
+ * initialized. */
+ int clipboardActive; /* 1 means we currently own the clipboard
+ * selection, 0 means we don't. */
+ TkMainInfo *clipboardAppPtr;
+ /* Last application that owned clipboard. */
+ TkClipboardTarget *clipTargetPtr;
+ /* First in list of clipboard type information
+ * records. Each entry contains information
+ * about the buffers for a given selection
+ * target. */
+
+ /*
+ * Information used by tkAtom.c only:
+ */
+
+ int atomInit; /* 0 means stuff below hasn't been
+ * initialized yet. */
+ Tcl_HashTable nameTable; /* Maps from names to Atom's. */
+ Tcl_HashTable atomTable; /* Maps from Atom's back to names. */
+
+ /*
+ * Information used by tkCursor.c only:
+ */
+
+ Font cursorFont; /* Font to use for standard cursors.
+ * None means font not loaded yet. */
+
+ /*
+ * Information used by tkGrab.c only:
+ */
+
+ TkWindow *grabWinPtr;
+ /* Window in which the pointer is currently
+ * grabbed, or NULL if none. */
+ TkWindow *eventualGrabWinPtr;
+ /* Value that grabWinPtr will have once the
+ * grab event queue (below) has been
+ * completely emptied. */
+ TkWindow *buttonWinPtr;
+ /* Window in which first mouse button was
+ * pressed while grab was in effect, or NULL
+ * if no such press in effect. */
+ TkWindow *serverWinPtr;
+ /* If no application contains the pointer then
+ * this is NULL. Otherwise it contains the
+ * last window for which we've gotten an
+ * Enter or Leave event from the server (i.e.
+ * the last window known to have contained
+ * the pointer). Doesn't reflect events
+ * that were synthesized in tkGrab.c. */
+ TkGrabEvent *firstGrabEventPtr;
+ /* First in list of enter/leave events
+ * synthesized by grab code. These events
+ * must be processed in order before any other
+ * events are processed. NULL means no such
+ * events. */
+ TkGrabEvent *lastGrabEventPtr;
+ /* Last in list of synthesized events, or NULL
+ * if list is empty. */
+ int grabFlags; /* Miscellaneous flag values. See definitions
+ * in tkGrab.c. */
+
+ /*
+ * Information used by tkXId.c only:
+ */
+
+ TkIdStack *idStackPtr;
+ /* First in list of chunks of free resource
+ * identifiers, or NULL if there are no free
+ * resources. */
+ XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
+ /* Default resource allocator for display. */
+ TkIdStack *windowStackPtr;
+ /* First in list of chunks of window
+ * identifers that can't be reused right
+ * now. */
+ int idCleanupScheduled; /* 1 means a call to WindowIdCleanup has
+ * already been scheduled, 0 means it
+ * hasn't. */
+
+ /*
+ * Information maintained by tkWindow.c for use later on by tkXId.c:
+ */
+
+
+ int destroyCount; /* Number of Tk_DestroyWindow operations
+ * in progress. */
+ unsigned long lastDestroyRequest;
+ /* Id of most recent XDestroyWindow request;
+ * can re-use ids in windowStackPtr when
+ * server has seen this request and event
+ * queue is empty. */
+
+ /*
+ * Information used by tkVisual.c only:
+ */
+
+ TkColormap *cmapPtr; /* First in list of all non-default colormaps
+ * allocated for this display. */
+
+ /*
+ * Information used by tkFocus.c only:
+ */
+#if (TK_MAJOR_VERSION == 4)
+
+ TkWindow *focusWinPtr;
+ /* Window that currently has the focus for
+ * this display, or NULL if none. */
+ TkWindow *implicitWinPtr;
+ /* If the focus arrived at a toplevel window
+ * implicitly via an Enter event (rather
+ * than via a FocusIn event), this points
+ * to the toplevel window. Otherwise it is
+ * NULL. */
+ TkWindow *focusOnMapPtr;
+ /* This points to a toplevel window that is
+ * supposed to receive the X input focus as
+ * soon as it is mapped (needed to handle the
+ * fact that X won't allow the focus on an
+ * unmapped window). NULL means no delayed
+ * focus op in progress. */
+ int forceFocus; /* Associated with focusOnMapPtr: non-zero
+ * means claim the focus even if some other
+ * application currently has it. */
+#else
+ TkWindow *implicitWinPtr;
+ /* If the focus arrived at a toplevel window
+ * implicitly via an Enter event (rather
+ * than via a FocusIn event), this points
+ * to the toplevel window. Otherwise it is
+ * NULL. */
+ TkWindow *focusPtr; /* Points to the window on this display that
+ * should be receiving keyboard events. When
+ * multiple applications on the display have
+ * the focus, this will refer to the
+ * innermost window in the innermost
+ * application. This information isn't used
+ * under Unix or Windows, but it's needed on
+ * the Macintosh. */
+#endif /* TK_MAJOR_VERSION == 4 */
+
+ /*
+ * Used by tkColor.c only:
+ */
+
+ TkStressedCmap *stressPtr; /* First in list of colormaps that have
+ * filled up, so we have to pick an
+ * approximate color. */
+
+ /*
+ * Used by tkEvent.c only:
+ */
+
+ TkWindowEvent *delayedMotionPtr;
+ /* Points to a malloc-ed motion event
+ * whose processing has been delayed in
+ * the hopes that another motion event
+ * will come along right away and we can
+ * merge the two of them together. NULL
+ * means that there is no delayed motion
+ * event. */
+ /*
+ * Miscellaneous information:
+ */
+
+#ifdef TK_USE_INPUT_METHODS
+ XIM inputMethod; /* Input method for this display */
+#endif /* TK_USE_INPUT_METHODS */
+ Tcl_HashTable winTable; /* Maps from X window ids to TkWindow ptrs. */
+#if (TK_MAJOR_VERSION > 4)
+ int refCount; /* Reference count of how many Tk applications
+ * are using this display. Used to clean up
+ * the display when we no longer have any
+ * Tk applications using it.
+ */
+#endif /* TK_MAJOR_VERSION > 4 */
+
+} TkDisplay;
+
+#endif /* TK_VERSION_NUMBER >= _VERSION(8,1,0) */
+
+
+struct TkWindowStruct {
+ Display *display;
+ TkDisplay *dispPtr;
+ int screenNum;
+ Visual *visual;
+ int depth;
+ Window window;
+ TkWindow *childList;
+ TkWindow *lastChildPtr;
+ TkWindow *parentPtr;
+ TkWindow *nextPtr;
+ TkMainInfo *infoPtr;
+ char *pathName;
+ Tk_Uid nameUid;
+ Tk_Uid classUid;
+ XWindowChanges changes;
+ unsigned int dirtyChanges;
+ XSetWindowAttributes atts;
+ unsigned long dirtyAtts;
+ unsigned int flags;
+ TkEventHandler *handlerList;
+#ifdef TK_USE_INPUT_METHODS
+ XIC inputContext;
+#endif /* TK_USE_INPUT_METHODS */
+ ClientData *tagPtr;
+ int nTags;
+ int optionLevel;
+ TkSelHandler *selHandlerList;
+ Tk_GeomMgr *geomMgrPtr;
+ ClientData geomData;
+ int reqWidth, reqHeight;
+ int internalBorderWidth;
+ TkWinInfo *wmInfoPtr;
+#if (TK_MAJOR_VERSION > 4)
+ TkClassProcs *classProcsPtr;
+ ClientData instanceData;
+#endif
+ TkWindowPrivate *privatePtr;
+};
+
+#ifdef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowHandle --
+ *
+ * Returns the XID for the Tk_Window given. Starting in Tk 8.0,
+ * the toplevel widgets are wrapped by another window.
+ * Currently there's no way to get at that window, other than
+ * what is done here: query the X window hierarchy and grab the
+ * parent.
+ *
+ * Results:
+ * Returns the X Window ID of the widget. If it's a toplevel, then
+ * the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static HWND
+GetWindowHandle(Tk_Window tkwin)
+{
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+#if (TK_MAJOR_VERSION > 4)
+ if (Tk_IsTopLevel(tkwin)) {
+ hWnd = GetParent(hWnd);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+ return hWnd;
+}
+
+#else
+
+Window
+Blt_GetParent(display, window)
+ Display *display;
+ Window window;
+{
+ Window root, parent;
+ Window *dummy;
+ unsigned int count;
+
+ if (XQueryTree(display, window, &root, &parent, &dummy, &count) > 0) {
+ XFree(dummy);
+ return parent;
+ }
+ return None;
+}
+
+/* Find the toplevel then */
+int
+Blt_RootX(tkwin)
+ Tk_Window tkwin;
+{
+ int x;
+
+ for (x = 0; tkwin != NULL; tkwin = Tk_Parent(tkwin)) {
+ x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+ if (Tk_IsTopLevel(tkwin)) {
+ break;
+ }
+ }
+ return x;
+}
+
+int
+Blt_RootY(tkwin)
+ Tk_Window tkwin;
+{
+ int y;
+
+ for (y = 0; tkwin != NULL; tkwin = Tk_Parent(tkwin)) {
+ y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+ if (Tk_IsTopLevel(tkwin)) {
+ break;
+ }
+ }
+ return y;
+}
+
+static Window
+GetWindowId(tkwin)
+ Tk_Window tkwin;
+{
+ Window window;
+
+ Tk_MakeWindowExist(tkwin);
+ window = Tk_WindowId(tkwin);
+#if (TK_MAJOR_VERSION > 4)
+ if (Tk_IsTopLevel(tkwin)) {
+ Window parent;
+
+ parent = Blt_GetParent(Tk_Display(tkwin), window);
+ if (parent != None) {
+ window = parent;
+ }
+ window = parent;
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+ return window;
+}
+
+#endif /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DoConfigureNotify --
+ *
+ * Generate a ConfigureNotify event describing the current
+ * configuration of a window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * An event is generated and processed by Tk_HandleEvent.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DoConfigureNotify(winPtr)
+ Tk_FakeWin *winPtr; /* Window whose configuration was just
+ * changed. */
+{
+ XEvent event;
+
+ event.type = ConfigureNotify;
+ event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display);
+ event.xconfigure.send_event = False;
+ event.xconfigure.display = winPtr->display;
+ event.xconfigure.event = winPtr->window;
+ event.xconfigure.window = winPtr->window;
+ 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.border_width = winPtr->changes.border_width;
+ if (winPtr->changes.stack_mode == Above) {
+ event.xconfigure.above = winPtr->changes.sibling;
+ } else {
+ event.xconfigure.above = None;
+ }
+ event.xconfigure.override_redirect = winPtr->atts.override_redirect;
+ Tk_HandleEvent(&event);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_MakeTransparentWindowExist --
+ *
+ * Similar to Tk_MakeWindowExist but instead creates a
+ * transparent window to block for user events from sibling
+ * windows.
+ *
+ * Differences from Tk_MakeWindowExist.
+ *
+ * 1. This is always a "busy" window. There's never a
+ * platform-specific class procedure to execute instead.
+ * 2. The window is transparent and never will contain children,
+ * so colormap information is irrelevant.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the procedure returns, the internal window associated
+ * with tkwin is guaranteed to exist. This may require the
+ * window's ancestors to be created too.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_MakeTransparentWindowExist(tkwin, parent, isBusy)
+ Tk_Window tkwin; /* Token for window. */
+ Window parent; /* Parent window. */
+ int isBusy; /* */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ TkWindow *winPtr2;
+ Tcl_HashEntry *hPtr;
+ int notUsed;
+ TkDisplay *dispPtr;
+#ifdef WIN32
+ HWND hParent;
+ int style;
+ DWORD exStyle;
+ HWND hWnd;
+#else
+ long int mask;
+#endif /* WIN32 */
+
+ if (winPtr->window != None) {
+ return; /* Window already exists. */
+ }
+#ifdef notdef
+ if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) {
+ parent = XRootWindow(winPtr->display, winPtr->screenNum);
+ /* TODO: Make the entire screen busy */
+ } else {
+ if (Tk_WindowId(winPtr->parentPtr) == None) {
+ Tk_MakeWindowExist((Tk_Window)winPtr->parentPtr);
+ }
+ }
+#endif
+
+ /* Create a transparent window and put it on top. */
+
+#ifdef WIN32
+ hParent = (HWND) parent;
+ style = (WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+ exStyle = (WS_EX_TRANSPARENT | WS_EX_TOPMOST);
+#define TK_WIN_CHILD_CLASS_NAME "TkChild"
+ hWnd = CreateWindowEx(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style,
+ Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
+ hParent, NULL, (HINSTANCE) Tk_GetHINSTANCE(), NULL);
+ winPtr->window = Tk_AttachHWND(tkwin, hWnd);
+#else
+ mask = (!isBusy) ? 0 : (CWDontPropagate | CWEventMask);
+ /* Ignore the important events while the window is mapped. */
+#define USER_EVENTS (EnterWindowMask | LeaveWindowMask | KeyPressMask | \
+ KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
+#define PROP_EVENTS (KeyPressMask | KeyReleaseMask | ButtonPressMask | \
+ ButtonReleaseMask | PointerMotionMask)
+
+ winPtr->atts.do_not_propagate_mask = PROP_EVENTS;
+ winPtr->atts.event_mask = USER_EVENTS;
+ winPtr->changes.border_width = 0;
+ winPtr->depth = 0;
+
+ winPtr->window = XCreateWindow(winPtr->display, parent,
+ winPtr->changes.x, winPtr->changes.y,
+ (unsigned)winPtr->changes.width, /* width */
+ (unsigned)winPtr->changes.height, /* height */
+ (unsigned)winPtr->changes.border_width, /* border_width */
+ winPtr->depth, /* depth */
+ InputOnly, /* class */
+ winPtr->visual, /* visual */
+ mask, /* valuemask */
+ &(winPtr->atts) /* attributes */ );
+#endif /* WIN32 */
+
+ dispPtr = winPtr->dispPtr;
+ hPtr = Tcl_CreateHashEntry(&(dispPtr->winTable), (char *)winPtr->window,
+ &notUsed);
+ Tcl_SetHashValue(hPtr, winPtr);
+ winPtr->dirtyAtts = 0;
+ winPtr->dirtyChanges = 0;
+#ifdef TK_USE_INPUT_METHODS
+ winPtr->inputContext = NULL;
+#endif /* TK_USE_INPUT_METHODS */
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ /*
+ * If any siblings higher up in the stacking order have already
+ * been created then move this window to its rightful position
+ * in the stacking order.
+ *
+ * NOTE: this code ignores any changes anyone might have made
+ * to the sibling and stack_mode field of the window's attributes,
+ * so it really isn't safe for these to be manipulated except
+ * by calling Tk_RestackWindow.
+ */
+ for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL;
+ winPtr2 = winPtr2->nextPtr) {
+ if ((winPtr2->window != None) && !(winPtr2->flags & TK_TOP_LEVEL)) {
+ XWindowChanges changes;
+ changes.sibling = winPtr2->window;
+ changes.stack_mode = Below;
+ XConfigureWindow(winPtr->display, winPtr->window,
+ CWSibling | CWStackMode, &changes);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Issue a ConfigureNotify event if there were deferred configuration
+ * changes (but skip it if the window is being deleted; the
+ * ConfigureNotify event could cause problems if we're being called
+ * from Tk_DestroyWindow under some conditions).
+ */
+ if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY)
+ && !(winPtr->flags & TK_ALREADY_DEAD)) {
+ winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY;
+ DoConfigureNotify((Tk_FakeWin *) tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindChild --
+ *
+ * Performs a linear search for the named child window in a given
+ * parent window.
+ *
+ * This can be done via Tcl, but not through Tk's C API. It's
+ * simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ * The child Tk_Window. If the named child can't be found, NULL
+ * is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Tk_Window
+Blt_FindChild(parent, name)
+ Tk_Window parent;
+ char *name;
+{
+ register TkWindow *winPtr;
+ TkWindow *parentPtr = (TkWindow *)parent;
+
+ for (winPtr = parentPtr->childList; winPtr != NULL;
+ winPtr = winPtr->nextPtr) {
+ if (strcmp(name, winPtr->nameUid) == 0) {
+ return (Tk_Window)winPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FirstChildWindow --
+ *
+ * Performs a linear search for the named child window in a given
+ * parent window.
+ *
+ * This can be done via Tcl, but not through Tk's C API. It's
+ * simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ * The child Tk_Window. If the named child can't be found, NULL
+ * is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Tk_Window
+Blt_FirstChild(parent)
+ Tk_Window parent;
+{
+ TkWindow *parentPtr = (TkWindow *)parent;
+ return (Tk_Window)parentPtr->childList;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindChild --
+ *
+ * Performs a linear search for the named child window in a given
+ * parent window.
+ *
+ * This can be done via Tcl, but not through Tk's C API. It's
+ * simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ * The child Tk_Window. If the named child can't be found, NULL
+ * is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Tk_Window
+Blt_NextChild(tkwin)
+ Tk_Window tkwin;
+{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+
+ if (winPtr == NULL) {
+ return NULL;
+ }
+ return (Tk_Window)winPtr->nextPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkWindow --
+ *
+ * This procedure removes a window from the childList of its
+ * parent.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UnlinkWindow(winPtr)
+ TkWindow *winPtr; /* Child window to be unlinked. */
+{
+ TkWindow *prevPtr;
+
+ prevPtr = winPtr->parentPtr->childList;
+ if (prevPtr == winPtr) {
+ winPtr->parentPtr->childList = winPtr->nextPtr;
+ if (winPtr->nextPtr == NULL) {
+ winPtr->parentPtr->lastChildPtr = NULL;
+ }
+ } else {
+ while (prevPtr->nextPtr != winPtr) {
+ prevPtr = prevPtr->nextPtr;
+ if (prevPtr == NULL) {
+ panic("UnlinkWindow couldn't find child in parent");
+ }
+ }
+ prevPtr->nextPtr = winPtr->nextPtr;
+ if (winPtr->nextPtr == NULL) {
+ winPtr->parentPtr->lastChildPtr = prevPtr;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RelinkWindow --
+ *
+ * Relinks a window into a new parent. The window is unlinked
+ * from its original parent's child list and added onto the end
+ * of the new parent's list.
+ *
+ * FIXME: If the window has focus, the focus should be moved
+ * to an ancestor. Otherwise, Tk becomes confused
+ * about which Toplevel turns on focus for the window.
+ * Right now this is done at the Tcl layer. For example,
+ * see blt::CreateTearoff in tabset.tcl.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RelinkWindow(tkwin, newParent, x, y)
+ Tk_Window tkwin; /* Child window to be linked. */
+ Tk_Window newParent;
+ int x, y;
+{
+ TkWindow *winPtr, *parentWinPtr;
+
+ if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ Tk_WindowId(newParent), x, y) != TCL_OK) {
+ return;
+ }
+ winPtr = (TkWindow *)tkwin;
+ parentWinPtr = (TkWindow *)newParent;
+
+ winPtr->flags &= ~TK_REPARENTED;
+ UnlinkWindow(winPtr); /* Remove the window from its parent's list */
+
+ /* Append the window onto the end of the parent's list of children */
+ winPtr->parentPtr = parentWinPtr;
+ winPtr->nextPtr = NULL;
+ if (parentWinPtr->childList == NULL) {
+ parentWinPtr->childList = winPtr;
+ } else {
+ parentWinPtr->lastChildPtr->nextPtr = winPtr;
+ }
+ parentWinPtr->lastChildPtr = winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RelinkWindow --
+ *
+ * Relinks a window into a new parent. The window is unlinked
+ * from its original parent's child list and added onto the end
+ * of the new parent's list.
+ *
+ * FIXME: If the window has focus, the focus should be moved
+ * to an ancestor. Otherwise, Tk becomes confused
+ * about which Toplevel turns on focus for the window.
+ * Right now this is done at the Tcl layer. For example,
+ * see blt::CreateTearoff in tabset.tcl.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RelinkWindow2(tkwin, window, newParent, x, y)
+ Tk_Window tkwin; /* Child window to be linked. */
+ Window window;
+ Tk_Window newParent;
+ int x, y;
+{
+#ifdef notdef
+ TkWindow *winPtr, *parentWinPtr;
+#endif
+ if (Blt_ReparentWindow(Tk_Display(tkwin), window,
+ Tk_WindowId(newParent), x, y) != TCL_OK) {
+ return;
+ }
+#ifdef notdef
+ winPtr = (TkWindow *)tkwin;
+ parentWinPtr = (TkWindow *)newParent;
+
+ winPtr->flags &= ~TK_REPARENTED;
+ UnlinkWindow(winPtr); /* Remove the window from its parent's list */
+
+ /* Append the window onto the end of the parent's list of children */
+ winPtr->parentPtr = parentWinPtr;
+ winPtr->nextPtr = NULL;
+ if (parentWinPtr->childList == NULL) {
+ parentWinPtr->childList = winPtr;
+ } else {
+ parentWinPtr->lastChildPtr->nextPtr = winPtr;
+ }
+ parentWinPtr->lastChildPtr = winPtr;
+#endif
+}
+
+void
+Blt_UnlinkWindow(tkwin)
+ Tk_Window tkwin; /* Child window to be linked. */
+{
+ TkWindow *winPtr;
+ Window root;
+
+ root = XRootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+ if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ root, 0, 0) != TCL_OK) {
+ return;
+ }
+ winPtr = (TkWindow *)tkwin;
+ winPtr->flags &= ~TK_REPARENTED;
+#ifdef notdef
+ UnlinkWindow(winPtr); /* Remove the window from its parent's list */
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_Toplevel --
+ *
+ * Climbs up the widget hierarchy to find the top level window of
+ * the window given.
+ *
+ * Results:
+ * Returns the Tk_Window of the toplevel widget.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Window
+Blt_Toplevel(tkwin)
+ register Tk_Window tkwin;
+{
+ while (!Tk_IsTopLevel(tkwin)) {
+ tkwin = Tk_Parent(tkwin);
+ }
+ return tkwin;
+}
+
+#ifdef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetRealWindowId --
+ *
+ * Returns the XID for the Tk_Window given. Starting in Tk 8.0,
+ * the toplevel widgets are wrapped by another window.
+ * Currently there's no way to get at that window, other than
+ * what is done here: query the X window hierarchy and grab the
+ * parent.
+ *
+ * Results:
+ * Returns the X Window ID of the widget. If it's a toplevel, then
+ * the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Window
+Blt_GetRealWindowId(Tk_Window tkwin)
+{
+ return (Window) GetWindowHandle(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetToplevel --
+ *
+ * Retrieves the toplevel window which is the nearest ancestor of
+ * of the specified window.
+ *
+ * Results:
+ * Returns the toplevel window or NULL if the window has no
+ * ancestor which is a toplevel.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Window
+Blt_GetToplevel(Tk_Window tkwin) /* Window for which the toplevel
+ * should be deterined. */
+{
+ while (!Tk_IsTopLevel(tkwin)) {
+ tkwin = Tk_Parent(tkwin);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ }
+ return tkwin;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RaiseToLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RaiseTopLevelWindow(Tk_Window tkwin)
+{
+ SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapTopLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapTopLevelWindow(Tk_Window tkwin)
+{
+ ShowWindow(GetWindowHandle(tkwin), SW_SHOWNORMAL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_UnmapTopLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UnmapTopLevelWindow(Tk_Window tkwin)
+{
+ ShowWindow(GetWindowHandle(tkwin), SW_HIDE);
+}
+
+int
+Blt_ReparentWindow(
+ Display *display,
+ Window window,
+ Window newParent,
+ int x, int y)
+{
+ XReparentWindow(display, window, newParent, x, y);
+ return TCL_OK;
+}
+
+#else /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetRealWindowId --
+ *
+ * Returns the XID for the Tk_Window given. Starting in Tk 8.0,
+ * the toplevel widgets are wrapped by another window.
+ * Currently there's no way to get at that window, other than
+ * what is done here: query the X window hierarchy and grab the
+ * parent.
+ *
+ * Results:
+ * Returns the X Window ID of the widget. If it's a toplevel, then
+ * the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Window
+Blt_GetRealWindowId(tkwin)
+ Tk_Window tkwin;
+{
+ return GetWindowId(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RaiseToLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RaiseTopLevelWindow(tkwin)
+ Tk_Window tkwin;
+{
+ XRaiseWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RaiseToLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ResizeTopLevelWindow(tkwin, width, height)
+ Tk_Window tkwin;
+ int width, height;
+{
+ XResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapTopLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapTopLevelWindow(tkwin)
+ Tk_Window tkwin;
+{
+ XMapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_UnmapTopLevelWindow --
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UnmapTopLevelWindow(tkwin)
+ Tk_Window tkwin;
+{
+ XUnmapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/* ARGSUSED */
+static int
+XReparentWindowErrorProc(clientData, errEventPtr)
+ ClientData clientData;
+ XErrorEvent *errEventPtr;
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+int
+Blt_ReparentWindow(display, window, newParent, x, y)
+ Display *display;
+ Window window, newParent;
+ int x, y;
+{
+ Tk_ErrorHandler handler;
+ int result;
+ int any = -1;
+
+ result = TCL_OK;
+ handler = Tk_CreateErrorHandler(display, any, X_ReparentWindow, any,
+ XReparentWindowErrorProc, &result);
+ XReparentWindow(display, window, newParent, x, y);
+ Tk_DeleteErrorHandler(handler);
+ XSync(display, False);
+ return result;
+}
+
+#endif /* WIN32 */
+
+#if (TK_MAJOR_VERSION == 4)
+#include <bltHash.h>
+static int initialized = FALSE;
+static Blt_HashTable windowTable;
+
+void
+Blt_SetWindowInstanceData(tkwin, instanceData)
+ Tk_Window tkwin;
+ ClientData instanceData;
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (!initialized) {
+ Blt_InitHashTable(&windowTable, BLT_ONE_WORD_KEYS);
+ initialized = TRUE;
+ }
+ hPtr = Blt_CreateHashEntry(&windowTable, (char *)tkwin, &isNew);
+ assert(isNew);
+ Blt_SetHashValue(hPtr, instanceData);
+}
+
+ClientData
+Blt_GetWindowInstanceData(tkwin)
+ Tk_Window tkwin;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return Blt_GetHashValue(hPtr);
+}
+
+void
+Blt_DeleteWindowInstanceData(tkwin)
+ Tk_Window tkwin;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
+ assert(hPtr);
+ Blt_DeleteHashEntry(&windowTable, hPtr);
+}
+
+#else
+
+void
+Blt_SetWindowInstanceData(tkwin, instanceData)
+ Tk_Window tkwin;
+ ClientData instanceData;
+{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+
+ winPtr->instanceData = instanceData;
+}
+
+ClientData
+Blt_GetWindowInstanceData(tkwin)
+ Tk_Window tkwin;
+{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+
+ return winPtr->instanceData;
+}
+
+void
+Blt_DeleteWindowInstanceData(tkwin)
+ Tk_Window tkwin;
+{
+}
+
+#endif
+
diff --git a/blt/src/bltWinop.c b/blt/src/bltWinop.c
new file mode 100644
index 00000000000..c1db0f5b73a
--- /dev/null
+++ b/blt/src/bltWinop.c
@@ -0,0 +1,1119 @@
+/*
+ * bltWinop.c --
+ *
+ * This module implements simple window commands for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_WINOP
+
+#include "bltImage.h"
+#include <X11/Xutil.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+#ifdef __STDC__
+static Tcl_CmdProc WinopCmd;
+#endif
+
+
+static int
+GetRealizedWindow(interp, string, tkwinPtr)
+ Tcl_Interp *interp;
+ char *string;
+ Tk_Window *tkwinPtr;
+{
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_WindowId(tkwin) == None) {
+ Tk_MakeWindowExist(tkwin);
+ }
+ *tkwinPtr = tkwin;
+ return TCL_OK;
+}
+
+static Window
+StringToWindow(interp, string)
+ Tcl_Interp *interp;
+ char *string;
+{
+ int xid;
+
+ if (string[0] == '.') {
+ Tk_Window tkwin;
+
+ if (GetRealizedWindow(interp, string, &tkwin) != TCL_OK) {
+ return None;
+ }
+ if (Tk_IsTopLevel(tkwin)) {
+ return Blt_GetRealWindowId(tkwin);
+ } else {
+ return Tk_WindowId(tkwin);
+ }
+ } else if (Tcl_GetInt(interp, string, &xid) == TCL_OK) {
+#ifdef WIN32
+ static TkWinWindow tkWinWindow;
+
+ tkWinWindow.handle = (HWND)xid;
+ tkWinWindow.winPtr = NULL;
+ tkWinWindow.type = TWD_WINDOW;
+ return (Window)&tkWinWindow;
+#else
+ return (Window)xid;
+#endif
+ }
+ return None;
+}
+
+#ifdef WIN32
+
+static int
+GetWindowSize(
+ Tcl_Interp *interp,
+ Window window,
+ int *widthPtr,
+ int *heightPtr)
+{
+ int result;
+ RECT region;
+ TkWinWindow *winPtr = (TkWinWindow *)window;
+
+ result = GetWindowRect(winPtr->handle, &region);
+ if (result) {
+ *widthPtr = region.right - region.left;
+ *heightPtr = region.bottom - region.top;
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+#else
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGeometryErrorProc --
+ *
+ * Flags errors generated from XGetGeometry calls to the X server.
+ *
+ * Results:
+ * Always returns 0.
+ *
+ * Side Effects:
+ * Sets a flag, indicating an error occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+XGeometryErrorProc(clientData, errEventPtr)
+ ClientData clientData;
+ XErrorEvent *errEventPtr;
+{
+ int *errorPtr = clientData;
+
+ *errorPtr = TCL_ERROR;
+ return 0;
+}
+
+static int
+GetWindowSize(interp, window, widthPtr, heightPtr)
+ Tcl_Interp *interp;
+ Window window;
+ int *widthPtr, *heightPtr;
+{
+ int result;
+ int any = -1;
+ int x, y, borderWidth, depth;
+ Window root;
+ Tk_ErrorHandler handler;
+ Tk_Window tkwin;
+
+ tkwin = Tk_MainWindow(interp);
+ handler = Tk_CreateErrorHandler(Tk_Display(tkwin), any, X_GetGeometry,
+ any, XGeometryErrorProc, &result);
+ result = XGetGeometry(Tk_Display(tkwin), window, &root, &x, &y,
+ (unsigned int *)widthPtr, (unsigned int *)heightPtr,
+ (unsigned int *)&borderWidth, (unsigned int *)&depth);
+ Tk_DeleteErrorHandler(handler);
+ XSync(Tk_Display(tkwin), False);
+ if (result) {
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+#endif /*WIN32*/
+
+
+#ifndef WIN32
+/*ARGSUSED*/
+static int
+ColormapOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ Tk_Window tkwin;
+#define MAXCOLORS 256
+ register XColor *colorPtr;
+ XColor colorArr[MAXCOLORS];
+ unsigned long int pixelValues[MAXCOLORS];
+ int inUse[MAXCOLORS];
+ char string[20];
+ unsigned long int *indexPtr;
+ int nFree;
+
+ if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Initially, we assume all color cells are allocated. */
+ memset((char *)inUse, 0, sizeof(int) * MAXCOLORS);
+
+ /*
+ * Start allocating color cells. This will tell us which color cells
+ * haven't already been allocated in the colormap. We'll release the
+ * cells as soon as we find out how many there are.
+ */
+ nFree = 0;
+ for (indexPtr = pixelValues, i = 0; i < MAXCOLORS; i++, indexPtr++) {
+ if (!XAllocColorCells(Tk_Display(tkwin), Tk_Colormap(tkwin),
+ False, NULL, 0, indexPtr, 1)) {
+ break;
+ }
+ inUse[*indexPtr] = TRUE;/* Indicate the cell is unallocated */
+ nFree++;
+ }
+ XFreeColors(Tk_Display(tkwin), Tk_Colormap(tkwin), pixelValues, nFree, 0);
+ for (colorPtr = colorArr, i = 0; i < MAXCOLORS; i++, colorPtr++) {
+ colorPtr->pixel = i;
+ }
+ XQueryColors(Tk_Display(tkwin), Tk_Colormap(tkwin), colorArr, MAXCOLORS);
+ for (colorPtr = colorArr, i = 0; i < MAXCOLORS; i++, colorPtr++) {
+ if (!inUse[colorPtr->pixel]) {
+ sprintf(string, "#%02x%02x%02x", (colorPtr->red >> 8),
+ (colorPtr->green >> 8), (colorPtr->blue >> 8));
+ Tcl_AppendElement(interp, string);
+ sprintf(string, "%ld", colorPtr->pixel);
+ Tcl_AppendElement(interp, string);
+ }
+ }
+ return TCL_OK;
+}
+
+#endif
+
+/*ARGSUSED*/
+static int
+LowerOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ Window window;
+ Display *display;
+
+ display = Tk_Display(Tk_MainWindow(interp));
+ for (i = 2; i < argc; i++) {
+ window = StringToWindow(interp, argv[i]);
+ if (window == None) {
+ return TCL_ERROR;
+ }
+ XLowerWindow(display, window);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+RaiseOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ Window window;
+ Display *display;
+
+ display = Tk_Display(Tk_MainWindow(interp));
+ for (i = 2; i < argc; i++) {
+ window = StringToWindow(interp, argv[i]);
+ if (window == None) {
+ return TCL_ERROR;
+ }
+ XRaiseWindow(display, window);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+MapOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ Window window;
+ Display *display;
+
+ display = Tk_Display(Tk_MainWindow(interp));
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] == '.') {
+ Tk_Window tkwin;
+ Tk_FakeWin *fakePtr;
+
+ if (GetRealizedWindow(interp, argv[i], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fakePtr = (Tk_FakeWin *) tkwin;
+ fakePtr->flags |= TK_MAPPED;
+ window = Tk_WindowId(tkwin);
+ } else {
+ int xid;
+
+ if (Tcl_GetInt(interp, argv[i], &xid) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ window = (Window)xid;
+ }
+ XMapWindow(display, window);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+MoveOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not Used. */
+ char **argv;
+{
+ int x, y;
+ Tk_Window tkwin;
+ Window window;
+ Display *display;
+
+ tkwin = Tk_MainWindow(interp);
+ display = Tk_Display(tkwin);
+ window = StringToWindow(interp, argv[2]);
+ if (window == None) {
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, tkwin, argv[3], &x) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, tkwin, argv[4], &y) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ XMoveWindow(display, window, x, y);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+UnmapOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ register int i;
+ Window window;
+ Display *display;
+
+ display = Tk_Display(Tk_MainWindow(interp));
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] == '.') {
+ Tk_Window tkwin;
+ Tk_FakeWin *fakePtr;
+
+ if (GetRealizedWindow(interp, argv[i], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fakePtr = (Tk_FakeWin *) tkwin;
+ fakePtr->flags &= ~TK_MAPPED;
+ window = Tk_WindowId(tkwin);
+ } else {
+ int xid;
+
+ if (Tcl_GetInt(interp, argv[i], &xid) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ window = (Window)xid;
+ }
+ XMapWindow(display, window);
+ }
+ return TCL_OK;
+}
+
+/* ARGSUSED */
+static int
+ChangesOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ Tk_Window tkwin;
+
+ if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_IsTopLevel(tkwin)) {
+ XSetWindowAttributes attrs;
+ Window window;
+ unsigned int mask;
+
+ window = Blt_GetRealWindowId(tkwin);
+ attrs.backing_store = WhenMapped;
+ attrs.save_under = True;
+ mask = CWBackingStore | CWSaveUnder;
+ XChangeWindowAttributes(Tk_Display(tkwin), window, mask, &attrs);
+ }
+ return TCL_OK;
+}
+
+/* ARGSUSED */
+static int
+QueryOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv; /* Not used. */
+{
+ int rootX, rootY, childX, childY;
+ Window root, child;
+ unsigned int mask;
+ Tk_Window tkwin = (Tk_Window)clientData;
+
+ /* GetCursorPos */
+ if (XQueryPointer(Tk_Display(tkwin), Tk_WindowId(tkwin), &root,
+ &child, &rootX, &rootY, &childX, &childY, &mask)) {
+ char string[200];
+
+ sprintf(string, "@%d,%d", rootX, rootY);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+WarpToOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_Window tkwin, mainWindow;
+
+ mainWindow = (Tk_Window)clientData;
+ if (argc > 2) {
+ if (argv[2][0] == '@') {
+ int x, y;
+ Window root;
+
+ if (Blt_GetXY(interp, mainWindow, argv[2], &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ root = RootWindow(Tk_Display(mainWindow),
+ Tk_ScreenNumber(mainWindow));
+ XWarpPointer(Tk_Display(mainWindow), None, root, 0, 0, 0, 0, x, y);
+ } else {
+ if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!Tk_IsMapped(tkwin)) {
+ Tcl_AppendResult(interp, "can't warp to unmapped window \"",
+ Tk_PathName(tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ XWarpPointer(Tk_Display(tkwin), None, Tk_WindowId(tkwin),
+ 0, 0, 0, 0, Tk_Width(tkwin) / 2, Tk_Height(tkwin) / 2);
+ }
+ }
+ return QueryOp(clientData, interp, 0, (char **)NULL);
+}
+
+#ifdef notdef
+static int
+ReparentOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Tk_Window tkwin;
+
+ if (GetRealizedWindow(interp, argv[2], &tkwin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ Tk_Window newParent;
+
+ if (GetRealizedWindow(interp, argv[3], &newParent) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_RelinkWindow2(tkwin, Blt_GetRealWindowId(tkwin), newParent, 0, 0);
+ } else {
+ Blt_UnlinkWindow(tkwin);
+ }
+ return TCL_OK;
+}
+#endif
+
+
+/*
+ * This is a temporary home for these image routines. They will be
+ * moved when a new image type is created for them.
+ */
+/*ARGSUSED*/
+static int
+ConvolveOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_PhotoHandle srcPhoto, destPhoto;
+ Blt_Colorimage srcImage, destImage;
+ Filter2D filter;
+ int nValues;
+ char **valueArr;
+ double *kernel;
+ double value, sum;
+ register int i;
+ int dim;
+ int result = TCL_ERROR;
+
+ srcPhoto = Blt_FindPhoto(interp, argv[2]);
+ if (srcPhoto == NULL) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPhoto = Blt_FindPhoto(interp, argv[3]);
+ if (destPhoto == NULL) {
+ Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_SplitList(interp, argv[4], &nValues, &valueArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ kernel = NULL;
+ if (nValues == 0) {
+ Tcl_AppendResult(interp, "empty kernel", (char *)NULL);
+ goto error;
+ }
+ dim = (int)sqrt((double)nValues);
+ if ((dim * dim) != nValues) {
+ Tcl_AppendResult(interp, "kernel must be square", (char *)NULL);
+ goto error;
+ }
+ kernel = Blt_Malloc(sizeof(double) * nValues);
+ sum = 0.0;
+ for (i = 0; i < nValues; i++) {
+ if (Tcl_GetDouble(interp, valueArr[i], &value) != TCL_OK) {
+ goto error;
+ }
+ kernel[i] = value;
+ sum += value;
+ }
+ filter.kernel = kernel;
+ filter.support = dim * 0.5;
+ filter.sum = (sum == 0.0) ? 1.0 : sum;
+ filter.scale = 1.0 / nValues;
+
+ srcImage = Blt_PhotoToColorimage(srcPhoto);
+ destImage = Blt_ConvolveColorimage(srcImage, &filter);
+ Blt_FreeColorimage(srcImage);
+ Blt_ColorimageToPhoto(destImage, destPhoto);
+ Blt_FreeColorimage(destImage);
+ result = TCL_OK;
+ error:
+ if (valueArr != NULL) {
+ Blt_Free(valueArr);
+ }
+ if (kernel != NULL) {
+ Blt_Free(kernel);
+ }
+ return result;
+}
+
+/*ARGSUSED*/
+static int
+QuantizeOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_PhotoHandle srcPhoto, destPhoto;
+ Tk_PhotoImageBlock src, dest;
+ Blt_Colorimage srcImage, destImage;
+ int nColors;
+ int result;
+
+ srcPhoto = Blt_FindPhoto(interp, argv[2]);
+ if (srcPhoto == NULL) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_PhotoGetImage(srcPhoto, &src);
+ if ((src.width <= 1) || (src.height <= 1)) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPhoto = Blt_FindPhoto(interp, argv[3]);
+ if (destPhoto == NULL) {
+ Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_PhotoGetImage(destPhoto, &dest);
+ if ((dest.width != src.width) || (dest.height != src.height)) {
+ Tk_PhotoSetSize(destPhoto, src.width, src.height);
+ }
+ if (Tcl_GetInt(interp, argv[4], &nColors) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ srcImage = Blt_PhotoToColorimage(srcPhoto);
+ destImage = Blt_PhotoToColorimage(destPhoto);
+ result = Blt_QuantizeColorimage(srcImage, destImage, nColors);
+ if (result == TCL_OK) {
+ Blt_ColorimageToPhoto(destImage, destPhoto);
+ }
+ Blt_FreeColorimage(srcImage);
+ Blt_FreeColorimage(destImage);
+ return result;
+}
+
+
+/*ARGSUSED*/
+static int
+ReadJPEGOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+#if HAVE_JPEGLIB_H
+ Tk_PhotoHandle photo; /* The photo image to write into. */
+
+ photo = Blt_FindPhoto(interp, argv[3]);
+ if (photo == NULL) {
+ Tcl_AppendResult(interp, "image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return Blt_JPEGToPhoto(interp, argv[2], photo);
+#else
+ Tcl_AppendResult(interp, "JPEG support not compiled", (char *)NULL);
+ return TCL_ERROR;
+#endif
+}
+
+
+/*ARGSUSED*/
+static int
+GradientOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_PhotoHandle photo;
+ Tk_PhotoImageBlock src;
+ XColor *leftPtr, *rightPtr;
+ Tk_Window tkwin;
+ double range[3];
+ double left[3];
+ Pix32 *destPtr;
+ Blt_Colorimage destImage;
+
+ tkwin = Tk_MainWindow(interp);
+ photo = Blt_FindPhoto(interp, argv[2]);
+ if (photo == NULL) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_PhotoGetImage(photo, &src);
+ leftPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[3]));
+ if (leftPtr == NULL) {
+ return TCL_ERROR;
+ }
+ rightPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(argv[4]));
+ if (leftPtr == NULL) {
+ return TCL_ERROR;
+ }
+ left[0] = (double)(leftPtr->red >> 8);
+ left[1] = (double)(leftPtr->green >> 8);
+ left[2] = (double)(leftPtr->blue >> 8);
+ range[0] = (double)((rightPtr->red - leftPtr->red) / 257.0);
+ range[1] = (double)((rightPtr->green - leftPtr->green) / 257.0);
+ range[2] = (double)((rightPtr->blue - leftPtr->blue) / 257.0);
+
+ destImage = Blt_CreateColorimage(src.width, src.height);
+ destPtr = Blt_ColorimageBits(destImage);
+#define CLAMP(c) ((((c) < 0.0) ? 0.0 : ((c) > 1.0) ? 1.0 : (c)))
+ if (strcmp(argv[5], "linear") == 0) {
+ register int x, y;
+ double t;
+
+ for (y = 0; y < src.height; y++) {
+ for (x = 0; x < src.width; x++) {
+ t = (double)x * (drand48() * 0.10 - 0.05);
+ t = CLAMP(t);
+ destPtr->Red = (unsigned char)(left[0] + t * range[0]);
+ destPtr->Green = (unsigned char)(left[1] + t * range[1]);
+ destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else if (strcmp(argv[5], "radial") == 0) {
+ register int x, y;
+ register double dx, dy;
+ double dy2;
+ double t;
+ double midX, midY;
+
+ midX = midY = 0.5;
+ for (y = 0; y < src.height; y++) {
+ dy = (y / (double)src.height) - midY;
+ dy2 = dy * dy;
+ for (x = 0; x < src.width; x++) {
+ dx = (x / (double)src.width) - midX;
+ t = 1.0 - (double)sqrt(dx * dx + dy2);
+ t += t * (drand48() * 0.10 - 0.05);
+ t = CLAMP(t);
+ destPtr->Red = (unsigned char)(left[0] + t * range[0]);
+ destPtr->Green = (unsigned char)(left[1] + t * range[1]);
+ destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else if (strcmp(argv[5], "rectangular") == 0) {
+ register int x, y;
+ register double dx, dy;
+ double t, px, py;
+ double midX, midY;
+ double cosTheta, sinTheta;
+ double angle;
+
+ angle = M_PI_2 * -0.3;
+ cosTheta = cos(angle);
+ sinTheta = sin(angle);
+
+ midX = 0.5, midY = 0.5;
+ for (y = 0; y < src.height; y++) {
+ dy = (y / (double)src.height) - midY;
+ for (x = 0; x < src.width; x++) {
+ dx = (x / (double)src.width) - midX;
+ px = dx * cosTheta - dy * sinTheta;
+ py = dx * sinTheta + dy * cosTheta;
+ t = FABS(px) + FABS(py);
+ t += t * (drand48() * 0.10 - 0.05);
+ t = CLAMP(t);
+ destPtr->Red = (unsigned char)(left[0] + t * range[0]);
+ destPtr->Green = (unsigned char)(left[1] + t * range[1]);
+ destPtr->Blue = (unsigned char)(left[2] + t * range[2]);
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ } else if (strcmp(argv[5], "blank") == 0) {
+ register int x, y;
+
+ for (y = 0; y < src.height; y++) {
+ for (x = 0; x < src.width; x++) {
+ destPtr->Red = (unsigned char)0xFF;
+ destPtr->Green = (unsigned char)0xFF;
+ destPtr->Blue = (unsigned char)0xFF;
+ destPtr->Alpha = (unsigned char)-1;
+ destPtr++;
+ }
+ }
+ }
+ Blt_ColorimageToPhoto(destImage, photo);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ResampleOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_PhotoHandle srcPhoto, destPhoto;
+ Tk_PhotoImageBlock src, dest;
+ ResampleFilter *filterPtr, *vertFilterPtr, *horzFilterPtr;
+ char *filterName;
+
+ srcPhoto = Blt_FindPhoto(interp, argv[2]);
+ if (srcPhoto == NULL) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPhoto = Blt_FindPhoto(interp, argv[3]);
+ if (destPhoto == NULL) {
+ Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ filterName = (argc > 4) ? argv[4] : "none";
+ if (Blt_GetResampleFilter(interp, filterName, &filterPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vertFilterPtr = horzFilterPtr = filterPtr;
+ if ((filterPtr != NULL) && (argc > 5)) {
+ if (Blt_GetResampleFilter(interp, argv[5], &filterPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vertFilterPtr = filterPtr;
+ }
+ Tk_PhotoGetImage(srcPhoto, &src);
+ if ((src.width <= 1) || (src.height <= 1)) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tk_PhotoGetImage(destPhoto, &dest);
+ if ((dest.width <= 1) || (dest.height <= 1)) {
+ Tk_PhotoSetSize(destPhoto, src.width, src.height);
+ goto copyImage;
+ }
+ if ((src.width == dest.width) && (src.height == dest.height)) {
+ copyImage:
+ /* Source and destination image sizes are the same. Don't
+ * resample. Simply make copy of image */
+ dest.width = src.width;
+ dest.height = src.height;
+ dest.pixelPtr = src.pixelPtr;
+ dest.pixelSize = src.pixelSize;
+ dest.pitch = src.pitch;
+ dest.offset[0] = src.offset[0];
+ dest.offset[1] = src.offset[1];
+ dest.offset[2] = src.offset[2];
+ Tk_PhotoPutBlock(destPhoto, &dest, 0, 0, dest.width, dest.height);
+ return TCL_OK;
+ }
+ if (filterPtr == NULL) {
+ Blt_ResizePhoto(srcPhoto, 0, 0, src.width, src.height, destPhoto);
+ } else {
+ Blt_ResamplePhoto(srcPhoto, 0, 0, src.width, src.height, destPhoto,
+ horzFilterPtr, vertFilterPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+RotateOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_PhotoHandle srcPhoto, destPhoto;
+ Blt_Colorimage srcImage, destImage;
+ double theta;
+
+ srcPhoto = Blt_FindPhoto(interp, argv[2]);
+ if (srcPhoto == NULL) {
+ Tcl_AppendResult(interp, "image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPhoto = Blt_FindPhoto(interp, argv[3]);
+ if (destPhoto == NULL) {
+ Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_ExprDouble(interp, argv[4], &theta) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ srcImage = Blt_PhotoToColorimage(srcPhoto);
+ destImage = Blt_RotateColorimage(srcImage, theta);
+
+ Blt_ColorimageToPhoto(destImage, destPhoto);
+ Blt_FreeColorimage(srcImage);
+ Blt_FreeColorimage(destImage);
+ return TCL_OK;
+}
+/*
+ * --------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ * Snaps a picture of a window and stores it in a designated
+ * photo image. The window must be completely visible or
+ * the snap will fail.
+ *
+ * Results:
+ * Returns a standard Tcl result. interp->result contains
+ * the list of the graph coordinates. If an error occurred
+ * while parsing the window positions, TCL_ERROR is returned,
+ * then interp->result will contain an error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SnapOp(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_Window tkwin;
+ int width, height, destWidth, destHeight;
+ Window window;
+
+ tkwin = Tk_MainWindow(interp);
+ window = StringToWindow(interp, argv[2]);
+ if (window == None) {
+ return TCL_ERROR;
+ }
+ if (GetWindowSize(interp, window, &width, &height) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't get window geometry of \"", argv[2],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ destWidth = width, destHeight = height;
+ if ((argc > 4) && (Blt_GetPixels(interp, tkwin, argv[4], PIXELS_POSITIVE,
+ &destWidth) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((argc > 5) && (Blt_GetPixels(interp, tkwin, argv[5], PIXELS_POSITIVE,
+ &destHeight) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ return Blt_SnapPhoto(interp, tkwin, window, 0, 0, width, height, destWidth,
+ destHeight, argv[3], GAMMA);
+}
+
+/*ARGSUSED*/
+static int
+SubsampleOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window of the interpreter. */
+ Tcl_Interp *interp;
+ int argc; /* Not used. */
+ char **argv;
+{
+ Tk_Window tkwin;
+ Tk_PhotoHandle srcPhoto, destPhoto;
+ Tk_PhotoImageBlock src, dest;
+ ResampleFilter *filterPtr, *vertFilterPtr, *horzFilterPtr;
+ char *filterName;
+ int flag;
+ int x, y;
+ int width, height;
+
+ srcPhoto = Blt_FindPhoto(interp, argv[2]);
+ if (srcPhoto == NULL) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ destPhoto = Blt_FindPhoto(interp, argv[3]);
+ if (destPhoto == NULL) {
+ Tcl_AppendResult(interp, "destination image \"", argv[3], "\" doesn't",
+ " exist or is not a photo image", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = (Tk_Window)clientData;
+ flag = PIXELS_NONNEGATIVE;
+ if (Blt_GetPixels(interp, tkwin, argv[4], flag, &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixels(interp, tkwin, argv[5], flag, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flag = PIXELS_POSITIVE;
+ if (Blt_GetPixels(interp, tkwin, argv[6], flag, &width) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixels(interp, tkwin, argv[7], flag, &height) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ filterName = (argc > 8) ? argv[8] : "box";
+ if (Blt_GetResampleFilter(interp, filterName, &filterPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vertFilterPtr = horzFilterPtr = filterPtr;
+ if ((filterPtr != NULL) && (argc > 9)) {
+ if (Blt_GetResampleFilter(interp, argv[9], &filterPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vertFilterPtr = filterPtr;
+ }
+ Tk_PhotoGetImage(srcPhoto, &src);
+ Tk_PhotoGetImage(destPhoto, &dest);
+ if ((src.width <= 1) || (src.height <= 1)) {
+ Tcl_AppendResult(interp, "source image \"", argv[2], "\" is empty",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (((x + width) > src.width) || ((y + height) > src.height)) {
+ Tcl_AppendResult(interp, "nonsensical dimensions for subregion: x=",
+ argv[4], " y=", argv[5], " width=", argv[6], " height=",
+ argv[7], (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((dest.width <= 1) || (dest.height <= 1)) {
+ Tk_PhotoSetSize(destPhoto, width, height);
+ }
+ if (filterPtr == NULL) {
+ Blt_ResizePhoto(srcPhoto, x, y, width, height, destPhoto);
+ } else {
+ Blt_ResamplePhoto(srcPhoto, x, y, width, height, destPhoto,
+ horzFilterPtr, vertFilterPtr);
+ }
+ return TCL_OK;
+}
+
+static Blt_OpSpec imageOps[] =
+{
+ {"convolve", 1, (Blt_Op)ConvolveOp, 6, 6,
+ "srcPhoto destPhoto filter",},
+ {"gradient", 1, (Blt_Op)GradientOp, 7, 7, "photo left right type",},
+ {"readjpeg", 3, (Blt_Op)ReadJPEGOp, 5, 5, "fileName photoName",},
+ {"resample", 3, (Blt_Op)ResampleOp, 5, 7,
+ "srcPhoto destPhoto ?horzFilter vertFilter?",},
+ {"rotate", 2, (Blt_Op)RotateOp, 6, 6, "srcPhoto destPhoto angle",},
+ {"snap", 2, (Blt_Op)SnapOp, 5, 7, "window photoName ?width height?",},
+ {"subsample", 2, (Blt_Op)SubsampleOp, 9, 11,
+ "srcPhoto destPhoto x y width height ?horzFilter? ?vertFilter?",},
+};
+
+static int nImageOps = sizeof(imageOps) / sizeof(Blt_OpSpec);
+
+/* ARGSUSED */
+static int
+ImageOp(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window of interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nImageOps, imageOps, BLT_OP_ARG2, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ clientData = (ClientData)Tk_MainWindow(interp);
+ result = (*proc) (clientData, interp, argc - 1, argv + 1);
+ return result;
+}
+
+static Blt_OpSpec winOps[] =
+{
+ {"changes", 3, (Blt_Op)ChangesOp, 3, 3, "window",},
+#ifndef WIN32
+ {"colormap", 3, (Blt_Op)ColormapOp, 3, 3, "window",},
+#endif
+ {"convolve", 3, (Blt_Op)ConvolveOp, 5, 5,
+ "srcPhoto destPhoto filter",},
+ {"image", 1, (Blt_Op)ImageOp, 2, 0, "args",},
+ {"lower", 1, (Blt_Op)LowerOp, 2, 0, "window ?window?...",},
+ {"map", 2, (Blt_Op)MapOp, 2, 0, "window ?window?...",},
+ {"move", 2, (Blt_Op)MoveOp, 5, 5, "window x y",},
+ {"quantize", 3, (Blt_Op)QuantizeOp, 4, 5, "srcPhoto destPhoto ?nColors?",},
+ {"query", 3, (Blt_Op)QueryOp, 2, 2, "",},
+ {"raise", 2, (Blt_Op)RaiseOp, 2, 0, "window ?window?...",},
+ {"readjpeg", 3, (Blt_Op)ReadJPEGOp, 4, 4, "fileName photoName",},
+#ifdef notdef
+ {"reparent", 3, (Blt_Op)ReparentOp, 3, 4, "window ?parent?",},
+#endif
+ {"resample", 3, (Blt_Op)ResampleOp, 4, 6,
+ "srcPhoto destPhoto ?horzFilter vertFilter?",},
+ {"snap", 2, (Blt_Op)SnapOp, 4, 6,
+ "window photoName ?width height?",},
+ {"subsample", 2, (Blt_Op)SubsampleOp, 8, 10,
+ "srcPhoto destPhoto x y width height ?horzFilter? ?vertFilter?",},
+ {"unmap", 1, (Blt_Op)UnmapOp, 2, 0, "window ?window?...",},
+ {"warpto", 1, (Blt_Op)WarpToOp, 2, 5, "?window?",},
+};
+
+static int nWinOps = sizeof(winOps) / sizeof(Blt_OpSpec);
+
+/* ARGSUSED */
+static int
+WinopCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window of interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Blt_Op proc;
+ int result;
+
+ proc = Blt_GetOp(interp, nWinOps, winOps, BLT_OP_ARG1, argc, argv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ clientData = (ClientData)Tk_MainWindow(interp);
+ result = (*proc) (clientData, interp, argc, argv);
+ return result;
+}
+
+int
+Blt_WinopInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec = {"winop", WinopCmd,};
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_WINOP */
diff --git a/blt/src/missing.h b/blt/src/missing.h
new file mode 100644
index 00000000000..f396c5d5b52
--- /dev/null
+++ b/blt/src/missing.h
@@ -0,0 +1,114 @@
+#ifndef _MISSING_H
+#define _MISSING_H
+
+#include <winspool.h>
+
+#ifndef DeleteBitmap
+#define DeleteBitmap(hbm) DeleteObject((HGDIOBJ)(HBITMAP)(hbm))
+#endif
+#ifndef DeleteBrush
+#define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
+#endif
+#ifndef DeleteFont
+#define DeleteFont(hfont) DeleteObject((HGDIOBJ)(HFONT)(hfont))
+#endif
+#ifndef DeletePalette
+#define DeletePalette(hpal) DeleteObject((HGDIOBJ)(HPALETTE)(hpal))
+#endif
+#ifndef DeletePen
+#define DeletePen(hpen) DeleteObject((HGDIOBJ)(HPEN)(hpen))
+#endif
+#ifndef SelectBitmap
+#define SelectBitmap(hdc, hbm) ((HBITMAP)SelectObject((hdc), (HGDIOBJ)(HBITMAP)(hbm)))
+#endif
+#ifndef SelectBrush
+#define SelectBrush(hdc, hbr) ((HBRUSH)SelectObject((hdc), (HGDIOBJ)(HBRUSH)(hbr)))
+#endif
+#ifndef SelectFont
+#define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
+#endif
+#ifndef SelectPen
+#define SelectPen(hdc, hpen) ((HPEN)SelectObject((hdc), (HGDIOBJ)(HPEN)(hpen)))
+#endif
+#ifndef GetNextWindow
+#define GetNextWindow(hWnd,wCmd) GetWindow((hWnd),(wCmd))
+#endif
+#ifndef GetStockBrush
+#define GetStockBrush(i) ((HBRUSH)GetStockObject(i))
+#endif
+#ifndef GetStockPen
+#define GetStockPen(i) ((HPEN)GetStockObject(i))
+#endif
+
+
+#define DM_SPECVERSION 0x0401
+
+#define DMPAPER_ISO_B4 42 /* B4 (ISO) 250 x 353 mm */
+#define DMPAPER_JAPANESE_POSTCARD 43 /* Japanese Postcard 100 x 148 mm */
+#define DMPAPER_9X11 44 /* 9 x 11 in */
+#define DMPAPER_10X11 45 /* 10 x 11 in */
+#define DMPAPER_15X11 46 /* 15 x 11 in */
+#define DMPAPER_ENV_INVITE 47 /* Envelope Invite 220 x 220 mm */
+#define DMPAPER_RESERVED_48 48 /* RESERVED--DO NOT USE */
+#define DMPAPER_RESERVED_49 49 /* RESERVED--DO NOT USE */
+#define DMPAPER_LETTER_EXTRA 50 /* Letter Extra 9 \275 x 12 in */
+#define DMPAPER_LEGAL_EXTRA 51 /* Legal Extra 9 \275 x 15 in */
+#define DMPAPER_TABLOID_EXTRA 52 /* Tabloid Extra 11.69 x 18 in */
+#define DMPAPER_A4_EXTRA 53 /* A4 Extra 9.27 x 12.69 in */
+#define DMPAPER_LETTER_TRANSVERSE 54 /* Letter Transverse 8 \275 x 11 in */
+#define DMPAPER_A4_TRANSVERSE 55 /* A4 Transverse 210 x 297 mm */
+#define DMPAPER_LETTER_EXTRA_TRANSVERSE 56 /* Letter Extra Transverse 9\275 x 12 in */
+#define DMPAPER_A_PLUS 57 /* SuperA/SuperA/A4 227 x 356 mm */
+#define DMPAPER_B_PLUS 58 /* SuperB/SuperB/A3 305 x 487 mm */
+#define DMPAPER_LETTER_PLUS 59 /* Letter Plus 8.5 x 12.69 in */
+#define DMPAPER_A4_PLUS 60 /* A4 Plus 210 x 330 mm */
+#define DMPAPER_A5_TRANSVERSE 61 /* A5 Transverse 148 x 210 mm */
+#define DMPAPER_B5_TRANSVERSE 62 /* B5 (JIS) Transverse 182 x 257 mm */
+#define DMPAPER_A3_EXTRA 63 /* A3 Extra 322 x 445 mm */
+#define DMPAPER_A5_EXTRA 64 /* A5 Extra 174 x 235 mm */
+#define DMPAPER_B5_EXTRA 65 /* B5 (ISO) Extra 201 x 276 mm */
+#define DMPAPER_A2 66 /* A2 420 x 594 mm */
+#define DMPAPER_A3_TRANSVERSE 67 /* A3 Transverse 297 x 420 mm */
+#define DMPAPER_A3_EXTRA_TRANSVERSE 68 /* A3 Extra Transverse 322 x 445 mm */
+#ifndef DMPAPER_LAST
+#define DMPAPER_LAST DMPAPER_A3_EXTRA_TRANSVERSE
+#endif /*DMPAPER_LAST */
+
+#define DMPAPER_USER 256
+
+/* bin selections */
+#ifndef DMPAPER_FIRST
+#define DMBIN_FIRST DMBIN_UPPER
+#endif /*DMPAPER_FIRST*/
+
+#define DMBIN_UPPER 1
+#define DMBIN_ONLYONE 1
+#define DMBIN_LOWER 2
+#define DMBIN_MIDDLE 3
+#define DMBIN_MANUAL 4
+#define DMBIN_ENVELOPE 5
+#define DMBIN_ENVMANUAL 6
+#define DMBIN_AUTO 7
+#define DMBIN_TRACTOR 8
+#define DMBIN_SMALLFMT 9
+#define DMBIN_LARGEFMT 10
+#define DMBIN_LARGECAPACITY 11
+#define DMBIN_CASSETTE 14
+#define DMBIN_FORMSOURCE 15
+
+#ifndef DMBIN_LAST
+#define DMBIN_LAST DMBIN_FORMSOURCE
+#endif /*DMBIN_LAST*/
+
+#define DMBIN_USER 256 /* device specific bins start here */
+
+/* print qualities */
+#define DMRES_DRAFT (-1)
+#define DMRES_LOW (-2)
+#define DMRES_MEDIUM (-3)
+#define DMRES_HIGH (-4)
+
+#define DMTT_DOWNLOAD_OUTLINE 4 /* download TT fonts as outline soft fonts */
+
+
+#endif /* _MISSING_H */
diff --git a/blt/src/pure_api.c b/blt/src/pure_api.c
new file mode 100644
index 00000000000..a863c7d2bf4
--- /dev/null
+++ b/blt/src/pure_api.c
@@ -0,0 +1,126 @@
+/*
+ * Header file of Pure API function declarations.
+ *
+ * Explicitly no copyright.
+ * You may recompile and redistribute these definitions as required.
+ *
+ * NOTE1: In some situations when compiling with MFC, you should
+ * enable the setting 'Not using precompiled headers' in Visual C++
+ * to avoid a compiler diagnostic.
+ *
+ * NOTE2: This file works through the use of deep magic. Calls to functions
+ * in this file are replaced with calls into the OCI runtime system
+ * when an instrumented version of this program is run.
+ *
+ * NOTE3: The static vars avoidGy_n (where n is a unique number) are used
+ * to prevent optimizing the functions away when compiler option
+ * /Gy is set. This is needed so that NOTE2 works properly.
+ */
+static int avoidGy_1;
+static int avoidGy_2;
+static int avoidGy_3;
+static int avoidGy_4;
+static int avoidGy_5;
+static int avoidGy_6;
+static int avoidGy_7;
+static int avoidGy_8;
+static int avoidGy_9;
+static int avoidGy_10;
+static int avoidGy_11;
+static int avoidGy_12;
+static int avoidGy_13;
+static int avoidGy_14;
+static int avoidGy_15;
+static int avoidGy_16;
+static int avoidGy_17;
+static int avoidGy_18;
+static int avoidGy_19;
+static int avoidGy_20;
+static int avoidGy_21;
+static int avoidGy_22;
+static int avoidGy_23;
+static int avoidGy_24;
+static int avoidGy_25;
+static int avoidGy_26;
+static int avoidGy_27;
+static int avoidGy_28;
+static int avoidGy_29;
+static int avoidGy_30;
+static int avoidGy_31;
+static int avoidGy_32;
+static int avoidGy_33;
+static int avoidGy_34;
+static int avoidGy_35;
+static int avoidGy_36;
+static int avoidGy_37;
+static int avoidGy_38;
+static int avoidGy_39;
+static int avoidGy_40;
+static int avoidGy_41;
+static int avoidGy_42;
+static int avoidGy_43;
+static int avoidGy_44;
+static int avoidGy_45;
+static int avoidGy_46;
+static int avoidGy_47;
+static int avoidGy_48;
+static int avoidGy_49;
+static int avoidGy_50;
+static int avoidGy_51;
+static int avoidGy_52;
+static int avoidGy_53;
+static int avoidGy_54;
+static int avoidGy_55;
+static int avoidGy_56;
+static int avoidGy_57;
+static int avoidGy_58;
+static int avoidGy_59;
+static int avoidGy_60;
+static int avoidGy_PL_01;
+__declspec(dllexport) int __cdecl PurePrintf(const char *fmt, ...) { avoidGy_1++; fmt; return 0; }
+__declspec(dllexport) int __cdecl PurifyIsRunning(void) { avoidGy_2++; return 0; }
+__declspec(dllexport) int __cdecl PurifyPrintf(const char *fmt, ...) { avoidGy_3++; fmt; return 0; }
+__declspec(dllexport) int __cdecl PurifyNewInuse(void) { avoidGy_4++; return 0; }
+__declspec(dllexport) int __cdecl PurifyAllInuse(void) { avoidGy_5++; return 0; }
+__declspec(dllexport) int __cdecl PurifyClearInuse(void) { avoidGy_6++; return 0; }
+__declspec(dllexport) int __cdecl PurifyNewLeaks(void) { avoidGy_7++; return 0; }
+__declspec(dllexport) int __cdecl PurifyAllLeaks(void) { avoidGy_8++; return 0; }
+__declspec(dllexport) int __cdecl PurifyClearLeaks(void) { avoidGy_9++; return 0; }
+__declspec(dllexport) int __cdecl PurifyAllHandlesInuse(void) { avoidGy_10++; return 0; }
+__declspec(dllexport) int __cdecl PurifyNewHandlesInuse(void) { avoidGy_11++; return 0; }
+__declspec(dllexport) int __cdecl PurifyDescribe(void *addr) { avoidGy_12++; addr; return 0; }
+__declspec(dllexport) int __cdecl PurifyWhatColors(void *addr, int size) { avoidGy_13++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyAssertIsReadable(const void *addr, int size) { avoidGy_14++; addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyAssertIsWritable(const void *addr, int size) { avoidGy_15++; addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsReadable(const void *addr, int size) { avoidGy_16++; addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsWritable(const void *addr, int size) { avoidGy_17++; addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsInitialized(const void *addr, int size) { avoidGy_18++; addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyRed(void *addr, int size) { avoidGy_19++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyGreen(void *addr, int size) { avoidGy_20++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyYellow(void *addr, int size) { avoidGy_21++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyBlue(void *addr, int size) { avoidGy_22++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkAsInitialized(void *addr, int size) { avoidGy_23++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkAsUninitialized(void *addr, int size) { avoidGy_24++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkForTrap(void *addr, int size) { avoidGy_25++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkForNoTrap(void *addr, int size) { avoidGy_26++; addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr)
+ { avoidGy_27++; hHeap; dwFlags; addr; return 1; }
+__declspec(dllexport) int __cdecl PurifySetLateDetectScanCounter(int counter) { avoidGy_28++; counter; return 0; };
+__declspec(dllexport) int __cdecl PurifySetLateDetectScanInterval(int seconds) { avoidGy_29++; seconds; return 0; };
+__declspec(dllexport) int __cdecl CoverageIsRunning(void) { avoidGy_30++; return 0; }
+__declspec(dllexport) int __cdecl CoverageDisableRecordingData(void) { avoidGy_31++; return 0; }
+__declspec(dllexport) int __cdecl CoverageStartRecordingData(void) { avoidGy_32++; return 0; }
+__declspec(dllexport) int __cdecl CoverageStopRecordingData(void) { avoidGy_33++; return 0; }
+__declspec(dllexport) int __cdecl CoverageClearData(void) { avoidGy_34++; return 0; }
+__declspec(dllexport) int __cdecl CoverageIsRecordingData(void) { avoidGy_35++; return 0; }
+__declspec(dllexport) int __cdecl CoverageAddAnnotation(char *str) { avoidGy_36++; str; return 0; }
+__declspec(dllexport) int __cdecl CoverageSaveData(void) { avoidGy_37++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyIsRunning(void) { avoidGy_42++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyDisableRecordingData(void) { avoidGy_43++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyStartRecordingData(void) { avoidGy_44++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyStopRecordingData(void) { avoidGy_45++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyClearData(void) { avoidGy_46++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyIsRecordingData(void) { avoidGy_47++; return 0; }
+__declspec(dllexport) int __cdecl QuantifyAddAnnotation(char *str) { avoidGy_48++; str; return 0; }
+__declspec(dllexport) int __cdecl QuantifySaveData(void) { avoidGy_49++; return 0; }
+__declspec(dllexport) int __cdecl PurelockIsRunning(void) { avoidGy_PL_01++; return 0; }
diff --git a/blt/src/shared/Makefile.in b/blt/src/shared/Makefile.in
new file mode 100644
index 00000000000..1b7fbb5fc84
--- /dev/null
+++ b/blt/src/shared/Makefile.in
@@ -0,0 +1,325 @@
+# ------------------------------------------------------------------------
+# Makefile for shared version of BLT library
+# ------------------------------------------------------------------------
+
+SHLIB_SUFFIX = @SHLIB_SUFFIX@
+version = @BLT_MAJOR_VERSION@@BLT_MINOR_VERSION@
+
+# ------------------------------------------------------------------------
+# C Compiler options
+# ------------------------------------------------------------------------
+
+CC = @CC@
+CFLAGS = @CFLAGS@
+EXTRA_CFLAGS = @GCCFLAGS@ @SHLIB_CFLAGS@
+DEFINES = @DEFINES@
+DEF_BLTINIT = -DBLT_LIBRARY=\"$(scriptdir)\"
+SHLIB_LD_FLAGS = @SHLIB_LD_FLAGS@ @LD_RUN_PATH@
+SHLIB_TCL_ONLY_LIB_SPECS = @SHLIB_TCL_ONLY_LIB_SPECS@
+SHLIB_LIB_SPECS = @SHLIB_LIB_SPECS@
+SHLIB_LD = @SHLIB_LD@
+LDFLAGS = @LDFLAGS@ @LD_RUN_PATH@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+libdir = @libdir@
+bindir = $(exec_prefix)/bin
+srcdir = @srcdir@/..
+
+instdirs = $(exec_prefix) $(libdir)
+
+scriptdir = @BLT_LIBRARY@
+
+LIBS = @LIB_SPECS@ @EXTRA_LIB_SPECS@
+TCL_ONLY_LIB_SPECS = @TCL_ONLY_LIB_SPECS@ @EXTRA_LIB_SPECS@
+
+# ------------------------------------------------------------------------
+# Don't edit anything beyond this point
+# ------------------------------------------------------------------------
+
+N_OBJS = bltTed.o
+V3_OBJS = bltTri.o bltGrMt.o
+
+TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o
+
+GRAPH_OBJS = bltGrAxis.o \
+ bltGrBar.o \
+ bltGrElem.o \
+ bltGrGrid.o \
+ bltGrHairs.o \
+ bltGrLegd.o \
+ bltGrLine.o \
+ bltGrMarker.o \
+ bltGrMisc.o \
+ bltGrPen.o \
+ bltGrPs.o \
+ bltGraph.o
+
+TCL_ONLY_OBJS = bltAlloc.o \
+ bltArrayObj.o \
+ bltBgexec.o \
+ bltChain.o \
+ bltDebug.o \
+ bltHash.o \
+ bltList.o \
+ bltNsUtil.o \
+ bltParse.o \
+ bltPool.o \
+ bltSpline.o \
+ bltSwitch.o \
+ bltTree.o \
+ bltTreeCmd.o \
+ bltUnixPipe.o \
+ bltUtil.o \
+ bltVector.o \
+ bltVecMath.o \
+ bltVecCmd.o \
+ bltVecObjCmd.o \
+ bltWatch.o
+
+OBJS = $(GRAPH_OBJS) \
+ $(TCL_ONLY_OBJS) \
+ bltBeep.o \
+ bltBind.o \
+ bltBitmap.o \
+ bltBusy.o \
+ bltCanvEps.o \
+ bltColor.o \
+ bltConfig.o \
+ bltContainer.o \
+ bltCutbuffer.o \
+ bltDragdrop.o \
+ bltHierbox.o \
+ bltHtext.o \
+ bltImage.o \
+ bltUnixImage.o \
+ bltPs.o \
+ bltTable.o \
+ bltTabnotebook.o \
+ bltTabset.o \
+ bltText.o \
+ bltTile.o \
+ bltTreeView.o \
+ bltTreeViewCmd.o \
+ bltTreeViewColumn.o \
+ bltTreeViewEdit.o \
+ bltTreeViewStyle.o \
+ bltUnixDnd.o \
+ bltWindow.o \
+ bltObjConfig.o \
+ bltWinop.o \
+ $(TK_OBJS) $(N_OBJS)
+
+INCLUDES = -I.. -I$(srcdir) -I$(srcdir)/.. @INCLUDES@
+CC_SWITCHES = $(EXTRA_CFLAGS) $(CFLAGS) $(DEFINES) $(INCLUDES)
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_ROOT =
+SHELL = /bin/sh
+RM = rm -f
+LN_S = @LN_S@
+bltwish = bltwish$(version)
+bltsh = bltsh$(version)
+lib_so = libBLT$(version)$(SHLIB_SUFFIX)
+tcl_only_lib_so = libBLTlite$(version)$(SHLIB_SUFFIX)
+
+all: build_lib build_demo
+
+build_demo: $(bltwish) $(bltsh)
+
+$(bltwish): $(lib_so)
+ $(RM) $(bltwish)
+ $(CC) $(CC_SWITCHES) $(LDFLAGS) -o $(bltwish) \
+ $(srcdir)/bltUnixMain.c $(lib_so) $(LIBS)
+
+$(bltsh): $(tcl_only_lib_so)
+ $(RM) $(bltsh)
+ $(CC) $(CC_SWITCHES) $(LDFLAGS) -DTCL_ONLY -o $(bltsh) \
+ $(srcdir)/bltUnixMain.c $(tcl_only_lib_so) \
+ $(TCL_ONLY_LIB_SPECS)
+
+build_lib: $(lib_so) $(tcl_only_lib_so)
+
+$(lib_so): $(OBJS)
+ $(CC) -c $(CC_SWITCHES) -DBLT_LIBRARY=\"$(scriptdir)\" \
+ $(srcdir)/bltInit.c
+ $(RM) $@
+ $(SHLIB_LD) $(SHLIB_LD_FLAGS) -o $@ bltInit.o $(OBJS) \
+ $(SHLIB_LIB_SPECS)
+
+$(tcl_only_lib_so): $(TCL_ONLY_OBJS)
+ $(CC) -c $(CC_SWITCHES) -DTCL_ONLY -DBLT_LIBRARY=\"$(scriptdir)\" \
+ $(srcdir)/bltInit.c
+ $(RM) $@
+ $(SHLIB_LD) $(SHLIB_LD_FLAGS) -o $@ bltInit.o $(TCL_ONLY_OBJS) \
+ $(SHLIB_TCL_ONLY_LIB_SPECS)
+
+install: mkdirs install-lib install-demo
+
+install-demo: $(bltwish)
+ $(INSTALL) -m 0755 bltwish$(version) $(INSTALL_ROOT)$(bindir)
+ $(INSTALL) -m 0755 bltsh$(version) $(INSTALL_ROOT)$(bindir)
+
+install-lib: $(lib_so) $(tcl_only_lib_so)
+ $(INSTALL) -m 0755 $(lib_so) $(INSTALL_ROOT)$(libdir)
+ $(INSTALL) -m 0755 $(tcl_only_lib_so) $(INSTALL_ROOT)$(libdir)
+
+mkdirs:
+ @for i in $(instdirs) ; do \
+ if test -d $(INSTALL_ROOT)$$i ; then \
+ : ;\
+ else \
+ echo " mkdir $(INSTALL_ROOT)$$i" ; \
+ mkdir $(INSTALL_ROOT)$$i ; \
+ fi ; \
+ done
+clean:
+ $(RM) $(OBJS) $(lib_so) $(tcl_only_lib_so) $(bltwish) $(bltsh) \
+ *pure* .pure*
+
+distclean: clean
+ $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"*
+
+# ------------------------------------------------------------------------
+# in lieu of viewpath-ing...
+#
+bltAlloc.o: $(srcdir)/bltAlloc.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltArrayObj.o: $(srcdir)/bltArrayObj.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltBeep.o: $(srcdir)/bltBeep.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltBgexec.o: $(srcdir)/bltBgexec.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltBind.o: $(srcdir)/bltBind.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltBitmap.o: $(srcdir)/bltBitmap.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltBusy.o: $(srcdir)/bltBusy.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltCanvEps.o: $(srcdir)/bltCanvEps.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltColor.o: $(srcdir)/bltColor.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltConfig.o: $(srcdir)/bltConfig.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltObjConfig.o: $(srcdir)/bltObjConfig.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltContainer.o: $(srcdir)/bltContainer.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltCutbuffer.o: $(srcdir)/bltCutbuffer.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltDebug.o: $(srcdir)/bltDebug.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltDragdrop.o: $(srcdir)/bltDragdrop.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltUnixDnd.o: $(srcdir)/bltUnixDnd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrAxis.o: $(srcdir)/bltGrAxis.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrBar.o: $(srcdir)/bltGrBar.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrElem.o: $(srcdir)/bltGrElem.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrGrid.o: $(srcdir)/bltGrGrid.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrHairs.o: $(srcdir)/bltGrHairs.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrLegd.o: $(srcdir)/bltGrLegd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrLine.o: $(srcdir)/bltGrLine.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrMisc.o: $(srcdir)/bltGrMisc.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrPen.o: $(srcdir)/bltGrPen.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrPs.o: $(srcdir)/bltGrPs.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrMarker.o: $(srcdir)/bltGrMarker.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrMt.o: $(srcdir)/bltGrMt.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGrCont.o: $(srcdir)/bltGrCont.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltGraph.o: $(srcdir)/bltGraph.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltHash.o: $(srcdir)/bltHash.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltHierbox.o: $(srcdir)/bltHierbox.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltHtext.o: $(srcdir)/bltHtext.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltImage.o: $(srcdir)/bltImage.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltUnixImage.o: $(srcdir)/bltUnixImage.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltList.o: $(srcdir)/bltList.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltChain.o: $(srcdir)/bltChain.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltNsUtil.o: $(srcdir)/bltNsUtil.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltParse.o: $(srcdir)/bltParse.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltPool.o: $(srcdir)/bltPool.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltPs.o: $(srcdir)/bltPs.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltSpline.o: $(srcdir)/bltSpline.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltSwitch.o: $(srcdir)/bltSwitch.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTable.o: $(srcdir)/bltTable.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTabset.o: $(srcdir)/bltTabset.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTabnotebook.o: $(srcdir)/bltTabnotebook.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTed.o: $(srcdir)/bltTed.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltText.o: $(srcdir)/bltText.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTile.o: $(srcdir)/bltTile.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTree.o: $(srcdir)/bltTree.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeCmd.o: $(srcdir)/bltTreeCmd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeView.o: $(srcdir)/bltTreeView.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeViewCmd.o: $(srcdir)/bltTreeViewCmd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeViewColumn.o: $(srcdir)/bltTreeViewColumn.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeViewEdit.o: $(srcdir)/bltTreeViewEdit.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTreeViewStyle.o: $(srcdir)/bltTreeViewStyle.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltTri.o: $(srcdir)/bltTri.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltVector.o: $(srcdir)/bltVector.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltVecObjCmd.o: $(srcdir)/bltVecObjCmd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltVecCmd.o: $(srcdir)/bltVecCmd.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltVecMath.o: $(srcdir)/bltVecMath.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltWatch.o: $(srcdir)/bltWatch.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltWindow.o: $(srcdir)/bltWindow.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltWinop.o: $(srcdir)/bltWinop.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltUnixPipe.o: $(srcdir)/bltUnixPipe.c
+ $(CC) -c $(CC_SWITCHES) $?
+bltUtil.o: $(srcdir)/bltUtil.c
+ $(CC) -c $(CC_SWITCHES) $?
+tkButton.o: $(srcdir)/tkButton.c
+ $(CC) -c $(CC_SWITCHES) $?
+tkFrame.o: $(srcdir)/tkFrame.c
+ $(CC) -c $(CC_SWITCHES) $?
+tkMenubutton.o: $(srcdir)/tkMenubutton.c
+ $(CC) -c $(CC_SWITCHES) $?
+tkScrollbar.o: $(srcdir)/tkScrollbar.c
+ $(CC) -c $(CC_SWITCHES) $?
diff --git a/blt/src/tkButton.c b/blt/src/tkButton.c
new file mode 100644
index 00000000000..fc2c6b55902
--- /dev/null
+++ b/blt/src/tkButton.c
@@ -0,0 +1,2143 @@
+/*
+ * tkButton.c --
+ *
+ * This module implements a collection of button-like
+ * widgets for the Tk toolkit. The widgets implemented
+ * include labels, buttons, check buttons, and radio
+ * buttons.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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: @(#) tkButton.c 1.128 96/03/01 17:34:49
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TILEBUTTON
+
+#include "bltTile.h"
+
+extern Tk_CustomOption bltTileOption;
+/*
+ * 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 NORMAL_BG "#d9d9d9"
+#define ACTIVE_BG "#ececec"
+#define SELECT_BG "#c3c3c3"
+#define TROUGH "#c3c3c3"
+#define INDICATOR "#b03060"
+#define DISABLED "#a3a3a3"
+
+static Tk_Uid tkNormalUid, tkActiveUid, tkDisabledUid;
+
+#define DEF_BUTTON_ANCHOR "center"
+#define DEF_BUTTON_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_BUTTON_ACTIVE_BG_MONO RGB_BLACK
+#define DEF_BUTTON_ACTIVE_FG_COLOR RGB_BLACK
+#define DEF_BUTTON_ACTIVE_FG_MONO RGB_WHITE
+#define DEF_BUTTON_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_BUTTON_BG_MONO RGB_WHITE
+#define DEF_BUTTON_BITMAP ""
+#define DEF_BUTTON_BORDER_WIDTH "2"
+#define DEF_BUTTON_CURSOR ""
+#define DEF_BUTTON_COMMAND ""
+#define DEF_BUTTON_COMPOUND "none"
+#define DEF_BUTTON_DEFAULT "disabled"
+#define DEF_BUTTON_DISABLED_FG_COLOR STD_COLOR_DISABLE_FG
+#define DEF_BUTTON_DISABLED_FG_MONO ""
+#define DEF_BUTTON_FG RGB_BLACK
+#define DEF_BUTTON_FONT "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
+#define DEF_BUTTON_HEIGHT "0"
+#define DEF_BUTTON_HIGHLIGHT_BG STD_COLOR_NORMAL_BG
+#define DEF_BUTTON_HIGHLIGHT RGB_BLACK
+#define DEF_LABEL_HIGHLIGHT_WIDTH "0"
+#define DEF_BUTTON_HIGHLIGHT_WIDTH "2"
+#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_OVER_RELIEF "flat"
+#define DEF_BUTTON_PADX "3m"
+#define DEF_LABCHKRAD_PADX "1"
+#define DEF_BUTTON_PADY "1m"
+#define DEF_LABCHKRAD_PADY "1"
+#define DEF_BUTTON_RELIEF "raised"
+#define DEF_BUTTON_REPEAT_DELAY "0"
+#define DEF_LABCHKRAD_RELIEF "flat"
+#define DEF_BUTTON_SELECT_COLOR STD_COLOR_INDICATOR
+#define DEF_BUTTON_SELECT_MONO RGB_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 ""
+
+/*
+ * A data structure of the following type is kept for each
+ * widget managed by this file:
+ */
+
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the button. NULL
+ * means that the window has been destroyed. */
+ Display *display; /* Display containing widget. Needed to
+ * free up resources after tkwin is gone. */
+ Tcl_Interp *interp; /* Interpreter associated with button. */
+ Tcl_Command widgetCmd; /* Token for button's widget command. */
+ int type; /* Type of widget: restricts operations
+ * that may be performed on widget. See
+ * below for possible values. */
+
+ /*
+ * Information about what's in the button.
+ */
+
+ char *text; /* Text to display in button (malloc'ed)
+ * or NULL. */
+ int numChars; /* # of characters in text. */
+ int underline; /* Index of character to underline. < 0 means
+ * don't underline anything. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, button displays the contents
+ * of this variable. */
+ Pixmap bitmap; /* Bitmap to display or None. If not None
+ * then text and textVar are ignored. */
+ char *imageString; /* Name of image to display (malloc'ed), or
+ * NULL. If non-NULL, bitmap, text, and
+ * textVarName are ignored. */
+ Tk_Image image; /* Image to display in window, or NULL if
+ * none. */
+ char *selectImageString; /* Name of image to display when selected
+ * (malloc'ed), or NULL. */
+ Tk_Image selectImage; /* Image to display in window when selected,
+ * or NULL if none. Ignored if image is
+ * NULL. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ Tk_Uid state; /* State of button for display purposes:
+ * normal, active, or disabled. */
+ Tk_3DBorder normalBorder; /* Structure used to draw 3-D
+ * border and background when window
+ * isn't active. NULL means no such
+ * border exists. */
+ Tk_3DBorder activeBorder; /* Structure used to draw 3-D
+ * border and background when window
+ * is active. NULL means no such
+ * border exists. */
+ int borderWidth; /* Width of border. */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */
+ int overRelief; /* Value of -overrelief option: specifies a 3-d
+ * effect for the border, such as
+ * TK_RELIEF_RAISED, to be used when the mouse
+ * is over the button. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int inset; /* Total width of all borders, including
+ * traversal highlight and 3-D border.
+ * Indicates how much interior stuff must
+ * be offset from outside edges to leave
+ * room for borders. */
+ Tk_Font font; /* Information about text font, or NULL. */
+ XColor *normalFg; /* Foreground color in normal mode. */
+ XColor *activeFg; /* Foreground color in active mode. NULL
+ * means use normalFg instead. */
+ XColor *disabledFg; /* Foreground color when disabled. NULL
+ * means use normalFg with a 50% stipple
+ * instead. */
+ GC normalTextGC; /* GC for drawing text in normal mode. Also
+ * used to copy from off-screen pixmap onto
+ * screen. */
+ GC activeTextGC; /* GC for drawing text in active mode (NULL
+ * means use normalTextGC). */
+ Pixmap gray; /* Pixmap for displaying disabled text if
+ * disabledFg is NULL. */
+ GC disabledGC; /* Used to produce disabled effect. If
+ * disabledFg isn't NULL, this GC is used to
+ * draw button text or icon. Otherwise
+ * text or icon is drawn with normalGC and
+ * this GC is used to stipple background
+ * across it. For labels this is None. */
+ GC copyGC; /* Used for copying information from an
+ * off-screen pixmap to the screen. */
+ char *widthString; /* Value of -width option. Malloc'ed. */
+ char *heightString; /* Value of -height option. Malloc'ed. */
+ int width, height; /* If > 0, these specify dimensions to request
+ * for window, in characters for text and in
+ * pixels for bitmaps. In this case the actual
+ * size of the text string or bitmap is
+ * ignored in computing desired window size. */
+ int wrapLength; /* Line length (in pixels) at which to wrap
+ * onto next line. <= 0 means don't wrap
+ * except at newlines. */
+ int padX, padY; /* Extra space around text (pixels to leave
+ * on each side). Ignored for bitmaps and
+ * images. */
+ Tk_Anchor anchor; /* Where text/bitmap should be displayed
+ * inside button region. */
+ Tk_Justify justify; /* Justification to use for multi-line text. */
+ int indicatorOn; /* True means draw indicator, false means
+ * don't draw it. */
+ Tk_3DBorder selectBorder; /* For drawing indicator background, or perhaps
+ * widget background, when selected. */
+ int textWidth; /* Width needed to display text as requested,
+ * in pixels. */
+ int textHeight; /* Height needed to display text as requested,
+ * in pixels. */
+#if (TK_MAJOR_VERSION > 4)
+ Tk_TextLayout textLayout; /* Saved text layout information. */
+#endif
+ int indicatorSpace; /* Horizontal space (in pixels) allocated for
+ * display of indicator. */
+ int indicatorDiameter; /* Diameter of indicator, in pixels. */
+
+ Tk_Uid defaultState; /* Used in 8.0 (not here) */
+
+ /*
+ * For check and radio buttons, the fields below are used
+ * to manage the variable indicating the button's state.
+ */
+
+ char *selVarName; /* Name of variable used to control selected
+ * state of button. Malloc'ed (if
+ * not NULL). */
+ char *onValue; /* Value to store in variable when
+ * this button is selected. Malloc'ed (if
+ * not NULL). */
+ char *offValue; /* Value to store in variable when this
+ * button isn't selected. Malloc'ed
+ * (if not NULL). Valid only for check
+ * buttons. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *command; /* Command to execute when button is
+ * invoked; valid for buttons only.
+ * If not NULL, it's malloc-ed. */
+ char *compound; /* Value of -compound option; specifies whether
+ * the button should show both an image and
+ * text, and, if so, how. */
+ int repeatDelay; /* Value of -repeatdelay option; specifies
+ * the number of ms after which the button will
+ * start to auto-repeat its command. */
+ int repeatInterval; /* Value of -repeatinterval option; specifies
+ * the number of ms between auto-repeat
+ * invocataions of the button command. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+ Blt_Tile tile, activeTile;
+} Button;
+
+/*
+ * Possible "type" values for buttons. These are the kinds of
+ * widgets supported by this file. The ordering of the type
+ * numbers is significant: greater means more features and is
+ * used in the code.
+ */
+
+#define TYPE_LABEL 0
+#define TYPE_BUTTON 1
+#define TYPE_CHECK_BUTTON 2
+#define TYPE_RADIO_BUTTON 3
+
+/*
+ * Class names for buttons, indexed by one of the type values above.
+ */
+
+static char *classNames[] =
+{"Label", "Button", "Checkbutton", "Radiobutton"};
+
+/*
+ * Flag bits for buttons:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * SELECTED: Non-zero means this button is selected,
+ * so special highlight should be drawn.
+ * GOT_FOCUS: Non-zero means this button currently
+ * has the input focus.
+ */
+
+#define REDRAW_PENDING 1
+#define SELECTED 2
+#define GOT_FOCUS 4
+
+/*
+ * Mask values used to selectively enable entries in the
+ * configuration specs:
+ */
+
+#define LABEL_MASK TK_CONFIG_USER_BIT
+#define BUTTON_MASK TK_CONFIG_USER_BIT << 1
+#define CHECK_BUTTON_MASK TK_CONFIG_USER_BIT << 2
+#define RADIO_BUTTON_MASK TK_CONFIG_USER_BIT << 3
+#define ALL_MASK (LABEL_MASK | BUTTON_MASK \
+ | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
+
+static int configFlags[] =
+{LABEL_MASK, BUTTON_MASK,
+ CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
+
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK
+ | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK
+ | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK
+ | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK
+ | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-activetile", "activeTile", "Tile",
+ (char *)NULL, Tk_Offset(Button, activeTile),
+ ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
+ ALL_MASK | TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
+ ALL_MASK | TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, ALL_MASK},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, ALL_MASK},
+ {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
+ DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
+ ALL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
+ {TK_CONFIG_STRING, "-command", "command", "Command",
+ DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-compound", "compound", "Compound",
+ DEF_BUTTON_COMPOUND, Tk_Offset(Button, compound),
+ ALL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
+ ALL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_UID, "-default", "default", "Default",
+ DEF_BUTTON_DEFAULT, Tk_Offset(Button, defaultState), BUTTON_MASK},
+ {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
+ Tk_Offset(Button, disabledFg), BUTTON_MASK | CHECK_BUTTON_MASK
+ | RADIO_BUTTON_MASK | TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
+ Tk_Offset(Button, disabledFg), BUTTON_MASK | CHECK_BUTTON_MASK
+ | RADIO_BUTTON_MASK | TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, ALL_MASK},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_BUTTON_FONT, Tk_Offset(Button, font),
+ ALL_MASK},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
+ {TK_CONFIG_STRING, "-height", "height", "Height",
+ DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
+ Tk_Offset(Button, highlightBgColorPtr), ALL_MASK},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
+ ALL_MASK},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
+ LABEL_MASK},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_STRING, "-image", "image", "Image",
+ DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
+ ALL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
+ DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
+ CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), ALL_MASK},
+ {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
+ DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
+ CHECK_BUTTON_MASK},
+ {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
+ DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
+ CHECK_BUTTON_MASK},
+ {TK_CONFIG_RELIEF, "-overrelief", "overRelief", "OverRelief",
+ DEF_BUTTON_OVER_RELIEF, Tk_Offset(Button, overRelief),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
+ DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK},
+ {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
+ DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
+ LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
+ DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK},
+ {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
+ DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
+ LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
+ LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
+ DEF_BUTTON_REPEAT_DELAY, Tk_Offset(Button, repeatDelay),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
+ DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
+ CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_COLOR_ONLY
+ | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
+ DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
+ CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_MONO_ONLY
+ | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
+ DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
+ CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_UID, "-state", "state", "State",
+ DEF_BUTTON_STATE, Tk_Offset(Button, state),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
+ LABEL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
+ BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
+ {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
+ ALL_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Button, tile),
+ ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
+ {TK_CONFIG_INT, "-underline", "underline", "Underline",
+ DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), ALL_MASK},
+ {TK_CONFIG_STRING, "-value", "value", "Value",
+ DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
+ RADIO_BUTTON_MASK},
+ {TK_CONFIG_STRING, "-variable", "variable", "Variable",
+ DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
+ RADIO_BUTTON_MASK},
+ {TK_CONFIG_STRING, "-variable", "variable", "Variable",
+ DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
+ CHECK_BUTTON_MASK | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-width", "width", "Width",
+ DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
+ {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
+ DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), ALL_MASK},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * String to print out in error messages, identifying options for
+ * widget commands for different types of labels or buttons:
+ */
+
+static char *optionStrings[] =
+{
+ "cget or configure",
+ "cget, configure, flash, or invoke",
+ "cget, configure, deselect, flash, invoke, select, or toggle",
+ "cget, configure, deselect, flash, invoke, or select"
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ButtonCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static int ButtonCreate _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv,
+ int type));
+static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void ButtonImageProc _ANSI_ARGS_((ClientData clientData,
+ int x, int y, int width, int height,
+ int imgWidth, int imgHeight));
+static void ButtonSelectImageProc _ANSI_ARGS_((
+ ClientData clientData, int x, int y, int width,
+ int height, int imgWidth, int imgHeight));
+static char *ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static char *ButtonVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
+static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
+ Button *butPtr, int argc, char **argv,
+ int flags));
+static void DestroyButton _ANSI_ARGS_((Button *butPtr));
+static void DisplayButton _ANSI_ARGS_((ClientData clientData));
+static int InvokeButton _ANSI_ARGS_((Button *butPtr));
+
+#ifdef __STDC__
+static Blt_TileChangedProc TileChangedProc;
+static Tcl_CmdProc ButtonCmd, LabelCmd, CheckbuttonCmd, RadiobuttonCmd;
+#endif
+
+EXTERN int TkCopyAndGlobalEval _ANSI_ARGS_((Tcl_Interp *interp, char *script));
+
+#if (TK_MAJOR_VERSION > 4)
+EXTERN void TkComputeAnchor _ANSI_ARGS_((Tk_Anchor anchor, Tk_Window tkwin,
+ int padX, int padY, int innerWidth, int innerHeight, int *xPtr,
+ int *yPtr));
+#endif
+
+#if (TK_MAJOR_VERSION == 4)
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkComputeAnchor --
+ *
+ * Determine where to place a rectangle so that it will be properly
+ * anchored with respect to the given window. Used by widgets
+ * to align a box of text inside a window. When anchoring with
+ * respect to one of the sides, the rectangle be placed inside of
+ * the internal border of the window.
+ *
+ * Results:
+ * *xPtr and *yPtr set to the upper-left corner of the rectangle
+ * anchored in the window.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+TkComputeAnchor(anchor, tkwin, padX, padY, innerWidth, innerHeight, xPtr, yPtr)
+ Tk_Anchor anchor; /* Desired anchor. */
+ Tk_Window tkwin; /* Anchored with respect to this window. */
+ int padX, padY; /* Use this extra padding inside window, in
+ * addition to the internal border. */
+ int innerWidth, innerHeight;/* Size of rectangle to anchor in window. */
+ int *xPtr, *yPtr; /* Returns upper-left corner of anchored
+ * rectangle. */
+{
+ switch (anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_SW:
+ *xPtr = Tk_InternalBorderWidth(tkwin) + padX;
+ break;
+
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_S:
+ *xPtr = (Tk_Width(tkwin) - innerWidth) / 2;
+ break;
+
+ default:
+ *xPtr = Tk_Width(tkwin) - (Tk_InternalBorderWidth(tkwin) + padX)
+ - innerWidth;
+ break;
+ }
+
+ switch (anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_NE:
+ *yPtr = Tk_InternalBorderWidth(tkwin) + padY;
+ break;
+
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_E:
+ *yPtr = (Tk_Height(tkwin) - innerHeight) / 2;
+ break;
+
+ default:
+ *yPtr = Tk_Height(tkwin) - Tk_InternalBorderWidth(tkwin) - padY
+ - innerHeight;
+ break;
+ }
+}
+
+#endif
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
+ *
+ * These procedures are invoked to process the "button", "label",
+ * "radiobutton", and "checkbutton" Tcl commands. See the
+ * user documentation for details on what they do.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation. These procedures are just wrappers;
+ * they call ButtonCreate to do all of the real work.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ButtonCmd(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 ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
+}
+
+static int
+CheckbuttonCmd(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 ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
+}
+
+static int
+LabelCmd(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 ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
+}
+
+static int
+RadiobuttonCmd(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 ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonCreate --
+ *
+ * This procedure does all the real work of implementing the
+ * "button", "label", "radiobutton", and "checkbutton" Tcl
+ * commands. See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+ButtonCreate(clientData, interp, argc, argv, type)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+ int type; /* Type of button to create: TYPE_LABEL,
+ * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
+ * TYPE_RADIO_BUTTON. */
+{
+ register Button *butPtr;
+ Tk_Window tkwin;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Create the new window.
+ */
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * Initialize the data structure for the button.
+ */
+
+ butPtr = Blt_Malloc(sizeof(Button));
+ butPtr->tkwin = tkwin;
+ butPtr->display = Tk_Display(tkwin);
+ butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
+ ButtonWidgetCmd, butPtr, ButtonCmdDeletedProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(butPtr->tkwin, butPtr->widgetCmd);
+#endif /* ITCL_NAMESPACES */
+
+ butPtr->interp = interp;
+ butPtr->type = type;
+ butPtr->text = NULL;
+ butPtr->numChars = 0;
+ butPtr->underline = -1;
+ butPtr->textVarName = NULL;
+ butPtr->bitmap = None;
+ butPtr->imageString = NULL;
+ butPtr->image = NULL;
+ butPtr->selectImageString = NULL;
+ butPtr->selectImage = NULL;
+ butPtr->state = tkNormalUid;
+ butPtr->normalBorder = NULL;
+ butPtr->activeBorder = NULL;
+ butPtr->borderWidth = 0;
+ butPtr->relief = TK_RELIEF_FLAT;
+ butPtr->highlightWidth = 0;
+ butPtr->highlightBgColorPtr = NULL;
+ butPtr->highlightColorPtr = NULL;
+ butPtr->inset = 0;
+ butPtr->font = NULL;
+ butPtr->normalFg = NULL;
+ butPtr->activeFg = NULL;
+ butPtr->disabledFg = NULL;
+ butPtr->normalTextGC = None;
+ butPtr->activeTextGC = None;
+ butPtr->gray = None;
+ butPtr->disabledGC = None;
+ butPtr->copyGC = None;
+ butPtr->widthString = NULL;
+ butPtr->heightString = NULL;
+ butPtr->width = 0;
+ butPtr->height = 0;
+ butPtr->wrapLength = 0;
+ butPtr->padX = 0;
+ butPtr->padY = 0;
+ butPtr->anchor = TK_ANCHOR_CENTER;
+ butPtr->justify = TK_JUSTIFY_CENTER;
+#if (TK_MAJOR_VERSION > 4)
+ butPtr->textLayout = NULL;
+#endif
+ butPtr->indicatorOn = 0;
+ butPtr->selectBorder = NULL;
+ butPtr->indicatorSpace = 0;
+ butPtr->indicatorDiameter = 0;
+ butPtr->selVarName = NULL;
+ butPtr->onValue = NULL;
+ butPtr->offValue = NULL;
+ butPtr->cursor = None;
+ butPtr->command = NULL;
+ butPtr->takeFocus = NULL;
+ butPtr->flags = 0;
+ butPtr->tile = butPtr->activeTile = NULL;
+ butPtr->defaultState = tkDisabledUid;
+ butPtr->compound = NULL;
+ butPtr->repeatDelay = 0;
+ butPtr->overRelief = TK_RELIEF_FLAT;
+
+ Tk_SetClass(tkwin, classNames[type]);
+ Tk_CreateEventHandler(butPtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask,
+ ButtonEventProc, butPtr);
+ if (ConfigureButton(interp, butPtr, argc - 2, argv + 2,
+ configFlags[type]) != TCL_OK) {
+ Tk_DestroyWindow(butPtr->tkwin);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(butPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ButtonWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about button widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Button *butPtr = clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(butPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *)NULL);
+ goto error;
+ }
+ result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
+ (char *)butPtr, argv[2], configFlags[butPtr->type]);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
+ (char *)butPtr, (char *)NULL, configFlags[butPtr->type]);
+ } else if (argc == 3) {
+ result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
+ (char *)butPtr, argv[2],
+ configFlags[butPtr->type]);
+ } else {
+ result = ConfigureButton(interp, butPtr, argc - 2, argv + 2,
+ configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
+ && (butPtr->type >= TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " deselect\"", (char *)NULL);
+ goto error;
+ }
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ }
+ } else if (butPtr->flags & SELECTED) {
+ if (Tcl_SetVar(interp, butPtr->selVarName, "",
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ };
+ }
+ } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
+ && (butPtr->type != TYPE_LABEL)) {
+ int i;
+
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " flash\"", (char *)NULL);
+ goto error;
+ }
+ if (butPtr->state != tkDisabledUid) {
+ for (i = 0; i < 4; i++) {
+ butPtr->state = (butPtr->state == tkNormalUid)
+ ? tkActiveUid : tkNormalUid;
+ Tk_SetBackgroundFromBorder(butPtr->tkwin,
+ (butPtr->state == tkActiveUid) ? butPtr->activeBorder
+ : butPtr->normalBorder);
+ DisplayButton(butPtr);
+
+ /*
+ * Special note: must cancel any existing idle handler
+ * for DisplayButton; it's no longer needed, and DisplayButton
+ * cleared the REDRAW_PENDING flag.
+ */
+
+ Tcl_CancelIdleCall(DisplayButton, butPtr);
+#ifndef WIN32
+ XFlush(butPtr->display);
+#endif
+ Tcl_Sleep(50);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
+ && (butPtr->type > TYPE_LABEL)) {
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ " invoke\"", (char *)NULL);
+ goto error;
+ }
+ if (butPtr->state != tkDisabledUid) {
+ result = InvokeButton(butPtr);
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
+ && (butPtr->type >= TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " select\"", (char *)NULL);
+ goto error;
+ }
+ if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
+ && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " toggle\"", (char *)NULL);
+ goto error;
+ }
+ if (butPtr->flags & SELECTED) {
+ if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
+ TCL_GLOBAL_ONLY) == NULL) {
+ result = TCL_ERROR;
+ }
+ } else {
+ if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY) == NULL) {
+ result = TCL_ERROR;
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be ",
+ optionStrings[butPtr->type], (char *)NULL);
+ goto error;
+ }
+ Tcl_Release(butPtr);
+ return result;
+
+ error:
+ Tcl_Release(butPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyButton --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a button at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyButton(butPtr)
+ Button *butPtr; /* Info about button widget. */
+{
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Tk_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (butPtr->textVarName != NULL) {
+ Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonTextVarProc, butPtr);
+ }
+ if (butPtr->image != NULL) {
+ Tk_FreeImage(butPtr->image);
+ }
+ if (butPtr->selectImage != NULL) {
+ Tk_FreeImage(butPtr->selectImage);
+ }
+ if (butPtr->normalTextGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
+ }
+ if (butPtr->activeTextGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
+ }
+ if (butPtr->gray != None) {
+ Tk_FreeBitmap(butPtr->display, butPtr->gray);
+ }
+ if (butPtr->disabledGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->disabledGC);
+ }
+ if (butPtr->copyGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->copyGC);
+ }
+ if (butPtr->selVarName != NULL) {
+ Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData)butPtr);
+ }
+ if (butPtr->activeTile != NULL) {
+ Blt_FreeTile(butPtr->activeTile);
+ }
+ if (butPtr->tile != NULL) {
+ Blt_FreeTile(butPtr->tile);
+ }
+#if (TK_MAJOR_VERSION > 4)
+ Tk_FreeTextLayout(butPtr->textLayout);
+#endif
+ Tk_FreeOptions(configSpecs, (char *)butPtr, butPtr->display,
+ configFlags[butPtr->type]);
+ Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
+}
+
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Button *butPtr = clientData;
+
+ if (butPtr->tkwin != NULL) {
+ /*
+ * Arrange for the button to be redisplayed.
+ */
+ if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureButton --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a button widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for butPtr; old resources get freed, if there
+ * were any. The button is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureButton(interp, butPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Button *butPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ XGCValues gcValues;
+ GC newGC;
+ unsigned long mask;
+ Tk_Image image;
+
+ /*
+ * Eliminate any existing trace on variables monitored by the button.
+ */
+
+ if (butPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonTextVarProc, (ClientData)butPtr);
+ }
+ if (butPtr->selVarName != NULL) {
+ Tcl_UntraceVar(interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData)butPtr);
+ }
+ if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
+ argc, argv, (char *)butPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * A few options need special processing, such as setting the
+ * background from a 3-D border, or filling in complicated
+ * defaults that couldn't be specified to Tk_ConfigureWidget.
+ */
+
+ if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
+ Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
+ } else {
+ Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
+ if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
+ && (butPtr->state != tkDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
+ "\": must be normal, active, or disabled", (char *)NULL);
+ butPtr->state = tkNormalUid;
+ return TCL_ERROR;
+ }
+ }
+
+ if ((butPtr->defaultState != tkActiveUid)
+ && (butPtr->defaultState != tkDisabledUid)
+ && (butPtr->defaultState != tkNormalUid)) {
+ Tcl_AppendResult(interp, "bad -default value \"", butPtr->defaultState,
+ "\": must be normal, active, or disabled", (char *)NULL);
+ butPtr->defaultState = tkDisabledUid;
+ return TCL_ERROR;
+ }
+ if (butPtr->highlightWidth < 0) {
+ butPtr->highlightWidth = 0;
+ }
+ gcValues.font = Tk_FontId(butPtr->font);
+ gcValues.foreground = butPtr->normalFg->pixel;
+ gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
+
+ if (butPtr->tile != NULL) {
+ Blt_SetTileChangedProc(butPtr->tile, TileChangedProc,
+ (ClientData)butPtr);
+ }
+ if (butPtr->activeTile != NULL) {
+ Blt_SetTileChangedProc(butPtr->activeTile, TileChangedProc,
+ (ClientData)butPtr);
+ }
+ /*
+ * Note: GraphicsExpose events are disabled in normalTextGC because it's
+ * used to copy stuff from an off-screen pixmap onto the screen (we know
+ * that there's no problem with obscured areas).
+ */
+
+ gcValues.graphics_exposures = False;
+ newGC = Tk_GetGC(butPtr->tkwin,
+ GCForeground | GCBackground | GCFont | GCGraphicsExposures,
+ &gcValues);
+ if (butPtr->normalTextGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
+ }
+ butPtr->normalTextGC = newGC;
+
+ if (butPtr->activeFg != NULL) {
+ gcValues.font = Tk_FontId(butPtr->font);
+ gcValues.foreground = butPtr->activeFg->pixel;
+ gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
+ newGC = Tk_GetGC(butPtr->tkwin,
+ GCForeground | GCBackground | GCFont, &gcValues);
+ if (butPtr->activeTextGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
+ }
+ butPtr->activeTextGC = newGC;
+ }
+ if (butPtr->type != TYPE_LABEL) {
+ gcValues.font = Tk_FontId(butPtr->font);
+ gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
+ if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
+ gcValues.foreground = butPtr->disabledFg->pixel;
+ mask = GCForeground | GCBackground | GCFont;
+ } else {
+ gcValues.foreground = gcValues.background;
+ if (butPtr->gray == None) {
+ butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
+ Tk_GetUid("gray50"));
+ if (butPtr->gray == None) {
+ return TCL_ERROR;
+ }
+ }
+ gcValues.fill_style = FillStippled;
+ gcValues.stipple = butPtr->gray;
+ mask = GCForeground | GCFillStyle | GCStipple;
+ }
+ newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
+ if (butPtr->disabledGC != None) {
+ Tk_FreeGC(butPtr->display, butPtr->disabledGC);
+ }
+ butPtr->disabledGC = newGC;
+ }
+ if (butPtr->copyGC == None) {
+ butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
+ }
+ if (butPtr->padX < 0) {
+ butPtr->padX = 0;
+ }
+ if (butPtr->padY < 0) {
+ butPtr->padY = 0;
+ }
+ if (butPtr->type >= TYPE_CHECK_BUTTON) {
+ char *value;
+
+ if (butPtr->selVarName == NULL) {
+ butPtr->selVarName = Blt_Malloc(strlen(Tk_Name(butPtr->tkwin)) + 1);
+ strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
+ }
+ /*
+ * Select the button if the associated variable has the
+ * appropriate value, initialize the variable if it doesn't
+ * exist, then set a trace on the variable to monitor future
+ * changes to its value.
+ */
+
+ value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
+ butPtr->flags &= ~SELECTED;
+ if (value != NULL) {
+ if (strcmp(value, butPtr->onValue) == 0) {
+ butPtr->flags |= SELECTED;
+ }
+ } else {
+ if (Tcl_SetVar(interp, butPtr->selVarName,
+ (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_TraceVar(interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData)butPtr);
+ }
+ /*
+ * Get the images for the widget, if there are any. Allocate the
+ * new images before freeing the old ones, so that the reference
+ * counts don't go to zero and cause image data to be discarded.
+ */
+
+ if (butPtr->imageString != NULL) {
+ image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
+ butPtr->imageString, ButtonImageProc, (ClientData)butPtr);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ image = NULL;
+ }
+ if (butPtr->image != NULL) {
+ Tk_FreeImage(butPtr->image);
+ }
+ butPtr->image = image;
+ if (butPtr->selectImageString != NULL) {
+ image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
+ butPtr->selectImageString, ButtonSelectImageProc,
+ (ClientData)butPtr);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ image = NULL;
+ }
+ if (butPtr->selectImage != NULL) {
+ Tk_FreeImage(butPtr->selectImage);
+ }
+ butPtr->selectImage = image;
+
+ if ((butPtr->image == NULL) && (butPtr->bitmap == None)
+ && (butPtr->textVarName != NULL)) {
+ /*
+ * The button must display the value of a variable: set up a trace
+ * on the variable's value, create the variable if it doesn't
+ * exist, and fetch its current value.
+ */
+
+ char *value;
+
+ value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (butPtr->text != NULL) {
+ Blt_Free(butPtr->text);
+ }
+ butPtr->text = Blt_Malloc(strlen(value) + 1);
+ strcpy(butPtr->text, value);
+ }
+ Tcl_TraceVar(interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonTextVarProc, (ClientData)butPtr);
+ }
+ if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
+ if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
+ &butPtr->width) != TCL_OK) {
+ widthError:
+ Tcl_AddErrorInfo(interp, "\n (processing -width option)");
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
+ &butPtr->height) != TCL_OK) {
+ heightError:
+ Tcl_AddErrorInfo(interp, "\n (processing -height option)");
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
+ != TCL_OK) {
+ goto widthError;
+ }
+ if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
+ != TCL_OK) {
+ goto heightError;
+ }
+ }
+ ComputeButtonGeometry(butPtr);
+
+ /*
+ * Lastly, arrange for the button to be redisplayed.
+ */
+
+ if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayButton --
+ *
+ * This procedure is invoked to display a button widget. It is
+ * normally invoked as an idle handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the button in its
+ * current mode. The REDRAW_PENDING flag is cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register Button *butPtr = 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 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. */
+ Blt_Tile tile;
+
+ butPtr->flags &= ~REDRAW_PENDING;
+ if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+ tile = butPtr->tile;
+ 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;
+ tile = butPtr->activeTile;
+ } 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;
+ }
+ offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
+
+ /*
+ * 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));
+ if (tile != NULL) {
+ Blt_SetTileOrigin(tkwin, tile, 0, 0);
+ Blt_TileRectangle(tkwin, pixmap, tile, 0, 0, Tk_Width(tkwin),
+ Tk_Height(tkwin));
+ } else {
+ 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;
+
+ x += offset;
+ y += offset;
+ if (relief == TK_RELIEF_RAISED) {
+ x -= offset;
+ y -= offset;
+ } else 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);
+ if (tile != NULL) {
+ XSetClipMask(butPtr->display, gc, butPtr->bitmap);
+ }
+ XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
+ (unsigned int)width, (unsigned int)height, x, y, 1);
+ if (tile != NULL) {
+ XSetClipMask(butPtr->display, gc, None);
+ }
+ 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 {
+ TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
+ butPtr->indicatorSpace + butPtr->textWidth,
+ butPtr->textHeight, &x, &y);
+
+ x += butPtr->indicatorSpace;
+
+ x += offset;
+ y += offset;
+ if (relief == TK_RELIEF_RAISED) {
+ x -= offset;
+ y -= offset;
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ x += offset;
+ y += offset;
+ }
+#if (TK_MAJOR_VERSION > 4)
+ Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
+ x, y, 0, -1);
+ Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
+ butPtr->textLayout, x, y, butPtr->underline);
+#else
+ TkDisplayText(butPtr->display, pixmap, butPtr->font,
+ butPtr->text, butPtr->numChars, x, y, butPtr->textWidth,
+ butPtr->justify, butPtr->underline, gc);
+#endif
+ 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) {
+ int dim;
+
+ dim = butPtr->indicatorDiameter;
+ x -= butPtr->indicatorSpace;
+ y -= dim / 2;
+ if (dim > 2 * butPtr->borderWidth) {
+ Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
+ butPtr->borderWidth,
+ (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
+ TK_RELIEF_RAISED);
+ x += butPtr->borderWidth;
+ y += butPtr->borderWidth;
+ dim -= 2 * butPtr->borderWidth;
+ if (butPtr->flags & SELECTED) {
+ GC borderGC;
+
+ borderGC = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
+ ? butPtr->selectBorder : butPtr->normalBorder,
+ TK_3D_FLAT_GC);
+ XFillRectangle(butPtr->display, pixmap, borderGC, x, y,
+ (unsigned int)dim, (unsigned int)dim);
+ } else {
+ Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
+ dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
+ }
+ }
+ } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
+ XPoint points[4];
+ int radius;
+
+ radius = butPtr->indicatorDiameter / 2;
+ points[0].x = x - butPtr->indicatorSpace;
+ points[0].y = y;
+ points[1].x = points[0].x + radius;
+ points[1].y = points[0].y + radius;
+ points[2].x = points[1].x + radius;
+ points[2].y = points[0].y;
+ points[3].x = points[1].x;
+ points[3].y = points[0].y - radius;
+ if (butPtr->flags & SELECTED) {
+ GC borderGC;
+
+ borderGC = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
+ ? butPtr->selectBorder : butPtr->normalBorder,
+ TK_3D_FLAT_GC);
+ XFillPolygon(butPtr->display, pixmap, borderGC, points, 4, Convex,
+ CoordModeOrigin);
+ } else {
+ Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
+ 4, butPtr->borderWidth, TK_RELIEF_FLAT);
+ }
+ Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
+ (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
+ TK_RELIEF_RAISED);
+ }
+ /*
+ * 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) {
+ int inset = butPtr->highlightWidth;
+ if (butPtr->defaultState == tkActiveUid) {
+ inset += 2;
+ Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
+ Tk_Width(tkwin) - 2 * inset, Tk_Height(tkwin) - 2 * inset,
+ 1, TK_RELIEF_SUNKEN);
+ inset += 3;
+ }
+ Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
+ Tk_Width(tkwin) - 2 * inset, Tk_Height(tkwin) - 2 * inset,
+ butPtr->borderWidth, relief);
+ }
+ if (butPtr->highlightWidth != 0) {
+ GC highlightGC;
+
+ if (butPtr->flags & GOT_FOCUS) {
+ highlightGC = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
+ } else {
+ highlightGC = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, highlightGC, butPtr->highlightWidth, pixmap);
+ }
+ /*
+ * 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);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeButtonGeometry --
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeButtonGeometry(butPtr)
+ register Button *butPtr; /* Button whose geometry may have changed. */
+{
+ int width, height;
+
+ if (butPtr->highlightWidth < 0) {
+ butPtr->highlightWidth = 0;
+ }
+ butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
+
+ /*
+ * Leave room for the default ring if needed.
+ */
+
+ if (butPtr->defaultState == tkActiveUid) {
+ butPtr->inset += 5;
+ }
+ butPtr->indicatorSpace = 0;
+ 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 = height;
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ butPtr->indicatorDiameter = (65 * height) / 100;
+ } else {
+ butPtr->indicatorDiameter = (75 * height) / 100;
+ }
+ }
+ } else if (butPtr->bitmap != None) {
+ Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ int avgWidth;
+ Tk_FontMetrics fm;
+
+#if (TK_MAJOR_VERSION > 4)
+ Tk_FreeTextLayout(butPtr->textLayout);
+ butPtr->textLayout = Tk_ComputeTextLayout(butPtr->font,
+ butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
+ &butPtr->textWidth, &butPtr->textHeight);
+#else
+ butPtr->numChars = strlen(butPtr->text);
+ TkComputeTextGeometry(butPtr->font, butPtr->text,
+ butPtr->numChars, butPtr->wrapLength, &butPtr->textWidth,
+ &butPtr->textHeight);
+#endif
+ width = butPtr->textWidth;
+ height = butPtr->textHeight;
+ avgWidth = Tk_TextWidth(butPtr->font, "0", 1);
+ Tk_GetFontMetrics(butPtr->font, &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 = fm.linespace;
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ butPtr->indicatorDiameter =
+ (80 * butPtr->indicatorDiameter) / 100;
+ }
+ butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
+ }
+ }
+
+ /*
+ * When issuing the geometry request, add extra space for the indicator,
+ * if any, and for the border and padding, plus two extra pixels 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) && !Tk_StrictMotif(butPtr->tkwin)) {
+ width += 2;
+ height += 2;
+ }
+ Tk_GeometryRequest(butPtr->tkwin, (int)(width + butPtr->indicatorSpace
+ + 2 * butPtr->inset), (int)(height + 2 * butPtr->inset));
+ Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on buttons.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ButtonEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Button *butPtr = clientData;
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ goto redraw;
+ } else if (eventPtr->type == ConfigureNotify) {
+ /*
+ * Must redraw after size changes, since layout could have changed
+ * and borders will need to be redrawn.
+ */
+
+ goto redraw;
+ } else if (eventPtr->type == DestroyNotify) {
+ if (butPtr->tkwin != NULL) {
+ butPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(butPtr->interp, butPtr->widgetCmd);
+ }
+ if (butPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayButton, (ClientData)butPtr);
+ }
+ /* This is a hack to workaround a bug in 8.3.3. */
+ DestroyButton((ClientData)butPtr);
+ /* Tcl_EventuallyFree((ClientData)butPtr, (Tcl_FreeProc *)Blt_Free); */
+ } else if (eventPtr->type == FocusIn) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ butPtr->flags |= GOT_FOCUS;
+ if (butPtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+ } else if (eventPtr->type == FocusOut) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ butPtr->flags &= ~GOT_FOCUS;
+ if (butPtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+ }
+ return;
+
+ redraw:
+ if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ButtonCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Button *butPtr = clientData;
+ Tk_Window tkwin = butPtr->tkwin;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (tkwin != NULL) {
+ butPtr->tkwin = NULL;
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeButton --
+ *
+ * This procedure is called to carry out the actions associated
+ * with a button, such as invoking a Tcl command or setting a
+ * variable. This procedure is invoked, for example, when the
+ * button is invoked via the mouse.
+ *
+ * Results:
+ * A standard Tcl return value. Information is also left in
+ * interp->result.
+ *
+ * Side effects:
+ * Depends on the button and its associated command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InvokeButton(butPtr)
+ register Button *butPtr; /* Information about button. */
+{
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ if (butPtr->flags & SELECTED) {
+ if (Tcl_SetVar(butPtr->interp, butPtr->selVarName,
+ butPtr->offValue,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ } else if (butPtr->type == TYPE_RADIO_BUTTON) {
+ if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
+ return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonVarProc --
+ *
+ * This procedure is invoked when someone changes the
+ * state variable associated with a radio button. Depending
+ * on the new value of the button's variable, the button
+ * may be selected or deselected.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The button may become selected or deselected.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static char *
+ButtonVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ register Button *butPtr = clientData;
+ char *value;
+
+ /*
+ * If the variable is being unset, then just re-establish the
+ * trace unless the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ butPtr->flags &= ~SELECTED;
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_TraceVar(interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonVarProc, clientData);
+ }
+ goto redisplay;
+ }
+ /*
+ * Use the value of the variable to update the selected status of
+ * the button.
+ */
+
+ value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (strcmp(value, butPtr->onValue) == 0) {
+ if (butPtr->flags & SELECTED) {
+ return (char *) NULL;
+ }
+ butPtr->flags |= SELECTED;
+ } else if (butPtr->flags & SELECTED) {
+ butPtr->flags &= ~SELECTED;
+ } else {
+ return (char *) NULL;
+ }
+
+ redisplay:
+ if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in a button.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the button will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static char *
+ButtonTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Not used. */
+ char *name2; /* Not used. */
+ int flags; /* Information about what happened. */
+{
+ register Button *butPtr = clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
+ TCL_GLOBAL_ONLY);
+ Tcl_TraceVar(interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ ButtonTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+ value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (butPtr->text != NULL) {
+ Blt_Free(butPtr->text);
+ }
+ butPtr->text = Blt_Malloc(strlen(value) + 1);
+ strcpy(butPtr->text, value);
+ ComputeButtonGeometry(butPtr);
+
+ if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonImageProc --
+ *
+ * This procedure is invoked by the image code whenever the manager
+ * for an image does something that affects the size of contents
+ * of an image displayed in a button.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the button to get redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static void
+ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
+ ClientData clientData; /* Pointer to widget record. */
+ int x, y; /* Upper left pixel (within image)
+ * that must be redisplayed. */
+ int width, height; /* Dimensions of area to redisplay
+ * (may be <= 0). */
+ int imgWidth, imgHeight; /* New dimensions of image. */
+{
+ register Button *butPtr = clientData;
+
+ if (butPtr->tkwin != NULL) {
+ ComputeButtonGeometry(butPtr);
+ if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonSelectImageProc --
+ *
+ * This procedure is invoked by the image code whenever the manager
+ * for an image does something that affects the size of contents
+ * of the image displayed in a button when it is selected.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May arrange for the button to get redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static void
+ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
+ ClientData clientData; /* Pointer to widget record. */
+ int x, y; /* Upper left pixel (within image)
+ * that must be redisplayed. */
+ int width, height; /* Dimensions of area to redisplay
+ * (may be <= 0). */
+ int imgWidth, imgHeight; /* New dimensions of image. */
+{
+ register Button *butPtr = clientData;
+
+ /*
+ * Don't recompute geometry: it's controlled by the primary image.
+ */
+
+ if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
+ && Tk_IsMapped(butPtr->tkwin)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+int
+Blt_ButtonInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpecs[4] =
+ {
+#if HAVE_NAMESPACES
+ {"button", ButtonCmd,},
+ {"checkbutton", CheckbuttonCmd,},
+ {"radiobutton", RadiobuttonCmd,},
+ {"label", LabelCmd,},
+#else
+ {"tilebutton", ButtonCmd,},
+ {"tilecheckbutton", CheckbuttonCmd,},
+ {"tileradiobutton", RadiobuttonCmd,},
+ {"tilelabel", LabelCmd,},
+#endif /* HAVE_NAMESPACES */
+ };
+ tkNormalUid = Tk_GetUid("normal");
+ tkDisabledUid = Tk_GetUid("disabled");
+ tkActiveUid = Tk_GetUid("active");
+ return Blt_InitCmds(interp, "blt::tile", cmdSpecs, 4);
+}
+
+#endif /* NO_TILEBUTTON */
diff --git a/blt/src/tkConsole.c b/blt/src/tkConsole.c
new file mode 100644
index 00000000000..02ae766865e
--- /dev/null
+++ b/blt/src/tkConsole.c
@@ -0,0 +1,641 @@
+/*
+ * tkConsole.c --
+ *
+ * This file implements a Tcl console for systems that may not
+ * otherwise have access to a console. It uses the Text widget
+ * and provides special access via a console command.
+ *
+ * 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: @(#) tkConsole.c 1.55 98/01/02 17:40:37
+ */
+
+#include "tk.h"
+#include <string.h>
+#include "bltInt.h"
+
+/*
+ * A data structure of the following type holds information for each console
+ * which a handler (i.e. a Tcl command) has been defined for a particular
+ * top-level window.
+ */
+
+typedef struct {
+ Tcl_Interp *consoleInterp; /* Interpreter for the console. */
+ Tcl_Interp *interp; /* Interpreter to send console commands. */
+} ConsoleInfo;
+
+static Tcl_Interp *gStdoutInterp = NULL;
+
+#if ((TCL_VERSION_NUMBER >= _VERSION(8,0,0)) && \
+ (TCL_VERSION_NUMBER < _VERSION(8,1,0)))
+#define HAVE_BROKEN_LIB_PATH 1
+#undef HAVE_UTF
+#endif
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,1))
+#define HAVE_UTF 1
+extern void TclInitSubsystems _ANSI_ARGS_((CONST char *argv0));
+#else
+#endif
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ *
+ * The first three will be used in the tk app shells...
+ */
+
+void TkConsoleCreate _ANSI_ARGS_((void));
+int TkConsoleInit _ANSI_ARGS_((Tcl_Interp *interp));
+void TkConsolePrint _ANSI_ARGS_((Tcl_Interp *interp,
+ int devId, char *buffer, long size));
+
+static int ConsoleCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static void ConsoleDeleteProc _ANSI_ARGS_((ClientData clientData));
+static void ConsoleEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int InterpreterCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+
+static int ConsoleInput _ANSI_ARGS_((ClientData instanceData,
+ char *buf, int toRead, int *errorCode));
+static int ConsoleOutput _ANSI_ARGS_((ClientData instanceData,
+ char *buf, int toWrite, int *errorCode));
+static int ConsoleClose _ANSI_ARGS_((ClientData instanceData,
+ Tcl_Interp *interp));
+static void ConsoleWatch _ANSI_ARGS_((ClientData instanceData,
+ int mask));
+static int ConsoleHandle _ANSI_ARGS_((ClientData instanceData,
+ int direction, ClientData *handlePtr));
+
+/*
+ * This structure describes the channel type structure for file based IO:
+ */
+
+static Tcl_ChannelType consoleChannelType =
+{
+ "console", /* Type name. */
+ NULL, /* Always non-blocking.*/
+ ConsoleClose, /* Close proc. */
+ ConsoleInput, /* Input proc. */
+ ConsoleOutput, /* Output proc. */
+ NULL, /* Seek proc. */
+ NULL, /* Set option proc. */
+ NULL, /* Get option proc. */
+ ConsoleWatch, /* Watch for events on console. */
+ ConsoleHandle, /* Get a handle from the device. */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkConsoleCreate --
+ *
+ * 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.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the console channel and installs it as the standard
+ * channels.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkConsoleCreate()
+{
+ Tcl_Channel consoleChannel;
+
+#ifdef HAVE_UTF
+ TclInitSubsystems(NULL);
+#endif
+ consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console0",
+ (ClientData)TCL_STDIN, TCL_READABLE);
+ if (consoleChannel != NULL) {
+ Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
+ Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
+#ifdef HAVE_UTF
+ Tcl_SetChannelOption(NULL, consoleChannel, "-encoding", "utf-8");
+#endif
+ }
+ Tcl_SetStdChannel(consoleChannel, TCL_STDIN);
+ consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1",
+ (ClientData)TCL_STDOUT, TCL_WRITABLE);
+ if (consoleChannel != NULL) {
+ Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
+ Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
+#ifdef HAVE_UTF
+ Tcl_SetChannelOption(NULL, consoleChannel, "-encoding", "utf-8");
+#endif
+ }
+ Tcl_SetStdChannel(consoleChannel, TCL_STDOUT);
+ consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console2",
+ (ClientData)TCL_STDERR, TCL_WRITABLE);
+ if (consoleChannel != NULL) {
+ Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
+ Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
+#ifdef HAVE_UTF
+ Tcl_SetChannelOption(NULL, consoleChannel, "-encoding", "utf-8");
+#endif
+ }
+ Tcl_SetStdChannel(consoleChannel, TCL_STDERR);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkConsoleInit --
+ *
+ * Initialize the console. This code actually creates a new
+ * application and associated interpreter. This effectivly hides
+ * the implementation from the main application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new console it created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkConsoleInit(interp)
+ Tcl_Interp *interp; /* Interpreter to use for prompting. */
+{
+ Tcl_Interp *consoleInterp;
+ ConsoleInfo *info;
+ Tk_Window mainWindow = Tk_MainWindow(interp);
+#ifdef MAC_TCL
+ static char initCmd[] = "source -rsrc {Console}";
+#else
+ static char initCmd[] = "source $tk_library/console.tcl";
+#endif
+
+ consoleInterp = Tcl_CreateInterp();
+ if (consoleInterp == NULL) {
+ goto error;
+ }
+#if defined(HAVE_BROKEN_LIB_PATH) && defined(TCLLIBPATH)
+ Tcl_SetVar(consoleInterp, "tclDefaultLibrary",
+ TCLLIBPATH, TCL_GLOBAL_ONLY);
+#endif
+ /*
+ * Initialized Tcl and Tk.
+ */
+
+ if (Tcl_Init(consoleInterp) != TCL_OK) {
+ goto error;
+ }
+ if (Tk_Init(consoleInterp) != TCL_OK) {
+ goto error;
+ }
+ gStdoutInterp = interp;
+
+ /*
+ * Add console commands to the interp
+ */
+ info = (ConsoleInfo *) Tcl_Alloc(sizeof(ConsoleInfo));
+ info->interp = interp;
+ info->consoleInterp = consoleInterp;
+ Tcl_CreateCommand(interp, "console", ConsoleCmd, (ClientData)info,
+ (Tcl_CmdDeleteProc *)ConsoleDeleteProc);
+ Tcl_CreateCommand(consoleInterp, "consoleinterp", InterpreterCmd,
+ (ClientData)info, (Tcl_CmdDeleteProc *)NULL);
+
+ Tk_CreateEventHandler(mainWindow, StructureNotifyMask, ConsoleEventProc,
+ (ClientData)info);
+
+ Tcl_Preserve((ClientData)consoleInterp);
+ if (Tcl_Eval(consoleInterp, initCmd) == TCL_ERROR) {
+ /* goto error; -- no problem for now... */
+ printf("Eval error: %s", consoleInterp->result);
+ }
+ Tcl_Release((ClientData)consoleInterp);
+ return TCL_OK;
+
+ error:
+ if (consoleInterp != NULL) {
+ Tcl_DeleteInterp(consoleInterp);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleOutput--
+ *
+ * Writes the given output on the IO channel. Returns count of how
+ * many characters were actually written, and an error indication.
+ *
+ * Results:
+ * A count of how many characters were written is returned and an
+ * error indication is returned in an output argument.
+ *
+ * Side effects:
+ * Writes output on the actual channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleOutput(instanceData, buf, toWrite, errorCode)
+ ClientData instanceData; /* Indicates which device to use. */
+ char *buf; /* The data buffer. */
+ int toWrite; /* How many bytes to write? */
+ int *errorCode; /* Where to store error code. */
+{
+ *errorCode = 0;
+ Tcl_SetErrno(0);
+
+ if (gStdoutInterp != NULL) {
+ TkConsolePrint(gStdoutInterp, (int)instanceData, buf, toWrite);
+ }
+ return toWrite;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleInput --
+ *
+ * Read input from the console. Not currently implemented.
+ *
+ * Results:
+ * Always returns EOF.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+ConsoleInput(instanceData, buf, bufSize, errorCode)
+ ClientData instanceData; /* Not Used.. */
+ char *buf; /* Where to store data read. */
+ int bufSize; /* How much space is available
+ * in the buffer? */
+ int *errorCode; /* Where to store error code. */
+{
+ return 0; /* Always return EOF. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleClose --
+ *
+ * Closes the IO channel.
+ *
+ * Results:
+ * Always returns 0 (success).
+ *
+ * Side effects:
+ * Frees the dummy file associated with the channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+ConsoleClose(instanceData, interp)
+ ClientData instanceData; /* Not Used.. */
+ Tcl_Interp *interp; /* Not Used.. */
+{
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleWatch --
+ *
+ * Called by the notifier to set up the console device so that
+ * events will be noticed. Since there are no events on the
+ * console, this routine just returns without doing anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+ConsoleWatch(instanceData, mask)
+ ClientData instanceData; /* Device ID for the channel. */
+ int mask; /* OR-ed combination of
+ * TCL_READABLE, TCL_WRITABLE and
+ * TCL_EXCEPTION, for the events
+ * we are interested in. */
+{
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleHandle --
+ *
+ * Invoked by the generic IO layer to get a handle from a channel.
+ * Because console channels are not devices, this function always
+ * fails.
+ *
+ * Results:
+ * Always returns TCL_ERROR.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+ConsoleHandle(instanceData, direction, handlePtr)
+ ClientData instanceData; /* Device ID for the channel. */
+ int direction; /* TCL_READABLE or TCL_WRITABLE to indicate
+ * which direction of the channel is being
+ * requested. */
+ ClientData *handlePtr; /* Where to store handle */
+{
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleCmd --
+ *
+ * The console command implements a Tcl interface to the various console
+ * options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ ConsoleInfo *info = (ConsoleInfo *) clientData;
+ char c;
+ int length;
+ int result;
+ Tcl_Interp *consoleInterp;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ result = TCL_OK;
+ consoleInterp = info->consoleInterp;
+ Tcl_Preserve((ClientData)consoleInterp);
+ if ((c == 't') && (strncmp(argv[1], "title", length)) == 0) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "wm title . ", -1);
+ if (argc == 3) {
+ Tcl_DStringAppendElement(&dString, argv[2]);
+ }
+ Tcl_Eval(consoleInterp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ } else if ((c == 'h') && (strncmp(argv[1], "hide", length)) == 0) {
+ Tcl_Eval(info->consoleInterp, "wm withdraw .");
+ } else if ((c == 's') && (strncmp(argv[1], "show", length)) == 0) {
+ Tcl_Eval(info->consoleInterp, "wm deiconify .");
+ } else if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
+ if (argc == 3) {
+ Tcl_Eval(info->consoleInterp, argv[2]);
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " eval command\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": should be hide, show, or title",
+ (char *)NULL);
+ result = TCL_ERROR;
+ }
+ Tcl_Release((ClientData)consoleInterp);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InterpreterCmd --
+ *
+ * This command allows the console interp to communicate with the
+ * main interpreter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InterpreterCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ ConsoleInfo *info = (ConsoleInfo *) clientData;
+ char c;
+ int length;
+ int result;
+ Tcl_Interp *otherInterp;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ otherInterp = info->interp;
+ Tcl_Preserve((ClientData)otherInterp);
+ if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
+ result = Tcl_GlobalEval(otherInterp, argv[2]);
+ Tcl_AppendResult(interp, otherInterp->result, (char *)NULL);
+ } else if ((c == 'r') && (strncmp(argv[1], "record", length)) == 0) {
+ Tcl_RecordAndEval(otherInterp, argv[2], TCL_EVAL_GLOBAL);
+ result = TCL_OK;
+ Tcl_AppendResult(interp, otherInterp->result, (char *)NULL);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": should be eval or record",
+ (char *)NULL);
+ result = TCL_ERROR;
+ }
+ Tcl_Release((ClientData)otherInterp);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleDeleteProc --
+ *
+ * If the console command is deleted we destroy the console window
+ * and all associated data structures.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new console it created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+ConsoleDeleteProc(clientData)
+ ClientData clientData;
+{
+ ConsoleInfo *info = (ConsoleInfo *) clientData;
+
+ Tcl_DeleteInterp(info->consoleInterp);
+ info->consoleInterp = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleEventProc --
+ *
+ * This event procedure is registered on the main window of the
+ * slave interpreter. If the user or a running script causes the
+ * main window to be destroyed, then we need to inform the console
+ * interpreter by invoking "tkConsoleExit".
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Invokes the "tkConsoleExit" procedure in the console interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleEventProc(clientData, eventPtr)
+ ClientData clientData;
+ XEvent *eventPtr;
+{
+ ConsoleInfo *info = (ConsoleInfo *) clientData;
+ Tcl_Interp *consoleInterp;
+
+ if (eventPtr->type == DestroyNotify) {
+ consoleInterp = info->consoleInterp;
+
+ /*
+ * It is possible that the console interpreter itself has
+ * already been deleted. In that case the consoleInterp
+ * field will be set to NULL. If the interpreter is already
+ * gone, we do not have to do any work here.
+ */
+
+ if (consoleInterp == (Tcl_Interp *)NULL) {
+ return;
+ }
+ Tcl_Preserve((ClientData)consoleInterp);
+ Tcl_Eval(consoleInterp, "tkConsoleExit");
+ Tcl_Release((ClientData)consoleInterp);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkConsolePrint --
+ *
+ * Prints to the give text to the console. Given the main interp
+ * this functions find the appropiate console interp and forwards
+ * the text to be added to that console.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkConsolePrint(interp, devId, buffer, size)
+ Tcl_Interp *interp; /* Main interpreter. */
+ int devId; /* TCL_STDOUT for stdout, TCL_STDERR for
+ * stderr. */
+ char *buffer; /* Text buffer. */
+ long size; /* Size of text buffer. */
+{
+ Tcl_DString command, output;
+ Tcl_CmdInfo cmdInfo;
+ char *cmd;
+ ConsoleInfo *info;
+ Tcl_Interp *consoleInterp;
+ int result;
+
+ if (interp == NULL) {
+ return;
+ }
+ if (devId == TCL_STDERR) {
+ cmd = "tkConsoleOutput stderr ";
+ } else {
+ cmd = "tkConsoleOutput stdout ";
+ }
+
+ result = Tcl_GetCommandInfo(interp, "console", &cmdInfo);
+ if (result == 0) {
+ return;
+ }
+ info = (ConsoleInfo *) cmdInfo.clientData;
+
+ Tcl_DStringInit(&output);
+ Tcl_DStringAppend(&output, buffer, size);
+
+ Tcl_DStringInit(&command);
+ Tcl_DStringAppend(&command, cmd, strlen(cmd));
+ Tcl_DStringAppendElement(&command, output.string);
+
+ consoleInterp = info->consoleInterp;
+ Tcl_Preserve((ClientData)consoleInterp);
+ Tcl_Eval(consoleInterp, command.string);
+ Tcl_Release((ClientData)consoleInterp);
+
+ Tcl_DStringFree(&command);
+ Tcl_DStringFree(&output);
+}
diff --git a/blt/src/tkFrame.c b/blt/src/tkFrame.c
new file mode 100644
index 00000000000..b11792baa9f
--- /dev/null
+++ b/blt/src/tkFrame.c
@@ -0,0 +1,1094 @@
+/*
+ * tkFrame.c --
+ *
+ * This module implements "frame" and "toplevel" widgets for
+ * the Tk toolkit. Frames are windows with a background color
+ * and possibly a 3-D effect, but not much else in the way of
+ * attributes.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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: @(#) tkFrame.c 1.68 96/02/15 18:53:30
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TILEFRAME
+
+#include "bltTile.h"
+/*
+ * Defaults for frames:
+ */
+
+#define DEF_FRAME_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_FRAME_BG_MONO STD_MONO_NORMAL_BG
+#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 STD_COLOR_NORMAL_BG
+#define DEF_FRAME_HIGHLIGHT RGB_BLACK
+#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 toplevels (most of the defaults for frames also apply
+ * to toplevels):
+ */
+
+#define DEF_TOPLEVEL_CLASS "Toplevel"
+#define DEF_TOPLEVEL_SCREEN ""
+#define DEF_TOPLEVEL_MENU ""
+
+extern Tk_CustomOption bltTileOption;
+
+/*
+ * A data structure of the following type is kept for each
+ * frame that currently exists for this process:
+ */
+
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the frame. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up. */
+ Display *display; /* Display containing widget. Used, among
+ * other things, so that resources can be
+ * freed even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for frame's widget command. */
+ char *className; /* Class name for widget (from configuration
+ * option). Malloc-ed. */
+ int mask; /* Either FRAME or TOPLEVEL; used to select
+ * which configuration options are valid for
+ * widget. */
+ char *screenName; /* Screen on which widget is created. Non-null
+ * only for top-levels. Malloc-ed, may be
+ * NULL. */
+ char *visualName; /* Textual description of visual for window,
+ * from -visual option. Malloc-ed, may be
+ * NULL. */
+ char *colormapName; /* Textual description of colormap for window,
+ * from -colormap option. Malloc-ed, may be
+ * NULL. */
+ char *menuName; /* Textual description of menu to use for
+ * menubar. Malloc-ed, may be NULL. */
+ Colormap colormap; /* If not None, identifies a colormap
+ * allocated for this window, which must be
+ * freed when the window is deleted. */
+ Tk_3DBorder border; /* Structure used to draw 3-D border and
+ * background. NULL means no background
+ * or border. */
+ int borderWidth; /* Width of 3-D border (if any). */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int width; /* Width to request for window. <= 0 means
+ * don't request any size. */
+ int height; /* Height to request for window. <= 0 means
+ * don't request any size. */
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int isContainer; /* 1 means this window is a container, 0 means
+ * that it isn't. */
+ char *useThis; /* If the window is embedded, this points to
+ * the name of the window in which it is
+ * embedded (malloc'ed). For non-embedded
+ * windows this is NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+ Blt_Tile tile;
+} Frame;
+
+/*
+ * Flag bits for frames:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * GOT_FOCUS: Non-zero means this widget currently
+ * has the input focus.
+ */
+
+#define REDRAW_PENDING 1
+#define GOT_FOCUS 4
+
+/*
+ * The following flag bits are used so that there can be separate
+ * defaults for some configuration options for frames and toplevels.
+ */
+
+#define FRAME TK_CONFIG_USER_BIT
+#define TOPLEVEL (TK_CONFIG_USER_BIT << 1)
+#define BOTH (FRAME | TOPLEVEL)
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_FRAME_BG_COLOR, Tk_Offset(Frame, border),
+ BOTH | TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_FRAME_BG_MONO, Tk_Offset(Frame, border),
+ BOTH | TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, BOTH},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, BOTH},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_FRAME_BORDER_WIDTH, Tk_Offset(Frame, borderWidth), BOTH},
+ {TK_CONFIG_STRING, "-class", "class", "Class",
+ DEF_FRAME_CLASS, Tk_Offset(Frame, className), FRAME},
+ {TK_CONFIG_STRING, "-class", "class", "Class",
+ DEF_TOPLEVEL_CLASS, Tk_Offset(Frame, className), TOPLEVEL},
+ {TK_CONFIG_STRING, "-colormap", "colormap", "Colormap",
+ DEF_FRAME_COLORMAP, Tk_Offset(Frame, colormapName),
+ BOTH | TK_CONFIG_NULL_OK},
+#if (TK_MAJOR_VERSION > 4)
+ {TK_CONFIG_BOOLEAN, "-container", "container", "Container",
+ DEF_FRAME_CONTAINER, Tk_Offset(Frame, isContainer), BOTH},
+#endif /* TK_MAJOR_VERSION > 4 */
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_FRAME_CURSOR, Tk_Offset(Frame, cursor), BOTH | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-height", "height", "Height",
+ DEF_FRAME_HEIGHT, Tk_Offset(Frame, height), BOTH},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_FRAME_HIGHLIGHT_BG,
+ Tk_Offset(Frame, highlightBgColorPtr), BOTH},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_FRAME_HIGHLIGHT, Tk_Offset(Frame, highlightColorPtr), BOTH},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_FRAME_HIGHLIGHT_WIDTH, Tk_Offset(Frame, highlightWidth), BOTH},
+#if (TK_MAJOR_VERSION > 4)
+ {TK_CONFIG_STRING, "-menu", "menu", "Menu",
+ DEF_TOPLEVEL_MENU, Tk_Offset(Frame, menuName),
+ TOPLEVEL | TK_CONFIG_NULL_OK},
+#endif /* TK_MAJOR_VERSION > 4 */
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_FRAME_RELIEF, Tk_Offset(Frame, relief), BOTH},
+ {TK_CONFIG_STRING, "-screen", "screen", "Screen",
+ DEF_TOPLEVEL_SCREEN, Tk_Offset(Frame, screenName),
+ TOPLEVEL | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_FRAME_TAKE_FOCUS, Tk_Offset(Frame, takeFocus),
+ BOTH | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Frame, tile), BOTH | TK_CONFIG_NULL_OK,
+ &bltTileOption},
+#if (TK_MAJOR_VERSION > 4)
+ {TK_CONFIG_STRING, "-use", "use", "Use",
+ DEF_FRAME_USE, Tk_Offset(Frame, useThis), TOPLEVEL|TK_CONFIG_NULL_OK},
+#endif
+ {TK_CONFIG_STRING, "-visual", "visual", "Visual",
+ DEF_FRAME_VISUAL, Tk_Offset(Frame, visualName),
+ BOTH | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-width", "width", "Width",
+ DEF_FRAME_WIDTH, Tk_Offset(Frame, width), BOTH},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int ConfigureFrame _ANSI_ARGS_((Tcl_Interp *interp,
+ Frame * framePtr, int argc, char **argv,
+ int flags));
+static void DestroyFrame _ANSI_ARGS_((DestroyData *memPtr));
+static void DisplayFrame _ANSI_ARGS_((ClientData clientData));
+static void FrameCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void FrameEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int FrameWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static void MapFrame _ANSI_ARGS_((ClientData clientData));
+
+#ifdef __STDC__
+static Blt_TileChangedProc TileChangedProc;
+static Tcl_CmdProc FrameCmd, ToplevelCmd;
+#endif
+
+#ifdef TILE_MAINWINDOW
+EXTERN
+#else
+static
+#endif
+int TkCreateFrame _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv,
+ int toplevel, char *appName));
+
+EXTERN void TkSetWindowMenuBar _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+ char *oldMenuName, char *menuName));
+
+EXTERN Tk_Window TkCreateMainWindow _ANSI_ARGS_((Tcl_Interp * interp,
+ char * screenName, char * baseName));
+EXTERN void TkSetClassProcs _ANSI_ARGS_((Tk_Window tkwin, void *procs,
+ ClientData instanceData));
+EXTERN void TkpSetMainMenubar _ANSI_ARGS_((Tcl_Interp * interp, Tk_Window tkwin,
+ char * menuName));
+EXTERN int TkpUseWindow _ANSI_ARGS_((Tcl_Interp * interp, Tk_Window tkwin,
+ char * string));
+EXTERN void TkpMakeContainer _ANSI_ARGS_((Tk_Window tkwin));
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FrameCmd, ToplevelCmd --
+ *
+ * These procedures are invoked to process the "frame" and
+ * "toplevel" Tcl commands. See the user documentation for
+ * details on what they do.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation. These procedures are just wrappers;
+ * they call ButtonCreate to do all of the real work.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+FrameCmd(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 TkCreateFrame(clientData, interp, argc, argv, 0, (char *)NULL);
+}
+
+static int
+ToplevelCmd(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 TkCreateFrame(clientData, interp, argc, argv, 1, (char *)NULL);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkFrameCreate --
+ *
+ * This procedure is invoked to process the "frame" and "toplevel"
+ * Tcl commands; it is also invoked directly by Tk_Init to create
+ * a new main window. See the user documentation for the "frame"
+ * and "toplevel" commands for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+#ifndef TILE_MAINWINDOW
+static
+#endif /* TILE_MAINWINDOW */
+int
+TkCreateFrame(clientData, interp, argc, argv, toplevel, appName)
+ ClientData clientData; /* Main window associated with interpreter.
+ * If we're called by Tk_Init to create a
+ * new application, then this is NULL. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+ int toplevel; /* Non-zero means create a toplevel window,
+ * zero means create a frame. */
+ char *appName; /* Should only be non-NULL if clientData is
+ * NULL: gives the base name to use for the
+ * new application. */
+{
+ Frame *framePtr;
+ Tk_Window new = NULL;
+ char *className, *screenName, *visualName, *colormapName, *arg, *useOption;
+ int i, c, length, depth;
+ unsigned int mask;
+ Colormap colormap;
+ Visual *visual;
+ Tk_Window tkwin;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Pre-process the argument list. Scan through it to find any
+ * "-class", "-screen", "-visual", and "-colormap" options. These
+ * arguments need to be processed specially, before the window
+ * is configured using the usual Tk mechanisms.
+ */
+
+ className = colormapName = screenName = visualName = useOption = NULL;
+ colormap = None;
+ for (i = 2; i < argc; i += 2) {
+ arg = argv[i];
+ length = strlen(arg);
+ if (length < 2) {
+ continue;
+ }
+ c = arg[1];
+ if ((c == 'c') && (strncmp(arg, "-class", strlen(arg)) == 0)
+ && (length >= 3)) {
+ className = argv[i + 1];
+ } else if ((c == 'c')
+ && (strncmp(arg, "-colormap", strlen(arg)) == 0)) {
+ colormapName = argv[i + 1];
+ } else if ((c == 's') && toplevel
+ && (strncmp(arg, "-screen", strlen(arg)) == 0)) {
+ screenName = argv[i + 1];
+ } else if ((c == 'u') && toplevel
+ && (strncmp(arg, "-use", strlen(arg)) == 0)) {
+ useOption = argv[i + 1];
+ } else if ((c == 'v')
+ && (strncmp(arg, "-visual", strlen(arg)) == 0)) {
+ visualName = argv[i + 1];
+ }
+ }
+
+ /*
+ * Create the window, and deal with the special options -use,
+ * -classname, -colormap, -screenname, and -visual. These options
+ * must be handle before calling ConfigureFrame below, and they must
+ * also be processed in a particular order, for the following
+ * reasons:
+ * 1. Must set the window's class before calling ConfigureFrame,
+ * so that unspecified options are looked up in the option
+ * database using the correct class.
+ * 2. Must set visual information before calling ConfigureFrame
+ * so that colors are allocated in a proper colormap.
+ * 3. Must call TkpUseWindow before setting non-default visual
+ * information, since TkpUseWindow changes the defaults.
+ */
+
+ if (screenName == NULL) {
+ screenName = (toplevel) ? "" : NULL;
+ }
+ tkwin = Tk_MainWindow(interp);
+ if (tkwin != NULL) {
+ new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], screenName);
+ } else {
+ /*
+ * We were called from Tk_Init; create a new application.
+ */
+
+ if (appName == NULL) {
+ panic("TkCreateFrame didn't get application name");
+ }
+ new = (Tk_Window)TkCreateMainWindow(interp, screenName, appName);
+ }
+ if (new == NULL) {
+ goto error;
+ }
+ if (className == NULL) {
+ className = Tk_GetOption(new, "class", "Class");
+ if (className == NULL) {
+ className = (toplevel) ? "Toplevel" : "Frame";
+ }
+ }
+ Tk_SetClass(new, className);
+#if (TK_MAJOR_VERSION > 4)
+ if (useOption == NULL) {
+ useOption = Tk_GetOption(new, "use", "Use");
+ }
+ if (useOption != NULL) {
+ if (TkpUseWindow(interp, new, useOption) != TCL_OK) {
+ goto error;
+ }
+ }
+#endif
+ if (visualName == NULL) {
+ visualName = Tk_GetOption(new, "visual", "Visual");
+ }
+ if (colormapName == NULL) {
+ colormapName = Tk_GetOption(new, "colormap", "Colormap");
+ }
+ if (visualName != NULL) {
+ visual = Tk_GetVisual(interp, new, visualName, &depth,
+ (colormapName == NULL) ? &colormap : (Colormap *) NULL);
+ if (visual == NULL) {
+ goto error;
+ }
+ Tk_SetWindowVisual(new, visual, depth, colormap);
+ }
+ if (colormapName != NULL) {
+ colormap = Tk_GetColormap(interp, new, colormapName);
+ if (colormap == None) {
+ goto error;
+ }
+ Tk_SetWindowColormap(new, colormap);
+ }
+ /*
+ * For top-level windows, provide an initial geometry request of
+ * 200x200, just so the window looks nicer on the screen if it
+ * doesn't request a size for itself.
+ */
+
+ if (toplevel) {
+ Tk_GeometryRequest(new, 200, 200);
+ }
+ /*
+ * Create the widget record, process configuration options, and
+ * create event handlers. Then fill in a few additional fields
+ * in the widget record from the special options.
+ */
+
+ framePtr = Blt_Malloc(sizeof(Frame));
+ framePtr->tkwin = new;
+ framePtr->display = Tk_Display(new);
+ framePtr->interp = interp;
+ framePtr->widgetCmd = Tcl_CreateCommand(interp,
+ Tk_PathName(new), FrameWidgetCmd,
+ (ClientData)framePtr, FrameCmdDeletedProc);
+ framePtr->className = NULL;
+ framePtr->mask = (toplevel) ? TOPLEVEL : FRAME;
+ framePtr->screenName = NULL;
+ framePtr->visualName = NULL;
+ framePtr->colormapName = NULL;
+ framePtr->colormap = colormap;
+ framePtr->border = NULL;
+ framePtr->borderWidth = 0;
+ framePtr->relief = TK_RELIEF_FLAT;
+ framePtr->highlightWidth = 0;
+ framePtr->highlightBgColorPtr = NULL;
+ framePtr->highlightColorPtr = NULL;
+ framePtr->width = 0;
+ framePtr->height = 0;
+ framePtr->cursor = None;
+ framePtr->takeFocus = NULL;
+ framePtr->isContainer = 0;
+ framePtr->useThis = NULL;
+ framePtr->flags = 0;
+ framePtr->tile = NULL;
+ framePtr->menuName = NULL;
+
+#if (TK_MAJOR_VERSION > 4)
+ /*
+ * Store backreference to frame widget in window structure.
+ */
+ TkSetClassProcs(new, NULL, (ClientData)framePtr);
+#endif
+
+ mask = ExposureMask | StructureNotifyMask | FocusChangeMask;
+ if (toplevel) {
+ mask |= ActivateMask;
+ }
+ Tk_CreateEventHandler(new, mask, FrameEventProc, (ClientData)framePtr);
+ if (ConfigureFrame(interp, framePtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ goto error;
+ }
+#if (TK_MAJOR_VERSION > 4)
+ if ((framePtr->isContainer)) {
+ if (framePtr->useThis == NULL) {
+ TkpMakeContainer(framePtr->tkwin);
+ } else {
+ Tcl_AppendResult(interp, "A window cannot have both the -use ",
+ "and the -container option set.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+#endif
+ if (toplevel) {
+ Tcl_DoWhenIdle(MapFrame, (ClientData)framePtr);
+ }
+ Tcl_SetResult(interp, Tk_PathName(new), TCL_VOLATILE);
+ return TCL_OK;
+
+ error:
+ if (new != NULL) {
+ Tk_DestroyWindow(new);
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FrameWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a frame widget. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+FrameWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about frame widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Frame *framePtr = (Frame *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c, i;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Preserve((ClientData)framePtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *)NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ result = Tk_ConfigureValue(interp, framePtr->tkwin, configSpecs,
+ (char *)framePtr, argv[2], framePtr->mask);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Tk_ConfigureInfo(interp, framePtr->tkwin, configSpecs,
+ (char *)framePtr, (char *)NULL, framePtr->mask);
+ } else if (argc == 3) {
+ result = Tk_ConfigureInfo(interp, framePtr->tkwin, configSpecs,
+ (char *)framePtr, argv[2], framePtr->mask);
+ } else {
+ /*
+ * Don't allow the options -class, -colormap, -container,
+ * -newcmap, -screen, -use, or -visual to be changed.
+ */
+
+ for (i = 2; i < argc; i++) {
+ length = strlen(argv[i]);
+ if (length < 2) {
+ continue;
+ }
+ c = argv[i][1];
+ if (((c == 'c') && (strncmp(argv[i], "-class", length) == 0)
+ && (length >= 2))
+ || ((c == 'c') && (framePtr->mask == TOPLEVEL)
+ && (strncmp(argv[i], "-colormap", length) == 0)
+ && (length >= 3))
+ || ((c == 'c')
+ && (strncmp(argv[i], "-container", length) == 0)
+ && (length >= 3))
+ || ((c == 's') && (framePtr->mask == TOPLEVEL)
+ && (strncmp(argv[i], "-screen", length) == 0))
+ || ((c == 'u') && (framePtr->mask == TOPLEVEL)
+ && (strncmp(argv[i], "-use", length) == 0))
+ || ((c == 'v') && (framePtr->mask == TOPLEVEL)
+ && (strncmp(argv[i], "-visual", length) == 0))) {
+ Tcl_AppendResult(interp, "can't modify ", argv[i],
+ " option after widget is created", (char *)NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ }
+ result = ConfigureFrame(interp, framePtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure", (char *)NULL);
+ result = TCL_ERROR;
+ }
+
+ done:
+ Tcl_Release((ClientData)framePtr);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyFrame --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a frame at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the frame is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyFrame(memPtr)
+ DestroyData *memPtr; /* Info about frame widget. */
+{
+ register Frame *framePtr = (Frame *) memPtr;
+
+ Tk_FreeOptions(configSpecs, (char *)framePtr, framePtr->display,
+ framePtr->mask);
+ if (framePtr->tile != NULL) {
+ Blt_FreeTile(framePtr->tile);
+ }
+ if (framePtr->colormap != None) {
+ Tk_FreeColormap(framePtr->display, framePtr->colormap);
+ }
+ Blt_Free(framePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile;
+{
+ Frame *framePtr = (Frame *) clientData;
+
+ if (framePtr->tkwin != NULL) {
+ if (!(framePtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayFrame, (ClientData)framePtr);
+ framePtr->flags |= REDRAW_PENDING;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureFrame --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a frame widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for framePtr; old resources get freed, if there
+ * were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureFrame(interp, framePtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Frame *framePtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ char *oldMenuName;
+
+ /*
+ * Need the old menubar name for the menu code to delete it.
+ */
+
+ oldMenuName = NULL;
+#if (TK_MAJOR_VERSION > 4)
+ if (framePtr->menuName == NULL) {
+ oldMenuName = NULL;
+ } else {
+ oldMenuName = Blt_Malloc(strlen(framePtr->menuName) + 1);
+ strcpy(oldMenuName, framePtr->menuName);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+
+ if (Tk_ConfigureWidget(interp, framePtr->tkwin, configSpecs,
+ argc, argv, (char *)framePtr, flags | framePtr->mask) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (framePtr->tile != NULL) {
+ Blt_SetTileChangedProc(framePtr->tile, TileChangedProc,
+ (ClientData)framePtr);
+ }
+#if (TK_MAJOR_VERSION > 4)
+ if (((oldMenuName == NULL) && (framePtr->menuName != NULL))
+ || ((oldMenuName != NULL) && (framePtr->menuName == NULL))
+ || ((oldMenuName != NULL) && (framePtr->menuName != NULL)
+ && strcmp(oldMenuName, framePtr->menuName) != 0)) {
+ TkSetWindowMenuBar(interp, framePtr->tkwin, oldMenuName,
+ framePtr->menuName);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+ if (framePtr->border != NULL) {
+ Tk_SetBackgroundFromBorder(framePtr->tkwin, framePtr->border);
+ } else {
+ Tk_SetWindowBackgroundPixmap(framePtr->tkwin, None);
+ }
+ if (framePtr->highlightWidth < 0) {
+ framePtr->highlightWidth = 0;
+ }
+ Tk_SetInternalBorder(framePtr->tkwin,
+ framePtr->borderWidth + framePtr->highlightWidth);
+ if ((framePtr->width > 0) || (framePtr->height > 0)) {
+ Tk_GeometryRequest(framePtr->tkwin, framePtr->width,
+ framePtr->height);
+ }
+#if (TK_MAJOR_VERSION > 4)
+ if (oldMenuName != NULL) {
+ Blt_Free(oldMenuName);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+
+ if (Tk_IsMapped(framePtr->tkwin)) {
+ if (!(framePtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayFrame, (ClientData)framePtr);
+ }
+ framePtr->flags |= REDRAW_PENDING;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayFrame --
+ *
+ * This procedure is invoked to display a frame widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the frame in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DisplayFrame(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register Frame *framePtr = (Frame *) clientData;
+ register Tk_Window tkwin = framePtr->tkwin;
+ GC gc;
+
+ framePtr->flags &= ~REDRAW_PENDING;
+ if ((framePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)
+ || framePtr->isContainer) {
+ return;
+ }
+ if (framePtr->tile == NULL) {
+ Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
+ framePtr->border, framePtr->highlightWidth,
+ framePtr->highlightWidth,
+ Tk_Width(tkwin) - 2 * framePtr->highlightWidth,
+ Tk_Height(tkwin) - 2 * framePtr->highlightWidth,
+ framePtr->borderWidth, framePtr->relief);
+ } else {
+ Blt_SetTileOrigin(tkwin, framePtr->tile, 0, 0);
+ Blt_TileRectangle(tkwin, Tk_WindowId(tkwin), framePtr->tile, 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin));
+ if ((framePtr->border != NULL) &&
+ (framePtr->relief != TK_RELIEF_FLAT)) {
+ Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
+ framePtr->border, framePtr->highlightWidth,
+ framePtr->highlightWidth,
+ Tk_Width(tkwin) - 2 * framePtr->highlightWidth,
+ Tk_Height(tkwin) - 2 * framePtr->highlightWidth,
+ framePtr->borderWidth, framePtr->relief);
+ }
+ }
+ if (framePtr->highlightWidth != 0) {
+ if (framePtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(framePtr->highlightColorPtr,
+ Tk_WindowId(tkwin));
+ } else {
+ gc = Tk_GCForColor(framePtr->highlightBgColorPtr,
+ Tk_WindowId(tkwin));
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, framePtr->highlightWidth,
+ Tk_WindowId(tkwin));
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FrameEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher on
+ * structure changes to a frame. For frames with 3D
+ * borders, this procedure is also invoked for exposures.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+FrameEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ register XEvent *eventPtr; /* Information about event. */
+{
+ register Frame *framePtr = (Frame *) clientData;
+
+ if (((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
+ || (eventPtr->type == ConfigureNotify)) {
+ goto redraw;
+ } else if (eventPtr->type == DestroyNotify) {
+#if (TK_MAJOR_VERSION > 4)
+ if (framePtr->menuName != NULL) {
+ TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin,
+ framePtr->menuName, NULL);
+ Blt_Free(framePtr->menuName);
+ framePtr->menuName = NULL;
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+ if (framePtr->tkwin != NULL) {
+
+ /*
+ * If this window is a container, then this event could be
+ * coming from the embedded application, in which case
+ * Tk_DestroyWindow hasn't been called yet. When Tk_DestroyWindow
+ * is called later, then another destroy event will be generated.
+ * We need to be sure we ignore the second event, since the frame
+ * could be gone by then. To do so, delete the event handler
+ * explicitly (normally it's done implicitly by Tk_DestroyWindow).
+ */
+ Tk_DeleteEventHandler(framePtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask,
+ FrameEventProc, (ClientData)framePtr);
+ framePtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(framePtr->interp, framePtr->widgetCmd);
+ }
+ if (framePtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayFrame, (ClientData)framePtr);
+ }
+ Tcl_CancelIdleCall(MapFrame, (ClientData)framePtr);
+ Tcl_EventuallyFree((ClientData)framePtr, (Tcl_FreeProc *)DestroyFrame);
+ } else if (eventPtr->type == FocusIn) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ framePtr->flags |= GOT_FOCUS;
+ if (framePtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+ } else if (eventPtr->type == FocusOut) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ framePtr->flags &= ~GOT_FOCUS;
+ if (framePtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+#if (TK_MAJOR_VERSION > 4)
+ } else if (eventPtr->type == ActivateNotify) {
+ TkpSetMainMenubar(framePtr->interp, framePtr->tkwin,
+ framePtr->menuName);
+#endif /* TK_MAJOR_VERSION > 4 */
+ }
+ return;
+
+ redraw:
+ if ((framePtr->tkwin != NULL) && !(framePtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayFrame, (ClientData)framePtr);
+ framePtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FrameCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FrameCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Frame *framePtr = (Frame *) clientData;
+ Tk_Window tkwin = framePtr->tkwin;
+
+#if (TK_MAJOR_VERSION > 4)
+ if (framePtr->menuName != NULL) {
+ TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin,
+ framePtr->menuName, NULL);
+ Blt_Free(framePtr->menuName);
+ framePtr->menuName = NULL;
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (tkwin != NULL) {
+ framePtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapFrame --
+ *
+ * This procedure is invoked as a when-idle handler to map a
+ * newly-created top-level frame.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The frame given by the clientData argument is mapped.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MapFrame(clientData)
+ ClientData clientData; /* Pointer to frame structure. */
+{
+ Frame *framePtr = (Frame *) clientData;
+
+ /*
+ * Wait for all other background events to be processed before
+ * mapping window. This ensures that the window's correct geometry
+ * will have been determined before it is first mapped, so that the
+ * window manager doesn't get a false idea of its desired geometry.
+ */
+
+ Tcl_Preserve((ClientData)framePtr);
+ for(;;) {
+ if (Tcl_DoOneEvent(TCL_IDLE_EVENTS) == 0) {
+ break;
+ }
+ /*
+ * After each event, make sure that the window still exists
+ * and quit if the window has been destroyed.
+ */
+
+ if (framePtr->tkwin == NULL) {
+ Tcl_Release((ClientData)framePtr);
+ return;
+ }
+ }
+ Tk_MapWindow(framePtr->tkwin);
+ Tcl_Release((ClientData)framePtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkInstallFrameMenu --
+ *
+ * This function is needed when a Windows HWND is created
+ * and a menubar has been set to the window with a system
+ * menu. It notifies the menu package so that the system
+ * menu can be rebuilt.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The system menu (if any) is created for the menubar
+ * associated with this frame.
+ *
+ *--------------------------------------------------------------
+ */
+
+#ifdef notdef
+void
+TkInstallFrameMenu(tkwin)
+ Tk_Window tkwin; /* The window that was just created. */
+{
+#if (TK_MAJOR_VERSION > 4)
+#define Tk_InstanceData(tkwin) (((Tk_FakeWin *)(tkwin))->dummy18)
+#define Tk_MainPtr(tkwin) (((Tk_FakeWin *)(tkwin))->dummy5)
+ if (Tk_MainPtr(tkwin) != NULL) {
+ Frame *framePtr;
+
+ framePtr = (Frame *) Tk_InstanceData(tkwin);
+ TkpMenuNotifyToplevelCreate(framePtr->interp, framePtr->menuName);
+ }
+#endif /* TK_MAJOR_VERSION > 4 */
+}
+
+#endif
+
+int
+Blt_FrameInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpecs[2] =
+ {
+#if HAVE_NAMESPACES
+ {"frame", FrameCmd,},
+ {"toplevel", ToplevelCmd,},
+#else
+ {"tileframe", FrameCmd,},
+ {"tiletoplevel", ToplevelCmd,},
+#endif /* HAVE_NAMESPACES */
+ };
+ return Blt_InitCmds(interp, "blt::tile", cmdSpecs, 2);
+}
+
+#endif /* NO_TILEFRAME */
diff --git a/blt/src/tkMenubutton.c b/blt/src/tkMenubutton.c
new file mode 100644
index 00000000000..ff463432fae
--- /dev/null
+++ b/blt/src/tkMenubutton.c
@@ -0,0 +1,1239 @@
+/*
+ * tkMenubutton.c --
+ *
+ * This module implements button-like widgets that are used
+ * to invoke pull-down menus.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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: @(#) tkMenubutton.c 1.77 96/02/15 18:52:22
+ */
+
+#include "tkPort.h"
+#include "default.h"
+#include "tkInt.h"
+
+/*
+ * A data structure of the following type is kept for each
+ * widget managed by this file:
+ */
+
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the widget. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Display *display; /* Display containing widget. Needed, among
+ * other things, so that resources can bee
+ * freed up even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with menubutton. */
+ Tcl_Command widgetCmd; /* Token for menubutton's widget command. */
+ char *menuName; /* Name of menu associated with widget.
+ * Malloc-ed. */
+
+ /*
+ * Information about what's displayed in the menu button:
+ */
+
+ char *text; /* Text to display in button (malloc'ed)
+ * or NULL. */
+ int numChars; /* # of characters in text. */
+ int underline; /* Index of character to underline. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, button displays the contents
+ * of this variable. */
+ Pixmap bitmap; /* Bitmap to display or None. If not None
+ * then text and textVar and underline
+ * are ignored. */
+ char *imageString; /* Name of image to display (malloc'ed), or
+ * NULL. If non-NULL, bitmap, text, and
+ * textVarName are ignored. */
+ Tk_Image image; /* Image to display in window, or NULL if
+ * none. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ Tk_Uid state; /* State of button for display purposes:
+ * normal, active, or disabled. */
+ Tk_3DBorder normalBorder; /* Structure used to draw 3-D
+ * border and background when window
+ * isn't active. NULL means no such
+ * border exists. */
+ Tk_3DBorder activeBorder; /* Structure used to draw 3-D
+ * border and background when window
+ * is active. NULL means no such
+ * border exists. */
+ int borderWidth; /* Width of border. */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int inset; /* Total width of all borders, including
+ * traversal highlight and 3-D border.
+ * Indicates how much interior stuff must
+ * be offset from outside edges to leave
+ * room for borders. */
+ XFontStruct *fontPtr; /* Information about text font, or NULL. */
+ XColor *normalFg; /* Foreground color in normal mode. */
+ XColor *activeFg; /* Foreground color in active mode. NULL
+ * means use normalFg instead. */
+ XColor *disabledFg; /* Foreground color when disabled. NULL
+ * means use normalFg with a 50% stipple
+ * instead. */
+ GC normalTextGC; /* GC for drawing text in normal mode. */
+ GC activeTextGC; /* GC for drawing text in active mode (NULL
+ * means use normalTextGC). */
+ Pixmap gray; /* Pixmap for displaying disabled text/icon if
+ * disabledFg is NULL. */
+ GC disabledGC; /* Used to produce disabled effect. If
+ * disabledFg isn't NULL, this GC is used to
+ * draw button text or icon. Otherwise
+ * text or icon is drawn with normalGC and
+ * this GC is used to stipple background
+ * across it. */
+ int leftBearing; /* Distance from text origin to leftmost drawn
+ * pixel (positive means to right). */
+ int rightBearing; /* Amount text sticks right from its origin. */
+ char *widthString; /* Value of -width option. Malloc'ed. */
+ char *heightString; /* Value of -height option. Malloc'ed. */
+ int width, height; /* If > 0, these specify dimensions to request
+ * for window, in characters for text and in
+ * pixels for bitmaps. In this case the actual
+ * size of the text string or bitmap is
+ * ignored in computing desired window size. */
+ int wrapLength; /* Line length (in pixels) at which to wrap
+ * onto next line. <= 0 means don't wrap
+ * except at newlines. */
+ int padX, padY; /* Extra space around text or bitmap (pixels
+ * on each side). */
+ Tk_Anchor anchor; /* Where text/bitmap should be displayed
+ * inside window region. */
+ Tk_Justify justify; /* Justification to use for multi-line text. */
+ int textWidth; /* Width needed to display text as requested,
+ * in pixels. */
+ int textHeight; /* Height needed to display text as requested,
+ * in pixels. */
+ int indicatorOn; /* Non-zero means display indicator; 0 means
+ * don't display. */
+ int indicatorHeight; /* Height of indicator in pixels. This same
+ * amount of extra space is also left on each
+ * side of the indicator. 0 if no indicator. */
+ int indicatorWidth; /* Width of indicator in pixels, including
+ * indicatorHeight in padding on each side.
+ * 0 if no indicator. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} MenuButton;
+
+/*
+ * Flag bits for buttons:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * POSTED: Non-zero means that the menu associated
+ * with this button has been posted (typically
+ * because of an active button press).
+ * GOT_FOCUS: Non-zero means this button currently
+ * has the input focus.
+ */
+
+#define REDRAW_PENDING 1
+#define POSTED 2
+#define GOT_FOCUS 4
+
+/*
+ * The following constants define the dimensions of the cascade indicator,
+ * which is displayed if the "-indicatoron" option is true. The units for
+ * these options are 1/10 millimeters.
+ */
+
+#define INDICATOR_WIDTH 40
+#define INDICATOR_HEIGHT 17
+
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
+ DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
+ Tk_Offset(MenuButton, disabledFg),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
+ Tk_Offset(MenuButton, disabledFg),
+ TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_FONT, "-font", "font", "Font",
+ DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
+ {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
+ {TK_CONFIG_STRING, "-height", "height", "Height",
+ DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, heightString), 0},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG,
+ Tk_Offset(MenuButton, highlightBgColorPtr), 0},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_MENUBUTTON_HIGHLIGHT, Tk_Offset(MenuButton, highlightColorPtr), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH,
+ Tk_Offset(MenuButton, highlightWidth), 0},
+ {TK_CONFIG_STRING, "-image", "image", "Image",
+ DEF_MENUBUTTON_IMAGE, Tk_Offset(MenuButton, imageString),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
+ DEF_MENUBUTTON_INDICATOR, Tk_Offset(MenuButton, indicatorOn), 0},
+ {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_MENUBUTTON_JUSTIFY, Tk_Offset(MenuButton, justify), 0},
+ {TK_CONFIG_STRING, "-menu", "menu", "Menu",
+ DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
+ DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
+ {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
+ DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
+ {TK_CONFIG_UID, "-state", "state", "State",
+ DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_MENUBUTTON_TAKE_FOCUS, Tk_Offset(MenuButton, takeFocus),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
+ {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_INT, "-underline", "underline", "Underline",
+ DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
+ {TK_CONFIG_STRING, "-width", "width", "Width",
+ DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, widthString), 0},
+ {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
+ DEF_MENUBUTTON_WRAP_LENGTH, Tk_Offset(MenuButton, wrapLength), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ComputeMenuButtonGeometry _ANSI_ARGS_((
+ MenuButton *mbPtr));
+static void MenuButtonCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void MenuButtonImageProc _ANSI_ARGS_((ClientData clientData,
+ int x, int y, int width, int height, int imgWidth,
+ int imgHeight));
+static char *MenuButtonTextVarProc _ANSI_ARGS_((
+ ClientData clientData, Tcl_Interp *interp,
+ char *name1, char *name2, int flags));
+static int MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
+ MenuButton *mbPtr, int argc, char **argv,
+ int flags));
+static void DestroyMenuButton _ANSI_ARGS_((char *memPtr));
+static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_MenubuttonCmd --
+ *
+ * This procedure is invoked to process the "button", "label",
+ * "radiobutton", and "checkbutton" Tcl commands. See the
+ * user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tk_MenubuttonCmd(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. */
+{
+ register MenuButton *mbPtr;
+ Tk_Window tkwin;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Create the new window.
+ */
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * Initialize the data structure for the button.
+ */
+
+ mbPtr = Blt_Malloc(sizeof(MenuButton));
+ mbPtr->tkwin = tkwin;
+ mbPtr->display = Tk_Display(tkwin);
+ mbPtr->interp = interp;
+ mbPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin),
+ MenuButtonWidgetCmd, (ClientData)mbPtr, MenuButtonCmdDeletedProc);
+ mbPtr->menuName = NULL;
+ mbPtr->text = NULL;
+ mbPtr->numChars = 0;
+ mbPtr->underline = -1;
+ mbPtr->textVarName = NULL;
+ mbPtr->bitmap = None;
+ mbPtr->imageString = NULL;
+ mbPtr->image = NULL;
+ mbPtr->state = tkNormalUid;
+ mbPtr->normalBorder = NULL;
+ mbPtr->activeBorder = NULL;
+ mbPtr->borderWidth = 0;
+ mbPtr->relief = TK_RELIEF_FLAT;
+ mbPtr->highlightWidth = 0;
+ mbPtr->highlightBgColorPtr = NULL;
+ mbPtr->highlightColorPtr = NULL;
+ mbPtr->inset = 0;
+ mbPtr->fontPtr = NULL;
+ mbPtr->normalFg = NULL;
+ mbPtr->activeFg = NULL;
+ mbPtr->disabledFg = NULL;
+ mbPtr->normalTextGC = None;
+ mbPtr->activeTextGC = None;
+ mbPtr->gray = None;
+ mbPtr->disabledGC = None;
+ mbPtr->leftBearing = 0;
+ mbPtr->rightBearing = 0;
+ mbPtr->widthString = NULL;
+ mbPtr->heightString = NULL;
+ mbPtr->width = 0;
+ mbPtr->width = 0;
+ mbPtr->wrapLength = 0;
+ mbPtr->padX = 0;
+ mbPtr->padY = 0;
+ mbPtr->anchor = TK_ANCHOR_CENTER;
+ mbPtr->justify = TK_JUSTIFY_CENTER;
+ mbPtr->indicatorOn = 0;
+ mbPtr->indicatorWidth = 0;
+ mbPtr->indicatorHeight = 0;
+ mbPtr->cursor = None;
+ mbPtr->takeFocus = NULL;
+ mbPtr->flags = 0;
+
+ Tk_SetClass(mbPtr->tkwin, "Menubutton");
+ Tk_CreateEventHandler(mbPtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask,
+ MenuButtonEventProc, (ClientData)mbPtr);
+ if (ConfigureMenuButton(interp, mbPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ Tk_DestroyWindow(mbPtr->tkwin);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(mbPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MenuButtonWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about button widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register MenuButton *mbPtr = clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Preserve((ClientData)mbPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *)NULL);
+ goto error;
+ }
+ result = Tk_ConfigureValue(interp, mbPtr->tkwin, configSpecs,
+ (char *)mbPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
+ (char *)mbPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
+ (char *)mbPtr, argv[2], 0);
+ } else {
+ result = ConfigureMenuButton(interp, mbPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure",
+ (char *)NULL);
+ goto error;
+ }
+ Tcl_Release((ClientData)mbPtr);
+ return result;
+
+ error:
+ Tcl_Release((ClientData)mbPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyMenuButton --
+ *
+ * This procedure is invoked to recycle all of the resources
+ * associated with a button widget. It is invoked as a
+ * when-idle handler in order to make sure that there is no
+ * other use of the button pending at the time of the deletion.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyMenuButton(memPtr)
+ char *memPtr; /* Info about button widget. */
+{
+ register MenuButton *mbPtr = (MenuButton *)memPtr;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Tk_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (mbPtr->textVarName != NULL) {
+ Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData)mbPtr);
+ }
+ if (mbPtr->image != NULL) {
+ Tk_FreeImage(mbPtr->image);
+ }
+ if (mbPtr->normalTextGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
+ }
+ if (mbPtr->activeTextGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
+ }
+ if (mbPtr->gray != None) {
+ Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
+ }
+ if (mbPtr->disabledGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
+ }
+ Tk_FreeOptions(configSpecs, (char *)mbPtr, mbPtr->display, 0);
+ ckBlt_Free(mbPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureMenuButton --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a menubutton widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for mbPtr; old resources get freed, if there
+ * were any. The menubutton is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register MenuButton *mbPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ XGCValues gcValues;
+ GC newGC;
+ unsigned long mask;
+ int result;
+ Tk_Image image;
+
+ /*
+ * Eliminate any existing trace on variables monitored by the menubutton.
+ */
+
+ if (mbPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData)mbPtr);
+ }
+ result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
+ argc, argv, (char *)mbPtr, flags);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * A few options need special processing, such as setting the
+ * background from a 3-D border, or filling in complicated
+ * defaults that couldn't be specified to Tk_ConfigureWidget.
+ */
+
+ if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
+ Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
+ } else {
+ Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
+ if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkActiveUid)
+ && (mbPtr->state != tkDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
+ "\": must be normal, active, or disabled", (char *)NULL);
+ mbPtr->state = tkNormalUid;
+ return TCL_ERROR;
+ }
+ }
+
+ if (mbPtr->highlightWidth < 0) {
+ mbPtr->highlightWidth = 0;
+ }
+ gcValues.font = mbPtr->fontPtr->fid;
+ gcValues.foreground = mbPtr->normalFg->pixel;
+ gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
+
+ /*
+ * Note: GraphicsExpose events are disabled in GC's because they're
+ * used to copy stuff from an off-screen pixmap onto the screen (we know
+ * that there's no problem with obscured areas).
+ */
+
+ gcValues.graphics_exposures = False;
+ newGC = Tk_GetGC(mbPtr->tkwin,
+ GCForeground | GCBackground | GCFont | GCGraphicsExposures, &gcValues);
+ if (mbPtr->normalTextGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
+ }
+ mbPtr->normalTextGC = newGC;
+
+ gcValues.font = mbPtr->fontPtr->fid;
+ gcValues.foreground = mbPtr->activeFg->pixel;
+ gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
+ newGC = Tk_GetGC(mbPtr->tkwin, GCForeground | GCBackground | GCFont,
+ &gcValues);
+ if (mbPtr->activeTextGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
+ }
+ mbPtr->activeTextGC = newGC;
+
+ gcValues.font = mbPtr->fontPtr->fid;
+ gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
+ if ((mbPtr->disabledFg != NULL) && (mbPtr->imageString == NULL)) {
+ gcValues.foreground = mbPtr->disabledFg->pixel;
+ mask = GCForeground | GCBackground | GCFont;
+ } else {
+ gcValues.foreground = gcValues.background;
+ if (mbPtr->gray == None) {
+ mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
+ Tk_GetUid("gray50"));
+ if (mbPtr->gray == None) {
+ return TCL_ERROR;
+ }
+ }
+ gcValues.fill_style = FillStippled;
+ gcValues.stipple = mbPtr->gray;
+ mask = GCForeground | GCFillStyle | GCStipple;
+ }
+ newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
+ if (mbPtr->disabledGC != None) {
+ Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
+ }
+ mbPtr->disabledGC = newGC;
+
+ if (mbPtr->padX < 0) {
+ mbPtr->padX = 0;
+ }
+ if (mbPtr->padY < 0) {
+ mbPtr->padY = 0;
+ }
+ /*
+ * Get the image for the widget, if there is one. Allocate the
+ * new image before freeing the old one, so that the reference
+ * count doesn't go to zero and cause image data to be discarded.
+ */
+
+ if (mbPtr->imageString != NULL) {
+ image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
+ mbPtr->imageString, MenuButtonImageProc, (ClientData)mbPtr);
+ if (image == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ image = NULL;
+ }
+ if (mbPtr->image != NULL) {
+ Tk_FreeImage(mbPtr->image);
+ }
+ mbPtr->image = image;
+
+ if ((mbPtr->image == NULL) && (mbPtr->bitmap == None)
+ && (mbPtr->textVarName != NULL)) {
+ /*
+ * The menubutton displays a variable. Set up a trace to watch
+ * for any changes in it.
+ */
+
+ char *value;
+
+ value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
+ TCL_GLOBAL_ONLY);
+ } else {
+ if (mbPtr->text != NULL) {
+ ckBlt_Free(mbPtr->text);
+ }
+ mbPtr->text = Blt_Malloc(strlen(value) + 1);
+ strcpy(mbPtr->text, value);
+ }
+ Tcl_TraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData)mbPtr);
+ }
+ /*
+ * Recompute the geometry for the button.
+ */
+
+ if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
+ if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
+ &mbPtr->width) != TCL_OK) {
+ widthError:
+ Tcl_AddErrorInfo(interp, "\n (processing -width option)");
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
+ &mbPtr->height) != TCL_OK) {
+ heightError:
+ Tcl_AddErrorInfo(interp, "\n (processing -height option)");
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
+ != TCL_OK) {
+ goto widthError;
+ }
+ if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
+ != TCL_OK) {
+ goto heightError;
+ }
+ }
+ ComputeMenuButtonGeometry(mbPtr);
+
+ /*
+ * Lastly, arrange for the button to be redisplayed.
+ */
+
+ if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayMenuButton --
+ *
+ * This procedure is invoked to display a menubutton widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menubutton in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayMenuButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register MenuButton *mbPtr = clientData;
+ GC gc;
+ Tk_3DBorder border;
+ Pixmap pixmap;
+ int x = 0; /* Initialization needed only to stop
+ * compiler warning. */
+ int y;
+ register Tk_Window tkwin = mbPtr->tkwin;
+ int width, height;
+
+ mbPtr->flags &= ~REDRAW_PENDING;
+ if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+ if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
+ gc = mbPtr->disabledGC;
+ border = mbPtr->normalBorder;
+ } else if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
+ gc = mbPtr->activeTextGC;
+ border = mbPtr->activeBorder;
+ } else {
+ gc = mbPtr->normalTextGC;
+ border = mbPtr->normalBorder;
+ }
+
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the menu 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(mbPtr->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 (mbPtr->image != None) {
+ Tk_SizeOfImage(mbPtr->image, &width, &height);
+
+ imageOrBitmap:
+ switch (mbPtr->anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_SW:
+ x += mbPtr->inset;
+ break;
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_S:
+ x += ((int)(Tk_Width(tkwin) - width
+ - mbPtr->indicatorWidth)) / 2;
+ break;
+ default:
+ x += Tk_Width(tkwin) - mbPtr->inset - width
+ - mbPtr->indicatorWidth;
+ break;
+ }
+ switch (mbPtr->anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_NE:
+ y = mbPtr->inset;
+ break;
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_E:
+ y = ((int)(Tk_Height(tkwin) - height)) / 2;
+ break;
+ default:
+ y = Tk_Height(tkwin) - mbPtr->inset - height;
+ break;
+ }
+ if (mbPtr->image != NULL) {
+ Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
+ x, y);
+ } else {
+ XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
+ gc, 0, 0, (unsigned)width, (unsigned)height, x, y, 1);
+ }
+ } else if (mbPtr->bitmap != None) {
+ Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ width = mbPtr->textWidth;
+ height = mbPtr->textHeight;
+ switch (mbPtr->anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_SW:
+ x = mbPtr->inset + mbPtr->padX;
+ break;
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_S:
+ x = ((int)(Tk_Width(tkwin) - width
+ - mbPtr->indicatorWidth)) / 2;
+ break;
+ default:
+ x = Tk_Width(tkwin) - width - mbPtr->padX - mbPtr->inset
+ - mbPtr->indicatorWidth;
+ break;
+ }
+ switch (mbPtr->anchor) {
+ case TK_ANCHOR_NW:
+ case TK_ANCHOR_N:
+ case TK_ANCHOR_NE:
+ y = mbPtr->inset + mbPtr->padY;
+ break;
+ case TK_ANCHOR_W:
+ case TK_ANCHOR_CENTER:
+ case TK_ANCHOR_E:
+ y = ((int)(Tk_Height(tkwin) - height)) / 2;
+ break;
+ default:
+ y = Tk_Height(tkwin) - mbPtr->inset - mbPtr->padY - height;
+ break;
+ }
+ TkDisplayText(mbPtr->display, pixmap, mbPtr->fontPtr,
+ mbPtr->text, mbPtr->numChars, x, y, mbPtr->textWidth,
+ mbPtr->justify, mbPtr->underline, gc);
+ }
+
+ /*
+ * If the menu button is disabled with a stipple rather than a special
+ * foreground color, generate the stippled effect.
+ */
+
+ if ((mbPtr->state == tkDisabledUid)
+ && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) {
+ XFillRectangle(mbPtr->display, pixmap, mbPtr->disabledGC,
+ mbPtr->inset, mbPtr->inset,
+ (unsigned)(Tk_Width(tkwin) - 2 * mbPtr->inset),
+ (unsigned)(Tk_Height(tkwin) - 2 * mbPtr->inset));
+ }
+ /*
+ * Draw the cascade indicator for the menu button on the
+ * right side of the window, if desired.
+ */
+
+ if (mbPtr->indicatorOn) {
+ int borderWidth;
+
+ borderWidth = (mbPtr->indicatorHeight + 1) / 3;
+ if (borderWidth < 1) {
+ borderWidth = 1;
+ }
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
+ + mbPtr->indicatorHeight,
+ y + ((int)(height - mbPtr->indicatorHeight)) / 2,
+ mbPtr->indicatorWidth - 2 * mbPtr->indicatorHeight,
+ mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED);
+ }
+ /*
+ * Draw the border and traversal highlight last. This way, if the
+ * menu button's contents overflow onto the border they'll be covered
+ * up by the border.
+ */
+
+ if (mbPtr->relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tkwin, pixmap, border,
+ mbPtr->highlightWidth, mbPtr->highlightWidth,
+ Tk_Width(tkwin) - 2 * mbPtr->highlightWidth,
+ Tk_Height(tkwin) - 2 * mbPtr->highlightWidth,
+ mbPtr->borderWidth, mbPtr->relief);
+ }
+ if (mbPtr->highlightWidth != 0) {
+ GC gc;
+
+ if (mbPtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
+ }
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin),
+ mbPtr->normalTextGC, 0, 0, (unsigned)Tk_Width(tkwin),
+ (unsigned)Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(mbPtr->display, pixmap);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on buttons.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MenuButtonEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ MenuButton *mbPtr = clientData;
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ goto redraw;
+ } else if (eventPtr->type == ConfigureNotify) {
+ /*
+ * Must redraw after size changes, since layout could have changed
+ * and borders will need to be redrawn.
+ */
+
+ goto redraw;
+ } else if (eventPtr->type == DestroyNotify) {
+ if (mbPtr->tkwin != NULL) {
+ mbPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd);
+ }
+ if (mbPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayMenuButton, (ClientData)mbPtr);
+ }
+ Tcl_EventuallyFree((ClientData)mbPtr, DestroyMenuButton);
+ } else if (eventPtr->type == FocusIn) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ mbPtr->flags |= GOT_FOCUS;
+ if (mbPtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+ } else if (eventPtr->type == FocusOut) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ mbPtr->flags &= ~GOT_FOCUS;
+ if (mbPtr->highlightWidth > 0) {
+ goto redraw;
+ }
+ }
+ }
+ return;
+
+ redraw:
+ if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuButtonCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuButtonCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ MenuButton *mbPtr = clientData;
+ Tk_Window tkwin = mbPtr->tkwin;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (tkwin != NULL) {
+ mbPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeMenuButtonGeometry --
+ *
+ * After changes in a menu button's text or bitmap, this procedure
+ * recomputes the menu button's geometry and passes this information
+ * along to the geometry manager for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu button's window may change size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeMenuButtonGeometry(mbPtr)
+ register MenuButton *mbPtr; /* Widget record for menu button. */
+{
+ int width, height, mm, pixels;
+
+ mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
+ if (mbPtr->image != None) {
+ Tk_SizeOfImage(mbPtr->image, &width, &height);
+ if (mbPtr->width > 0) {
+ width = mbPtr->width;
+ }
+ if (mbPtr->height > 0) {
+ height = mbPtr->height;
+ }
+ } else if (mbPtr->bitmap != None) {
+ Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
+ if (mbPtr->width > 0) {
+ width = mbPtr->width;
+ }
+ if (mbPtr->height > 0) {
+ height = mbPtr->height;
+ }
+ } else {
+ mbPtr->numChars = strlen(mbPtr->text);
+ TkComputeTextGeometry(mbPtr->fontPtr, mbPtr->text,
+ mbPtr->numChars, mbPtr->wrapLength, &mbPtr->textWidth,
+ &mbPtr->textHeight);
+ width = mbPtr->textWidth;
+ height = mbPtr->textHeight;
+ if (mbPtr->width > 0) {
+ width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
+ }
+ if (mbPtr->height > 0) {
+ height = mbPtr->height * (mbPtr->fontPtr->ascent
+ + mbPtr->fontPtr->descent);
+ }
+ width += 2 * mbPtr->padX;
+ height += 2 * mbPtr->padY;
+ }
+
+ if (mbPtr->indicatorOn) {
+ mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
+ pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
+ mbPtr->indicatorHeight = (INDICATOR_HEIGHT * pixels) / (10 * mm);
+ mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels) / (10 * mm)
+ + 2 * mbPtr->indicatorHeight;
+ width += mbPtr->indicatorWidth;
+ } else {
+ mbPtr->indicatorHeight = 0;
+ mbPtr->indicatorWidth = 0;
+ }
+
+ Tk_GeometryRequest(mbPtr->tkwin, (int)(width + 2 * mbPtr->inset),
+ (int)(height + 2 * mbPtr->inset));
+ Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in a menu button.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the menu button will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static char *
+MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ register MenuButton *mbPtr = clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
+ TCL_GLOBAL_ONLY);
+ Tcl_TraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+ value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (mbPtr->text != NULL) {
+ ckBlt_Free(mbPtr->text);
+ }
+ mbPtr->text = Blt_Malloc(strlen(value) + 1);
+ strcpy(mbPtr->text, value);
+ ComputeMenuButtonGeometry(mbPtr);
+
+ if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
+ && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuButtonImageProc --
+ *
+ * This procedure is invoked by the image code whenever the manager
+ * for an image does something that affects the size of contents
+ * of an image displayed in a button.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the button to get redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
+ ClientData clientData; /* Pointer to widget record. */
+ int x, y; /* Upper left pixel (within image)
+ * that must be redisplayed. */
+ int width, height; /* Dimensions of area to redisplay
+ * (may be <= 0). */
+ int imgWidth, imgHeight; /* New dimensions of image. */
+{
+ register MenuButton *mbPtr = clientData;
+
+ if (mbPtr->tkwin != NULL) {
+ ComputeMenuButtonGeometry(mbPtr);
+ if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+ }
+}
diff --git a/blt/src/tkScrollbar.c b/blt/src/tkScrollbar.c
new file mode 100644
index 00000000000..60b6a4b358a
--- /dev/null
+++ b/blt/src/tkScrollbar.c
@@ -0,0 +1,1405 @@
+/*
+ * tkScrollbar.c --
+ *
+ * This module implements a scrollbar widgets for the Tk
+ * toolkit. A scrollbar displays a slider and two arrows;
+ * mouse clicks on features within the scrollbar cause
+ * scrolling commands to be invoked.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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: @(#) tkScrollbar.c 1.79 96/02/15 18:52:40
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TILESCROLLBAR
+
+#include "bltTile.h"
+
+extern Tk_CustomOption bltTileOption;
+
+#define NORMAL_BG "#d9d9d9"
+#define ACTIVE_BG "#ececec"
+#define SELECT_BG "#c3c3c3"
+#define TROUGH "#c3c3c3"
+#define INDICATOR "#b03060"
+#define DISABLED "#a3a3a3"
+
+
+/*
+ * Defaults for scrollbars:
+ */
+
+#define DEF_SCROLLBAR_ACTIVE_BG_COLOR ACTIVE_BG
+#define DEF_SCROLLBAR_ACTIVE_BG_MONO RGB_BLACK
+#define DEF_SCROLLBAR_ACTIVE_RELIEF "raised"
+#define DEF_SCROLLBAR_BG_COLOR NORMAL_BG
+#define DEF_SCROLLBAR_BG_MONO RGB_WHITE
+#define DEF_SCROLLBAR_BORDER_WIDTH "2"
+#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 RGB_BLACK
+#define DEF_SCROLLBAR_HIGHLIGHT_WIDTH "2"
+#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 RGB_WHITE
+#define DEF_SCROLLBAR_WIDTH "15"
+
+
+/*
+ * A data structure of the following type is kept for each scrollbar
+ * widget managed by this file:
+ */
+
+typedef struct {
+ Tk_Window tkwin; /* Window that embodies the scrollbar. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Display *display; /* Display containing widget. Used, among
+ * other things, so that resources can be
+ * freed even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with scrollbar. */
+ Tcl_Command widgetCmd; /* Token for scrollbar's widget command. */
+ Tk_Uid orientUid; /* Orientation for window ("vertical" or
+ * "horizontal"). */
+ int vertical; /* Non-zero means vertical orientation
+ * requested, zero means horizontal. */
+ int width; /* Desired narrow dimension of scrollbar,
+ * in pixels. */
+ char *command; /* Command prefix to use when invoking
+ * scrolling commands. NULL means don't
+ * invoke commands. Malloc'ed. */
+ int commandSize; /* Number of non-NULL bytes in command. */
+ int repeatDelay; /* How long to wait before auto-repeating
+ * on scrolling actions (in ms). */
+ int repeatInterval; /* Interval between autorepeats (in ms). */
+ int jump; /* Value of -jump option. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int borderWidth; /* Width of 3-D borders. */
+ Tk_3DBorder bgBorder; /* Used for drawing background (all flat
+ * surfaces except for trough). */
+ Tk_3DBorder activeBorder; /* For drawing backgrounds when active (i.e.
+ * when mouse is positioned over element). */
+ XColor *troughColorPtr; /* Color for drawing trough. */
+ GC troughGC; /* For drawing trough. */
+ GC copyGC; /* Used for copying from pixmap onto screen. */
+ int relief; /* Indicates whether window as a whole is
+ * raised, sunken, or flat. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * <= 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int inset; /* Total width of all borders, including
+ * traversal highlight and 3-D border.
+ * Indicates how much interior stuff must
+ * be offset from outside edges to leave
+ * room for borders. */
+ int elementBorderWidth; /* Width of border to draw around elements
+ * inside scrollbar (arrows and slider).
+ * -1 means use borderWidth. */
+ int arrowLength; /* Length of arrows along long dimension of
+ * scrollbar, including space for a small gap
+ * between the arrow and the slider.
+ * Recomputed on window size changes. */
+ int sliderFirst; /* Pixel coordinate of top or left edge
+ * of slider area, including border. */
+ int sliderLast; /* Coordinate of pixel just after bottom
+ * or right edge of slider area, including
+ * border. */
+ int activeField; /* Names field to be displayed in active
+ * colors, such as TOP_ARROW, or 0 for
+ * no field. */
+ int activeRelief; /* Value of -activeRelief option: relief
+ * to use for active element. */
+
+ /*
+ * Information describing the application related to the scrollbar.
+ * This information is provided by the application by invoking the
+ * "set" widget command. This information can now be provided in
+ * two ways: the "old" form (totalUnits, windowUnits, firstUnit,
+ * and lastUnit), or the "new" form (firstFraction and lastFraction).
+ * FirstFraction and lastFraction will always be valid, but
+ * the old-style information is only valid if the NEW_STYLE_COMMANDS
+ * flag is 0.
+ */
+
+ int totalUnits; /* Total dimension of application, in
+ * units. Valid only if the NEW_STYLE_COMMANDS
+ * flag isn't set. */
+ int windowUnits; /* Maximum number of units that can be
+ * displayed in the window at once. Valid
+ * only if the NEW_STYLE_COMMANDS flag isn't
+ * set. */
+ int firstUnit; /* Number of last unit visible in
+ * application's window. Valid only if the
+ * NEW_STYLE_COMMANDS flag isn't set. */
+ int lastUnit; /* Index of last unit visible in window.
+ * Valid only if the NEW_STYLE_COMMANDS
+ * flag isn't set. */
+ double firstFraction; /* Position of first visible thing in window,
+ * specified as a fraction between 0 and
+ * 1.0. */
+ double lastFraction; /* Position of last visible thing in window,
+ * specified as a fraction between 0 and
+ * 1.0. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+ Blt_Tile tile, activeTile;
+} Scrollbar;
+
+/*
+ * Legal values for "activeField" field of Scrollbar structures. These
+ * are also the return values from the ScrollbarPosition procedure.
+ */
+
+#define OUTSIDE 0
+#define TOP_ARROW 1
+#define TOP_GAP 2
+#define SLIDER 3
+#define BOTTOM_GAP 4
+#define BOTTOM_ARROW 5
+
+/*
+ * Flag bits for scrollbars:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * NEW_STYLE_COMMANDS: Non-zero means the new style of commands
+ * should be used to communicate with the
+ * widget: ".t yview scroll 2 lines", instead
+ * of ".t yview 40", for example.
+ * GOT_FOCUS: Non-zero means this window has the input
+ * focus.
+ */
+
+#define REDRAW_PENDING 1
+#define NEW_STYLE_COMMANDS 2
+#define GOT_FOCUS 4
+
+/*
+ * Minimum slider length, in pixels (designed to make sure that the slider
+ * is always easy to grab with the mouse).
+ */
+
+#define MIN_SLIDER_LENGTH 8
+
+/*
+ * Information used for argv parsing.
+ */
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_SCROLLBAR_ACTIVE_BG_COLOR, Tk_Offset(Scrollbar, activeBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
+ DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(Scrollbar, activeBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(Scrollbar, activeRelief), 0},
+ {TK_CONFIG_CUSTOM, "-activetile", "activeTile", "Tile",
+ (char *)NULL, Tk_Offset(Scrollbar, activeTile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
+ {TK_CONFIG_STRING, "-command", "command", "Command",
+ DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
+ "BorderWidth", DEF_SCROLLBAR_EL_BORDER_WIDTH,
+ Tk_Offset(Scrollbar, elementBorderWidth), 0},
+ {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
+ Tk_Offset(Scrollbar, highlightBgColorPtr), 0},
+ {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_SCROLLBAR_HIGHLIGHT,
+ Tk_Offset(Scrollbar, highlightColorPtr), 0},
+ {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness",
+ DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(Scrollbar, highlightWidth), 0},
+ {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
+ DEF_SCROLLBAR_JUMP, Tk_Offset(Scrollbar, jump), 0},
+ {TK_CONFIG_UID, "-orient", "orient", "Orient",
+ DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
+ {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
+ DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
+ {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
+ DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
+ {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(Scrollbar, takeFocus),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+ (char *)NULL, Tk_Offset(Scrollbar, tile), TK_CONFIG_NULL_OK,
+ &bltTileOption},
+ {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
+ DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(Scrollbar, troughColorPtr),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
+ DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(Scrollbar, troughColorPtr),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-width", "width", "Width",
+ DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ComputeScrollbarGeometry _ANSI_ARGS_((
+ Scrollbar *scrollPtr));
+static int ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
+ Scrollbar *scrollPtr, int argc, char **argv,
+ int flags));
+static void DestroyScrollbar _ANSI_ARGS_((DestroyData *memPtr));
+static void DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
+static void EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
+static void ScrollbarCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
+ int x, int y));
+static int ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *, int argc, char **argv));
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarCmd --
+ *
+ * This procedure is invoked to process the "scrollbar" Tcl
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ScrollbarCmd(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. */
+{
+ register Scrollbar *scrollPtr;
+ Tk_Window tkwin;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+ (char *)NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * Initialize fields that won't be initialized by ConfigureScrollbar,
+ * or which ConfigureScrollbar expects to have reasonable values
+ * (e.g. resource pointers).
+ */
+
+ scrollPtr = Blt_Malloc(sizeof(Scrollbar));
+ scrollPtr->tkwin = tkwin;
+ scrollPtr->display = Tk_Display(tkwin);
+ scrollPtr->interp = interp;
+ scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
+ Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
+ (ClientData)scrollPtr, ScrollbarCmdDeletedProc);
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(scrollPtr->tkwin, scrollPtr->widgetCmd);
+#endif /* ITCL_NAMESPACES */
+ scrollPtr->orientUid = NULL;
+ scrollPtr->vertical = 0;
+ scrollPtr->width = 0;
+ scrollPtr->command = NULL;
+ scrollPtr->commandSize = 0;
+ scrollPtr->repeatDelay = 0;
+ scrollPtr->repeatInterval = 0;
+ scrollPtr->borderWidth = 0;
+ scrollPtr->bgBorder = NULL;
+ scrollPtr->activeBorder = NULL;
+ scrollPtr->troughColorPtr = NULL;
+ scrollPtr->troughGC = None;
+ scrollPtr->copyGC = None;
+ scrollPtr->relief = TK_RELIEF_FLAT;
+ scrollPtr->highlightWidth = 0;
+ scrollPtr->highlightBgColorPtr = NULL;
+ scrollPtr->highlightColorPtr = NULL;
+ scrollPtr->inset = 0;
+ scrollPtr->elementBorderWidth = -1;
+ scrollPtr->arrowLength = 0;
+ scrollPtr->sliderFirst = 0;
+ scrollPtr->sliderLast = 0;
+ scrollPtr->activeField = 0;
+ scrollPtr->activeRelief = TK_RELIEF_RAISED;
+ scrollPtr->totalUnits = 0;
+ scrollPtr->windowUnits = 0;
+ scrollPtr->firstUnit = 0;
+ scrollPtr->lastUnit = 0;
+ scrollPtr->firstFraction = 0.0;
+ scrollPtr->lastFraction = 0.0;
+ scrollPtr->cursor = None;
+ scrollPtr->takeFocus = NULL;
+ scrollPtr->flags = 0;
+ scrollPtr->tile = scrollPtr->activeTile = NULL;
+
+ Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
+ Tk_CreateEventHandler(scrollPtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask,
+ ScrollbarEventProc, (ClientData)scrollPtr);
+ if (ConfigureScrollbar(interp, scrollPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+ goto error;
+ }
+ Tcl_SetResult(interp, Tk_PathName(scrollPtr->tkwin), TCL_VOLATILE);
+ return TCL_OK;
+
+ error:
+ Tk_DestroyWindow(scrollPtr->tkwin);
+ return TCL_ERROR;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ScrollbarWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about scrollbar
+ * widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Scrollbar *scrollPtr = clientData;
+ char string[200];
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Preserve((ClientData)scrollPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
+ if (argc == 2) {
+ switch (scrollPtr->activeField) {
+ case TOP_ARROW:
+ Tcl_SetResult(interp, "arrow1", TCL_STATIC);
+ break;
+ case SLIDER:
+ Tcl_SetResult(interp, "slider", TCL_STATIC);
+ break;
+ case BOTTOM_ARROW:
+ Tcl_SetResult(interp, "arrow2", TCL_STATIC);
+ break;
+ }
+ goto done;
+ }
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " activate element\"", (char *)NULL);
+ goto error;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
+ scrollPtr->activeField = TOP_ARROW;
+ } else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
+ scrollPtr->activeField = BOTTOM_ARROW;
+ } else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
+ scrollPtr->activeField = SLIDER;
+ } else {
+ scrollPtr->activeField = OUTSIDE;
+ }
+ EventuallyRedraw(scrollPtr);
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *)NULL);
+ goto error;
+ }
+ result = Tk_ConfigureValue(interp, scrollPtr->tkwin, configSpecs,
+ (char *)scrollPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
+ (char *)scrollPtr, (char *)NULL, 0);
+ } else if (argc == 3) {
+ result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
+ (char *)scrollPtr, argv[2], 0);
+ } else {
+ result = ConfigureScrollbar(interp, scrollPtr, argc - 2, argv + 2,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
+ int xDelta, yDelta, pixels, barWidth;
+ double fraction;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " delta xDelta yDelta\"", (char *)NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
+ goto error;
+ }
+ if (scrollPtr->vertical) {
+ pixels = yDelta;
+ barWidth = Tk_Height(scrollPtr->tkwin) - 1
+ - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
+ } else {
+ pixels = xDelta;
+ barWidth = Tk_Width(scrollPtr->tkwin) - 1
+ - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
+ }
+ if (barWidth == 0) {
+ fraction = 0.0;
+ } else {
+ fraction = ((double)pixels / (double)barWidth);
+ }
+ sprintf(interp->result, "%g", fraction);
+ } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
+ int x, y, pos, barWidth;
+ double fraction;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " fraction x y\"", (char *)NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+ if (scrollPtr->vertical) {
+ pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
+ barWidth = Tk_Height(scrollPtr->tkwin) - 1
+ - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
+ } else {
+ pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
+ barWidth = Tk_Width(scrollPtr->tkwin) - 1
+ - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
+ }
+ if (barWidth == 0) {
+ fraction = 0.0;
+ } else {
+ fraction = ((double)pos / (double)barWidth);
+ }
+ if (fraction < 0) {
+ fraction = 0;
+ } else if (fraction > 1.0) {
+ fraction = 1.0;
+ }
+ sprintf(string, "%g", fraction);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get\"", (char *)NULL);
+ goto error;
+ }
+ if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
+ char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
+
+ Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
+ Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
+ Tcl_AppendResult(interp, first, " ", last, (char *)NULL);
+ } else {
+ sprintf(string, "%d %d %d %d", scrollPtr->totalUnits,
+ scrollPtr->windowUnits, scrollPtr->firstUnit,
+ scrollPtr->lastUnit);
+ Tcl_SetResult(interp, string, TCL_VOLATILE);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
+ int x, y, thing;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " identify x y\"", (char *)NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+ thing = ScrollbarPosition(scrollPtr, x, y);
+ switch (thing) {
+ case TOP_ARROW:
+ Tcl_SetResult(interp, "arrow1", TCL_STATIC);
+ break;
+ case TOP_GAP:
+ Tcl_SetResult(interp, "trough1", TCL_STATIC);
+ break;
+ case SLIDER:
+ Tcl_SetResult(interp, "slider", TCL_STATIC);
+ break;
+ case BOTTOM_GAP:
+ Tcl_SetResult(interp, "trough2", TCL_STATIC);
+ break;
+ case BOTTOM_ARROW:
+ Tcl_SetResult(interp, "arrow2", TCL_STATIC);
+ break;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
+ int totalUnits, windowUnits, firstUnit, lastUnit;
+
+ if (argc == 4) {
+ double first, last;
+
+ if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
+ goto error;
+ }
+ if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
+ goto error;
+ }
+ if (first < 0) {
+ scrollPtr->firstFraction = 0;
+ } else if (first > 1.0) {
+ scrollPtr->firstFraction = 1.0;
+ } else {
+ scrollPtr->firstFraction = first;
+ }
+ if (last < scrollPtr->firstFraction) {
+ scrollPtr->lastFraction = scrollPtr->firstFraction;
+ } else if (last > 1.0) {
+ scrollPtr->lastFraction = 1.0;
+ } else {
+ scrollPtr->lastFraction = last;
+ }
+ scrollPtr->flags |= NEW_STYLE_COMMANDS;
+ } else if (argc == 6) {
+ if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
+ goto error;
+ }
+ if (totalUnits < 0) {
+ totalUnits = 0;
+ }
+ if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
+ goto error;
+ }
+ if (windowUnits < 0) {
+ windowUnits = 0;
+ }
+ if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
+ goto error;
+ }
+ if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
+ goto error;
+ }
+ if (totalUnits > 0) {
+ if (lastUnit < firstUnit) {
+ lastUnit = firstUnit;
+ }
+ } else {
+ firstUnit = lastUnit = 0;
+ }
+ scrollPtr->totalUnits = totalUnits;
+ scrollPtr->windowUnits = windowUnits;
+ scrollPtr->firstUnit = firstUnit;
+ scrollPtr->lastUnit = lastUnit;
+ if (scrollPtr->totalUnits == 0) {
+ scrollPtr->firstFraction = 0.0;
+ scrollPtr->lastFraction = 1.0;
+ } else {
+ scrollPtr->firstFraction = ((double)firstUnit) / totalUnits;
+ scrollPtr->lastFraction = ((double)(lastUnit + 1)) / totalUnits;
+ }
+ scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " set firstFraction lastFraction\" or \"",
+ argv[0],
+ " set totalUnits windowUnits firstUnit lastUnit\"",
+ (char *)NULL);
+ goto error;
+ }
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be activate, cget, configure, delta, fraction, ",
+ "get, identify, or set", (char *)NULL);
+ goto error;
+ }
+ done:
+ Tcl_Release((ClientData)scrollPtr);
+ return result;
+
+ error:
+ Tcl_Release((ClientData)scrollPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyScrollbar --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a scrollbar at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the scrollbar is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyScrollbar(memPtr)
+ DestroyData *memPtr; /* Info about scrollbar widget. */
+{
+ register Scrollbar *scrollPtr = (Scrollbar *)memPtr;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Tk_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (scrollPtr->troughGC != None) {
+ Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
+ }
+ if (scrollPtr->copyGC != None) {
+ Tk_FreeGC(scrollPtr->display, scrollPtr->copyGC);
+ }
+ if (scrollPtr->activeTile != NULL) {
+ Blt_FreeTile(scrollPtr->activeTile);
+ }
+ if (scrollPtr->tile != NULL) {
+ Blt_FreeTile(scrollPtr->tile);
+ }
+ Tk_FreeOptions(configSpecs, (char *)scrollPtr, scrollPtr->display, 0);
+ Blt_Free(scrollPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Routine for tile change notifications.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Scrollbar *scrollPtr = clientData;
+
+ if (scrollPtr->tkwin != NULL) {
+ EventuallyRedraw(scrollPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureScrollbar --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a scrollbar widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as colors, border width,
+ * etc. get set for scrollPtr; old resources get freed,
+ * if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Scrollbar *scrollPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to
+ * Tk_ConfigureWidget. */
+{
+ size_t length;
+ XGCValues gcValues;
+ GC new;
+
+ if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
+ argc, argv, (char *)scrollPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * A few options need special processing, such as parsing the
+ * orientation or setting the background from a 3-D border.
+ */
+
+ length = strlen(scrollPtr->orientUid);
+ if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
+ scrollPtr->vertical = 1;
+ } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
+ scrollPtr->vertical = 0;
+ } else {
+ Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
+ "\": must be vertical or horizontal", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if (scrollPtr->command != NULL) {
+ scrollPtr->commandSize = strlen(scrollPtr->command);
+ } else {
+ scrollPtr->commandSize = 0;
+ }
+ if (scrollPtr->activeTile != NULL) {
+ Blt_SetTileChangedProc(scrollPtr->activeTile, TileChangedProc,
+ (ClientData)scrollPtr);
+ }
+ if (scrollPtr->tile != NULL) {
+ Blt_SetTileChangedProc(scrollPtr->tile, TileChangedProc,
+ (ClientData)scrollPtr);
+ }
+ Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
+
+ gcValues.foreground = scrollPtr->troughColorPtr->pixel;
+ new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
+ if (scrollPtr->troughGC != None) {
+ Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
+ }
+ scrollPtr->troughGC = new;
+ if (scrollPtr->copyGC == None) {
+ gcValues.graphics_exposures = False;
+ scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
+ &gcValues);
+ }
+ /*
+ * 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.
+ */
+
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayScrollbar --
+ *
+ * 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.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayScrollbar(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ register Scrollbar *scrollPtr = clientData;
+ register Tk_Window tkwin = scrollPtr->tkwin;
+ XPoint points[7];
+ Tk_3DBorder border;
+ int relief, width, elementBorderWidth;
+ Pixmap pixmap;
+ Blt_Tile tile;
+
+ if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ goto done;
+ }
+ if (scrollPtr->vertical) {
+ width = Tk_Width(tkwin) - 2 * scrollPtr->inset;
+ } else {
+ width = Tk_Height(tkwin) - 2 * scrollPtr->inset;
+ }
+ elementBorderWidth = scrollPtr->elementBorderWidth;
+ if (elementBorderWidth < 0) {
+ elementBorderWidth = scrollPtr->borderWidth;
+ }
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the scrollbar 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(scrollPtr->display, Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+
+ if (scrollPtr->highlightWidth != 0) {
+ GC gc;
+
+ if (scrollPtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
+ }
+ Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
+ scrollPtr->highlightWidth, scrollPtr->highlightWidth,
+ Tk_Width(tkwin) - 2 * scrollPtr->highlightWidth,
+ Tk_Height(tkwin) - 2 * scrollPtr->highlightWidth,
+ scrollPtr->borderWidth, scrollPtr->relief);
+
+ if (scrollPtr->tile != NULL) {
+ Blt_SetTileOrigin(tkwin, scrollPtr->tile, 0, 0);
+ Blt_TileRectangle(tkwin, pixmap, scrollPtr->tile, scrollPtr->inset,
+ scrollPtr->inset,
+ (unsigned)(Tk_Width(tkwin) - 2 * scrollPtr->inset),
+ (unsigned)(Tk_Height(tkwin) - 2 * scrollPtr->inset));
+ } else {
+ XFillRectangle(scrollPtr->display, pixmap, scrollPtr->troughGC,
+ scrollPtr->inset, scrollPtr->inset,
+ (unsigned)(Tk_Width(tkwin) - 2 * scrollPtr->inset),
+ (unsigned)(Tk_Height(tkwin) - 2 * scrollPtr->inset));
+ }
+
+ /*
+ * Draw the top or left arrow. The coordinates of the polygon
+ * points probably seem odd, but they were carefully chosen with
+ * respect to X's rules for filling polygons. These point choices
+ * cause the arrows to just fill the narrow dimension of the
+ * scrollbar and be properly centered.
+ */
+ tile = NULL;
+ if (scrollPtr->activeField == TOP_ARROW) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
+ : TK_RELIEF_RAISED;
+ if (scrollPtr->activeTile != NULL) {
+ Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
+ tile = scrollPtr->activeTile;
+ }
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ if (scrollPtr->tile != NULL) {
+ tile = scrollPtr->tile;
+ }
+ }
+ if (scrollPtr->vertical) {
+ points[0].x = scrollPtr->inset - 1;
+ points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1;
+ points[1].x = width + scrollPtr->inset;
+ points[1].y = points[0].y;
+ points[2].x = width / 2 + scrollPtr->inset;
+ points[2].y = scrollPtr->inset - 1;
+ } else {
+ points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1;
+ points[0].y = scrollPtr->inset - 1;
+ points[1].x = scrollPtr->inset;
+ points[1].y = width / 2 + scrollPtr->inset;
+ points[2].x = points[0].x;
+ points[2].y = width + scrollPtr->inset;
+ }
+ if (tile != NULL) {
+ Blt_TilePolygon(tkwin, pixmap, tile, points, 3);
+ Tk_Draw3DPolygon(tkwin, pixmap, border, points, 3,
+ scrollPtr->borderWidth, relief);
+ } else {
+ Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
+ scrollPtr->borderWidth, relief);
+ }
+
+ /*
+ * Display the bottom or right arrow.
+ */
+ tile = NULL;
+ if (scrollPtr->activeField == BOTTOM_ARROW) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == BOTTOM_ARROW
+ ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
+ if (scrollPtr->activeTile != NULL) {
+ Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
+ tile = scrollPtr->activeTile;
+ }
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ if (scrollPtr->tile != NULL) {
+ tile = scrollPtr->tile;
+ }
+ }
+ if (scrollPtr->vertical) {
+ points[0].x = scrollPtr->inset;
+ points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
+ - scrollPtr->inset + 1;
+ points[1].x = width / 2 + scrollPtr->inset;
+ points[1].y = Tk_Height(tkwin) - scrollPtr->inset;
+ points[2].x = width + scrollPtr->inset;
+ points[2].y = points[0].y;
+ } else {
+ points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
+ - scrollPtr->inset + 1;
+ points[0].y = scrollPtr->inset - 1;
+ points[1].x = points[0].x;
+ points[1].y = width + scrollPtr->inset;
+ points[2].x = Tk_Width(tkwin) - scrollPtr->inset;
+ points[2].y = width / 2 + scrollPtr->inset;
+ }
+ if (tile != NULL) {
+ Blt_TilePolygon(tkwin, pixmap, tile, points, 3);
+ Tk_Draw3DPolygon(tkwin, pixmap, border, points, 3,
+ scrollPtr->borderWidth, relief);
+ } else {
+ Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
+ scrollPtr->borderWidth, relief);
+ }
+
+ /*
+ * Display the slider.
+ */
+ tile = NULL;
+ if (scrollPtr->activeField == SLIDER) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
+ : TK_RELIEF_RAISED;
+ if (scrollPtr->activeTile != NULL) {
+ Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
+ tile = scrollPtr->activeTile;
+ }
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ if (scrollPtr->tile != NULL) {
+ tile = scrollPtr->tile;
+ }
+ }
+ if (scrollPtr->vertical) {
+ if (tile != NULL) {
+ Blt_TileRectangle(tkwin, pixmap, tile, scrollPtr->inset,
+ scrollPtr->sliderFirst, width - 1,
+ scrollPtr->sliderLast - scrollPtr->sliderFirst - 1);
+ Tk_Draw3DRectangle(tkwin, pixmap, border,
+ scrollPtr->inset, scrollPtr->sliderFirst,
+ width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
+ scrollPtr->borderWidth, relief);
+ } else {
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ scrollPtr->inset, scrollPtr->sliderFirst,
+ width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
+ elementBorderWidth, relief);
+ }
+ } else {
+ if (tile != NULL) {
+ Blt_TileRectangle(tkwin, pixmap, tile, scrollPtr->sliderFirst,
+ scrollPtr->inset,
+ scrollPtr->sliderLast - scrollPtr->sliderFirst - 1, width - 1);
+ Tk_Draw3DRectangle(tkwin, pixmap, border,
+ scrollPtr->sliderFirst, scrollPtr->inset,
+ scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
+ scrollPtr->borderWidth, relief);
+ } else {
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ scrollPtr->sliderFirst, scrollPtr->inset,
+ scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
+ scrollPtr->borderWidth, relief);
+ }
+ }
+
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
+ scrollPtr->copyGC, 0, 0, (unsigned)Tk_Width(tkwin),
+ (unsigned)Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(scrollPtr->display, pixmap);
+
+ done:
+ scrollPtr->flags &= ~REDRAW_PENDING;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on scrollbars.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ScrollbarEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ Scrollbar *scrollPtr = clientData;
+
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ EventuallyRedraw(scrollPtr);
+ } else if (eventPtr->type == DestroyNotify) {
+ if (scrollPtr->tkwin != NULL) {
+ scrollPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(scrollPtr->interp,scrollPtr->widgetCmd);
+ }
+ if (scrollPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayScrollbar, (ClientData)scrollPtr);
+ }
+ Tcl_EventuallyFree((ClientData)scrollPtr,
+ (Tcl_FreeProc *)DestroyScrollbar);
+ } else if (eventPtr->type == ConfigureNotify) {
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ } else if (eventPtr->type == FocusIn) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ scrollPtr->flags |= GOT_FOCUS;
+ if (scrollPtr->highlightWidth > 0) {
+ EventuallyRedraw(scrollPtr);
+ }
+ }
+ } else if (eventPtr->type == FocusOut) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ scrollPtr->flags &= ~GOT_FOCUS;
+ if (scrollPtr->highlightWidth > 0) {
+ EventuallyRedraw(scrollPtr);
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollbarCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ScrollbarCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Scrollbar *scrollPtr = clientData;
+ Tk_Window tkwin = scrollPtr->tkwin;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (tkwin != NULL) {
+#ifdef ITCL_NAMESPACES
+ Itk_SetWidgetCommand(scrollPtr->tkwin, (Tcl_Command) NULL);
+#endif
+ scrollPtr->tkwin = NULL;
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeScrollbarGeometry --
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeScrollbarGeometry(scrollPtr)
+ register Scrollbar *scrollPtr; /* Scrollbar whose geometry may
+ * have changed. */
+{
+ int width, fieldLength;
+
+ if (scrollPtr->highlightWidth < 0) {
+ scrollPtr->highlightWidth = 0;
+ }
+ scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
+ width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
+ : Tk_Height(scrollPtr->tkwin);
+ scrollPtr->arrowLength = width - 2 * scrollPtr->inset + 1;
+ fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
+ : Tk_Width(scrollPtr->tkwin))
+ - 2 * (scrollPtr->arrowLength + scrollPtr->inset);
+ if (fieldLength < 0) {
+ fieldLength = 0;
+ }
+ scrollPtr->sliderFirst = fieldLength * scrollPtr->firstFraction;
+ scrollPtr->sliderLast = 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 - 2 * scrollPtr->borderWidth)) {
+ scrollPtr->sliderFirst = fieldLength - 2 * scrollPtr->borderWidth;
+ }
+ if (scrollPtr->sliderFirst < 0) {
+ scrollPtr->sliderFirst = 0;
+ }
+ if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
+ + MIN_SLIDER_LENGTH)) {
+ scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
+ }
+ if (scrollPtr->sliderLast > fieldLength) {
+ scrollPtr->sliderLast = fieldLength;
+ }
+ scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
+ scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
+
+ /*
+ * 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->inset,
+ 2 * (scrollPtr->arrowLength + scrollPtr->borderWidth
+ + scrollPtr->inset));
+ } else {
+ Tk_GeometryRequest(scrollPtr->tkwin,
+ 2 * (scrollPtr->arrowLength + scrollPtr->borderWidth
+ + scrollPtr->inset), scrollPtr->width + 2 * scrollPtr->inset);
+ }
+ Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarPosition --
+ *
+ * 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.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+ScrollbarPosition(scrollPtr, x, y)
+ register Scrollbar *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
+ * DisplayScrollbar. 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;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Arrange for one or more of the fields of a scrollbar
+ * to be redrawn.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EventuallyRedraw(scrollPtr)
+ register Scrollbar *scrollPtr; /* Information about widget. */
+{
+ if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
+ return;
+ }
+ if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
+ Tcl_DoWhenIdle(DisplayScrollbar, (ClientData)scrollPtr);
+ scrollPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+int
+Blt_ScrollbarInit(interp)
+ Tcl_Interp *interp;
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+#if HAVE_NAMESPACES
+ "scrollbar", ScrollbarCmd,
+#else
+ "tilescrollbar", ScrollbarCmd,
+#endif /* HAVE_NAMESPACES */
+ };
+
+ if (Blt_InitCmd(interp, "blt::tile", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#endif /* NO_TILESCROLLBAR */
diff --git a/blt/win/README b/blt/win/README
new file mode 100644
index 00000000000..0073ba58e7d
--- /dev/null
+++ b/blt/win/README
@@ -0,0 +1,122 @@
+
+This file describes how to build BLT under Window 95/98/NT.
+
+It's not necessary to compile BLT for Windows 95/98/NT. Binary versions
+are available on ftp.tcltk.com/pub/blt.
+
+ ftp://ftp.tcltk.com/pub/blt/blt2.4v-for-8.0.exe
+ -or-
+ ftp://ftp.tcltk.com/pub/blt/blt2.4v-for-8.1.exe
+ -or-
+ ftp://ftp.tcltk.com/pub/blt/blt2.4v-for-8.2.exe
+ -or-
+ ftp://ftp.tcltk.com/pub/blt/blt2.4v-for-8.3.exe
+
+They will dynamically load into a vanilla wish80.exe, wish81.exe,
+wish82.exe, or wish83.exe by invoking
+
+ package require BLT
+
+from within your script.
+
+Most Windows software is designed to be delivered as a self-installing
+binary executable, so most Windows installations don't have the tools
+needed to build and install BLT from the source code. It's a lot more
+difficult to build BLT under Windows than under Unix. So hold your hat.
+
+1. What's needed?
+
+First build and install the Tcl/Tk sources. They should reside in the
+same directory tree as the BLT sources. The Tcl/Tk sources are
+required for Windows, since they contain X11 header files that aren't
+normally installed.
+
+ ______________|______________
+ | | | |
+ blt2.4 tcl8.3.1 tk8.3.2 jpeg-6b
+
+
+I've built this version of BLT with Tcl/Tk versions 8.0.5, 8.1.1, and
+8.2.3, and 8.3.2.
+
+By default, JPEG support is added into BLT. It uses the jpeg-6b
+libraries from ftp.uu.net. You can pick up the sources from
+
+ ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
+
+Note: JPEG support is optional. See below how to disable it.
+
+The BLT Makefiles work with GNU make, not Microsoft's nmake. This is
+what I use to build BLT. But you'll need the Cygnus cygwin32-b20.1
+tool suite. You can pick this up from
+
+ http://go.cygnus.com/pub/sourceware.cygnus.com/pub/cygwin/latest/full.exe
+
+Alternatively, I've included a sample nmakefile called "blt.mak". It
+compiles BLT with using the Microsoft Visual C++ compiler. The catch
+is that that to install BLT, you must use the ./win/install.tcl
+script. The limitations of Windows shell tools make it too painful to
+install with nmake.
+
+I've built BLT under Windows 95/NT 4.0 using the
+
+ Cygnus egcs-1.1.[1-2]
+ Microsoft Visual C++ 5.0/6.0
+
+C compilers. I don't know whether or not BLT will build with other
+compilers.
+
+Note: Use the same compiler for both Tcl/Tk and BLT. That is, if you
+ compiled Tcl/Tk with the Cygnus mingw32 compiler, compile BLT
+ with it too. This will make your life a lot easier.
+
+The BLT24.dll you build should dynamically load into "wish",
+regardless of the compiler that was used to build "wish".
+
+2. What files do you need to edit?
+
+You should only need to edit ./win/makedefs. The following macros may
+need to be reset.
+
+ v1 Tcl/Tk version.
+ v2 Version number without dots.
+ v3 Suffix of Tcl/Tk directories
+ prefix Location of installed Tcl/Tk binaries.
+ TOOLS32 Location of MS C compiler and tools.
+
+ EXTRA_DEFINES By default set to -DHAVE_JPEGLIB_H.
+ JPEGLIB By default set $(JPEGDIR)/libjpeg.lib.
+
+If you don't want JPEG support included, set both the EXTRA_DEFINES
+and JPEGLIB macros to nothing.
+
+ JPEGLIB =
+ EXTRA_DEFINES =
+
+3. Compiling and installing
+
+Using "make"
+
+ From the top directory run make. Use the Makefile for your compiler.
+
+ make -f Makefile.vc Microsoft Visual C++ compiler
+ -or-
+ make -f Makefile.gnu Cygnus egcs-1.1.2 compiler
+
+ To install, run make again with the "install" target.
+
+ make -f Makefile.vc install
+ -or-
+ make -f Makefile.gnu install
+
+Using "nmake"
+
+ From the top directory run nmake using the "blt.mak" nmakefile.
+ It assumes that you are using the Microsoft Visual C++ compiler.
+
+ nmake -f blt.mak
+
+ To install, run the ./win/install.tcl script from wish.
+
+ wish82.exe ./win/install.tcl
+
diff --git a/blt/win/install.tcl b/blt/win/install.tcl
new file mode 100644
index 00000000000..0a94ecec9f8
--- /dev/null
+++ b/blt/win/install.tcl
@@ -0,0 +1,739 @@
+#
+# Script for installation of BLT under Windows
+#
+
+namespace eval Installer {
+ variable commandList {}
+ variable cmdLog {}
+ variable component
+ array set component {
+ binaries 1
+ headers 1
+ html 1
+ scripts 1
+ demos 1
+ }
+ variable totalBytes 0
+ variable totalFiles 0
+ variable panel 0
+ variable panelList {
+ Welcome Directory Components Ready Finish
+ }
+}
+
+proc Installer::DoInstall { package version } {
+ global prefix srcdir
+
+ regsub {\.} $version {} v2
+ variable scriptdir
+ set scriptdir $prefix/lib/blt${version}
+ variable component
+ global tcl_platform
+ if { $component(binaries) } {
+ if { $tcl_platform(platform) == "unix" } {
+ Add ${srcdir}/src -perm 0755 \
+ -file bltwish \
+ -file bltsh \
+ -rename "bltwish bltwish${v2}" \
+ -rename "bltsh bltsh${v2}" \
+ $prefix/bin
+ set ext [info sharedlibextension]
+ Add ${srcdir}/src -perm 0755 \
+ -file libBLT${v2}.a \
+ -file libBLTlite${v2}.a \
+ -file shared/libBLT${v2}${ext} \
+ -file shared/libBLTlite${v2}${ext} \
+ $prefix/lib
+ } else {
+ Add ${srcdir}/src -perm 0755 \
+ -file bltwish.exe \
+ -file bltsh.exe \
+ -file BLT${v2}.dll \
+ -file BLTlite${v2}.dll \
+ -rename "bltwish.exe bltwish${v2}.exe" \
+ -rename "bltsh.exe bltsh${v2}.exe" \
+ $prefix/bin
+ Add ${srcdir}/src -perm 0755 \
+ -file BLT${v2}.lib \
+ -file BLTlite${v2}.lib \
+ $prefix/lib
+ }
+ }
+ if { $component(headers) } {
+ Add ${srcdir}/src \
+ -file blt.h \
+ -file bltChain.h \
+ -file bltVector.h \
+ -file bltTree.h \
+ $prefix/include
+ }
+ if { $component(html) } {
+ Add ${srcdir}/html -pattern *.html $scriptdir/html
+ }
+ if { $component(scripts) } {
+ Add ${srcdir} -file README -file PROBLEMS $scriptdir
+ Add ${srcdir}/library \
+ -pattern *.cur \
+ -pattern *.tcl \
+ -pattern *.pro \
+ -file tclIndex \
+ $scriptdir
+ Add ${srcdir}/library/dd_protocols \
+ -pattern *.tcl \
+ -file tclIndex \
+ $scriptdir/dd_protocols
+ }
+ if { $component(demos) } {
+ Add ${srcdir}/demos \
+ -pattern *.tcl \
+ -file htext.txt \
+ $scriptdir/demos
+ Add ${srcdir}/demos/bitmaps -pattern *.xbm $scriptdir/bitmaps
+ Add ${srcdir}/demos/bitmaps/hand -pattern *.xbm $scriptdir/bitmaps/hand
+ Add ${srcdir}/demos/bitmaps/fish -pattern *.xbm $scriptdir/bitmaps/fish
+ Add ${srcdir}/demos/images \
+ -pattern *.gif \
+ -file out.ps \
+ $scriptdir/images
+ Add ${srcdir}/demos/scripts -pattern *.tcl $scriptdir/scripts
+ }
+ Install $package $version
+}
+
+proc Installer::InstallDirectory { dest } {
+ variable commandList
+ lappend commandList [list CheckPath $dest]
+}
+
+proc Installer::Update { src dest size perm } {
+ variable currentBytes
+ variable totalBytes
+
+ .install.text insert end "file copy -force $src $dest\n"
+ if { [catch {file copy -force $src $dest} results] != 0 } {
+ .install.text insert end "Error: $results\n" fail
+ } else {
+ incr currentBytes $size
+ }
+ global tcl_platform
+ if { $tcl_platform(platform) == "unix" } {
+ .install.text insert end "file attributes $dest -permissions $perm\n"
+ if { [catch {file attributes $dest -permissions $perm} results] != 0 } {
+ .install.text insert end "Error: $results\n" fail
+ }
+ }
+ set percent [expr round(double($currentBytes)/$totalBytes * 100.0)]
+ .install.current configure -text "$percent% complete"
+ update
+}
+
+proc Installer::InstallFile { src dest perm } {
+ variable commandList
+ variable totalBytes
+ variable totalFiles
+
+ if { [catch { file size $src } size ] != 0 } {
+ set size 0
+ }
+ lappend commandList [list Update $src $dest $size $perm]
+ incr totalBytes $size
+ incr totalFiles
+}
+
+proc Installer::Add { dir args } {
+ variable commandList
+ variable totalBytes
+
+ if { ![file exists $dir] } {
+ error "can't find directory \"$dir\""
+ }
+ set argc [llength $args]
+ set destDir [lindex $args end]
+ incr argc -2
+ set perm 0644
+
+ InstallDirectory $destDir
+ foreach { option value } [lrange $args 0 $argc] {
+ switch -- $option {
+ "-pattern" {
+ foreach f [lsort [glob $dir/$value]] {
+ InstallFile $f $destDir/[file tail $f] $perm
+ }
+ }
+ "-rename" {
+ set src [lindex $value 0]
+ set dest [lindex $value 1]
+ InstallFile $dir/$src $destDir/$dest $perm
+ }
+ "-perm" {
+ set perm $value
+ }
+ "-file" {
+ InstallFile $dir/$value $destDir/$value $perm
+ }
+ default {
+ error "Unknown option \"$option\""
+ }
+ }
+ }
+}
+
+proc Installer::CheckPath { dest } {
+ set save [pwd]
+ if { [file pathtype $dest] == "absolute" } {
+ if { [string match {[a-zA-Z]:*} $dest] } {
+ cd [string range $dest 0 2]
+ set dest [string range $dest 3 end]
+ } else {
+ cd /
+ set dest [string range $dest 1 end]
+ }
+ }
+ set dirs [file split $dest]
+ foreach d $dirs {
+ if { ![file exists $d] } {
+ .install.text insert end "file mkdir $d\n"
+ if { [catch { file mkdir $d } result] != 0 } {
+ .install.text insert end "Error: $result\n" fail
+ break
+ }
+ }
+ if { ![file isdirectory $d] } {
+ .install.text insert end "Error: Not a directory: \"$d\"" fail
+ break
+ }
+ update
+ cd $d
+ }
+ cd $save
+}
+
+proc Installer::MakePackageIndex { package version file } {
+ global prefix
+ set suffix [info sharedlibextension]
+ regsub {\.} $version {} version_no_dots
+ set libName "${package}${version_no_dots}${suffix}"
+ set libPath [file join ${prefix}/bin $libName]
+ set cmd [list load $libPath $package]
+
+ if { [file exists $file] } {
+ file delete $file
+ }
+ set cmd {
+ set fid [open $file "w"]
+ puts $fid "# Package Index for $package"
+ puts $fid "# generated on [clock format [clock seconds]]"
+ puts $fid ""
+ puts $fid [list package ifneeded $package $version $cmd]
+ close $fid
+ }
+ if { [catch $cmd result] != 0 } {
+ .install.text insert end "Error: $result\n" fail
+ }
+}
+
+proc Installer::SetRegistryKey { package version valueName } {
+ variable scriptdir
+ global tcl_version
+
+ package require registry
+ set key HKEY_LOCAL_MACHINE\\Software\\$package\\$version\\$tcl_version
+ registry set $key $valueName $scriptdir
+}
+
+proc Installer::Install { package version } {
+ variable commandList
+ variable totalBytes
+ variable currentBytes 0
+ variable totalFiles
+ variable scriptdir
+
+ .install.totals configure -text "Files: $totalFiles Size: $totalBytes"
+ foreach cmd $commandList {
+ if { ![winfo exists .install] } {
+ return
+ }
+ if { [catch $cmd result] != 0 } {
+ .install.text insert end "Error: $result\n" fail
+ }
+ update
+ }
+ global tcl_version tcl_platform prefix
+ set name [string tolower $package]
+ MakePackageIndex $package $version $scriptdir/pkgIndex.tcl
+ MakePackageIndex $package $version \
+ $prefix/lib/tcl${tcl_version}/${name}${version}/pkgIndex.tcl
+ if { $tcl_platform(platform) == "windows" } {
+ SetRegistryKey $package $version ${package}_LIBRARY
+ }
+ .install.cancel configure -text "Done"
+}
+
+proc Installer::Next {} {
+ variable panel
+ variable continue
+ variable panelList
+
+ incr panel
+ set max [llength $panelList]
+ if { $panel >= $max } {
+ exit 0
+ }
+ if { ($panel + 1) == $max } {
+ .next configure -text "Finish"
+ .cancel configure -state disabled
+ } else {
+ .next configure -text "Next"
+ }
+ if { $panel > 0 } {
+ .back configure -state normal
+ }
+ set continue 1
+}
+
+proc Installer::Back {} {
+ variable panel
+ variable continue
+
+ incr panel -1
+ if { $panel <= 0 } {
+ .back configure -state disabled
+ set panel 0
+ } else {
+ .back configure -state normal
+ }
+ .next configure -text "Next"
+ .cancel configure -state normal
+ set continue 0
+}
+
+proc Installer::Cancel {} {
+ exit 0
+}
+
+if { $tcl_platform(platform) == "unix" } {
+ font create textFont -family Helvetica -weight normal -size 11
+ font create titleFont -family Helvetica -weight normal -size 14
+} else {
+ font create titleFont -family Arial -weight bold -size 12
+ font create textFont -family Arial -weight normal -size 9
+}
+font create hugeFont -family {Times New Roman} -size 18 -slant italic \
+ -weight bold
+
+proc Installer::MakeLink { widget tag command } {
+ $widget tag configure $tag -foreground blue -underline yes
+ $widget tag bind $tag <ButtonRelease-1> \
+ "$command; $widget tag configure $tag -foreground blue"
+ $widget tag bind $tag <ButtonPress-1> \
+ [list $widget tag configure $tag -foreground red]
+}
+
+proc Installer::Welcome { package version } {
+ global tcl_version
+ if { [winfo exists .panel] } {
+ destroy .panel
+ }
+ text .panel -wrap word -width 10 -height 18 \
+ -relief flat -padx 4 -pady 4 -cursor arrow \
+ -background [. cget -bg]
+ .panel tag configure welcome -font titleFont -justify center \
+ -foreground navyblue
+ .panel tag configure package -font hugeFont -foreground red \
+ -justify center
+ MakeLink .panel next "Installer::Next"
+ MakeLink .panel cancel "Installer::Cancel"
+ .panel insert end "Welcome!\n" welcome
+ .panel insert end "\n"
+ .panel insert end \
+ "This installs the compiled \n" "" \
+ "${package} ${version}\n" package \
+ "binaries and components from the source directories to " "" \
+ "where Tcl/Tk is currently installed.\n\nThe compiled binaries " "" \
+ "require Tcl/Tk $tcl_version.\n\n"
+ .panel insert end \
+ "Press the " "" \
+ "Next" next \
+ " button to continue. Press the " "" \
+ "Cancel" cancel \
+ " button if you do not wish to install $package at this time."
+ .panel configure -state disabled
+ blt::table . \
+ 0,1 .panel -columnspan 3 -pady 10 -padx { 0 10 } -fill x -anchor n
+ tkwait variable Installer::continue
+}
+
+option add *Hierbox.openCommand {Installer::OpenDir %W "%P" %n}
+
+proc Installer::OpenDir { widget path atnode } {
+ puts path=$path
+ set save [pwd]
+ global tcl_platform
+
+ if { $tcl_platform(platform) == "windows" } {
+ if { $path == "/" } {
+ foreach v [file volumes] {
+ if { ![string match {[A-B]:/} $v] } {
+ .browser.h insert end $v -button yes
+ }
+ }
+ return
+ }
+ set path [string trimleft $path /]
+ }
+ cd $path/
+ foreach dir [lsort [glob -nocomplain */ ]] {
+ set node [$widget insert -at $atnode end $dir]
+ # Does the directory have subdirectories?
+ set subdirs [glob -nocomplain $dir/*/ ]
+ if { $subdirs != "" } {
+ $widget entry configure $node -button yes
+ } else {
+ $widget entry configure $node -button no
+ }
+ }
+ cd $save
+}
+
+proc Installer::CenterPanel { panel } {
+ update idletasks
+ set x [expr ([winfo width .] - [winfo reqwidth $panel]) / 2]
+ set y [expr ([winfo height .] - [winfo reqheight $panel]) / 2]
+ incr x [winfo rootx .]
+ incr y [winfo rooty .]
+ wm geometry $panel +$x+$y
+ wm deiconify $panel
+}
+
+proc Installer::Browse { } {
+ if { [winfo exists .browser] } {
+ raise .browser
+ wm deiconify .browser
+ return
+ }
+ toplevel .browser
+ entry .browser.entry -bg white -borderwidth 2 -relief sunken \
+ -textvariable Installer::selection
+ bind .browser.entry <KeyPress-Return> {
+ .browser.ok flash
+ .browser.ok invoke
+ }
+ blt::hierbox .browser.h -hideroot yes -separator / -height 1i -width 3i \
+ -borderwidth 2 -relief sunken -autocreate yes -trim / \
+ -yscrollcommand { .browser.sbar set } \
+ -selectcommand {
+ set index [.browser.h curselection]
+ set path [lindex [.browser.h get -full $index] 0]
+ if { $tcl_platform(platform) == "windows" } {
+ set path [string trimleft $path /]
+ }
+ puts $path
+ set Installer::selection $path
+ }
+ scrollbar .browser.sbar -command { .browser.h yview }
+ wm protocol .browser WM_DELETE_WINDOW { .browser.cancel invoke }
+ wm transient .browser .
+ frame .browser.sep -height 2 -borderwidth 1 -relief sunken
+ button .browser.ok -text "Continue" -command {
+ set prefix $Installer::selection
+ grab release .browser
+ destroy .browser
+ }
+ button .browser.cancel -text "Cancel" -command {
+ grab release .browser
+ destroy .browser
+ }
+ global tcl_platform
+ global prefix
+ if { $tcl_platform(platform) == "windows" } {
+ set root "C:/"
+ .browser.h open "C:/"
+ .browser.h open "C:/Program Files"
+ if { [file exists $prefix] } {
+ .browser.h see $prefix/
+ }
+ } else {
+ set root /usr/local
+ #.browser.h open $root
+ }
+ set Installer::selection $prefix
+ wm title .browser "Installer: Select Install Directory"
+ blt::table .browser \
+ 0,0 .browser.entry -fill x -columnspan 2 -padx 4 -pady 4 \
+ 1,0 .browser.h -fill both -columnspan 2 -padx 4 -pady { 0 4 } \
+ 1,2 .browser.sbar -fill y \
+ 2,0 .browser.sep -fill x -padx 10 -cspan 3 -pady { 4 0 } \
+ 3,0 .browser.ok -width .75i -padx 4 -pady 4 \
+ 3,1 .browser.cancel -width .75i -padx 4 -pady 4
+ blt::table configure .browser c2 r0 r2 r3 -resize none
+ wm withdraw .browser
+ after idle {
+ Installer::CenterPanel .browser
+ grab set .browser
+ }
+}
+
+proc Installer::Directory { package version } {
+ global tcl_version
+ if { [winfo exists .panel] } {
+ destroy .panel
+ }
+ frame .panel
+ text .panel.text -wrap word -width 10 \
+ -height 10 -borderwidth 0 -padx 4 -pady 4 -cursor arrow \
+ -background [.panel cget -background]
+ .panel.text tag configure title -font titleFont -justify center \
+ -foreground navyblue
+ .panel.text insert end "Select Destination Directory\n" title
+ .panel.text insert end "\n\n"
+ .panel.text insert end "Please select the directory where Tcl/Tk \
+$tcl_version is installed. This is also where $package $version will be \
+installed.\n"
+ .panel.text configure -state disabled
+ frame .panel.frame -relief groove -borderwidth 2
+ label .panel.frame.label -textvariable ::prefix
+ button .panel.frame.button -text "Browse..." -command Installer::Browse
+ blt::table .panel.frame \
+ 0,0 .panel.frame.label -padx 4 -pady 4 -anchor w \
+ 0,1 .panel.frame.button -padx 4 -pady 4 -anchor e
+ blt::table .panel \
+ 0,0 .panel.text -padx 4 -pady 4 -fill both \
+ 1,0 .panel.frame -padx 4 -fill x
+ blt::table . \
+ 0,1 .panel -columnspan 3 -pady 10 -padx { 0 10 } -fill both
+ tkwait variable Installer::continue
+}
+
+proc Installer::Components { package version } {
+ global tcl_version
+ if { [winfo exists .panel] } {
+ destroy .panel
+ }
+ regsub {\.} $version {} v2
+ frame .panel
+ text .panel.text -wrap word -width 10 \
+ -height 8 -borderwidth 0 -padx 4 -pady 4 -cursor arrow \
+ -background [.panel cget -background]
+ .panel.text tag configure title -font titleFont -justify center \
+ -foreground navyblue
+ .panel.text insert end "Select Components\n" title
+ .panel.text insert end "\n\n"
+ .panel.text insert end "Please select the components you wish to install. \
+You should install all components.\n"
+ .panel.text configure -state disabled
+ frame .panel.frame -relief groove -borderwidth 2
+ variable component
+ global tcl_platform
+ if { $tcl_platform(platform) == "unix" } {
+ set ext [info sharedlibextension]
+ set sharedlib lib${package}${v2}${ext}
+ set lib lib${package}${v2}.a
+ set exe ""
+ } else {
+ set sharedlib ${package}${v2}.dll
+ set lib ${package}${v2}.lib
+ set exe ".exe"
+ }
+ checkbutton .panel.frame.binaries \
+ -text "bltwish${exe} and Shared Library" \
+ -variable Installer::component(binaries)
+ checkbutton .panel.frame.scripts \
+ -text "Script Library" \
+ -variable Installer::component(scripts)
+ checkbutton .panel.frame.headers \
+ -text "Include Files and Static Library" \
+ -variable Installer::component(headers)
+ checkbutton .panel.frame.html -text "HTML Manual Pages" \
+ -variable Installer::component(html)
+ checkbutton .panel.frame.demos -text "Demos" \
+ -variable Installer::component(demos)
+ blt::table .panel.frame \
+ 0,0 .panel.frame.binaries -padx 4 -pady 4 -anchor w \
+ 1,0 .panel.frame.scripts -padx 4 -pady 4 -anchor w \
+ 2,0 .panel.frame.headers -padx 4 -pady 4 -anchor w \
+ 3,0 .panel.frame.html -padx 4 -pady 4 -anchor w \
+ 4,0 .panel.frame.demos -padx 4 -pady 4 -anchor w
+ blt::table .panel \
+ 0,0 .panel.text -padx 4 -pady 4 -fill both \
+ 1,0 .panel.frame -padx 4 -fill both
+ blt::table . \
+ 0,1 .panel -columnspan 3 -pady 10 -padx { 0 10 } -fill both
+ tkwait variable Installer::continue
+}
+
+proc Installer::Ready { package version } {
+ global tcl_version
+ if { [winfo exists .panel] } {
+ destroy .panel
+ }
+ text .panel -wrap word -width 10 -height 18 \
+ -relief flat -padx 4 -pady 4 -cursor arrow \
+ -background [. cget -bg]
+ .panel tag configure welcome -font titleFont -justify center \
+ -foreground navyblue
+ .panel tag configure package -font hugeFont -foreground red \
+ -justify center
+ MakeLink .panel next "Installer::Next"
+ MakeLink .panel cancel "Installer::Cancel"
+ MakeLink .panel back "Installer::Back"
+ .panel insert end "Ready To Install!\n" welcome
+ .panel insert end "\n"
+ .panel insert end "We're now ready to install ${package} ${version} \
+and its components.\n\n"
+ .panel insert end \
+ "Press the " "" \
+ "Next" next \
+ " button to install all selected components.\n\n" ""
+ .panel insert end \
+ "To reselect components, click on the " "" \
+ "Back" back \
+ " button.\n\n"
+ .panel insert end \
+ "Press the " "" \
+ "Cancel" cancel \
+ " button if you do not wish to install $package at this time."
+ .panel configure -state disabled
+ blt::table . \
+ 0,1 .panel -columnspan 3 -pady 10 -padx { 0 10 } -fill x -anchor n
+ tkwait variable Installer::continue
+ if { $Installer::continue } {
+ Results
+ update
+ DoInstall $package $version
+ }
+}
+
+proc Installer::Results { } {
+ if { [winfo exists .install] } {
+ destroy .install
+ }
+ toplevel .install
+ text .install.text -height 10 -width 50 -wrap none -bg white \
+ -yscrollcommand { .install.ybar set } \
+ -xscrollcommand { .install.xbar set }
+ .install.text tag configure fail -foreground red
+ label .install.totals -text "Files: 0 Bytes: 0" -width 50
+ label .install.current -text "Installing:\n" -height 2 -width 50
+ scrollbar .install.ybar -command { .install.text yview }
+ scrollbar .install.xbar -command { .install.text xview } -orient horizontal
+ wm protocol .install WM_DELETE_WINDOW { .install.cancel invoke }
+ wm transient .install .
+ button .install.cancel -text "Cancel" -command {
+ grab release .install
+ destroy .install
+ }
+ blt::table .install \
+ 0,0 .install.totals -anchor w -columnspan 2 \
+ 1,0 .install.current -anchor w -cspan 2 \
+ 2,0 .install.text -fill both \
+ 2,1 .install.ybar -fill y \
+ 3,0 .install.xbar -fill x \
+ 4,0 .install.cancel -width .75i -padx 4 -pady 4 -cspan 2
+ blt::table configure .install c1 r0 r1 r3 -resize none
+ wm withdraw .install
+ after idle {
+ Installer::CenterPanel .install
+ grab set .install
+ }
+}
+
+proc Installer::Finish { package version } {
+ global tcl_version
+ if { [winfo exists .panel] } {
+ destroy .panel
+ }
+ text .panel -wrap word -width 10 -height 18 \
+ -relief flat -padx 4 -pady 4 -cursor arrow \
+ -background [. cget -bg]
+ .panel tag configure welcome -font titleFont -justify center \
+ -foreground navyblue
+ .panel tag configure package -font hugeFont -foreground red \
+ -justify center
+ .panel insert end "Installation Completed\n" welcome
+ .panel insert end "\n"
+ .panel insert end "${package} ${version} is now installed.\n\n"
+ MakeLink .panel finish "Installer::Next"
+ .panel insert end \
+ "Press the " "" \
+ "Finish" finish \
+ " button to exit this installation"
+ .panel configure -state disabled
+ blt::table . \
+ 0,1 .panel -columnspan 3 -pady 10 -padx { 0 10 } -fill x -anchor n
+ tkwait variable Installer::continue
+}
+
+
+set prefix [lindex $argv 2]
+set srcdir [lindex $argv 1]
+set version [lindex $argv 0]
+
+set version 2.4
+set package BLT
+regsub {\.} $version {} v2
+set ext [info sharedlibextension]
+
+if { $tcl_platform(platform) == "unix" } {
+ set ext [info sharedlibextension]
+ set sharedlib shared/lib${package}${v2}${ext}
+ set prefix "/usr/local/blt"
+} else {
+ set sharedlib ${package}${v2}.dll
+ set prefix "C:/Program Files/Tcl"
+}
+if { [file exists ./src/$sharedlib] } {
+ load ./src/$sharedlib $package
+} else {
+ error "Can't find library \"$sharedlib\" to load"
+}
+set blt_library $srcdir/library
+
+image create photo openFolder -format gif -data {
+R0lGODlhEAANAPIAAAAAAH9/f7+/v///////AAAAAAAAAAAAACH+JEZpbGUgd3JpdHRlbiBi
+eSBBZG9iZSBQaG90b3Nob3CoIDUuMAAsAAAAABAADQAAAzk4Gsz6cIQ44xqCZCGbk4MmclAA
+gNs4ml7rEaxVAkKc3gTAnBO+sbyQT6M7gVQpk9HlAhgHzqhUmgAAOw==
+}
+image create photo closeFolder -format gif -data {
+R0lGODlhEAANAPIAAAAAAH9/f7+/v///AP///wAAAAAAAAAAACH+JEZpbGUgd3JpdHRlbiBi
+eSBBZG9iZSBQaG90b3Nob3CoIDUuMAAsAAAAABAADQAAAzNIGsz6kAQxqAjxzcpvc1KWBUDY
+nRQZWmilYi37EmztlrAt43R8mzrO60P8lAiApHK5TAAAOw==
+}
+image create photo blt -file ${srcdir}/demos/images/blt98.gif
+option add *Text.font textFont
+option add *HighlightThickness 0
+option add *Hierbox.icons "closeFolder openFolder"
+option add *Hierbox.button yes
+
+set color \#accaff
+option add *Frame.background $color
+option add *Toplevel.background $color
+#option add *Button.background $color
+option add *Checkbutton.background $color
+option add *Label.background $color
+option add *Text.background $color
+. configure -bg $color
+wm title . "$package $version Installer"
+label .image -image blt -borderwidth 2 -relief groove
+button .back -text "Back" -state disabled -command Installer::Back -underline 0
+button .next -text "Next" -command Installer::Next -underline 0
+button .cancel -text "Cancel" -command Installer::Cancel -underline 0
+frame .sep -height 2 -borderwidth 1 -relief sunken
+blt::table . \
+ 0,0 .image -fill both -padx 10 -pady 10 \
+ 1,0 .sep -fill x -padx 4 -pady 4 -columnspan 4 -padx 5 \
+ 2,1 .back -anchor e -padx 4 -pady 4 \
+ 2,2 .next -anchor w -padx 4 -pady 4 \
+ 2,3 .cancel -padx 20 -pady 4
+
+blt::table configure . .back .next .cancel -width .75i
+blt::table configure . r1 r2 -resize none
+blt::table configure . r3 -height 0.125i
+
+while { 1 } {
+ namespace eval Installer {
+ variable panel
+ set cmd [lindex $panelList $panel]
+ eval [list $cmd $package $version]
+ }
+ update
+}
diff --git a/blt/win/makedefs b/blt/win/makedefs
new file mode 100644
index 00000000000..5c272fa92b6
--- /dev/null
+++ b/blt/win/makedefs
@@ -0,0 +1,52 @@
+DEBUG=0
+SHARED=1
+
+v1 = 8.3
+v2 = 83
+v3 = 8.3.2
+
+#v1 = 8.2
+#v2 = 82
+#v3 = 8.2.3
+
+#v1 = 8.1
+#v2 = 81
+#v3 = 8.1.1
+
+#v1 = 8.0
+#v2 = 80
+#v3 = 8.0.5
+
+#Use Independent JPEG Group (IJG) library or Intel JPEG Library (IJL)
+# 0 = None.
+# 1 = IJG
+# 2 = IJL
+WITH_JPEG=2
+
+# ------------------------------------------------------------------------
+# You shouldn't need to edit anything beyond this point
+# ------------------------------------------------------------------------
+
+BLT_MAJOR_VERSION = 2
+BLT_MINOR_VERSION = 4
+BLT_VERSION = 2.4
+
+prefix = C:/Program\ Files/Tcl
+exec_prefix = $(prefix)
+includedir = $(prefix)/include
+bindir = $(prefix)/bin
+libdir = $(prefix)/lib
+scriptdir = $(libdir)/blt$(BLT_VERSION)
+BLT_LIBRARY = $(libdir)/blt$(BLT_VERSION)
+TCLLIBPATH = $(libdir)/tcl$(v1)
+
+
+AUX_LIBS =
+SHLIB_SUFFIX = .dll
+
+INSTALL = install -m 0755
+INSTALL_DATA = install -m 0444
+RANLIB = :
+SHELL = bash.exe
+RM = rm -f
+